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