1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/base/json_stringifier.h"
17
18 #include "ecmascript/global_dictionary-inl.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_api/js_api_hashset.h"
21 #include "ecmascript/js_map.h"
22 #include "ecmascript/js_primitive_ref.h"
23 #include "ecmascript/js_set.h"
24 #include "ecmascript/object_fast_operator-inl.h"
25 #include "ecmascript/shared_objects/js_shared_map.h"
26 #include "ecmascript/tagged_hash_array.h"
27
28 namespace panda::ecmascript::base {
29 constexpr int GAP_MAX_LEN = 10;
30 using TransformType = base::JsonHelper::TransformType;
31
Stringify(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer,const JSHandle<JSTaggedValue> & gap)32 JSHandle<JSTaggedValue> JsonStringifier::Stringify(const JSHandle<JSTaggedValue> &value,
33 const JSHandle<JSTaggedValue> &replacer,
34 const JSHandle<JSTaggedValue> &gap)
35 {
36 factory_ = thread_->GetEcmaVM()->GetFactory();
37 handleValue_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
38 handleKey_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
39 // Let isArray be IsArray(replacer).
40 bool isArray = replacer->IsArray(thread_);
41 // ReturnIfAbrupt(isArray).
42 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
43 // If isArray is true, then
44 if (isArray) {
45 uint32_t len = 0;
46 if (replacer->IsJSArray()) {
47 // FastPath
48 JSHandle<JSArray> arr(replacer);
49 len = arr->GetArrayLength();
50 } else if (replacer->IsJSSharedArray()) {
51 JSHandle<JSSharedArray> arr(replacer);
52 len = arr->GetArrayLength();
53 } else {
54 // Let len be ToLength(Get(replacer, "length")).
55 JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
56 JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread_, replacer, lengthKey).GetValue();
57 // ReturnIfAbrupt(len).
58 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
59 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenResult);
60 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
61 len = lenNumber.ToUint32();
62 }
63 if (len > 0) {
64 JSMutableHandle<JSTaggedValue> propHandle(thread_, JSTaggedValue::Undefined());
65 // Repeat while k<len.
66 for (uint32_t i = 0; i < len; i++) {
67 // a. Let v be Get(replacer, ToString(k)).
68 JSTaggedValue prop = ObjectFastOperator::FastGetPropertyByIndex(thread_, replacer.GetTaggedValue(), i);
69 // b. ReturnIfAbrupt(v).
70 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
71 /*
72 * c. Let item be undefined.
73 * d. If Type(v) is String, let item be v.
74 * e. Else if Type(v) is Number, let item be ToString(v).
75 * f. Else if Type(v) is Object, then
76 * i. If v has a [[StringData]] or [[NumberData]] internal slot, let item be ToString(v).
77 * ii. ReturnIfAbrupt(item).
78 * g. If item is not undefined and item is not currently an element of PropertyList, then
79 * i. Append item to the end of PropertyList.
80 * h. Let k be k+1.
81 */
82 propHandle.Update(prop);
83 if (prop.IsNumber() || prop.IsString()) {
84 AddDeduplicateProp(propHandle);
85 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
86 } else if (prop.IsJSPrimitiveRef()) {
87 JSTaggedValue primitive = JSPrimitiveRef::Cast(prop.GetTaggedObject())->GetValue();
88 if (primitive.IsNumber() || primitive.IsString()) {
89 AddDeduplicateProp(propHandle);
90 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
91 }
92 }
93 }
94 }
95 }
96
97 // If Type(space) is Object, then
98 if (gap->IsJSPrimitiveRef()) {
99 JSTaggedValue primitive = JSPrimitiveRef::Cast(gap->GetTaggedObject())->GetValue();
100 // a. If space has a [[NumberData]] internal slot, then
101 if (primitive.IsNumber()) {
102 // i. Let space be ToNumber(space).
103 JSTaggedNumber num = JSTaggedValue::ToNumber(thread_, gap);
104 // ii. ReturnIfAbrupt(space).
105 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
106 CalculateNumberGap(num);
107 } else if (primitive.IsString()) {
108 // b. Else if space has a [[StringData]] internal slot, then
109 // i. Let space be ToString(space).
110 auto str = JSTaggedValue::ToString(thread_, gap);
111 // ii. ReturnIfAbrupt(space).
112 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
113 CalculateStringGap(JSHandle<EcmaString>(thread_, str.GetTaggedValue()));
114 }
115 } else if (gap->IsNumber()) {
116 // If Type(space) is Number
117 CalculateNumberGap(gap.GetTaggedValue());
118 } else if (gap->IsString()) {
119 // Else if Type(space) is String
120 CalculateStringGap(JSHandle<EcmaString>::Cast(gap));
121 }
122
123 JSHandle<JSTaggedValue> key(factory_->GetEmptyString());
124 JSTaggedValue serializeValue = GetSerializeValue(value, key, value, replacer);
125 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
126 handleValue_.Update(serializeValue);
127 JSTaggedValue result = SerializeJSONProperty(handleValue_, replacer);
128 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
129 if (!result.IsUndefined()) {
130 return JSHandle<JSTaggedValue>(
131 factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(result_.c_str()), result_.size()));
132 }
133 return thread_->GlobalConstants()->GetHandledUndefined();
134 }
135
AddDeduplicateProp(const JSHandle<JSTaggedValue> & property)136 void JsonStringifier::AddDeduplicateProp(const JSHandle<JSTaggedValue> &property)
137 {
138 JSHandle<EcmaString> primString = JSTaggedValue::ToString(thread_, property);
139 RETURN_IF_ABRUPT_COMPLETION(thread_);
140 JSHandle<JSTaggedValue> addVal(thread_, *primString);
141
142 uint32_t propLen = propList_.size();
143 for (uint32_t i = 0; i < propLen; i++) {
144 if (JSTaggedValue::SameValue(propList_[i], addVal)) {
145 return;
146 }
147 }
148 propList_.emplace_back(addVal);
149 }
150
CalculateNumberGap(JSTaggedValue gap)151 bool JsonStringifier::CalculateNumberGap(JSTaggedValue gap)
152 {
153 double value = std::min(gap.GetNumber(), 10.0); // means GAP_MAX_LEN.
154 if (value > 0) {
155 int gapLength = static_cast<int>(value);
156 gap_.append(gapLength, ' ');
157 gap_.append("\0");
158 }
159 return true;
160 }
161
CalculateStringGap(const JSHandle<EcmaString> & primString)162 bool JsonStringifier::CalculateStringGap(const JSHandle<EcmaString> &primString)
163 {
164 CString gapString = ConvertToString(*primString, StringConvertedUsage::LOGICOPERATION);
165 uint32_t gapLen = gapString.length();
166 if (gapLen > 0) {
167 uint32_t gapLength = gapLen;
168 if (gapLen > GAP_MAX_LEN) {
169 if (gapString.at(GAP_MAX_LEN - 1) == static_cast<char>(utf_helper::UTF8_2B_FIRST)) {
170 gapLength = GAP_MAX_LEN + 1;
171 } else {
172 gapLength = GAP_MAX_LEN;
173 }
174 }
175 gap_.append(gapString.c_str(), gapLength);
176 gap_.append("\0");
177 }
178 return true;
179 }
180
GetSerializeValue(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)181 JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle<JSTaggedValue> &object,
182 const JSHandle<JSTaggedValue> &key,
183 const JSHandle<JSTaggedValue> &value,
184 const JSHandle<JSTaggedValue> &replacer)
185 {
186 JSTaggedValue tagValue = value.GetTaggedValue();
187 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
188 // If Type(value) is Object, then
189 if (value->IsECMAObject() || value->IsBigInt()) {
190 // a. Let toJSON be Get(value, "toJSON").
191 JSHandle<JSTaggedValue> toJson = thread_->GlobalConstants()->GetHandledToJsonString();
192 JSHandle<JSTaggedValue> toJsonFun(
193 thread_, ObjectFastOperator::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue()));
194 // b. ReturnIfAbrupt(toJSON).
195 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
196 tagValue = value.GetTaggedValue();
197 // c. If IsCallable(toJSON) is true
198 if (UNLIKELY(toJsonFun->IsCallable())) {
199 // Let value be Call(toJSON, value, «key»).
200 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, toJsonFun, value, undefined, 1);
201 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
202 info->SetCallArg(key.GetTaggedValue());
203 tagValue = JSFunction::Call(info);
204 // ii. ReturnIfAbrupt(value).
205 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
206 }
207 }
208
209 if (UNLIKELY(replacer->IsCallable())) {
210 handleValue_.Update(tagValue);
211 // a. Let value be Call(ReplacerFunction, holder, «key, value»).
212 const uint32_t argsLength = 2; // 2: «key, value»
213 JSHandle<JSTaggedValue> holder = SerializeHolder(object, value);
214 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
215 EcmaRuntimeCallInfo *info =
216 EcmaInterpreter::NewRuntimeCallInfo(thread_, replacer, holder, undefined, argsLength);
217 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
218 info->SetCallArg(key.GetTaggedValue(), handleValue_.GetTaggedValue());
219 tagValue = JSFunction::Call(info);
220 // b. ReturnIfAbrupt(value).
221 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
222 }
223 return tagValue;
224 }
225
SerializeHolder(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & value)226 JSHandle<JSTaggedValue> JsonStringifier::SerializeHolder(const JSHandle<JSTaggedValue> &object,
227 const JSHandle<JSTaggedValue> &value)
228 {
229 if (stack_.size() <= 0) {
230 JSHandle<JSObject> holder = factory_->CreateNullJSObject();
231 JSHandle<JSTaggedValue> holderKey(factory_->GetEmptyString());
232 JSObject::CreateDataPropertyOrThrow(thread_, holder, holderKey, value);
233 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
234 return JSHandle<JSTaggedValue>(holder);
235 }
236 return object;
237 }
238
SerializeJSONProperty(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)239 JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle<JSTaggedValue> &value,
240 const JSHandle<JSTaggedValue> &replacer)
241 {
242 STACK_LIMIT_CHECK(thread_, JSTaggedValue::Exception());
243 JSTaggedValue tagValue = value.GetTaggedValue();
244 if (!tagValue.IsHeapObject()) {
245 JSTaggedType type = tagValue.GetRawData();
246 switch (type) {
247 // If value is false, return "false".
248 case JSTaggedValue::VALUE_FALSE:
249 result_ += "false";
250 return tagValue;
251 // If value is true, return "true".
252 case JSTaggedValue::VALUE_TRUE:
253 result_ += "true";
254 return tagValue;
255 // If value is null, return "null".
256 case JSTaggedValue::VALUE_NULL:
257 result_ += "null";
258 return tagValue;
259 default:
260 // If Type(value) is Number, then
261 if (tagValue.IsNumber()) {
262 // a. If value is finite, return ToString(value).
263 if (std::isfinite(tagValue.GetNumber())) {
264 #if ENABLE_NEXT_OPTIMIZATION
265 ConvertAndAppendToString(result_, *base::NumberHelper::NumberToString(thread_, tagValue));
266 #else
267 result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue));
268 #endif
269 } else {
270 // b. Else, return "null".
271 result_ += "null";
272 }
273 return tagValue;
274 }
275 }
276 } else {
277 JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType();
278 JSHandle<JSTaggedValue> valHandle(thread_, tagValue);
279 switch (jsType) {
280 case JSType::JS_ARRAY:
281 case JSType::JS_SHARED_ARRAY: {
282 SerializeJSArray(valHandle, replacer);
283 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
284 return tagValue;
285 }
286 case JSType::JS_API_LINKED_LIST: {
287 JSHandle listHandle = JSHandle<JSAPILinkedList>(thread_, tagValue);
288 CheckStackPushSameValue(valHandle);
289 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
290 valHandle = JSHandle<JSTaggedValue>(thread_, JSAPILinkedList::ConvertToArray(thread_, listHandle));
291 SerializeJSONObject(valHandle, replacer);
292 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
293 return tagValue;
294 }
295 case JSType::JS_API_LIST: {
296 JSHandle listHandle = JSHandle<JSAPIList>(thread_, tagValue);
297 CheckStackPushSameValue(valHandle);
298 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
299 valHandle = JSHandle<JSTaggedValue>(thread_, JSAPIList::ConvertToArray(thread_, listHandle));
300 SerializeJSONObject(valHandle, replacer);
301 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
302 return tagValue;
303 }
304 // If Type(value) is String, return QuoteJSONString(value).
305 case JSType::LINE_STRING:
306 case JSType::CONSTANT_STRING:
307 case JSType::TREE_STRING:
308 case JSType::SLICED_STRING: {
309 JSHandle<EcmaString> strHandle = JSHandle<EcmaString>(valHandle);
310 auto string = JSHandle<EcmaString>(thread_,
311 EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
312 #if ENABLE_NEXT_OPTIMIZATION
313 ConvertQuotedAndAppendToString(result_, *string, StringConvertedUsage::LOGICOPERATION);
314 #else
315 CString str = ConvertToString(*string, StringConvertedUsage::LOGICOPERATION);
316 JsonHelper::AppendValueToQuotedString(str, result_);
317 #endif
318 return tagValue;
319 }
320 case JSType::JS_PRIMITIVE_REF: {
321 SerializePrimitiveRef(valHandle);
322 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, JSTaggedValue::Exception());
323 return tagValue;
324 }
325 case JSType::SYMBOL:
326 return JSTaggedValue::Undefined();
327 case JSType::BIGINT: {
328 if (transformType_ == TransformType::NORMAL) {
329 THROW_TYPE_ERROR_AND_RETURN(thread_, "cannot serialize a BigInt", JSTaggedValue::Exception());
330 } else {
331 #if ENABLE_NEXT_OPTIMIZATION
332 BigInt::AppendToCString(result_, BigInt::Cast(valHandle->GetTaggedObject()));
333 #else
334 JSHandle<BigInt> thisBigint(thread_, valHandle.GetTaggedValue());
335 auto bigIntStr = BigInt::ToString(thread_, thisBigint);
336 result_ += ConvertToString(*bigIntStr);
337 #endif
338 return tagValue;
339 }
340 }
341 case JSType::JS_NATIVE_POINTER: {
342 result_ += "{}";
343 return tagValue;
344 }
345 case JSType::JS_SHARED_MAP: {
346 if (transformType_ == TransformType::SENDABLE) {
347 CheckStackPushSameValue(valHandle);
348 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
349 SerializeJSONSharedMap(valHandle, replacer);
350 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
351 return tagValue;
352 }
353 [[fallthrough]];
354 }
355 case JSType::JS_MAP: {
356 if (transformType_ == TransformType::SENDABLE) {
357 CheckStackPushSameValue(valHandle);
358 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
359 SerializeJSONMap(valHandle, replacer);
360 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
361 return tagValue;
362 }
363 [[fallthrough]];
364 }
365 case JSType::JS_SET: {
366 if (transformType_ == TransformType::SENDABLE) {
367 CheckStackPushSameValue(valHandle);
368 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
369 SerializeJSONSet(valHandle, replacer);
370 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
371 return tagValue;
372 }
373 [[fallthrough]];
374 }
375 case JSType::JS_SHARED_SET: {
376 if (transformType_ == TransformType::SENDABLE) {
377 CheckStackPushSameValue(valHandle);
378 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
379 SerializeJSONSharedSet(valHandle, replacer);
380 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
381 return tagValue;
382 }
383 [[fallthrough]];
384 }
385 case JSType::JS_API_HASH_MAP: {
386 if (transformType_ == TransformType::SENDABLE) {
387 CheckStackPushSameValue(valHandle);
388 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
389 SerializeJSONHashMap(valHandle, replacer);
390 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
391 return tagValue;
392 }
393 [[fallthrough]];
394 }
395 case JSType::JS_API_HASH_SET: {
396 if (transformType_ == TransformType::SENDABLE) {
397 CheckStackPushSameValue(valHandle);
398 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
399 SerializeJSONHashSet(valHandle, replacer);
400 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
401 return tagValue;
402 }
403 [[fallthrough]];
404 }
405 default: {
406 if (!tagValue.IsCallable()) {
407 JSHClass *jsHclass = tagValue.GetTaggedObject()->GetClass();
408 if (UNLIKELY(jsHclass->IsJSProxy() &&
409 JSProxy::Cast(tagValue.GetTaggedObject())->IsArray(thread_))) {
410 SerializeJSProxy(valHandle, replacer);
411 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
412 } else {
413 CheckStackPushSameValue(valHandle);
414 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
415 SerializeJSONObject(valHandle, replacer);
416 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
417 }
418 return tagValue;
419 }
420 }
421 }
422 }
423 return JSTaggedValue::Undefined();
424 }
425
426 #if ENABLE_NEXT_OPTIMIZATION
SerializeObjectKey(const JSHandle<JSTaggedValue> & key,bool hasContent)427 void JsonStringifier::SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent)
428 {
429 if (hasContent) {
430 result_ += ",";
431 }
432 if (!gap_.empty()) {
433 result_.append("\n").append(indent_);
434 }
435 if (key->IsString()) {
436 ConvertQuotedAndAppendToString(result_, EcmaString::Cast(key->GetTaggedObject()),
437 StringConvertedUsage::LOGICOPERATION);
438 } else if (key->IsInt()) {
439 result_ += "\"";
440 NumberHelper::AppendIntToString(result_, static_cast<int32_t>(key->GetInt()));
441 result_ += "\"";
442 } else {
443 ConvertQuotedAndAppendToString(result_, *JSTaggedValue::ToString(thread_, key),
444 StringConvertedUsage::LOGICOPERATION);
445 }
446
447 result_ += ":";
448 if (!gap_.empty()) {
449 result_ += " ";
450 }
451 }
452 #else
SerializeObjectKey(const JSHandle<JSTaggedValue> & key,bool hasContent)453 void JsonStringifier::SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent)
454 {
455 CString stepBegin;
456 CString stepEnd;
457 if (hasContent) {
458 result_ += ",";
459 }
460 if (!gap_.empty()) {
461 stepBegin += "\n";
462 stepBegin += indent_;
463 stepEnd += " ";
464 }
465 CString str;
466 if (key->IsString()) {
467 str = ConvertToString(EcmaString::Cast(key->GetTaggedObject()), StringConvertedUsage::LOGICOPERATION);
468 } else if (key->IsInt()) {
469 str = NumberHelper::IntToString(static_cast<int32_t>(key->GetInt()));
470 } else {
471 str = ConvertToString(*JSTaggedValue::ToString(thread_, key), StringConvertedUsage::LOGICOPERATION);
472 }
473 result_ += stepBegin;
474 JsonHelper::AppendValueToQuotedString(str, result_);
475 result_ += ":";
476 result_ += stepEnd;
477 }
478 #endif
479
PushValue(const JSHandle<JSTaggedValue> & value)480 bool JsonStringifier::PushValue(const JSHandle<JSTaggedValue> &value)
481 {
482 uint32_t thisLen = stack_.size();
483
484 for (uint32_t i = 0; i < thisLen; i++) {
485 bool equal = JSTaggedValue::SameValue(stack_[i].GetTaggedValue(), value.GetTaggedValue());
486 if (equal) {
487 return true;
488 }
489 }
490
491 stack_.emplace_back(value);
492 return false;
493 }
494
PopValue()495 void JsonStringifier::PopValue()
496 {
497 stack_.pop_back();
498 }
499
SerializeJSONObject(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)500 bool JsonStringifier::SerializeJSONObject(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer)
501 {
502 #if ENABLE_NEXT_OPTIMIZATION
503 uint32_t preIndentLength = indent_.size();
504 #else
505 CString stepback = indent_;
506 #endif
507 indent_ += gap_;
508
509 result_ += "{";
510 bool hasContent = false;
511
512 ASSERT(!value->IsAccessor());
513 JSHandle<JSObject> obj(value);
514 if (!replacer->IsArray(thread_)) {
515 if (UNLIKELY(value->IsJSProxy() || value->IsTypedArray())) { // serialize proxy and typedArray
516 JSHandle<TaggedArray> propertyArray = JSObject::EnumerableOwnNames(thread_, obj);
517 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
518 uint32_t arrLength = propertyArray->GetLength();
519 for (uint32_t i = 0; i < arrLength; i++) {
520 handleKey_.Update(propertyArray->Get(i));
521 JSHandle<JSTaggedValue> valueHandle = JSTaggedValue::GetProperty(thread_, value, handleKey_).GetValue();
522 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
523 JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, valueHandle, replacer);
524 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
525 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
526 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
527 continue;
528 }
529 handleValue_.Update(serializeValue);
530 SerializeObjectKey(handleKey_, hasContent);
531 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
532 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
533 if (!res.IsUndefined()) {
534 hasContent = true;
535 }
536 }
537 } else {
538 uint32_t numOfKeys = obj->GetNumberOfKeys();
539 uint32_t numOfElements = obj->GetNumberOfElements(thread_);
540 if (numOfElements > 0) {
541 hasContent = JsonStringifier::SerializeElements(obj, replacer, hasContent);
542 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
543 }
544 if (numOfKeys > 0) {
545 hasContent = JsonStringifier::SerializeKeys(obj, replacer, hasContent);
546 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
547 }
548 }
549 } else {
550 uint32_t propLen = propList_.size();
551 for (uint32_t i = 0; i < propLen; i++) {
552 JSTaggedValue tagVal =
553 ObjectFastOperator::FastGetPropertyByValue(thread_, obj.GetTaggedValue(),
554 propList_[i].GetTaggedValue());
555 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
556 handleValue_.Update(tagVal);
557 JSTaggedValue serializeValue = GetSerializeValue(value, propList_[i], handleValue_, replacer);
558 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
559 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
560 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
561 continue;
562 }
563 handleValue_.Update(serializeValue);
564 SerializeObjectKey(propList_[i], hasContent);
565 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
566 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
567 if (!res.IsUndefined()) {
568 hasContent = true;
569 }
570 }
571 }
572 #if ENABLE_NEXT_OPTIMIZATION
573 if (hasContent && gap_.length() != 0) {
574 result_ += "\n";
575 result_ += std::string_view(indent_.data(), preIndentLength);
576 }
577 result_ += "}";
578 PopValue();
579 indent_.resize(preIndentLength); // reset indent_ after recursion
580 #else
581 if (hasContent && gap_.length() != 0) {
582 result_ += "\n";
583 result_ += stepback;
584 }
585 result_ += "}";
586 PopValue();
587 indent_ = stepback;
588 #endif
589 return true;
590 }
591
SerializeJSONSharedMap(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)592 bool JsonStringifier::SerializeJSONSharedMap(const JSHandle<JSTaggedValue> &value,
593 const JSHandle<JSTaggedValue> &replacer)
594 {
595 [[maybe_unused]] ConcurrentApiScope<JSSharedMap> scope(thread_, value);
596 JSHandle<JSSharedMap> sharedMap(value);
597 JSHandle<LinkedHashMap> hashMap(thread_, sharedMap->GetLinkedMap());
598 return SerializeLinkedHashMap(hashMap, replacer);
599 }
600
SerializeJSONSharedSet(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)601 bool JsonStringifier::SerializeJSONSharedSet(const JSHandle<JSTaggedValue> &value,
602 const JSHandle<JSTaggedValue> &replacer)
603 {
604 [[maybe_unused]] ConcurrentApiScope<JSSharedSet> scope(thread_, value);
605 JSHandle<JSSharedSet> sharedSet(value);
606 JSHandle<LinkedHashSet> hashSet(thread_, sharedSet->GetLinkedSet());
607 return SerializeLinkedHashSet(hashSet, replacer);
608 }
609
SerializeJSONMap(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)610 bool JsonStringifier::SerializeJSONMap(const JSHandle<JSTaggedValue> &value,
611 const JSHandle<JSTaggedValue> &replacer)
612 {
613 JSHandle<JSMap> jsMap(value);
614 JSHandle<LinkedHashMap> hashMap(thread_, jsMap->GetLinkedMap());
615 return SerializeLinkedHashMap(hashMap, replacer);
616 }
617
SerializeJSONSet(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)618 bool JsonStringifier::SerializeJSONSet(const JSHandle<JSTaggedValue> &value,
619 const JSHandle<JSTaggedValue> &replacer)
620 {
621 JSHandle<JSSet> jsSet(value);
622 JSHandle<LinkedHashSet> hashSet(thread_, jsSet->GetLinkedSet());
623 return SerializeLinkedHashSet(hashSet, replacer);
624 }
625
SerializeJSONHashMap(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)626 bool JsonStringifier::SerializeJSONHashMap(const JSHandle<JSTaggedValue> &value,
627 const JSHandle<JSTaggedValue> &replacer)
628 {
629 #if ENABLE_NEXT_OPTIMIZATION
630 uint32_t preIndentLength = indent_.size();
631 #else
632 CString stepback = indent_;
633 #endif
634 result_ += "{";
635 JSHandle<JSAPIHashMap> hashMap(value);
636 JSHandle<TaggedHashArray> table(thread_, hashMap->GetTable());
637 uint32_t len = table->GetLength();
638 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
639 JSMutableHandle<TaggedQueue> queue(thread_, factory->NewTaggedQueue(0));
640 JSMutableHandle<TaggedNode> node(thread_, JSTaggedValue::Undefined());
641 JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
642 JSMutableHandle<JSTaggedValue> valueHandle(thread_, JSTaggedValue::Undefined());
643 uint32_t index = 0;
644 bool needRemove = false;
645 while (index < len) {
646 node.Update(TaggedHashArray::GetCurrentNode(thread_, queue, table, index));
647 if (node.GetTaggedValue().IsHole()) {
648 continue;
649 }
650 keyHandle.Update(node->GetKey());
651 valueHandle.Update(node->GetValue());
652 if (valueHandle->IsUndefined()) {
653 continue;
654 }
655 if (UNLIKELY(!keyHandle->IsString())) {
656 result_ += "\"";
657 SerializeJSONProperty(keyHandle, replacer);
658 result_ += "\"";
659 } else {
660 SerializeJSONProperty(keyHandle, replacer);
661 }
662 result_ += ":";
663 SerializeJSONProperty(valueHandle, replacer);
664 result_ += ",";
665 needRemove = true;
666 }
667 if (needRemove) {
668 result_.pop_back();
669 }
670 result_ += "}";
671 PopValue();
672 #if ENABLE_NEXT_OPTIMIZATION
673 indent_.resize(preIndentLength); // reset indent_ after recursion
674 #else
675 indent_ = stepback;
676 #endif
677 return true;
678 }
679
SerializeJSONHashSet(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)680 bool JsonStringifier::SerializeJSONHashSet(const JSHandle<JSTaggedValue> &value,
681 const JSHandle<JSTaggedValue> &replacer)
682 {
683 #if ENABLE_NEXT_OPTIMIZATION
684 uint32_t preIndentLength = indent_.size();
685 #else
686 CString stepback = indent_;
687 #endif
688 result_ += "[";
689 JSHandle<JSAPIHashSet> hashSet(value);
690 JSHandle<TaggedHashArray> table(thread_, hashSet->GetTable());
691 uint32_t len = table->GetLength();
692 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
693 JSMutableHandle<TaggedQueue> queue(thread_, factory->NewTaggedQueue(0));
694 JSMutableHandle<TaggedNode> node(thread_, JSTaggedValue::Undefined());
695 JSMutableHandle<JSTaggedValue> currentKey(thread_, JSTaggedValue::Undefined());
696 uint32_t index = 0;
697 bool needRemove = false;
698 while (index < len) {
699 node.Update(TaggedHashArray::GetCurrentNode(thread_, queue, table, index));
700 if (node.GetTaggedValue().IsHole()) {
701 continue;
702 }
703 currentKey.Update(node->GetKey());
704 JSTaggedValue res = SerializeJSONProperty(currentKey, replacer);
705 if (res.IsUndefined()) {
706 result_ += "null";
707 }
708 result_ += ",";
709 needRemove = true;
710 }
711 if (needRemove) {
712 result_.pop_back();
713 }
714 result_ += "]";
715 PopValue();
716 #if ENABLE_NEXT_OPTIMIZATION
717 indent_.resize(preIndentLength); // reset indent_ after recursion
718 #else
719 indent_ = stepback;
720 #endif
721 return true;
722 }
723
SerializeLinkedHashMap(const JSHandle<LinkedHashMap> & hashMap,const JSHandle<JSTaggedValue> & replacer)724 bool JsonStringifier::SerializeLinkedHashMap(const JSHandle<LinkedHashMap> &hashMap,
725 const JSHandle<JSTaggedValue> &replacer)
726 {
727 #if ENABLE_NEXT_OPTIMIZATION
728 uint32_t preIndentLength = indent_.size();
729 #else
730 CString stepback = indent_;
731 #endif
732 result_ += "{";
733 int index = 0;
734 int totalElements = hashMap->NumberOfElements() + hashMap->NumberOfDeletedElements();
735 JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
736 JSMutableHandle<JSTaggedValue> valHandle(thread_, JSTaggedValue::Undefined());
737 bool needRemove = false;
738 while (index < totalElements) {
739 keyHandle.Update(hashMap->GetKey(index++));
740 valHandle.Update(hashMap->GetValue(index - 1));
741 if (keyHandle->IsHole() || valHandle->IsUndefined()) {
742 continue;
743 }
744 if (UNLIKELY(keyHandle->IsUndefined())) {
745 result_ += "\"undefined\"";
746 } else if (UNLIKELY(!keyHandle->IsString())) {
747 result_ += "\"";
748 SerializeJSONProperty(keyHandle, replacer);
749 result_ += "\"";
750 } else {
751 SerializeJSONProperty(keyHandle, replacer);
752 }
753 result_ += ":";
754 SerializeJSONProperty(valHandle, replacer);
755 result_ += ",";
756 needRemove = true;
757 }
758 if (needRemove) {
759 result_.pop_back();
760 }
761 result_ += "}";
762 PopValue();
763 #if ENABLE_NEXT_OPTIMIZATION
764 indent_.resize(preIndentLength); // reset indent_ after recursion
765 #else
766 indent_ = stepback;
767 #endif
768 return true;
769 }
770
SerializeLinkedHashSet(const JSHandle<LinkedHashSet> & hashSet,const JSHandle<JSTaggedValue> & replacer)771 bool JsonStringifier::SerializeLinkedHashSet(const JSHandle<LinkedHashSet> &hashSet,
772 const JSHandle<JSTaggedValue> &replacer)
773 {
774 #if ENABLE_NEXT_OPTIMIZATION
775 uint32_t preIndentLength = indent_.size();
776 #else
777 CString stepback = indent_;
778 #endif
779 result_ += "[";
780 JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
781 bool needRemove = false;
782
783 int index = 0;
784 int totalElements = hashSet->NumberOfElements() + hashSet->NumberOfDeletedElements();
785 while (index < totalElements) {
786 keyHandle.Update(hashSet->GetKey(index++));
787 if (keyHandle->IsHole()) {
788 continue;
789 }
790 JSTaggedValue res = SerializeJSONProperty(keyHandle, replacer);
791 if (res.IsUndefined()) {
792 result_ += "null";
793 }
794 result_ += ",";
795 needRemove = true;
796 }
797 if (needRemove) {
798 result_.pop_back();
799 }
800 result_ += "]";
801 PopValue();
802 #if ENABLE_NEXT_OPTIMIZATION
803 indent_.resize(preIndentLength); // reset indent_ after recursion
804 #else
805 indent_ = stepback;
806 #endif
807 return true;
808 }
809
810 #if ENABLE_NEXT_OPTIMIZATION
SerializeJSProxy(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & replacer)811 bool JsonStringifier::SerializeJSProxy(const JSHandle<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &replacer)
812 {
813 bool isContain = PushValue(object);
814 if (isContain) {
815 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
816 }
817
818 uint32_t preIndentLength = indent_.size();
819 CString stepBegin;
820 indent_ += gap_;
821
822 if (!gap_.empty()) {
823 stepBegin += "\n";
824 stepBegin += indent_;
825 }
826 result_ += "[";
827 JSHandle<JSProxy> proxy(object);
828 JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
829 JSHandle<JSTaggedValue> lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
830 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
831 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
832 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
833 uint32_t length = lenNumber.ToUint32();
834 for (uint32_t i = 0; i < length; i++) {
835 handleKey_.Update(JSTaggedValue(i));
836 JSHandle<JSTaggedValue> valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
837 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
838 if (i > 0) {
839 result_ += ",";
840 }
841 result_ += stepBegin;
842 JSTaggedValue serializeValue = GetSerializeValue(object, handleKey_, valHandle, replacer);
843 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
844 handleValue_.Update(serializeValue);
845 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
846 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
847 if (res.IsUndefined()) {
848 result_ += "null";
849 }
850 }
851
852 if (length > 0 && !gap_.empty()) {
853 result_ += "\n";
854 result_ += std::string_view(indent_.data(), preIndentLength);
855 }
856 result_ += "]";
857 PopValue();
858 indent_.resize(preIndentLength); // reset indent_ after recursion
859 return true;
860 }
861 #else
SerializeJSProxy(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & replacer)862 bool JsonStringifier::SerializeJSProxy(const JSHandle<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &replacer)
863 {
864 bool isContain = PushValue(object);
865 if (isContain) {
866 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
867 }
868
869 CString stepback = indent_;
870 CString stepBegin;
871 indent_ += gap_;
872
873 if (!gap_.empty()) {
874 stepBegin += "\n";
875 stepBegin += indent_;
876 }
877 result_ += "[";
878 JSHandle<JSProxy> proxy(object);
879 JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
880 JSHandle<JSTaggedValue> lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
881 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
882 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
883 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
884 uint32_t length = lenNumber.ToUint32();
885 for (uint32_t i = 0; i < length; i++) {
886 handleKey_.Update(JSTaggedValue(i));
887 JSHandle<JSTaggedValue> valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
888 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
889 if (i > 0) {
890 result_ += ",";
891 }
892 result_ += stepBegin;
893 JSTaggedValue serializeValue = GetSerializeValue(object, handleKey_, valHandle, replacer);
894 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
895 handleValue_.Update(serializeValue);
896 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
897 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
898 if (res.IsUndefined()) {
899 result_ += "null";
900 }
901 }
902
903 if (length > 0 && !gap_.empty()) {
904 result_ += "\n";
905 result_ += stepback;
906 }
907 result_ += "]";
908 PopValue();
909 indent_ = stepback;
910 return true;
911 }
912 #endif
913
SerializeJSArray(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)914 bool JsonStringifier::SerializeJSArray(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer)
915 {
916 // If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
917 bool isContain = PushValue(value);
918 if (isContain) {
919 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
920 }
921
922 #if ENABLE_NEXT_OPTIMIZATION
923 uint32_t preIndentLength = indent_.size();
924 #else
925 CString stepback = indent_;
926 #endif
927 CString stepBegin;
928 indent_ += gap_;
929
930 if (!gap_.empty()) {
931 stepBegin += "\n";
932 stepBegin += indent_;
933 }
934 result_ += "[";
935 uint32_t len = 0;
936 if (value->IsJSArray()) {
937 JSHandle<JSArray> jsArr(value);
938 len = jsArr->GetArrayLength();
939 } else if (value->IsJSSharedArray()) {
940 JSHandle<JSSharedArray> jsArr(value);
941 len = jsArr->GetArrayLength();
942 }
943 if (len > 0) {
944 for (uint32_t i = 0; i < len; i++) {
945 JSTaggedValue tagVal = ObjectFastOperator::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i);
946 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
947 if (UNLIKELY(tagVal.IsAccessor())) {
948 tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value);
949 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
950 }
951 handleKey_.Update(JSTaggedValue(i));
952 handleValue_.Update(tagVal);
953
954 if (i > 0) {
955 result_ += ",";
956 }
957 result_ += stepBegin;
958 JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, handleValue_, replacer);
959 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
960 handleValue_.Update(serializeValue);
961 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
962 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
963 if (res.IsUndefined()) {
964 result_ += "null";
965 }
966 }
967
968 if (!gap_.empty()) {
969 result_ += "\n";
970 #if ENABLE_NEXT_OPTIMIZATION
971 result_ += std::string_view(indent_.data(), preIndentLength);
972 #else
973 result_ += stepback;
974 #endif
975 }
976 }
977
978 result_ += "]";
979 PopValue();
980 #if ENABLE_NEXT_OPTIMIZATION
981 indent_.resize(preIndentLength); // reset indent_ after recursion
982 #else
983 indent_ = stepback;
984 #endif
985 return true;
986 }
987
988 #if ENABLE_NEXT_OPTIMIZATION
SerializePrimitiveRef(const JSHandle<JSTaggedValue> & primitiveRef)989 void JsonStringifier::SerializePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef)
990 {
991 JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue();
992 if (primitive.IsString()) {
993 auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
994 RETURN_IF_ABRUPT_COMPLETION(thread_);
995 ConvertQuotedAndAppendToString(result_, *priStr, StringConvertedUsage::LOGICOPERATION);
996 } else if (primitive.IsNumber()) {
997 auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef);
998 RETURN_IF_ABRUPT_COMPLETION(thread_);
999 if (std::isfinite(priNum.GetNumber())) {
1000 ConvertAndAppendToString(result_, *base::NumberHelper::NumberToString(thread_, priNum));
1001 } else {
1002 result_ += "null";
1003 }
1004 } else if (primitive.IsBoolean()) {
1005 result_ += primitive.IsTrue() ? "true" : "false";
1006 } else if (primitive.IsBigInt()) {
1007 if (transformType_ == TransformType::NORMAL) {
1008 THROW_TYPE_ERROR(thread_, "cannot serialize a BigInt");
1009 } else {
1010 BigInt::AppendToCString(result_, BigInt::Cast(primitive.GetTaggedObject()));
1011 }
1012 }
1013 }
1014 #else
SerializePrimitiveRef(const JSHandle<JSTaggedValue> & primitiveRef)1015 void JsonStringifier::SerializePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef)
1016 {
1017 JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue();
1018 if (primitive.IsString()) {
1019 auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
1020 RETURN_IF_ABRUPT_COMPLETION(thread_);
1021 CString str = ConvertToString(*priStr, StringConvertedUsage::LOGICOPERATION);
1022 JsonHelper::AppendValueToQuotedString(str, result_);
1023 } else if (primitive.IsNumber()) {
1024 auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef);
1025 RETURN_IF_ABRUPT_COMPLETION(thread_);
1026 if (std::isfinite(priNum.GetNumber())) {
1027 result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, priNum));
1028 } else {
1029 result_ += "null";
1030 }
1031 } else if (primitive.IsBoolean()) {
1032 result_ += primitive.IsTrue() ? "true" : "false";
1033 } else if (primitive.IsBigInt()) {
1034 if (transformType_ == TransformType::NORMAL) {
1035 THROW_TYPE_ERROR(thread_, "cannot serialize a BigInt");
1036 } else {
1037 JSHandle<BigInt> thisBigint(thread_, primitive);
1038 auto bigIntStr = BigInt::ToString(thread_, thisBigint);
1039 result_ += ConvertToString(*bigIntStr);
1040 }
1041 }
1042 }
1043 #endif
1044
SerializeElements(const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & replacer,bool hasContent)1045 bool JsonStringifier::SerializeElements(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
1046 bool hasContent)
1047 {
1048 if (!ElementAccessor::IsDictionaryMode(obj)) {
1049 uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
1050 for (uint32_t i = 0; i < elementsLen; ++i) {
1051 if (!ElementAccessor::Get(thread_, obj, i).IsHole()) {
1052 handleKey_.Update(JSTaggedValue(i));
1053 handleValue_.Update(ElementAccessor::Get(thread_, obj, i));
1054 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1055 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1056 }
1057 }
1058 } else {
1059 JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
1060 JSHandle<NumberDictionary> numberDic(elementsArr);
1061 CVector<JSHandle<JSTaggedValue>> sortArr;
1062 int size = numberDic->Size();
1063 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
1064 JSTaggedValue key = numberDic->GetKey(hashIndex);
1065 if (!key.IsUndefined() && !key.IsHole()) {
1066 PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
1067 if (attr.IsEnumerable()) {
1068 JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
1069 sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
1070 }
1071 }
1072 }
1073 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
1074 for (const auto &entry : sortArr) {
1075 JSTaggedValue entryKey = entry.GetTaggedValue();
1076 handleKey_.Update(entryKey);
1077 int index = numberDic->FindEntry(entryKey);
1078 if (index < 0) {
1079 continue;
1080 }
1081 JSTaggedValue value = numberDic->GetValue(index);
1082 if (UNLIKELY(value.IsAccessor())) {
1083 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1084 JSHandle<JSTaggedValue>(obj));
1085 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1086 }
1087 handleValue_.Update(value);
1088 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1089 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1090 }
1091 }
1092 return hasContent;
1093 }
1094
SerializeKeys(const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & replacer,bool hasContent)1095 bool JsonStringifier::SerializeKeys(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
1096 bool hasContent)
1097 {
1098 JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
1099 if (!propertiesArr->IsDictionaryMode()) {
1100 bool hasChangedToDictionaryMode = false;
1101 JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
1102 JSTaggedValue enumCache = jsHclass->GetEnumCache();
1103 if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
1104 JSHandle<TaggedArray> cache(thread_, enumCache);
1105 uint32_t length = cache->GetLength();
1106 uint32_t dictStart = length;
1107 for (uint32_t i = 0; i < length; i++) {
1108 JSTaggedValue key = cache->Get(i);
1109 if (!key.IsString()) {
1110 continue;
1111 }
1112 handleKey_.Update(key);
1113 JSTaggedValue value;
1114 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
1115 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
1116 PropertyAttributes attr(layoutInfo->GetAttr(index));
1117 ASSERT(static_cast<int>(attr.GetOffset()) == index);
1118 value = attr.IsInlinedProps()
1119 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
1120 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
1121 if (attr.IsInlinedProps() && value.IsHole()) {
1122 continue;
1123 }
1124 if (UNLIKELY(value.IsAccessor())) {
1125 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1126 JSHandle<JSTaggedValue>(obj));
1127 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1128 if (obj->GetProperties().IsDictionary()) {
1129 dictStart = i;
1130 handleValue_.Update(value);
1131 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1132 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1133 break;
1134 }
1135 }
1136 handleValue_.Update(value);
1137 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1138 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1139 }
1140 if (dictStart < length) {
1141 propertiesArr = JSHandle<TaggedArray>(thread_, obj->GetProperties());
1142 JSHandle<NameDictionary> nameDic(propertiesArr);
1143 for (uint32_t i = dictStart + 1;i < length; i++) {
1144 JSTaggedValue key = cache->Get(i);
1145 int hashIndex = nameDic->FindEntry(key);
1146 PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
1147 if (!key.IsString() || hashIndex < 0 || !attr.IsEnumerable()) {
1148 continue;
1149 }
1150 handleKey_.Update(key);
1151 JSTaggedValue value = nameDic->GetValue(hashIndex);
1152 if (UNLIKELY(value.IsAccessor())) {
1153 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1154 JSHandle<JSTaggedValue>(obj));
1155 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1156 }
1157 handleValue_.Update(value);
1158 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1159 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1160 }
1161 }
1162 return hasContent;
1163 }
1164 int end = static_cast<int>(jsHclass->NumberOfProps());
1165 if (end <= 0) {
1166 return hasContent;
1167 }
1168 for (int i = 0; i < end; i++) {
1169 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
1170 JSTaggedValue key = layoutInfo->GetKey(i);
1171 if (!hasChangedToDictionaryMode) {
1172 if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
1173 handleKey_.Update(key);
1174 JSTaggedValue value;
1175 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
1176 PropertyAttributes attr(layoutInfo->GetAttr(index));
1177 ASSERT(static_cast<int>(attr.GetOffset()) == index);
1178 value = attr.IsInlinedProps()
1179 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
1180 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
1181 if (attr.IsInlinedProps() && value.IsHole()) {
1182 continue;
1183 }
1184 if (UNLIKELY(value.IsAccessor())) {
1185 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1186 JSHandle<JSTaggedValue>(obj));
1187 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1188 }
1189 handleValue_.Update(value);
1190 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1191 if (obj->GetProperties().IsDictionary()) {
1192 hasChangedToDictionaryMode = true;
1193 propertiesArr = JSHandle<TaggedArray>(thread_, obj->GetProperties());
1194 }
1195 jsHclass = JSHandle<JSHClass>(thread_, obj->GetJSHClass());
1196 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1197 }
1198 } else {
1199 JSHandle<NameDictionary> nameDic(propertiesArr);
1200 int index = nameDic->FindEntry(key);
1201 if (!key.IsString()) {
1202 continue;
1203 }
1204 PropertyAttributes attr = nameDic->GetAttributes(index);
1205 if (!attr.IsEnumerable() || index < 0) {
1206 continue;
1207 }
1208 JSTaggedValue value = nameDic->GetValue(index);
1209 handleKey_.Update(key);
1210 if (UNLIKELY(value.IsAccessor())) {
1211 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1212 JSHandle<JSTaggedValue>(obj));
1213 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1214 jsHclass = JSHandle<JSHClass>(thread_, obj->GetJSHClass());
1215 }
1216 handleValue_.Update(value);
1217 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1218 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1219 }
1220 }
1221 return hasContent;
1222 }
1223 if (obj->IsJSGlobalObject()) {
1224 JSHandle<GlobalDictionary> globalDic(propertiesArr);
1225 int size = globalDic->Size();
1226 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
1227 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
1228 JSTaggedValue key = globalDic->GetKey(hashIndex);
1229 if (!key.IsString()) {
1230 continue;
1231 }
1232 PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
1233 if (!attr.IsEnumerable()) {
1234 continue;
1235 }
1236 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
1237 sortArr.emplace_back(pair);
1238 }
1239 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
1240 for (const auto &entry : sortArr) {
1241 JSTaggedValue entryKey = entry.first.GetTaggedValue();
1242 handleKey_.Update(entryKey);
1243 int index = globalDic->FindEntry(entryKey);
1244 if (index == -1) {
1245 continue;
1246 }
1247 JSTaggedValue value = globalDic->GetValue(index);
1248 if (UNLIKELY(value.IsAccessor())) {
1249 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1250 JSHandle<JSTaggedValue>(obj));
1251 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1252 }
1253 handleValue_.Update(value);
1254 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1255 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1256 }
1257 return hasContent;
1258 }
1259 JSHandle<NameDictionary> nameDic(propertiesArr);
1260 int size = nameDic->Size();
1261 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
1262 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
1263 JSTaggedValue key = nameDic->GetKey(hashIndex);
1264 if (!key.IsString()) {
1265 continue;
1266 }
1267 PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
1268 if (!attr.IsEnumerable()) {
1269 continue;
1270 }
1271 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
1272 sortArr.emplace_back(pair);
1273 }
1274 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
1275 for (const auto &entry : sortArr) {
1276 JSTaggedValue entryKey = entry.first.GetTaggedValue();
1277 handleKey_.Update(entryKey);
1278 int index = nameDic->FindEntry(entryKey);
1279 if (index < 0) {
1280 continue;
1281 }
1282 JSTaggedValue value = nameDic->GetValue(index);
1283 if (UNLIKELY(value.IsAccessor())) {
1284 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1285 JSHandle<JSTaggedValue>(obj));
1286 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1287 }
1288 handleValue_.Update(value);
1289 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1290 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1291 }
1292 return hasContent;
1293 }
1294
AppendJsonString(const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & replacer,bool hasContent)1295 bool JsonStringifier::AppendJsonString(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
1296 bool hasContent)
1297 {
1298 JSTaggedValue serializeValue = GetSerializeValue(JSHandle<JSTaggedValue>(obj), handleKey_, handleValue_, replacer);
1299 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1300 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
1301 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
1302 return hasContent;
1303 }
1304 handleValue_.Update(serializeValue);
1305 SerializeObjectKey(handleKey_, hasContent);
1306 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
1307 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1308 if (!res.IsUndefined()) {
1309 return true;
1310 }
1311 return hasContent;
1312 }
1313
CheckStackPushSameValue(JSHandle<JSTaggedValue> value)1314 bool JsonStringifier::CheckStackPushSameValue(JSHandle<JSTaggedValue> value)
1315 {
1316 bool isContain = PushValue(value);
1317 if (isContain) {
1318 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
1319 }
1320 return false;
1321 }
1322
1323 } // namespace panda::ecmascript::base
1324