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