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