• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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