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