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