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