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