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/ecma_string.h"
19 #include "ecmascript/global_dictionary-inl.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/js_api/js_api_hashset.h"
22 #include "ecmascript/js_map.h"
23 #include "ecmascript/js_primitive_ref.h"
24 #include "ecmascript/js_set.h"
25 #include "ecmascript/object_fast_operator-inl.h"
26 #include "ecmascript/shared_objects/js_shared_map.h"
27 #include "ecmascript/tagged_hash_array.h"
28
29 namespace panda::ecmascript::base {
30 constexpr int GAP_MAX_LEN = 10;
31 using TransformType = base::JsonHelper::TransformType;
32
Stringify(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer,const JSHandle<JSTaggedValue> & gap)33 JSHandle<JSTaggedValue> JsonStringifier::Stringify(const JSHandle<JSTaggedValue> &value,
34 const JSHandle<JSTaggedValue> &replacer,
35 const JSHandle<JSTaggedValue> &gap)
36 {
37 factory_ = thread_->GetEcmaVM()->GetFactory();
38 handleValue_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
39 handleKey_ = JSMutableHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
40 // Let isArray be IsArray(replacer).
41 bool isArray = replacer->IsArray(thread_);
42 // ReturnIfAbrupt(isArray).
43 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
44 // If isArray is true, then
45 if (isArray) {
46 uint32_t len = 0;
47 if (replacer->IsJSArray()) {
48 // FastPath
49 JSHandle<JSArray> arr(replacer);
50 len = arr->GetArrayLength();
51 } else if (replacer->IsJSSharedArray()) {
52 JSHandle<JSSharedArray> arr(replacer);
53 len = arr->GetArrayLength();
54 } else {
55 // Let len be ToLength(Get(replacer, "length")).
56 JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
57 JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread_, replacer, lengthKey).GetValue();
58 // ReturnIfAbrupt(len).
59 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
60 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenResult);
61 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
62 len = lenNumber.ToUint32();
63 }
64 if (len > 0) {
65 JSMutableHandle<JSTaggedValue> propHandle(thread_, JSTaggedValue::Undefined());
66 // Repeat while k<len.
67 for (uint32_t i = 0; i < len; i++) {
68 // a. Let v be Get(replacer, ToString(k)).
69 JSTaggedValue prop = ObjectFastOperator::FastGetPropertyByIndex(thread_, replacer.GetTaggedValue(), i);
70 // b. ReturnIfAbrupt(v).
71 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
72 /*
73 * c. Let item be undefined.
74 * d. If Type(v) is String, let item be v.
75 * e. Else if Type(v) is Number, let item be ToString(v).
76 * f. Else if Type(v) is Object, then
77 * i. If v has a [[StringData]] or [[NumberData]] internal slot, let item be ToString(v).
78 * ii. ReturnIfAbrupt(item).
79 * g. If item is not undefined and item is not currently an element of PropertyList, then
80 * i. Append item to the end of PropertyList.
81 * h. Let k be k+1.
82 */
83 propHandle.Update(prop);
84 if (prop.IsNumber() || prop.IsString()) {
85 AddDeduplicateProp(propHandle);
86 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
87 } else if (prop.IsJSPrimitiveRef()) {
88 JSTaggedValue primitive = JSPrimitiveRef::Cast(prop.GetTaggedObject())->GetValue(thread_);
89 if (primitive.IsNumber() || primitive.IsString()) {
90 AddDeduplicateProp(propHandle);
91 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
92 }
93 }
94 }
95 }
96 }
97
98 // If Type(space) is Object, then
99 if (gap->IsJSPrimitiveRef()) {
100 JSTaggedValue primitive = JSPrimitiveRef::Cast(gap->GetTaggedObject())->GetValue(thread_);
101 // a. If space has a [[NumberData]] internal slot, then
102 if (primitive.IsNumber()) {
103 // i. Let space be ToNumber(space).
104 JSTaggedNumber num = JSTaggedValue::ToNumber(thread_, gap);
105 // ii. ReturnIfAbrupt(space).
106 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
107 CalculateNumberGap(num);
108 } else if (primitive.IsString()) {
109 // b. Else if space has a [[StringData]] internal slot, then
110 // i. Let space be ToString(space).
111 auto str = JSTaggedValue::ToString(thread_, gap);
112 // ii. ReturnIfAbrupt(space).
113 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
114 CalculateStringGap(JSHandle<EcmaString>(thread_, str.GetTaggedValue()));
115 }
116 } else if (gap->IsNumber()) {
117 // If Type(space) is Number
118 CalculateNumberGap(gap.GetTaggedValue());
119 } else if (gap->IsString()) {
120 // Else if Type(space) is String
121 CalculateStringGap(JSHandle<EcmaString>::Cast(gap));
122 }
123
124 JSHandle<JSTaggedValue> key(factory_->GetEmptyString());
125 JSTaggedValue serializeValue = GetSerializeValue(value, key, value, replacer);
126 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
127 handleValue_.Update(serializeValue);
128 JSTaggedValue result = SerializeJSONProperty(handleValue_, replacer);
129 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
130 if (!result.IsUndefined()) {
131 return JSHandle<JSTaggedValue>(
132 factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(result_.c_str()), result_.size()));
133 }
134 return thread_->GlobalConstants()->GetHandledUndefined();
135 }
136
AddDeduplicateProp(const JSHandle<JSTaggedValue> & property)137 void JsonStringifier::AddDeduplicateProp(const JSHandle<JSTaggedValue> &property)
138 {
139 JSHandle<EcmaString> primString = JSTaggedValue::ToString(thread_, property);
140 RETURN_IF_ABRUPT_COMPLETION(thread_);
141 JSHandle<JSTaggedValue> addVal(thread_, *primString);
142
143 uint32_t propLen = propList_.size();
144 for (uint32_t i = 0; i < propLen; i++) {
145 if (JSTaggedValue::SameValue(thread_, propList_[i], addVal)) {
146 return;
147 }
148 }
149 propList_.emplace_back(addVal);
150 }
151
CalculateNumberGap(JSTaggedValue gap)152 bool JsonStringifier::CalculateNumberGap(JSTaggedValue gap)
153 {
154 double value = std::min(gap.GetNumber(), 10.0); // means GAP_MAX_LEN.
155 if (value > 0) {
156 int gapLength = static_cast<int>(value);
157 gap_.append(gapLength, ' ');
158 gap_.append("\0");
159 }
160 return true;
161 }
162
CalculateStringGap(const JSHandle<EcmaString> & primString)163 bool JsonStringifier::CalculateStringGap(const JSHandle<EcmaString> &primString)
164 {
165 CString gapString = ConvertToString(thread_, *primString, StringConvertedUsage::LOGICOPERATION);
166 uint32_t gapLen = gapString.length();
167 if (gapLen > 0) {
168 uint32_t gapLength = gapLen;
169 if (gapLen > GAP_MAX_LEN) {
170 if (gapString.at(GAP_MAX_LEN - 1) == static_cast<char>(common::utf_helper::UTF8_2B_FIRST)) {
171 gapLength = GAP_MAX_LEN + 1;
172 } else {
173 gapLength = GAP_MAX_LEN;
174 }
175 }
176 gap_.append(gapString.c_str(), gapLength);
177 gap_.append("\0");
178 }
179 return true;
180 }
181
GetSerializeValue(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)182 JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle<JSTaggedValue> &object,
183 const JSHandle<JSTaggedValue> &key,
184 const JSHandle<JSTaggedValue> &value,
185 const JSHandle<JSTaggedValue> &replacer)
186 {
187 JSTaggedValue tagValue = value.GetTaggedValue();
188 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
189 // If Type(value) is Object, then
190 if (value->IsECMAObject() || value->IsBigInt()) {
191 // a. Let toJSON be Get(value, "toJSON").
192 JSHandle<JSTaggedValue> toJson = thread_->GlobalConstants()->GetHandledToJsonString();
193 JSHandle<JSTaggedValue> toJsonFun(
194 thread_, ObjectFastOperator::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue()));
195 // b. ReturnIfAbrupt(toJSON).
196 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
197 tagValue = value.GetTaggedValue();
198 // c. If IsCallable(toJSON) is true
199 if (UNLIKELY(toJsonFun->IsCallable())) {
200 // Let value be Call(toJSON, value, «key»).
201 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, toJsonFun, value, undefined, 1);
202 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
203 info->SetCallArg(key.GetTaggedValue());
204 tagValue = JSFunction::Call(info);
205 // ii. ReturnIfAbrupt(value).
206 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
207 }
208 }
209
210 if (UNLIKELY(replacer->IsCallable())) {
211 handleValue_.Update(tagValue);
212 // a. Let value be Call(ReplacerFunction, holder, «key, value»).
213 const uint32_t argsLength = 2; // 2: «key, value»
214 JSHandle<JSTaggedValue> holder = SerializeHolder(object, value);
215 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
216 EcmaRuntimeCallInfo *info =
217 EcmaInterpreter::NewRuntimeCallInfo(thread_, replacer, holder, undefined, argsLength);
218 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
219 info->SetCallArg(key.GetTaggedValue(), handleValue_.GetTaggedValue());
220 tagValue = JSFunction::Call(info);
221 // b. ReturnIfAbrupt(value).
222 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
223 }
224 return tagValue;
225 }
226
SerializeHolder(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & value)227 JSHandle<JSTaggedValue> JsonStringifier::SerializeHolder(const JSHandle<JSTaggedValue> &object,
228 const JSHandle<JSTaggedValue> &value)
229 {
230 if (stack_.size() <= 0) {
231 JSHandle<JSObject> holder = factory_->CreateNullJSObject();
232 JSHandle<JSTaggedValue> holderKey(factory_->GetEmptyString());
233 JSObject::CreateDataPropertyOrThrow(thread_, holder, holderKey, value);
234 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
235 return JSHandle<JSTaggedValue>(holder);
236 }
237 return object;
238 }
239
SerializeJSONProperty(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)240 JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle<JSTaggedValue> &value,
241 const JSHandle<JSTaggedValue> &replacer)
242 {
243 STACK_LIMIT_CHECK(thread_, JSTaggedValue::Exception());
244 JSTaggedValue tagValue = value.GetTaggedValue();
245 if (!tagValue.IsHeapObject()) {
246 JSTaggedType type = tagValue.GetRawData();
247 switch (type) {
248 // If value is false, return "false".
249 case JSTaggedValue::VALUE_FALSE:
250 result_ += "false";
251 return tagValue;
252 // If value is true, return "true".
253 case JSTaggedValue::VALUE_TRUE:
254 result_ += "true";
255 return tagValue;
256 // If value is null, return "null".
257 case JSTaggedValue::VALUE_NULL:
258 result_ += "null";
259 return tagValue;
260 default:
261 // If Type(value) is Number, then
262 if (tagValue.IsNumber()) {
263 // a. If value is finite, return ToString(value).
264 if (std::isfinite(tagValue.GetNumber())) {
265 result_ += ConvertToString(thread_, *base::NumberHelper::NumberToString(thread_, tagValue));
266 } else {
267 // b. Else, return "null".
268 result_ += "null";
269 }
270 return tagValue;
271 }
272 }
273 } else {
274 JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType();
275 JSHandle<JSTaggedValue> valHandle(thread_, tagValue);
276 switch (jsType) {
277 case JSType::JS_ARRAY:
278 case JSType::JS_SHARED_ARRAY: {
279 SerializeJSArray(valHandle, replacer);
280 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
281 return tagValue;
282 }
283 case JSType::JS_API_LINKED_LIST: {
284 JSHandle listHandle = JSHandle<JSAPILinkedList>(thread_, tagValue);
285 CheckStackPushSameValue(valHandle);
286 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
287 valHandle = JSHandle<JSTaggedValue>(thread_, JSAPILinkedList::ConvertToArray(thread_, listHandle));
288 SerializeJSONObject(valHandle, replacer);
289 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
290 return tagValue;
291 }
292 case JSType::JS_API_LIST: {
293 JSHandle listHandle = JSHandle<JSAPIList>(thread_, tagValue);
294 CheckStackPushSameValue(valHandle);
295 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
296 valHandle = JSHandle<JSTaggedValue>(thread_, JSAPIList::ConvertToArray(thread_, listHandle));
297 SerializeJSONObject(valHandle, replacer);
298 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
299 return tagValue;
300 } case JSType::JS_API_FAST_BUFFER: {
301 JSHandle bufferHandle = JSHandle<JSTaggedValue>(thread_, tagValue);
302 CheckStackPushSameValue(valHandle);
303 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
304 valHandle = JSHandle<JSTaggedValue>(thread_, JSAPIFastBuffer::FromBufferToArray(thread_, bufferHandle));
305 SerializeJSONObject(valHandle, replacer);
306 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
307 return tagValue;
308 }
309 // If Type(value) is String, return QuoteJSONString(value).
310 case JSType::LINE_STRING:
311 case JSType::TREE_STRING:
312 case JSType::SLICED_STRING: {
313 JSHandle<EcmaString> strHandle = JSHandle<EcmaString>(valHandle);
314 auto string = JSHandle<EcmaString>(thread_,
315 EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
316 CString str = ConvertToString(thread_, *string, StringConvertedUsage::LOGICOPERATION);
317 JsonHelper::AppendValueToQuotedString(str, result_);
318 return tagValue;
319 }
320 case JSType::JS_PRIMITIVE_REF: {
321 SerializePrimitiveRef(valHandle);
322 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, JSTaggedValue::Exception());
323 return tagValue;
324 }
325 case JSType::SYMBOL:
326 return JSTaggedValue::Undefined();
327 case JSType::BIGINT: {
328 if (transformType_ == TransformType::NORMAL) {
329 THROW_TYPE_ERROR_AND_RETURN(thread_, "cannot serialize a BigInt", JSTaggedValue::Exception());
330 } else {
331 JSHandle<BigInt> thisBigint(thread_, valHandle.GetTaggedValue());
332 auto bigIntStr = BigInt::ToString(thread_, thisBigint);
333 result_ += ConvertToString(thread_, *bigIntStr);
334 return tagValue;
335 }
336 }
337 case JSType::JS_NATIVE_POINTER: {
338 result_ += "{}";
339 return tagValue;
340 }
341 case JSType::JS_SHARED_MAP: {
342 if (transformType_ == TransformType::SENDABLE) {
343 CheckStackPushSameValue(valHandle);
344 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
345 SerializeJSONSharedMap(valHandle, replacer);
346 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
347 return tagValue;
348 }
349 [[fallthrough]];
350 }
351 case JSType::JS_MAP: {
352 if (transformType_ == TransformType::SENDABLE) {
353 CheckStackPushSameValue(valHandle);
354 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
355 SerializeJSONMap(valHandle, replacer);
356 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
357 return tagValue;
358 }
359 [[fallthrough]];
360 }
361 case JSType::JS_SET: {
362 if (transformType_ == TransformType::SENDABLE) {
363 CheckStackPushSameValue(valHandle);
364 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
365 SerializeJSONSet(valHandle, replacer);
366 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
367 return tagValue;
368 }
369 [[fallthrough]];
370 }
371 case JSType::JS_SHARED_SET: {
372 if (transformType_ == TransformType::SENDABLE) {
373 CheckStackPushSameValue(valHandle);
374 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
375 SerializeJSONSharedSet(valHandle, replacer);
376 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
377 return tagValue;
378 }
379 [[fallthrough]];
380 }
381 case JSType::JS_API_HASH_MAP: {
382 if (transformType_ == TransformType::SENDABLE) {
383 CheckStackPushSameValue(valHandle);
384 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
385 SerializeJSONHashMap(valHandle, replacer);
386 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
387 return tagValue;
388 }
389 [[fallthrough]];
390 }
391 case JSType::JS_API_HASH_SET: {
392 if (transformType_ == TransformType::SENDABLE) {
393 CheckStackPushSameValue(valHandle);
394 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
395 SerializeJSONHashSet(valHandle, replacer);
396 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
397 return tagValue;
398 }
399 [[fallthrough]];
400 }
401 default: {
402 if (!tagValue.IsCallable()) {
403 JSHClass *jsHclass = tagValue.GetTaggedObject()->GetClass();
404 if (UNLIKELY(jsHclass->IsJSProxy() &&
405 JSProxy::Cast(tagValue.GetTaggedObject())->IsArray(thread_))) {
406 SerializeJSProxy(valHandle, replacer);
407 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
408 } else {
409 CheckStackPushSameValue(valHandle);
410 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
411 SerializeJSONObject(valHandle, replacer);
412 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
413 }
414 return tagValue;
415 }
416 }
417 }
418 }
419 return JSTaggedValue::Undefined();
420 }
421
SerializeObjectKey(const JSHandle<JSTaggedValue> & key,bool hasContent)422 void JsonStringifier::SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent)
423 {
424 CString stepBegin;
425 CString stepEnd;
426 if (hasContent) {
427 result_ += ",";
428 }
429 if (!gap_.empty()) {
430 stepBegin += "\n";
431 stepBegin += indent_;
432 stepEnd += " ";
433 }
434 CString str;
435 if (key->IsString()) {
436 str = ConvertToString(thread_, EcmaString::Cast(key->GetTaggedObject()), StringConvertedUsage::LOGICOPERATION);
437 } else if (key->IsInt()) {
438 str = NumberHelper::IntToString(static_cast<int32_t>(key->GetInt()));
439 } else {
440 str = ConvertToString(thread_, *JSTaggedValue::ToString(thread_, key), StringConvertedUsage::LOGICOPERATION);
441 }
442 result_ += stepBegin;
443 JsonHelper::AppendValueToQuotedString(str, result_);
444 result_ += ":";
445 result_ += stepEnd;
446 }
447
PushValue(const JSHandle<JSTaggedValue> & value)448 bool JsonStringifier::PushValue(const JSHandle<JSTaggedValue> &value)
449 {
450 uint32_t thisLen = stack_.size();
451
452 for (uint32_t i = 0; i < thisLen; i++) {
453 bool equal = JSTaggedValue::SameValue(thread_, stack_[i].GetTaggedValue(), value.GetTaggedValue());
454 if (equal) {
455 return true;
456 }
457 }
458
459 stack_.emplace_back(value);
460 return false;
461 }
462
PopValue()463 void JsonStringifier::PopValue()
464 {
465 stack_.pop_back();
466 }
467
SerializeJSONObject(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)468 bool JsonStringifier::SerializeJSONObject(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer)
469 {
470 CString stepback = indent_;
471 indent_ += gap_;
472 result_ += "{";
473
474 bool hasContent = false;
475
476 ASSERT(!value->IsAccessor());
477 JSHandle<JSObject> obj(value);
478 if (!replacer->IsArray(thread_)) {
479 if (UNLIKELY(value->IsJSProxy() || value->IsTypedArray())) { // serialize proxy and typedArray
480 JSHandle<TaggedArray> propertyArray = JSObject::EnumerableOwnNames(thread_, obj);
481 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
482 uint32_t arrLength = propertyArray->GetLength();
483 for (uint32_t i = 0; i < arrLength; i++) {
484 handleKey_.Update(propertyArray->Get(thread_, i));
485 JSHandle<JSTaggedValue> valueHandle = JSTaggedValue::GetProperty(thread_, value, handleKey_).GetValue();
486 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
487 JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, valueHandle, replacer);
488 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
489 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
490 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
491 continue;
492 }
493 handleValue_.Update(serializeValue);
494 SerializeObjectKey(handleKey_, hasContent);
495 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
496 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
497 if (!res.IsUndefined()) {
498 hasContent = true;
499 }
500 }
501 } else {
502 uint32_t numOfKeys = obj->GetNumberOfKeys(thread_);
503 uint32_t numOfElements = obj->GetNumberOfElements(thread_);
504 if (numOfElements > 0) {
505 hasContent = JsonStringifier::SerializeElements(obj, replacer, hasContent);
506 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
507 }
508 if (numOfKeys > 0) {
509 hasContent = JsonStringifier::SerializeKeys(obj, replacer, hasContent);
510 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
511 }
512 }
513 } else {
514 uint32_t propLen = propList_.size();
515 for (uint32_t i = 0; i < propLen; i++) {
516 JSTaggedValue tagVal =
517 ObjectFastOperator::FastGetPropertyByValue(thread_, obj.GetTaggedValue(),
518 propList_[i].GetTaggedValue());
519 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
520 handleValue_.Update(tagVal);
521 JSTaggedValue serializeValue = GetSerializeValue(value, propList_[i], handleValue_, replacer);
522 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
523 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
524 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
525 continue;
526 }
527 handleValue_.Update(serializeValue);
528 SerializeObjectKey(propList_[i], hasContent);
529 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
530 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
531 if (!res.IsUndefined()) {
532 hasContent = true;
533 }
534 }
535 }
536 if (hasContent && gap_.length() != 0) {
537 result_ += "\n";
538 result_ += stepback;
539 }
540 result_ += "}";
541 PopValue();
542 indent_ = stepback;
543 return true;
544 }
545
SerializeJSONSharedMap(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)546 bool JsonStringifier::SerializeJSONSharedMap(const JSHandle<JSTaggedValue> &value,
547 const JSHandle<JSTaggedValue> &replacer)
548 {
549 [[maybe_unused]] ConcurrentApiScope<JSSharedMap> scope(thread_, value);
550 JSHandle<JSSharedMap> sharedMap(value);
551 JSHandle<LinkedHashMap> hashMap(thread_, sharedMap->GetLinkedMap(thread_));
552 return SerializeLinkedHashMap(hashMap, replacer);
553 }
554
SerializeJSONSharedSet(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)555 bool JsonStringifier::SerializeJSONSharedSet(const JSHandle<JSTaggedValue> &value,
556 const JSHandle<JSTaggedValue> &replacer)
557 {
558 [[maybe_unused]] ConcurrentApiScope<JSSharedSet> scope(thread_, value);
559 JSHandle<JSSharedSet> sharedSet(value);
560 JSHandle<LinkedHashSet> hashSet(thread_, sharedSet->GetLinkedSet(thread_));
561 return SerializeLinkedHashSet(hashSet, replacer);
562 }
563
SerializeJSONMap(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)564 bool JsonStringifier::SerializeJSONMap(const JSHandle<JSTaggedValue> &value,
565 const JSHandle<JSTaggedValue> &replacer)
566 {
567 JSHandle<JSMap> jsMap(value);
568 JSHandle<LinkedHashMap> hashMap(thread_, jsMap->GetLinkedMap(thread_));
569 return SerializeLinkedHashMap(hashMap, replacer);
570 }
571
SerializeJSONSet(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)572 bool JsonStringifier::SerializeJSONSet(const JSHandle<JSTaggedValue> &value,
573 const JSHandle<JSTaggedValue> &replacer)
574 {
575 JSHandle<JSSet> jsSet(value);
576 JSHandle<LinkedHashSet> hashSet(thread_, jsSet->GetLinkedSet(thread_));
577 return SerializeLinkedHashSet(hashSet, replacer);
578 }
579
SerializeJSONHashMap(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)580 bool JsonStringifier::SerializeJSONHashMap(const JSHandle<JSTaggedValue> &value,
581 const JSHandle<JSTaggedValue> &replacer)
582 {
583 CString stepback = indent_;
584 result_ += "{";
585 JSHandle<JSAPIHashMap> hashMap(value);
586 JSHandle<TaggedHashArray> table(thread_, hashMap->GetTable(thread_));
587 uint32_t len = table->GetLength();
588 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
589 JSMutableHandle<TaggedQueue> queue(thread_, factory->NewTaggedQueue(0));
590 JSMutableHandle<TaggedNode> node(thread_, JSTaggedValue::Undefined());
591 JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
592 JSMutableHandle<JSTaggedValue> valueHandle(thread_, JSTaggedValue::Undefined());
593 uint32_t index = 0;
594 bool needRemove = false;
595 while (index < len) {
596 node.Update(TaggedHashArray::GetCurrentNode(thread_, queue, table, index));
597 if (node.GetTaggedValue().IsHole()) {
598 continue;
599 }
600 keyHandle.Update(node->GetKey(thread_));
601 valueHandle.Update(node->GetValue(thread_));
602 if (valueHandle->IsUndefined()) {
603 continue;
604 }
605 if (UNLIKELY(!keyHandle->IsString())) {
606 result_ += "\"";
607 SerializeJSONProperty(keyHandle, replacer);
608 result_ += "\"";
609 } else {
610 SerializeJSONProperty(keyHandle, replacer);
611 }
612 result_ += ":";
613 SerializeJSONProperty(valueHandle, replacer);
614 result_ += ",";
615 needRemove = true;
616 }
617 if (needRemove) {
618 result_.pop_back();
619 }
620 result_ += "}";
621 PopValue();
622 indent_ = stepback;
623 return true;
624 }
625
SerializeJSONHashSet(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)626 bool JsonStringifier::SerializeJSONHashSet(const JSHandle<JSTaggedValue> &value,
627 const JSHandle<JSTaggedValue> &replacer)
628 {
629 CString stepback = indent_;
630 result_ += "[";
631 JSHandle<JSAPIHashSet> hashSet(value);
632 JSHandle<TaggedHashArray> table(thread_, hashSet->GetTable(thread_));
633 uint32_t len = table->GetLength();
634 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
635 JSMutableHandle<TaggedQueue> queue(thread_, factory->NewTaggedQueue(0));
636 JSMutableHandle<TaggedNode> node(thread_, JSTaggedValue::Undefined());
637 JSMutableHandle<JSTaggedValue> currentKey(thread_, JSTaggedValue::Undefined());
638 uint32_t index = 0;
639 bool needRemove = false;
640 while (index < len) {
641 node.Update(TaggedHashArray::GetCurrentNode(thread_, queue, table, index));
642 if (node.GetTaggedValue().IsHole()) {
643 continue;
644 }
645 currentKey.Update(node->GetKey(thread_));
646 JSTaggedValue res = SerializeJSONProperty(currentKey, replacer);
647 if (res.IsUndefined()) {
648 result_ += "null";
649 }
650 result_ += ",";
651 needRemove = true;
652 }
653 if (needRemove) {
654 result_.pop_back();
655 }
656 result_ += "]";
657 PopValue();
658 indent_ = stepback;
659 return true;
660 }
661
SerializeLinkedHashMap(const JSHandle<LinkedHashMap> & hashMap,const JSHandle<JSTaggedValue> & replacer)662 bool JsonStringifier::SerializeLinkedHashMap(const JSHandle<LinkedHashMap> &hashMap,
663 const JSHandle<JSTaggedValue> &replacer)
664 {
665 CString stepback = indent_;
666 result_ += "{";
667 int index = 0;
668 int totalElements = hashMap->NumberOfElements() + hashMap->NumberOfDeletedElements();
669 JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
670 JSMutableHandle<JSTaggedValue> valHandle(thread_, JSTaggedValue::Undefined());
671 bool needRemove = false;
672 while (index < totalElements) {
673 keyHandle.Update(hashMap->GetKey(thread_, index++));
674 valHandle.Update(hashMap->GetValue(thread_, index - 1));
675 if (keyHandle->IsHole() || valHandle->IsUndefined()) {
676 continue;
677 }
678 if (UNLIKELY(keyHandle->IsUndefined())) {
679 result_ += "\"undefined\"";
680 } else if (UNLIKELY(!keyHandle->IsString())) {
681 result_ += "\"";
682 SerializeJSONProperty(keyHandle, replacer);
683 result_ += "\"";
684 } else {
685 SerializeJSONProperty(keyHandle, replacer);
686 }
687 result_ += ":";
688 SerializeJSONProperty(valHandle, replacer);
689 result_ += ",";
690 needRemove = true;
691 }
692 if (needRemove) {
693 result_.pop_back();
694 }
695 result_ += "}";
696 PopValue();
697 indent_ = stepback;
698 return true;
699 }
700
SerializeLinkedHashSet(const JSHandle<LinkedHashSet> & hashSet,const JSHandle<JSTaggedValue> & replacer)701 bool JsonStringifier::SerializeLinkedHashSet(const JSHandle<LinkedHashSet> &hashSet,
702 const JSHandle<JSTaggedValue> &replacer)
703 {
704 CString stepback = indent_;
705 result_ += "[";
706 JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
707 bool needRemove = false;
708
709 int index = 0;
710 int totalElements = hashSet->NumberOfElements() + hashSet->NumberOfDeletedElements();
711 while (index < totalElements) {
712 keyHandle.Update(hashSet->GetKey(thread_, index++));
713 if (keyHandle->IsHole()) {
714 continue;
715 }
716 JSTaggedValue res = SerializeJSONProperty(keyHandle, replacer);
717 if (res.IsUndefined()) {
718 result_ += "null";
719 }
720 result_ += ",";
721 needRemove = true;
722 }
723 if (needRemove) {
724 result_.pop_back();
725 }
726 result_ += "]";
727 PopValue();
728 indent_ = stepback;
729 return true;
730 }
731
SerializeJSProxy(const JSHandle<JSTaggedValue> & object,const JSHandle<JSTaggedValue> & replacer)732 bool JsonStringifier::SerializeJSProxy(const JSHandle<JSTaggedValue> &object, const JSHandle<JSTaggedValue> &replacer)
733 {
734 bool isContain = PushValue(object);
735 if (isContain) {
736 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
737 }
738
739 CString stepback = indent_;
740 CString stepBegin;
741 indent_ += gap_;
742
743 if (!gap_.empty()) {
744 stepBegin += "\n";
745 stepBegin += indent_;
746 }
747 result_ += "[";
748 JSHandle<JSProxy> proxy(object);
749 JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
750 JSHandle<JSTaggedValue> lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
751 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
752 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
753 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
754 uint32_t length = lenNumber.ToUint32();
755 for (uint32_t i = 0; i < length; i++) {
756 handleKey_.Update(JSTaggedValue(i));
757 JSHandle<JSTaggedValue> valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
758 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
759 if (i > 0) {
760 result_ += ",";
761 }
762 result_ += stepBegin;
763 JSTaggedValue serializeValue = GetSerializeValue(object, handleKey_, valHandle, replacer);
764 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
765 handleValue_.Update(serializeValue);
766 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
767 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
768 if (res.IsUndefined()) {
769 result_ += "null";
770 }
771 }
772
773 if (length > 0 && !gap_.empty()) {
774 result_ += "\n";
775 result_ += stepback;
776 }
777 result_ += "]";
778 PopValue();
779 indent_ = stepback;
780 return true;
781 }
782
SerializeJSArray(const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & replacer)783 bool JsonStringifier::SerializeJSArray(const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &replacer)
784 {
785 // If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
786 bool isContain = PushValue(value);
787 if (isContain) {
788 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
789 }
790
791 CString stepback = indent_;
792 result_ += "[";
793 CString stepBegin;
794 indent_ += gap_;
795
796 if (!gap_.empty()) {
797 stepBegin += "\n";
798 stepBegin += indent_;
799 }
800 uint32_t len = 0;
801 if (value->IsJSArray()) {
802 JSHandle<JSArray> jsArr(value);
803 len = jsArr->GetArrayLength();
804 } else if (value->IsJSSharedArray()) {
805 JSHandle<JSSharedArray> jsArr(value);
806 len = jsArr->GetArrayLength();
807 }
808 if (len > 0) {
809 for (uint32_t i = 0; i < len; i++) {
810 JSTaggedValue tagVal = ObjectFastOperator::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i);
811 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
812 if (UNLIKELY(tagVal.IsAccessor())) {
813 tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value);
814 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
815 }
816 handleKey_.Update(JSTaggedValue(i));
817 handleValue_.Update(tagVal);
818 if (i > 0) {
819 result_ += ",";
820 }
821 result_ += stepBegin;
822 JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, handleValue_, replacer);
823 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
824 handleValue_.Update(serializeValue);
825 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
826 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
827 if (res.IsUndefined()) {
828 result_ += "null";
829 }
830 }
831
832 if (!gap_.empty()) {
833 result_ += "\n";
834 result_ += stepback;
835 indent_ = stepback;
836 }
837 }
838
839 result_ += "]";
840 PopValue();
841 indent_ = stepback;
842 return true;
843 }
844
SerializePrimitiveRef(const JSHandle<JSTaggedValue> & primitiveRef)845 void JsonStringifier::SerializePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef)
846 {
847 JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue(thread_);
848 if (primitive.IsString()) {
849 auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
850 RETURN_IF_ABRUPT_COMPLETION(thread_);
851 CString str = ConvertToString(thread_, *priStr, StringConvertedUsage::LOGICOPERATION);
852 JsonHelper::AppendValueToQuotedString(str, result_);
853 } else if (primitive.IsNumber()) {
854 auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef);
855 RETURN_IF_ABRUPT_COMPLETION(thread_);
856 if (std::isfinite(priNum.GetNumber())) {
857 result_ += ConvertToString(thread_, *base::NumberHelper::NumberToString(thread_, priNum));
858 } else {
859 result_ += "null";
860 }
861 } else if (primitive.IsBoolean()) {
862 result_ += primitive.IsTrue() ? "true" : "false";
863 } else if (primitive.IsBigInt()) {
864 if (transformType_ == TransformType::NORMAL) {
865 THROW_TYPE_ERROR(thread_, "cannot serialize a BigInt");
866 } else {
867 JSHandle<BigInt> thisBigint(thread_, primitive);
868 auto bigIntStr = BigInt::ToString(thread_, thisBigint);
869 result_ += ConvertToString(thread_, *bigIntStr);
870 }
871 }
872 }
873
SerializeElements(const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & replacer,bool hasContent)874 bool JsonStringifier::SerializeElements(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
875 bool hasContent)
876 {
877 if (!ElementAccessor::IsDictionaryMode(thread_, obj)) {
878 uint32_t elementsLen = ElementAccessor::GetElementsLength(thread_, obj);
879 for (uint32_t i = 0; i < elementsLen; ++i) {
880 if (!ElementAccessor::Get(thread_, obj, i).IsHole()) {
881 handleKey_.Update(JSTaggedValue(i));
882 handleValue_.Update(ElementAccessor::Get(thread_, obj, i));
883 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
884 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
885 }
886 }
887 } else {
888 JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements(thread_));
889 JSHandle<NumberDictionary> numberDic(elementsArr);
890 CVector<JSHandle<JSTaggedValue>> sortArr;
891 int size = numberDic->Size();
892 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
893 JSTaggedValue key = numberDic->GetKey(thread_, hashIndex);
894 if (!key.IsUndefined() && !key.IsHole()) {
895 PropertyAttributes attr = numberDic->GetAttributes(thread_, hashIndex);
896 if (attr.IsEnumerable()) {
897 JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
898 sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
899 }
900 }
901 }
902 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
903 for (const auto &entry : sortArr) {
904 JSTaggedValue entryKey = entry.GetTaggedValue();
905 handleKey_.Update(entryKey);
906 int index = numberDic->FindEntry(thread_, entryKey);
907 if (index < 0) {
908 continue;
909 }
910 JSTaggedValue value = numberDic->GetValue(thread_, index);
911 if (UNLIKELY(value.IsAccessor())) {
912 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
913 JSHandle<JSTaggedValue>(obj));
914 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
915 }
916 handleValue_.Update(value);
917 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
918 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
919 }
920 }
921 return hasContent;
922 }
923
SerializeKeys(const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & replacer,bool hasContent)924 bool JsonStringifier::SerializeKeys(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
925 bool hasContent)
926 {
927 JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties(thread_));
928 if (!propertiesArr->IsDictionaryMode()) {
929 bool hasChangedToDictionaryMode = false;
930 JSHandle<JSHClass> jsHClass(thread_, obj->GetJSHClass());
931 if (jsHClass->GetEnumCache(thread_).IsEnumCacheOwnValid(thread_)) {
932 auto cache = JSHClass::GetEnumCacheOwnWithOutCheck(thread_, jsHClass);
933 uint32_t length = cache->GetLength();
934 uint32_t dictStart = length;
935 for (uint32_t i = 0; i < length; i++) {
936 JSTaggedValue key = cache->Get(thread_, i);
937 if (!key.IsString()) {
938 continue;
939 }
940 handleKey_.Update(key);
941 JSTaggedValue value;
942 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHClass->GetLayout(thread_).GetTaggedObject());
943 int index = JSHClass::FindPropertyEntry(thread_, *jsHClass, key);
944 PropertyAttributes attr(layoutInfo->GetAttr(thread_, index));
945 ASSERT(static_cast<int>(attr.GetOffset()) == index);
946 value = attr.IsInlinedProps()
947 ? obj->GetPropertyInlinedPropsWithRep(thread_, static_cast<uint32_t>(index), attr)
948 : propertiesArr->Get(thread_, static_cast<uint32_t>(index) - jsHClass->GetInlinedProperties());
949 if (attr.IsInlinedProps() && value.IsHole()) {
950 continue;
951 }
952 if (UNLIKELY(value.IsAccessor())) {
953 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
954 JSHandle<JSTaggedValue>(obj));
955 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
956 if (obj->GetProperties(thread_).IsDictionary()) {
957 dictStart = i;
958 handleValue_.Update(value);
959 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
960 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
961 break;
962 }
963 }
964 handleValue_.Update(value);
965 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
966 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
967 }
968 if (dictStart < length) {
969 propertiesArr = JSHandle<TaggedArray>(thread_, obj->GetProperties(thread_));
970 JSHandle<NameDictionary> nameDic(propertiesArr);
971 for (uint32_t i = dictStart + 1;i < length; i++) {
972 JSTaggedValue key = cache->Get(thread_, i);
973 int hashIndex = nameDic->FindEntry(thread_, key);
974 PropertyAttributes attr = nameDic->GetAttributes(thread_, hashIndex);
975 if (!key.IsString() || hashIndex < 0 || !attr.IsEnumerable()) {
976 continue;
977 }
978 handleKey_.Update(key);
979 JSTaggedValue value = nameDic->GetValue(thread_, hashIndex);
980 if (UNLIKELY(value.IsAccessor())) {
981 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
982 JSHandle<JSTaggedValue>(obj));
983 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
984 }
985 handleValue_.Update(value);
986 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
987 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
988 }
989 }
990 return hasContent;
991 }
992 int end = static_cast<int>(jsHClass->NumberOfProps());
993 if (end <= 0) {
994 return hasContent;
995 }
996 for (int i = 0; i < end; i++) {
997 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHClass->GetLayout(thread_).GetTaggedObject());
998 JSTaggedValue key = layoutInfo->GetKey(thread_, i);
999 if (!hasChangedToDictionaryMode) {
1000 PropertyAttributes attr(layoutInfo->GetAttr(thread_, i));
1001 ASSERT(static_cast<int>(attr.GetOffset()) == i);
1002 if (key.IsString() && attr.IsEnumerable()) {
1003 handleKey_.Update(key);
1004 JSTaggedValue value = attr.IsInlinedProps()
1005 ? obj->GetPropertyInlinedPropsWithRep(thread_, static_cast<uint32_t>(i), attr)
1006 : propertiesArr->Get(thread_, static_cast<uint32_t>(i) - jsHClass->GetInlinedProperties());
1007 if (attr.IsInlinedProps() && value.IsHole()) {
1008 continue;
1009 }
1010 if (UNLIKELY(value.IsAccessor())) {
1011 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1012 JSHandle<JSTaggedValue>(obj));
1013 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1014 }
1015 handleValue_.Update(value);
1016 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1017 if (obj->GetProperties(thread_).IsDictionary()) {
1018 hasChangedToDictionaryMode = true;
1019 propertiesArr = JSHandle<TaggedArray>(thread_, obj->GetProperties(thread_));
1020 }
1021 jsHClass = JSHandle<JSHClass>(thread_, obj->GetJSHClass());
1022 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1023 }
1024 } else {
1025 JSHandle<NameDictionary> nameDic(propertiesArr);
1026 int index = nameDic->FindEntry(thread_, key);
1027 if (!key.IsString()) {
1028 continue;
1029 }
1030 PropertyAttributes attr = nameDic->GetAttributes(thread_, index);
1031 if (!attr.IsEnumerable() || index < 0) {
1032 continue;
1033 }
1034 JSTaggedValue value = nameDic->GetValue(thread_, index);
1035 handleKey_.Update(key);
1036 if (UNLIKELY(value.IsAccessor())) {
1037 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1038 JSHandle<JSTaggedValue>(obj));
1039 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1040 jsHClass = JSHandle<JSHClass>(thread_, obj->GetJSHClass());
1041 }
1042 handleValue_.Update(value);
1043 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1044 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1045 }
1046 }
1047 return hasContent;
1048 }
1049 if (obj->IsJSGlobalObject()) {
1050 JSHandle<GlobalDictionary> globalDic(propertiesArr);
1051 int size = globalDic->Size();
1052 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
1053 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
1054 JSTaggedValue key = globalDic->GetKey(thread_, hashIndex);
1055 if (!key.IsString()) {
1056 continue;
1057 }
1058 PropertyAttributes attr = globalDic->GetAttributes(thread_, hashIndex);
1059 if (!attr.IsEnumerable()) {
1060 continue;
1061 }
1062 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
1063 sortArr.emplace_back(pair);
1064 }
1065 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
1066 for (const auto &entry : sortArr) {
1067 JSTaggedValue entryKey = entry.first.GetTaggedValue();
1068 handleKey_.Update(entryKey);
1069 int index = globalDic->FindEntry(thread_, entryKey);
1070 if (index == -1) {
1071 continue;
1072 }
1073 JSTaggedValue value = globalDic->GetValue(thread_, index);
1074 if (UNLIKELY(value.IsAccessor())) {
1075 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1076 JSHandle<JSTaggedValue>(obj));
1077 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1078 }
1079 handleValue_.Update(value);
1080 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1081 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1082 }
1083 return hasContent;
1084 }
1085 JSHandle<NameDictionary> nameDic(propertiesArr);
1086 int size = nameDic->Size();
1087 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
1088 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
1089 JSTaggedValue key = nameDic->GetKey(thread_, hashIndex);
1090 if (!key.IsString()) {
1091 continue;
1092 }
1093 PropertyAttributes attr = nameDic->GetAttributes(thread_, hashIndex);
1094 if (!attr.IsEnumerable()) {
1095 continue;
1096 }
1097 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
1098 sortArr.emplace_back(pair);
1099 }
1100 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
1101 for (const auto &entry : sortArr) {
1102 JSTaggedValue entryKey = entry.first.GetTaggedValue();
1103 handleKey_.Update(entryKey);
1104 int index = nameDic->FindEntry(thread_, entryKey);
1105 if (index < 0) {
1106 continue;
1107 }
1108 JSTaggedValue value = nameDic->GetValue(thread_, index);
1109 if (UNLIKELY(value.IsAccessor())) {
1110 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
1111 JSHandle<JSTaggedValue>(obj));
1112 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1113 }
1114 handleValue_.Update(value);
1115 hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent);
1116 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1117 }
1118 return hasContent;
1119 }
1120
AppendJsonString(const JSHandle<JSObject> & obj,const JSHandle<JSTaggedValue> & replacer,bool hasContent)1121 bool JsonStringifier::AppendJsonString(const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &replacer,
1122 bool hasContent)
1123 {
1124 JSTaggedValue serializeValue = GetSerializeValue(JSHandle<JSTaggedValue>(obj), handleKey_, handleValue_, replacer);
1125 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1126 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
1127 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
1128 return hasContent;
1129 }
1130 handleValue_.Update(serializeValue);
1131 SerializeObjectKey(handleKey_, hasContent);
1132 JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer);
1133 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
1134 if (!res.IsUndefined()) {
1135 return true;
1136 }
1137 return hasContent;
1138 }
1139
CheckStackPushSameValue(JSHandle<JSTaggedValue> value)1140 bool JsonStringifier::CheckStackPushSameValue(JSHandle<JSTaggedValue> value)
1141 {
1142 bool isContain = PushValue(value);
1143 if (isContain) {
1144 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value, usually caused by circular structure", true);
1145 }
1146 return false;
1147 }
1148 } // namespace panda::ecmascript::base
1149