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