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