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