• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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/fast_json_stringifier.h"
17 
18 #include "ecmascript/base/builtins_base.h"
19 #include "ecmascript/base/json_helper.h"
20 #include "ecmascript/base/number_helper.h"
21 #include "ecmascript/builtins/builtins_errors.h"
22 #include "ecmascript/ecma_runtime_call_info.h"
23 #include "ecmascript/ecma_string-inl.h"
24 #include "ecmascript/ecma_vm.h"
25 #include "ecmascript/element_accessor-inl.h"
26 #include "ecmascript/global_dictionary-inl.h"
27 #include "ecmascript/js_array.h"
28 #include "ecmascript/js_function.h"
29 #include "ecmascript/js_handle.h"
30 #include "ecmascript/js_object-inl.h"
31 #include "ecmascript/js_primitive_ref.h"
32 #include "ecmascript/js_tagged_value-inl.h"
33 #include "ecmascript/js_tagged_value.h"
34 #include "ecmascript/object_fast_operator-inl.h"
35 
36 namespace panda::ecmascript::base {
Stringify(const JSHandle<JSTaggedValue> & value)37 JSHandle<JSTaggedValue> FastJsonStringifier::Stringify(const JSHandle<JSTaggedValue> &value)
38 {
39     factory_ = thread_->GetEcmaVM()->GetFactory();
40     JSHandle<JSTaggedValue> jsonCache = thread_->GetEcmaVM()->GetGlobalEnv()->GetJsonObjectHclassCache();
41     if (jsonCache->IsHole()) {
42         hclassCache_ = factory_->NewTaggedArray(JSON_CACHE_SIZE);
43     } else {
44         hclassCache_ = JSHandle<TaggedArray>::Cast(jsonCache);
45     }
46     JSTaggedValue tagValue = value.GetTaggedValue();
47     handleValue_ = JSMutableHandle<JSTaggedValue>(thread_, tagValue);
48     handleKey_ = JSMutableHandle<JSTaggedValue>(thread_, factory_->GetEmptyString());
49 
50     if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
51         JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
52         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
53         handleValue_.Update(serializeValue);
54     }
55 
56     JSTaggedValue result = SerializeJSONProperty(handleValue_);
57 
58     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
59     if (!result.IsUndefined()) {
60         return JSHandle<JSTaggedValue>(
61             factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(result_.c_str()), result_.size()));
62     }
63     return thread_->GlobalConstants()->GetHandledUndefined();
64 }
65 
GetSerializeValue(const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)66 JSTaggedValue FastJsonStringifier::GetSerializeValue(const JSHandle<JSTaggedValue> &key,
67                                                      const JSHandle<JSTaggedValue> &value)
68 {
69     JSTaggedValue tagValue = value.GetTaggedValue();
70     JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
71     // a. Let toJSON be Get(value, "toJSON").
72     JSHandle<JSTaggedValue> toJson = thread_->GlobalConstants()->GetHandledToJsonString();
73     JSHandle<JSTaggedValue> toJsonFun(
74         thread_, ObjectFastOperator::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue()));
75     // b. ReturnIfAbrupt(toJSON).
76     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
77     tagValue = value.GetTaggedValue();
78     // c. If IsCallable(toJSON) is true
79     if (UNLIKELY(toJsonFun->IsCallable())) {
80         // Let value be Call(toJSON, value, «key»).
81         EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, toJsonFun, value, undefined, 1);
82         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
83         info->SetCallArg(key.GetTaggedValue());
84         tagValue = JSFunction::Call(info);
85         // ii. ReturnIfAbrupt(value).
86         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
87     }
88     return tagValue;
89 }
90 
SerializeJSONProperty(const JSHandle<JSTaggedValue> & value)91 JSTaggedValue FastJsonStringifier::SerializeJSONProperty(const JSHandle<JSTaggedValue> &value)
92 {
93     JSTaggedValue tagValue = value.GetTaggedValue();
94     if (!tagValue.IsHeapObject()) {
95         JSTaggedType type = tagValue.GetRawData();
96         switch (type) {
97             // If value is false, return "false".
98             case JSTaggedValue::VALUE_FALSE:
99                 result_ += "false";
100                 return tagValue;
101             // If value is true, return "true".
102             case JSTaggedValue::VALUE_TRUE:
103                 result_ += "true";
104                 return tagValue;
105             // If value is null, return "null".
106             case JSTaggedValue::VALUE_NULL:
107                 result_ += "null";
108                 return tagValue;
109             default:
110                 // If Type(value) is Number, then
111                 if (tagValue.IsNumber()) {
112                     // a. If value is finite, return ToString(value).
113                     if (std::isfinite(tagValue.GetNumber())) {
114                         result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue));
115                     } else {
116                         // b. Else, return "null".
117                         result_ += "null";
118                     }
119                     return tagValue;
120                 }
121         }
122     } else {
123         JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType();
124         JSHandle<JSTaggedValue> valHandle(thread_, tagValue);
125         switch (jsType) {
126             case JSType::JS_ARRAY: {
127                 SerializeJSArray(valHandle);
128                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
129                 return tagValue;
130             }
131             // If Type(value) is String, return QuoteJSONString(value).
132             case JSType::LINE_STRING:
133             case JSType::CONSTANT_STRING:
134             case JSType::TREE_STRING:
135             case JSType::SLICED_STRING: {
136                 JSHandle<EcmaString> strHandle = JSHandle<EcmaString>(valHandle);
137                 auto string = JSHandle<EcmaString>(thread_,
138                     EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
139                 CString str = ConvertToString(*string, StringConvertedUsage::LOGICOPERATION);
140                 str = JsonHelper::ValueToQuotedString(str);
141                 result_ += str;
142                 return tagValue;
143             }
144             case JSType::JS_PRIMITIVE_REF: {
145                 SerializePrimitiveRef(valHandle);
146                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, JSTaggedValue::Exception());
147                 return tagValue;
148             }
149             case JSType::SYMBOL:
150                 return JSTaggedValue::Undefined();
151             case JSType::BIGINT: {
152                 THROW_TYPE_ERROR_AND_RETURN(thread_, "cannot serialize a BigInt", JSTaggedValue::Exception());
153             }
154             default: {
155                 if (!tagValue.IsCallable()) {
156                     JSHClass *jsHclass = tagValue.GetTaggedObject()->GetClass();
157                     if (UNLIKELY(jsHclass->IsJSProxy() &&
158                         JSProxy::Cast(tagValue.GetTaggedObject())->IsArray(thread_))) {
159                         SerializeJSProxy(valHandle);
160                         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
161                     } else {
162                         SerializeJSONObject(valHandle);
163                         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
164                     }
165                     return tagValue;
166                 }
167             }
168         }
169     }
170     return JSTaggedValue::Undefined();
171 }
172 
SerializeObjectKey(const JSHandle<JSTaggedValue> & key,bool hasContent)173 CString FastJsonStringifier::SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent)
174 {
175     if (hasContent) {
176         result_ += ",";
177     }
178 
179     CString str;
180     if (key->IsString()) {
181         str = ConvertToString(EcmaString::Cast(key->GetTaggedObject()), StringConvertedUsage::LOGICOPERATION);
182     } else if (key->IsInt()) {
183         str = NumberHelper::IntToString(static_cast<int32_t>(key->GetInt()));
184     } else {
185         str = ConvertToString(*JSTaggedValue::ToString(thread_, key), StringConvertedUsage::LOGICOPERATION);
186     }
187     str = JsonHelper::ValueToQuotedString(str);
188     result_ += str;
189     result_ += ":";
190 
191     return str;
192 }
193 
PushValue(const JSHandle<JSTaggedValue> & value)194 bool FastJsonStringifier::PushValue(const JSHandle<JSTaggedValue> &value)
195 {
196     uint32_t thisLen = stack_.size();
197 
198     for (uint32_t i = 0; i < thisLen; i++) {
199         bool equal = JSTaggedValue::SameValue(stack_[i].GetTaggedValue(), value.GetTaggedValue());
200         if (equal) {
201             return true;
202         }
203     }
204 
205     stack_.emplace_back(value);
206     return false;
207 }
208 
PopValue()209 void FastJsonStringifier::PopValue()
210 {
211     stack_.pop_back();
212 }
213 
SerializeJSONObject(const JSHandle<JSTaggedValue> & value)214 bool FastJsonStringifier::SerializeJSONObject(const JSHandle<JSTaggedValue> &value)
215 {
216     bool isContain = PushValue(value);
217     if (isContain) {
218         THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
219     }
220 
221     result_ += "{";
222     bool hasContent = false;
223 
224     ASSERT(!value->IsAccessor());
225     JSHandle<JSObject> obj(value);
226     if (UNLIKELY(value->IsJSProxy() || value->IsTypedArray())) {  // serialize proxy and typedArray
227         JSHandle<TaggedArray> propertyArray = JSObject::EnumerableOwnNames(thread_, obj);
228         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
229         uint32_t arrLength = propertyArray->GetLength();
230         for (uint32_t i = 0; i < arrLength; i++) {
231             handleKey_.Update(propertyArray->Get(i));
232             JSHandle<JSTaggedValue> valueHandle = JSTaggedValue::GetProperty(thread_, value, handleKey_).GetValue();
233             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
234             if (UNLIKELY(valueHandle->IsECMAObject() || valueHandle->IsBigInt())) {
235                 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, valueHandle);
236                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
237                 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
238                     (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
239                     continue;
240                 }
241                 handleValue_.Update(serializeValue);
242             } else {
243                 handleValue_.Update(valueHandle);
244             }
245             SerializeObjectKey(handleKey_, hasContent);
246             JSTaggedValue res = SerializeJSONProperty(handleValue_);
247             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
248             if (!res.IsUndefined()) {
249                 hasContent = true;
250             }
251         }
252     } else {
253         uint32_t numOfKeys = obj->GetNumberOfKeys();
254         uint32_t numOfElements = obj->GetNumberOfElements();
255         if (numOfKeys + numOfElements < CACHE_MINIMUN_SIZIE || !cacheable_) {
256             if (numOfElements > 0) {
257                 hasContent = DefaultSerializeElements(obj, hasContent);
258                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
259             }
260             if (numOfKeys > 0) {
261                 hasContent = DefaultSerializeKeys(obj, hasContent);
262                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
263             }
264         } else {
265             JSHClass *jsHclass = value->GetTaggedObject()->GetClass();
266             int32_t index = FindCache(jsHclass, numOfKeys + numOfElements);
267             if (index != INVALID_INDEX) {
268                 auto strCache = thread_->GetCurrentEcmaContext()->GetJsonStringifyCache(index);
269                 uint32_t cacheIndex = 0;
270                 if (numOfElements > 0) {
271                     hasContent = SerializeElementsWithCache(obj, hasContent, strCache, cacheIndex, numOfElements);
272                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
273                 }
274                 if (numOfKeys > 0) {
275                     hasContent = SerializeKeysWithCache(obj, hasContent, strCache, cacheIndex);
276                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
277                 }
278             } else {
279                 CVector<std::pair<CString, int>> strCache;
280                 if (numOfElements > 0) {
281                     hasContent = TryCacheSerializeElements(obj, hasContent, strCache);
282                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
283                 }
284                 if (numOfKeys > 0) {
285                     hasContent = TryCacheSerializeKeys(obj, hasContent, strCache);
286                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
287                 }
288                 if (cacheable_) {
289                     SetCache(value->GetTaggedObject()->GetClass(), numOfElements + numOfKeys, strCache);
290                 }
291             }
292         }
293     }
294 
295     result_ += "}";
296     PopValue();
297     return true;
298 }
299 
SerializeJSProxy(const JSHandle<JSTaggedValue> & object)300 bool FastJsonStringifier::SerializeJSProxy(const JSHandle<JSTaggedValue> &object)
301 {
302     bool isContain = PushValue(object);
303     if (isContain) {
304         THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
305     }
306 
307     result_ += "[";
308     JSHandle<JSProxy> proxy(object);
309     JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
310     JSHandle<JSTaggedValue> lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
311     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
312     JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
313     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
314     uint32_t length = lenNumber.ToUint32();
315     for (uint32_t i = 0; i < length; i++) {
316         handleKey_.Update(JSTaggedValue(i));
317         JSHandle<JSTaggedValue> valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
318         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
319         if (i > 0) {
320             result_ += ",";
321         }
322         if (UNLIKELY(valHandle->IsECMAObject() || valHandle->IsBigInt())) {
323             JSTaggedValue serializeValue = GetSerializeValue(handleKey_, valHandle);
324             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
325             handleValue_.Update(serializeValue);
326         } else {
327             handleValue_.Update(valHandle);
328         }
329         JSTaggedValue res = SerializeJSONProperty(handleValue_);
330         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
331         if (res.IsUndefined()) {
332             result_ += "null";
333         }
334     }
335 
336     result_ += "]";
337     PopValue();
338     return true;
339 }
340 
SerializeJSArray(const JSHandle<JSTaggedValue> & value)341 bool FastJsonStringifier::SerializeJSArray(const JSHandle<JSTaggedValue> &value)
342 {
343     // If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
344     bool isContain = PushValue(value);
345     if (isContain) {
346         THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
347     }
348 
349     result_ += "[";
350     JSHandle<JSArray> jsArr(value);
351     uint32_t len = jsArr->GetArrayLength();
352     if (len > 0) {
353         for (uint32_t i = 0; i < len; i++) {
354             JSTaggedValue tagVal = ObjectFastOperator::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i);
355             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
356             if (UNLIKELY(tagVal.IsAccessor())) {
357                 tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value);
358                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
359             }
360             handleKey_.Update(JSTaggedValue(i));
361             handleValue_.Update(tagVal);
362 
363             if (i > 0) {
364                 result_ += ",";
365             }
366             if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
367                 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
368                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
369                 handleValue_.Update(serializeValue);
370             }
371             JSTaggedValue res = SerializeJSONProperty(handleValue_);
372             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
373             if (res.IsUndefined()) {
374                 result_ += "null";
375             }
376         }
377     }
378 
379     result_ += "]";
380     PopValue();
381     return true;
382 }
383 
SerializePrimitiveRef(const JSHandle<JSTaggedValue> & primitiveRef)384 void FastJsonStringifier::SerializePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef)
385 {
386     JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue();
387     if (primitive.IsString()) {
388         auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
389         RETURN_IF_ABRUPT_COMPLETION(thread_);
390         CString str = ConvertToString(*priStr, StringConvertedUsage::LOGICOPERATION);
391         str = JsonHelper::ValueToQuotedString(str);
392         result_ += str;
393     } else if (primitive.IsNumber()) {
394         auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef);
395         RETURN_IF_ABRUPT_COMPLETION(thread_);
396         if (std::isfinite(priNum.GetNumber())) {
397             result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, priNum));
398         } else {
399             result_ += "null";
400         }
401     } else if (primitive.IsBoolean()) {
402         result_ += primitive.IsTrue() ? "true" : "false";
403     } else if (primitive.IsBigInt()) {
404         THROW_TYPE_ERROR(thread_, "cannot serialize a BigInt");
405     }
406 }
407 
TryCacheSerializeElements(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache)408 bool FastJsonStringifier::TryCacheSerializeElements(const JSHandle<JSObject> &obj, bool hasContent,
409                                                     CVector<std::pair<CString, int>> &strCache)
410 {
411     if (!ElementAccessor::IsDictionaryMode(obj)) {
412         uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
413         for (uint32_t i = 0; i < elementsLen; ++i) {
414             if (!ElementAccessor::Get(obj, i).IsHole()) {
415                 handleKey_.Update(JSTaggedValue(i));
416                 handleValue_.Update(ElementAccessor::Get(obj, i));
417                 hasContent = AppendJsonString(hasContent, strCache, i);
418                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
419             }
420         }
421     } else {
422         JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
423         JSHandle<NumberDictionary> numberDic(elementsArr);
424         CVector<JSHandle<JSTaggedValue>> sortArr;
425         int size = numberDic->Size();
426         for (int hashIndex = 0; hashIndex < size; hashIndex++) {
427             JSTaggedValue key = numberDic->GetKey(hashIndex);
428             if (!key.IsUndefined() && !key.IsHole()) {
429                 PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
430                 if (attr.IsEnumerable()) {
431                     JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
432                     sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
433                 }
434             }
435         }
436         std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
437         for (const auto &entry : sortArr) {
438             JSTaggedValue entryKey = entry.GetTaggedValue();
439             handleKey_.Update(entryKey);
440             int index = numberDic->FindEntry(entryKey);
441             JSTaggedValue value = numberDic->GetValue(index);
442             if (UNLIKELY(value.IsAccessor())) {
443                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
444                                              JSHandle<JSTaggedValue>(obj));
445                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
446             }
447             handleValue_.Update(value);
448             hasContent = AppendJsonString(hasContent, strCache, index);
449             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
450         }
451     }
452     return hasContent;
453 }
454 
SerializeElementsWithCache(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache,uint32_t & cacheIndex,uint32_t elementSize)455 bool FastJsonStringifier::SerializeElementsWithCache(const JSHandle<JSObject> &obj, bool hasContent,
456     CVector<std::pair<CString, int>> &strCache, uint32_t &cacheIndex, uint32_t elementSize)
457 {
458     if (!ElementAccessor::IsDictionaryMode(obj)) {
459         uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
460         for (uint32_t i = 0; i < elementsLen; ++i) {
461             if (!ElementAccessor::Get(obj, i).IsHole()) {
462                 CString key = strCache[cacheIndex++].first;
463                 handleValue_.Update(ElementAccessor::Get(obj, i));
464                 hasContent = FastAppendJsonString(hasContent, key);
465                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
466             }
467         }
468     } else {
469         JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
470         JSHandle<NumberDictionary> numberDic(elementsArr);
471         for (; cacheIndex < elementSize; cacheIndex++) {
472             CString key = strCache[cacheIndex].first;
473             int index = strCache[cacheIndex].second;
474             JSTaggedValue value = numberDic->GetValue(index);
475             if (UNLIKELY(value.IsAccessor())) {
476                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
477                                              JSHandle<JSTaggedValue>(obj));
478                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
479             }
480             handleValue_.Update(value);
481             hasContent = FastAppendJsonString(hasContent, key);
482             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
483         }
484     }
485     return hasContent;
486 }
487 
TryCacheSerializeKeys(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache)488 bool FastJsonStringifier::TryCacheSerializeKeys(const JSHandle<JSObject> &obj, bool hasContent,
489                                                 CVector<std::pair<CString, int>> &strCache)
490 {
491     JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
492     if (!propertiesArr->IsDictionaryMode()) {
493         JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
494         JSTaggedValue enumCache = jsHclass->GetEnumCache();
495         if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
496             JSHandle<TaggedArray> cache(thread_, enumCache);
497             uint32_t length = cache->GetLength();
498             for (uint32_t i = 0; i < length; i++) {
499                 JSTaggedValue key = cache->Get(i);
500                 if (!key.IsString()) {
501                     continue;
502                 }
503                 handleKey_.Update(key);
504                 JSTaggedValue value;
505                 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
506                 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
507                 PropertyAttributes attr(layoutInfo->GetAttr(index));
508                 ASSERT(static_cast<int>(attr.GetOffset()) == index);
509                 value = attr.IsInlinedProps()
510                         ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
511                         : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
512                 if (attr.IsInlinedProps() && value.IsHole()) {
513                     continue;
514                 }
515                 if (UNLIKELY(value.IsAccessor())) {
516                     value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
517                                                  JSHandle<JSTaggedValue>(obj));
518                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
519                 }
520                 handleValue_.Update(value);
521                 hasContent = AppendJsonString(hasContent, strCache, index);
522                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
523             }
524             return hasContent;
525         }
526         int end = static_cast<int>(jsHclass->NumberOfProps());
527         if (end <= 0) {
528             return hasContent;
529         }
530         for (int i = 0; i < end; i++) {
531             LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
532             JSTaggedValue key = layoutInfo->GetKey(i);
533             if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
534                 handleKey_.Update(key);
535                 JSTaggedValue value;
536                 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
537                 PropertyAttributes attr(layoutInfo->GetAttr(index));
538                 ASSERT(static_cast<int>(attr.GetOffset()) == index);
539                 value = attr.IsInlinedProps()
540                         ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
541                         : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
542                 if (attr.IsInlinedProps() && value.IsHole()) {
543                     continue;
544                 }
545                 if (UNLIKELY(value.IsAccessor())) {
546                     value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
547                                                  JSHandle<JSTaggedValue>(obj));
548                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
549                 }
550                 handleValue_.Update(value);
551                 hasContent = AppendJsonString(hasContent, strCache, index);
552                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
553             }
554         }
555         return hasContent;
556     }
557     if (obj->IsJSGlobalObject()) {
558         JSHandle<GlobalDictionary> globalDic(propertiesArr);
559         int size = globalDic->Size();
560         CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
561         for (int hashIndex = 0; hashIndex < size; hashIndex++) {
562             JSTaggedValue key = globalDic->GetKey(hashIndex);
563             if (!key.IsString()) {
564                 continue;
565             }
566             PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
567             if (!attr.IsEnumerable()) {
568                 continue;
569             }
570             std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
571             sortArr.emplace_back(pair);
572         }
573         std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
574         for (const auto &entry : sortArr) {
575             JSTaggedValue entryKey = entry.first.GetTaggedValue();
576             handleKey_.Update(entryKey);
577             int index = globalDic->FindEntry(entryKey);
578             JSTaggedValue value = globalDic->GetValue(index);
579             if (UNLIKELY(value.IsAccessor())) {
580                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
581                                              JSHandle<JSTaggedValue>(obj));
582                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
583             }
584             handleValue_.Update(value);
585             hasContent = AppendJsonString(hasContent, strCache, index);
586             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
587         }
588         return hasContent;
589     }
590     JSHandle<NameDictionary> nameDic(propertiesArr);
591     int size = nameDic->Size();
592     CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
593     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
594         JSTaggedValue key = nameDic->GetKey(hashIndex);
595         if (!key.IsString()) {
596             continue;
597         }
598         PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
599         if (!attr.IsEnumerable()) {
600             continue;
601         }
602         std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
603         sortArr.emplace_back(pair);
604     }
605     std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
606     for (const auto &entry : sortArr) {
607         JSTaggedValue entryKey = entry.first.GetTaggedValue();
608         handleKey_.Update(entryKey);
609         int index = nameDic->FindEntry(entryKey);
610         JSTaggedValue value = nameDic->GetValue(index);
611         if (UNLIKELY(value.IsAccessor())) {
612             value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
613                                          JSHandle<JSTaggedValue>(obj));
614             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
615         }
616         handleValue_.Update(value);
617         hasContent = AppendJsonString(hasContent, strCache, index);
618         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
619     }
620     return hasContent;
621 }
622 
SerializeKeysWithCache(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache,uint32_t & cacheIndex)623 bool FastJsonStringifier::SerializeKeysWithCache(const JSHandle<JSObject> &obj, bool hasContent,
624                                                  CVector<std::pair<CString, int>> &strCache, uint32_t &cacheIndex)
625 {
626     JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
627     JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
628     if (!propertiesArr->IsDictionaryMode()) {
629         for (; cacheIndex < strCache.size(); cacheIndex++) {
630             auto cacheValue = strCache[cacheIndex];
631             CString str = cacheValue.first;
632             int index = cacheValue.second;
633             LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
634             PropertyAttributes attr(layoutInfo->GetAttr(index));
635             JSTaggedValue value = attr.IsInlinedProps()
636                     ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
637                     : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
638             if (UNLIKELY(value.IsAccessor())) {
639                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
640                                              JSHandle<JSTaggedValue>(obj));
641                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
642             }
643             handleValue_.Update(value);
644             hasContent = FastAppendJsonString(hasContent, str);
645             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
646         }
647         return hasContent;
648     }
649     if (obj->IsJSGlobalObject()) {
650         JSHandle<GlobalDictionary> globalDic(propertiesArr);
651         for (; cacheIndex < strCache.size(); cacheIndex++) {
652             auto cacheValue = strCache[cacheIndex];
653             CString str = cacheValue.first;
654             int index = cacheValue.second;
655             JSTaggedValue value = globalDic->GetValue(index);
656             if (UNLIKELY(value.IsAccessor())) {
657                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
658                                              JSHandle<JSTaggedValue>(obj));
659                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
660             }
661             handleValue_.Update(value);
662             hasContent = FastAppendJsonString(hasContent, str);
663             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
664         }
665         return hasContent;
666     }
667     JSHandle<NameDictionary> nameDic(propertiesArr);
668     for (; cacheIndex < strCache.size(); cacheIndex++) {
669         auto cacheValue = strCache[cacheIndex];
670         CString str = cacheValue.first;
671         int index = cacheValue.second;
672         JSTaggedValue value = nameDic->GetValue(index);
673         if (UNLIKELY(value.IsAccessor())) {
674             value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
675                                          JSHandle<JSTaggedValue>(obj));
676             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
677         }
678         handleValue_.Update(value);
679         hasContent = FastAppendJsonString(hasContent, str);
680         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
681     }
682     return hasContent;
683 }
684 
AppendJsonString(bool hasContent,CVector<std::pair<CString,int>> & strCache,int index)685 bool FastJsonStringifier::AppendJsonString(bool hasContent, CVector<std::pair<CString, int>> &strCache, int index)
686 {
687     if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
688         JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
689         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
690         if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
691             (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
692             return hasContent;
693         }
694         handleValue_.Update(serializeValue);
695     }
696     CString keyStr = SerializeObjectKey(handleKey_, hasContent);
697     strCache.emplace_back(std::pair<CString, int>(keyStr, index));
698     JSTaggedValue res = SerializeJSONProperty(handleValue_);
699     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
700     if (!res.IsUndefined()) {
701         return true;
702     }
703     EraseKeyString(keyStr, hasContent);
704     return hasContent;
705 }
706 
FastAppendJsonString(bool hasContent,CString & key)707 bool FastJsonStringifier::FastAppendJsonString(bool hasContent, CString &key)
708 {
709     if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
710         JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
711         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
712         if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
713             (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
714             return hasContent;
715         }
716         handleValue_.Update(serializeValue);
717     }
718     FastSerializeObjectKey(key, hasContent);
719     JSTaggedValue res = SerializeJSONProperty(handleValue_);
720     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
721     if (!res.IsUndefined()) {
722         return true;
723     }
724     EraseKeyString(key, hasContent);
725     return hasContent;
726 }
727 
DefaultSerializeElements(const JSHandle<JSObject> & obj,bool hasContent)728 bool FastJsonStringifier::DefaultSerializeElements(const JSHandle<JSObject> &obj, bool hasContent)
729 {
730     if (!ElementAccessor::IsDictionaryMode(obj)) {
731         uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
732         for (uint32_t i = 0; i < elementsLen; ++i) {
733             if (!ElementAccessor::Get(obj, i).IsHole()) {
734                 handleKey_.Update(JSTaggedValue(i));
735                 handleValue_.Update(ElementAccessor::Get(obj, i));
736                 hasContent = AppendJsonString(hasContent);
737                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
738             }
739         }
740     } else {
741         JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
742         JSHandle<NumberDictionary> numberDic(elementsArr);
743         CVector<JSHandle<JSTaggedValue>> sortArr;
744         int size = numberDic->Size();
745         for (int hashIndex = 0; hashIndex < size; hashIndex++) {
746             JSTaggedValue key = numberDic->GetKey(hashIndex);
747             if (!key.IsUndefined() && !key.IsHole()) {
748                 PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
749                 if (attr.IsEnumerable()) {
750                     JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
751                     sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
752                 }
753             }
754         }
755         std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
756         for (const auto &entry : sortArr) {
757             JSTaggedValue entryKey = entry.GetTaggedValue();
758             handleKey_.Update(entryKey);
759             int index = numberDic->FindEntry(entryKey);
760             JSTaggedValue value = numberDic->GetValue(index);
761             if (UNLIKELY(value.IsAccessor())) {
762                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
763                                              JSHandle<JSTaggedValue>(obj));
764                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
765             }
766             handleValue_.Update(value);
767             hasContent = AppendJsonString(hasContent);
768             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
769         }
770     }
771     return hasContent;
772 }
773 
DefaultSerializeKeys(const JSHandle<JSObject> & obj,bool hasContent)774 bool FastJsonStringifier::DefaultSerializeKeys(const JSHandle<JSObject> &obj, bool hasContent)
775 {
776     JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
777     if (!propertiesArr->IsDictionaryMode()) {
778         JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
779         JSTaggedValue enumCache = jsHclass->GetEnumCache();
780         if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
781             JSHandle<TaggedArray> cache(thread_, enumCache);
782             uint32_t length = cache->GetLength();
783             for (uint32_t i = 0; i < length; i++) {
784                 JSTaggedValue key = cache->Get(i);
785                 if (!key.IsString()) {
786                     continue;
787                 }
788                 handleKey_.Update(key);
789                 JSTaggedValue value;
790                 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
791                 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
792                 PropertyAttributes attr(layoutInfo->GetAttr(index));
793                 ASSERT(static_cast<int>(attr.GetOffset()) == index);
794                 value = attr.IsInlinedProps()
795                         ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
796                         : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
797                 if (attr.IsInlinedProps() && value.IsHole()) {
798                     continue;
799                 }
800                 if (UNLIKELY(value.IsAccessor())) {
801                     value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
802                                                  JSHandle<JSTaggedValue>(obj));
803                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
804                 }
805                 handleValue_.Update(value);
806                 hasContent = AppendJsonString(hasContent);
807                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
808             }
809             return hasContent;
810         }
811         int end = static_cast<int>(jsHclass->NumberOfProps());
812         if (end <= 0) {
813             return hasContent;
814         }
815         for (int i = 0; i < end; i++) {
816             LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
817             JSTaggedValue key = layoutInfo->GetKey(i);
818             if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
819                 handleKey_.Update(key);
820                 JSTaggedValue value;
821                 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
822                 PropertyAttributes attr(layoutInfo->GetAttr(index));
823                 ASSERT(static_cast<int>(attr.GetOffset()) == index);
824                 value = attr.IsInlinedProps()
825                         ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
826                         : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
827                 if (attr.IsInlinedProps() && value.IsHole()) {
828                     continue;
829                 }
830                 if (UNLIKELY(value.IsAccessor())) {
831                     value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
832                                                  JSHandle<JSTaggedValue>(obj));
833                     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
834                 }
835                 handleValue_.Update(value);
836                 hasContent = AppendJsonString(hasContent);
837                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
838             }
839         }
840         return hasContent;
841     }
842     if (obj->IsJSGlobalObject()) {
843         JSHandle<GlobalDictionary> globalDic(propertiesArr);
844         int size = globalDic->Size();
845         CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
846         for (int hashIndex = 0; hashIndex < size; hashIndex++) {
847             JSTaggedValue key = globalDic->GetKey(hashIndex);
848             if (!key.IsString()) {
849                 continue;
850             }
851             PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
852             if (!attr.IsEnumerable()) {
853                 continue;
854             }
855             std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
856             sortArr.emplace_back(pair);
857         }
858         std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
859         for (const auto &entry : sortArr) {
860             JSTaggedValue entryKey = entry.first.GetTaggedValue();
861             handleKey_.Update(entryKey);
862             int index = globalDic->FindEntry(entryKey);
863             JSTaggedValue value = globalDic->GetValue(index);
864             if (UNLIKELY(value.IsAccessor())) {
865                 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
866                                              JSHandle<JSTaggedValue>(obj));
867                 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
868             }
869             handleValue_.Update(value);
870             hasContent = AppendJsonString(hasContent);
871             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
872         }
873         return hasContent;
874     }
875     JSHandle<NameDictionary> nameDic(propertiesArr);
876     int size = nameDic->Size();
877     CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
878     for (int hashIndex = 0; hashIndex < size; hashIndex++) {
879         JSTaggedValue key = nameDic->GetKey(hashIndex);
880         if (!key.IsString()) {
881             continue;
882         }
883         PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
884         if (!attr.IsEnumerable()) {
885             continue;
886         }
887         std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
888         sortArr.emplace_back(pair);
889     }
890     std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
891     for (const auto &entry : sortArr) {
892         JSTaggedValue entryKey = entry.first.GetTaggedValue();
893         handleKey_.Update(entryKey);
894         int index = nameDic->FindEntry(entryKey);
895         JSTaggedValue value = nameDic->GetValue(index);
896         if (UNLIKELY(value.IsAccessor())) {
897             value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
898                                          JSHandle<JSTaggedValue>(obj));
899             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
900         }
901         handleValue_.Update(value);
902         hasContent = AppendJsonString(hasContent);
903         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
904     }
905     return hasContent;
906 }
907 
AppendJsonString(bool hasContent)908 bool FastJsonStringifier::AppendJsonString(bool hasContent)
909 {
910     if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
911         JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
912         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
913         if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
914             (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
915             return hasContent;
916         }
917         handleValue_.Update(serializeValue);
918     }
919     CString keyStr = SerializeObjectKey(handleKey_, hasContent);
920     JSTaggedValue res = SerializeJSONProperty(handleValue_);
921     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
922     if (!res.IsUndefined()) {
923         return true;
924     }
925     EraseKeyString(keyStr, hasContent);
926     return hasContent;
927 }
928 
DefaultSerializeObject(const JSTaggedValue & object,uint32_t numOfKeys,uint32_t numOfElements)929 bool FastJsonStringifier::DefaultSerializeObject(const JSTaggedValue &object, uint32_t numOfKeys,
930                                                  uint32_t numOfElements)
931 {
932     JSHandle<JSTaggedValue> value(thread_, object);
933     bool isContain = PushValue(value);
934     if (isContain) {
935         THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
936     }
937 
938     result_ += "{";
939     bool hasContent = false;
940 
941     JSHandle<JSObject> obj(value);
942     if (numOfElements > 0) {
943         hasContent = DefaultSerializeElements(obj, hasContent);
944         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
945     }
946     if (numOfKeys > 0) {
947         hasContent = DefaultSerializeKeys(obj, hasContent);
948         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
949         }
950 
951     result_ += "}";
952     PopValue();
953     return true;
954 }
955 }  // namespace panda::ecmascript::base
956