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