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