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