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