1 /*
2 * Copyright (c) 2023 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/fast_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 {
Stringify(const JSHandle<JSTaggedValue> & value)37 JSHandle<JSTaggedValue> FastJsonStringifier::Stringify(const JSHandle<JSTaggedValue> &value)
38 {
39 factory_ = thread_->GetEcmaVM()->GetFactory();
40 JSHandle<JSTaggedValue> jsonCache = thread_->GetEcmaVM()->GetGlobalEnv()->GetJsonObjectHclassCache();
41 if (jsonCache->IsHole()) {
42 hclassCache_ = factory_->NewTaggedArray(JSON_CACHE_SIZE);
43 } else {
44 hclassCache_ = JSHandle<TaggedArray>::Cast(jsonCache);
45 }
46 JSTaggedValue tagValue = value.GetTaggedValue();
47 handleValue_ = JSMutableHandle<JSTaggedValue>(thread_, tagValue);
48 handleKey_ = JSMutableHandle<JSTaggedValue>(thread_, factory_->GetEmptyString());
49
50 if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
51 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
52 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
53 handleValue_.Update(serializeValue);
54 }
55
56 JSTaggedValue result = SerializeJSONProperty(handleValue_);
57
58 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
59 if (!result.IsUndefined()) {
60 return JSHandle<JSTaggedValue>(
61 factory_->NewFromUtf8Literal(reinterpret_cast<const uint8_t *>(result_.c_str()), result_.size()));
62 }
63 return thread_->GlobalConstants()->GetHandledUndefined();
64 }
65
GetSerializeValue(const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)66 JSTaggedValue FastJsonStringifier::GetSerializeValue(const JSHandle<JSTaggedValue> &key,
67 const JSHandle<JSTaggedValue> &value)
68 {
69 JSTaggedValue tagValue = value.GetTaggedValue();
70 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
71 // a. Let toJSON be Get(value, "toJSON").
72 JSHandle<JSTaggedValue> toJson = thread_->GlobalConstants()->GetHandledToJsonString();
73 JSHandle<JSTaggedValue> toJsonFun(
74 thread_, ObjectFastOperator::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue()));
75 // b. ReturnIfAbrupt(toJSON).
76 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
77 tagValue = value.GetTaggedValue();
78 // c. If IsCallable(toJSON) is true
79 if (UNLIKELY(toJsonFun->IsCallable())) {
80 // Let value be Call(toJSON, value, «key»).
81 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, toJsonFun, value, undefined, 1);
82 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
83 info->SetCallArg(key.GetTaggedValue());
84 tagValue = JSFunction::Call(info);
85 // ii. ReturnIfAbrupt(value).
86 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
87 }
88 return tagValue;
89 }
90
SerializeJSONProperty(const JSHandle<JSTaggedValue> & value)91 JSTaggedValue FastJsonStringifier::SerializeJSONProperty(const JSHandle<JSTaggedValue> &value)
92 {
93 JSTaggedValue tagValue = value.GetTaggedValue();
94 if (!tagValue.IsHeapObject()) {
95 JSTaggedType type = tagValue.GetRawData();
96 switch (type) {
97 // If value is false, return "false".
98 case JSTaggedValue::VALUE_FALSE:
99 result_ += "false";
100 return tagValue;
101 // If value is true, return "true".
102 case JSTaggedValue::VALUE_TRUE:
103 result_ += "true";
104 return tagValue;
105 // If value is null, return "null".
106 case JSTaggedValue::VALUE_NULL:
107 result_ += "null";
108 return tagValue;
109 default:
110 // If Type(value) is Number, then
111 if (tagValue.IsNumber()) {
112 // a. If value is finite, return ToString(value).
113 if (std::isfinite(tagValue.GetNumber())) {
114 result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue));
115 } else {
116 // b. Else, return "null".
117 result_ += "null";
118 }
119 return tagValue;
120 }
121 }
122 } else {
123 JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType();
124 JSHandle<JSTaggedValue> valHandle(thread_, tagValue);
125 switch (jsType) {
126 case JSType::JS_ARRAY: {
127 SerializeJSArray(valHandle);
128 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
129 return tagValue;
130 }
131 // If Type(value) is String, return QuoteJSONString(value).
132 case JSType::LINE_STRING:
133 case JSType::CONSTANT_STRING:
134 case JSType::TREE_STRING:
135 case JSType::SLICED_STRING: {
136 JSHandle<EcmaString> strHandle = JSHandle<EcmaString>(valHandle);
137 auto string = JSHandle<EcmaString>(thread_,
138 EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
139 CString str = ConvertToString(*string, StringConvertedUsage::LOGICOPERATION);
140 str = JsonHelper::ValueToQuotedString(str);
141 result_ += str;
142 return tagValue;
143 }
144 case JSType::JS_PRIMITIVE_REF: {
145 SerializePrimitiveRef(valHandle);
146 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, JSTaggedValue::Exception());
147 return tagValue;
148 }
149 case JSType::SYMBOL:
150 return JSTaggedValue::Undefined();
151 case JSType::BIGINT: {
152 THROW_TYPE_ERROR_AND_RETURN(thread_, "cannot serialize a BigInt", JSTaggedValue::Exception());
153 }
154 default: {
155 if (!tagValue.IsCallable()) {
156 JSHClass *jsHclass = tagValue.GetTaggedObject()->GetClass();
157 if (UNLIKELY(jsHclass->IsJSProxy() &&
158 JSProxy::Cast(tagValue.GetTaggedObject())->IsArray(thread_))) {
159 SerializeJSProxy(valHandle);
160 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
161 } else {
162 SerializeJSONObject(valHandle);
163 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
164 }
165 return tagValue;
166 }
167 }
168 }
169 }
170 return JSTaggedValue::Undefined();
171 }
172
SerializeObjectKey(const JSHandle<JSTaggedValue> & key,bool hasContent)173 CString FastJsonStringifier::SerializeObjectKey(const JSHandle<JSTaggedValue> &key, bool hasContent)
174 {
175 if (hasContent) {
176 result_ += ",";
177 }
178
179 CString str;
180 if (key->IsString()) {
181 str = ConvertToString(EcmaString::Cast(key->GetTaggedObject()), StringConvertedUsage::LOGICOPERATION);
182 } else if (key->IsInt()) {
183 str = NumberHelper::IntToString(static_cast<int32_t>(key->GetInt()));
184 } else {
185 str = ConvertToString(*JSTaggedValue::ToString(thread_, key), StringConvertedUsage::LOGICOPERATION);
186 }
187 str = JsonHelper::ValueToQuotedString(str);
188 result_ += str;
189 result_ += ":";
190
191 return str;
192 }
193
PushValue(const JSHandle<JSTaggedValue> & value)194 bool FastJsonStringifier::PushValue(const JSHandle<JSTaggedValue> &value)
195 {
196 uint32_t thisLen = stack_.size();
197
198 for (uint32_t i = 0; i < thisLen; i++) {
199 bool equal = JSTaggedValue::SameValue(stack_[i].GetTaggedValue(), value.GetTaggedValue());
200 if (equal) {
201 return true;
202 }
203 }
204
205 stack_.emplace_back(value);
206 return false;
207 }
208
PopValue()209 void FastJsonStringifier::PopValue()
210 {
211 stack_.pop_back();
212 }
213
SerializeJSONObject(const JSHandle<JSTaggedValue> & value)214 bool FastJsonStringifier::SerializeJSONObject(const JSHandle<JSTaggedValue> &value)
215 {
216 bool isContain = PushValue(value);
217 if (isContain) {
218 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
219 }
220
221 result_ += "{";
222 bool hasContent = false;
223
224 ASSERT(!value->IsAccessor());
225 JSHandle<JSObject> obj(value);
226 if (UNLIKELY(value->IsJSProxy() || value->IsTypedArray())) { // serialize proxy and typedArray
227 JSHandle<TaggedArray> propertyArray = JSObject::EnumerableOwnNames(thread_, obj);
228 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
229 uint32_t arrLength = propertyArray->GetLength();
230 for (uint32_t i = 0; i < arrLength; i++) {
231 handleKey_.Update(propertyArray->Get(i));
232 JSHandle<JSTaggedValue> valueHandle = JSTaggedValue::GetProperty(thread_, value, handleKey_).GetValue();
233 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
234 if (UNLIKELY(valueHandle->IsECMAObject() || valueHandle->IsBigInt())) {
235 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, valueHandle);
236 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
237 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
238 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
239 continue;
240 }
241 handleValue_.Update(serializeValue);
242 } else {
243 handleValue_.Update(valueHandle);
244 }
245 SerializeObjectKey(handleKey_, hasContent);
246 JSTaggedValue res = SerializeJSONProperty(handleValue_);
247 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
248 if (!res.IsUndefined()) {
249 hasContent = true;
250 }
251 }
252 } else {
253 uint32_t numOfKeys = obj->GetNumberOfKeys();
254 uint32_t numOfElements = obj->GetNumberOfElements();
255 if (numOfKeys + numOfElements < CACHE_MINIMUN_SIZIE || !cacheable_) {
256 if (numOfElements > 0) {
257 hasContent = DefaultSerializeElements(obj, hasContent);
258 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
259 }
260 if (numOfKeys > 0) {
261 hasContent = DefaultSerializeKeys(obj, hasContent);
262 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
263 }
264 } else {
265 JSHClass *jsHclass = value->GetTaggedObject()->GetClass();
266 int32_t index = FindCache(jsHclass, numOfKeys + numOfElements);
267 if (index != INVALID_INDEX) {
268 auto strCache = thread_->GetCurrentEcmaContext()->GetJsonStringifyCache(index);
269 uint32_t cacheIndex = 0;
270 if (numOfElements > 0) {
271 hasContent = SerializeElementsWithCache(obj, hasContent, strCache, cacheIndex, numOfElements);
272 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
273 }
274 if (numOfKeys > 0) {
275 hasContent = SerializeKeysWithCache(obj, hasContent, strCache, cacheIndex);
276 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
277 }
278 } else {
279 CVector<std::pair<CString, int>> strCache;
280 if (numOfElements > 0) {
281 hasContent = TryCacheSerializeElements(obj, hasContent, strCache);
282 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
283 }
284 if (numOfKeys > 0) {
285 hasContent = TryCacheSerializeKeys(obj, hasContent, strCache);
286 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
287 }
288 if (cacheable_) {
289 SetCache(value->GetTaggedObject()->GetClass(), numOfElements + numOfKeys, strCache);
290 }
291 }
292 }
293 }
294
295 result_ += "}";
296 PopValue();
297 return true;
298 }
299
SerializeJSProxy(const JSHandle<JSTaggedValue> & object)300 bool FastJsonStringifier::SerializeJSProxy(const JSHandle<JSTaggedValue> &object)
301 {
302 bool isContain = PushValue(object);
303 if (isContain) {
304 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
305 }
306
307 result_ += "[";
308 JSHandle<JSProxy> proxy(object);
309 JSHandle<JSTaggedValue> lengthKey = thread_->GlobalConstants()->GetHandledLengthString();
310 JSHandle<JSTaggedValue> lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue();
311 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
312 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle);
313 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
314 uint32_t length = lenNumber.ToUint32();
315 for (uint32_t i = 0; i < length; i++) {
316 handleKey_.Update(JSTaggedValue(i));
317 JSHandle<JSTaggedValue> valHandle = JSProxy::GetProperty(thread_, proxy, handleKey_).GetValue();
318 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
319 if (i > 0) {
320 result_ += ",";
321 }
322 if (UNLIKELY(valHandle->IsECMAObject() || valHandle->IsBigInt())) {
323 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, valHandle);
324 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
325 handleValue_.Update(serializeValue);
326 } else {
327 handleValue_.Update(valHandle);
328 }
329 JSTaggedValue res = SerializeJSONProperty(handleValue_);
330 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
331 if (res.IsUndefined()) {
332 result_ += "null";
333 }
334 }
335
336 result_ += "]";
337 PopValue();
338 return true;
339 }
340
SerializeJSArray(const JSHandle<JSTaggedValue> & value)341 bool FastJsonStringifier::SerializeJSArray(const JSHandle<JSTaggedValue> &value)
342 {
343 // If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.
344 bool isContain = PushValue(value);
345 if (isContain) {
346 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
347 }
348
349 result_ += "[";
350 JSHandle<JSArray> jsArr(value);
351 uint32_t len = jsArr->GetArrayLength();
352 if (len > 0) {
353 for (uint32_t i = 0; i < len; i++) {
354 JSTaggedValue tagVal = ObjectFastOperator::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i);
355 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
356 if (UNLIKELY(tagVal.IsAccessor())) {
357 tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value);
358 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
359 }
360 handleKey_.Update(JSTaggedValue(i));
361 handleValue_.Update(tagVal);
362
363 if (i > 0) {
364 result_ += ",";
365 }
366 if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
367 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
368 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
369 handleValue_.Update(serializeValue);
370 }
371 JSTaggedValue res = SerializeJSONProperty(handleValue_);
372 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
373 if (res.IsUndefined()) {
374 result_ += "null";
375 }
376 }
377 }
378
379 result_ += "]";
380 PopValue();
381 return true;
382 }
383
SerializePrimitiveRef(const JSHandle<JSTaggedValue> & primitiveRef)384 void FastJsonStringifier::SerializePrimitiveRef(const JSHandle<JSTaggedValue> &primitiveRef)
385 {
386 JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue();
387 if (primitive.IsString()) {
388 auto priStr = JSTaggedValue::ToString(thread_, primitiveRef);
389 RETURN_IF_ABRUPT_COMPLETION(thread_);
390 CString str = ConvertToString(*priStr, StringConvertedUsage::LOGICOPERATION);
391 str = JsonHelper::ValueToQuotedString(str);
392 result_ += str;
393 } else if (primitive.IsNumber()) {
394 auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef);
395 RETURN_IF_ABRUPT_COMPLETION(thread_);
396 if (std::isfinite(priNum.GetNumber())) {
397 result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, priNum));
398 } else {
399 result_ += "null";
400 }
401 } else if (primitive.IsBoolean()) {
402 result_ += primitive.IsTrue() ? "true" : "false";
403 } else if (primitive.IsBigInt()) {
404 THROW_TYPE_ERROR(thread_, "cannot serialize a BigInt");
405 }
406 }
407
TryCacheSerializeElements(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache)408 bool FastJsonStringifier::TryCacheSerializeElements(const JSHandle<JSObject> &obj, bool hasContent,
409 CVector<std::pair<CString, int>> &strCache)
410 {
411 if (!ElementAccessor::IsDictionaryMode(obj)) {
412 uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
413 for (uint32_t i = 0; i < elementsLen; ++i) {
414 if (!ElementAccessor::Get(obj, i).IsHole()) {
415 handleKey_.Update(JSTaggedValue(i));
416 handleValue_.Update(ElementAccessor::Get(obj, i));
417 hasContent = AppendJsonString(hasContent, strCache, i);
418 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
419 }
420 }
421 } else {
422 JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
423 JSHandle<NumberDictionary> numberDic(elementsArr);
424 CVector<JSHandle<JSTaggedValue>> sortArr;
425 int size = numberDic->Size();
426 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
427 JSTaggedValue key = numberDic->GetKey(hashIndex);
428 if (!key.IsUndefined() && !key.IsHole()) {
429 PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
430 if (attr.IsEnumerable()) {
431 JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
432 sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
433 }
434 }
435 }
436 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
437 for (const auto &entry : sortArr) {
438 JSTaggedValue entryKey = entry.GetTaggedValue();
439 handleKey_.Update(entryKey);
440 int index = numberDic->FindEntry(entryKey);
441 JSTaggedValue value = numberDic->GetValue(index);
442 if (UNLIKELY(value.IsAccessor())) {
443 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
444 JSHandle<JSTaggedValue>(obj));
445 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
446 }
447 handleValue_.Update(value);
448 hasContent = AppendJsonString(hasContent, strCache, index);
449 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
450 }
451 }
452 return hasContent;
453 }
454
SerializeElementsWithCache(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache,uint32_t & cacheIndex,uint32_t elementSize)455 bool FastJsonStringifier::SerializeElementsWithCache(const JSHandle<JSObject> &obj, bool hasContent,
456 CVector<std::pair<CString, int>> &strCache, uint32_t &cacheIndex, uint32_t elementSize)
457 {
458 if (!ElementAccessor::IsDictionaryMode(obj)) {
459 uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
460 for (uint32_t i = 0; i < elementsLen; ++i) {
461 if (!ElementAccessor::Get(obj, i).IsHole()) {
462 CString key = strCache[cacheIndex++].first;
463 handleValue_.Update(ElementAccessor::Get(obj, i));
464 hasContent = FastAppendJsonString(hasContent, key);
465 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
466 }
467 }
468 } else {
469 JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
470 JSHandle<NumberDictionary> numberDic(elementsArr);
471 for (; cacheIndex < elementSize; cacheIndex++) {
472 CString key = strCache[cacheIndex].first;
473 int index = strCache[cacheIndex].second;
474 JSTaggedValue value = numberDic->GetValue(index);
475 if (UNLIKELY(value.IsAccessor())) {
476 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
477 JSHandle<JSTaggedValue>(obj));
478 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
479 }
480 handleValue_.Update(value);
481 hasContent = FastAppendJsonString(hasContent, key);
482 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
483 }
484 }
485 return hasContent;
486 }
487
TryCacheSerializeKeys(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache)488 bool FastJsonStringifier::TryCacheSerializeKeys(const JSHandle<JSObject> &obj, bool hasContent,
489 CVector<std::pair<CString, int>> &strCache)
490 {
491 JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
492 if (!propertiesArr->IsDictionaryMode()) {
493 JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
494 JSTaggedValue enumCache = jsHclass->GetEnumCache();
495 if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
496 JSHandle<TaggedArray> cache(thread_, enumCache);
497 uint32_t length = cache->GetLength();
498 for (uint32_t i = 0; i < length; i++) {
499 JSTaggedValue key = cache->Get(i);
500 if (!key.IsString()) {
501 continue;
502 }
503 handleKey_.Update(key);
504 JSTaggedValue value;
505 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
506 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
507 PropertyAttributes attr(layoutInfo->GetAttr(index));
508 ASSERT(static_cast<int>(attr.GetOffset()) == index);
509 value = attr.IsInlinedProps()
510 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
511 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
512 if (attr.IsInlinedProps() && value.IsHole()) {
513 continue;
514 }
515 if (UNLIKELY(value.IsAccessor())) {
516 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
517 JSHandle<JSTaggedValue>(obj));
518 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
519 }
520 handleValue_.Update(value);
521 hasContent = AppendJsonString(hasContent, strCache, index);
522 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
523 }
524 return hasContent;
525 }
526 int end = static_cast<int>(jsHclass->NumberOfProps());
527 if (end <= 0) {
528 return hasContent;
529 }
530 for (int i = 0; i < end; i++) {
531 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
532 JSTaggedValue key = layoutInfo->GetKey(i);
533 if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
534 handleKey_.Update(key);
535 JSTaggedValue value;
536 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
537 PropertyAttributes attr(layoutInfo->GetAttr(index));
538 ASSERT(static_cast<int>(attr.GetOffset()) == index);
539 value = attr.IsInlinedProps()
540 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
541 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
542 if (attr.IsInlinedProps() && value.IsHole()) {
543 continue;
544 }
545 if (UNLIKELY(value.IsAccessor())) {
546 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
547 JSHandle<JSTaggedValue>(obj));
548 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
549 }
550 handleValue_.Update(value);
551 hasContent = AppendJsonString(hasContent, strCache, index);
552 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
553 }
554 }
555 return hasContent;
556 }
557 if (obj->IsJSGlobalObject()) {
558 JSHandle<GlobalDictionary> globalDic(propertiesArr);
559 int size = globalDic->Size();
560 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
561 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
562 JSTaggedValue key = globalDic->GetKey(hashIndex);
563 if (!key.IsString()) {
564 continue;
565 }
566 PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
567 if (!attr.IsEnumerable()) {
568 continue;
569 }
570 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
571 sortArr.emplace_back(pair);
572 }
573 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
574 for (const auto &entry : sortArr) {
575 JSTaggedValue entryKey = entry.first.GetTaggedValue();
576 handleKey_.Update(entryKey);
577 int index = globalDic->FindEntry(entryKey);
578 JSTaggedValue value = globalDic->GetValue(index);
579 if (UNLIKELY(value.IsAccessor())) {
580 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
581 JSHandle<JSTaggedValue>(obj));
582 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
583 }
584 handleValue_.Update(value);
585 hasContent = AppendJsonString(hasContent, strCache, index);
586 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
587 }
588 return hasContent;
589 }
590 JSHandle<NameDictionary> nameDic(propertiesArr);
591 int size = nameDic->Size();
592 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
593 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
594 JSTaggedValue key = nameDic->GetKey(hashIndex);
595 if (!key.IsString()) {
596 continue;
597 }
598 PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
599 if (!attr.IsEnumerable()) {
600 continue;
601 }
602 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
603 sortArr.emplace_back(pair);
604 }
605 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
606 for (const auto &entry : sortArr) {
607 JSTaggedValue entryKey = entry.first.GetTaggedValue();
608 handleKey_.Update(entryKey);
609 int index = nameDic->FindEntry(entryKey);
610 JSTaggedValue value = nameDic->GetValue(index);
611 if (UNLIKELY(value.IsAccessor())) {
612 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
613 JSHandle<JSTaggedValue>(obj));
614 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
615 }
616 handleValue_.Update(value);
617 hasContent = AppendJsonString(hasContent, strCache, index);
618 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
619 }
620 return hasContent;
621 }
622
SerializeKeysWithCache(const JSHandle<JSObject> & obj,bool hasContent,CVector<std::pair<CString,int>> & strCache,uint32_t & cacheIndex)623 bool FastJsonStringifier::SerializeKeysWithCache(const JSHandle<JSObject> &obj, bool hasContent,
624 CVector<std::pair<CString, int>> &strCache, uint32_t &cacheIndex)
625 {
626 JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
627 JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
628 if (!propertiesArr->IsDictionaryMode()) {
629 for (; cacheIndex < strCache.size(); cacheIndex++) {
630 auto cacheValue = strCache[cacheIndex];
631 CString str = cacheValue.first;
632 int index = cacheValue.second;
633 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
634 PropertyAttributes attr(layoutInfo->GetAttr(index));
635 JSTaggedValue value = attr.IsInlinedProps()
636 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
637 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
638 if (UNLIKELY(value.IsAccessor())) {
639 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
640 JSHandle<JSTaggedValue>(obj));
641 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
642 }
643 handleValue_.Update(value);
644 hasContent = FastAppendJsonString(hasContent, str);
645 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
646 }
647 return hasContent;
648 }
649 if (obj->IsJSGlobalObject()) {
650 JSHandle<GlobalDictionary> globalDic(propertiesArr);
651 for (; cacheIndex < strCache.size(); cacheIndex++) {
652 auto cacheValue = strCache[cacheIndex];
653 CString str = cacheValue.first;
654 int index = cacheValue.second;
655 JSTaggedValue value = globalDic->GetValue(index);
656 if (UNLIKELY(value.IsAccessor())) {
657 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
658 JSHandle<JSTaggedValue>(obj));
659 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
660 }
661 handleValue_.Update(value);
662 hasContent = FastAppendJsonString(hasContent, str);
663 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
664 }
665 return hasContent;
666 }
667 JSHandle<NameDictionary> nameDic(propertiesArr);
668 for (; cacheIndex < strCache.size(); cacheIndex++) {
669 auto cacheValue = strCache[cacheIndex];
670 CString str = cacheValue.first;
671 int index = cacheValue.second;
672 JSTaggedValue value = nameDic->GetValue(index);
673 if (UNLIKELY(value.IsAccessor())) {
674 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
675 JSHandle<JSTaggedValue>(obj));
676 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
677 }
678 handleValue_.Update(value);
679 hasContent = FastAppendJsonString(hasContent, str);
680 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
681 }
682 return hasContent;
683 }
684
AppendJsonString(bool hasContent,CVector<std::pair<CString,int>> & strCache,int index)685 bool FastJsonStringifier::AppendJsonString(bool hasContent, CVector<std::pair<CString, int>> &strCache, int index)
686 {
687 if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
688 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
689 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
690 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
691 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
692 return hasContent;
693 }
694 handleValue_.Update(serializeValue);
695 }
696 CString keyStr = SerializeObjectKey(handleKey_, hasContent);
697 strCache.emplace_back(std::pair<CString, int>(keyStr, index));
698 JSTaggedValue res = SerializeJSONProperty(handleValue_);
699 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
700 if (!res.IsUndefined()) {
701 return true;
702 }
703 EraseKeyString(keyStr, hasContent);
704 return hasContent;
705 }
706
FastAppendJsonString(bool hasContent,CString & key)707 bool FastJsonStringifier::FastAppendJsonString(bool hasContent, CString &key)
708 {
709 if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
710 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
711 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
712 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
713 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
714 return hasContent;
715 }
716 handleValue_.Update(serializeValue);
717 }
718 FastSerializeObjectKey(key, hasContent);
719 JSTaggedValue res = SerializeJSONProperty(handleValue_);
720 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
721 if (!res.IsUndefined()) {
722 return true;
723 }
724 EraseKeyString(key, hasContent);
725 return hasContent;
726 }
727
DefaultSerializeElements(const JSHandle<JSObject> & obj,bool hasContent)728 bool FastJsonStringifier::DefaultSerializeElements(const JSHandle<JSObject> &obj, bool hasContent)
729 {
730 if (!ElementAccessor::IsDictionaryMode(obj)) {
731 uint32_t elementsLen = ElementAccessor::GetElementsLength(obj);
732 for (uint32_t i = 0; i < elementsLen; ++i) {
733 if (!ElementAccessor::Get(obj, i).IsHole()) {
734 handleKey_.Update(JSTaggedValue(i));
735 handleValue_.Update(ElementAccessor::Get(obj, i));
736 hasContent = AppendJsonString(hasContent);
737 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
738 }
739 }
740 } else {
741 JSHandle<TaggedArray> elementsArr(thread_, obj->GetElements());
742 JSHandle<NumberDictionary> numberDic(elementsArr);
743 CVector<JSHandle<JSTaggedValue>> sortArr;
744 int size = numberDic->Size();
745 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
746 JSTaggedValue key = numberDic->GetKey(hashIndex);
747 if (!key.IsUndefined() && !key.IsHole()) {
748 PropertyAttributes attr = numberDic->GetAttributes(hashIndex);
749 if (attr.IsEnumerable()) {
750 JSTaggedValue numberKey = JSTaggedValue(static_cast<uint32_t>(key.GetInt()));
751 sortArr.emplace_back(JSHandle<JSTaggedValue>(thread_, numberKey));
752 }
753 }
754 }
755 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber);
756 for (const auto &entry : sortArr) {
757 JSTaggedValue entryKey = entry.GetTaggedValue();
758 handleKey_.Update(entryKey);
759 int index = numberDic->FindEntry(entryKey);
760 JSTaggedValue value = numberDic->GetValue(index);
761 if (UNLIKELY(value.IsAccessor())) {
762 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
763 JSHandle<JSTaggedValue>(obj));
764 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
765 }
766 handleValue_.Update(value);
767 hasContent = AppendJsonString(hasContent);
768 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
769 }
770 }
771 return hasContent;
772 }
773
DefaultSerializeKeys(const JSHandle<JSObject> & obj,bool hasContent)774 bool FastJsonStringifier::DefaultSerializeKeys(const JSHandle<JSObject> &obj, bool hasContent)
775 {
776 JSHandle<TaggedArray> propertiesArr(thread_, obj->GetProperties());
777 if (!propertiesArr->IsDictionaryMode()) {
778 JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
779 JSTaggedValue enumCache = jsHclass->GetEnumCache();
780 if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
781 JSHandle<TaggedArray> cache(thread_, enumCache);
782 uint32_t length = cache->GetLength();
783 for (uint32_t i = 0; i < length; i++) {
784 JSTaggedValue key = cache->Get(i);
785 if (!key.IsString()) {
786 continue;
787 }
788 handleKey_.Update(key);
789 JSTaggedValue value;
790 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
791 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
792 PropertyAttributes attr(layoutInfo->GetAttr(index));
793 ASSERT(static_cast<int>(attr.GetOffset()) == index);
794 value = attr.IsInlinedProps()
795 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
796 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
797 if (attr.IsInlinedProps() && value.IsHole()) {
798 continue;
799 }
800 if (UNLIKELY(value.IsAccessor())) {
801 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
802 JSHandle<JSTaggedValue>(obj));
803 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
804 }
805 handleValue_.Update(value);
806 hasContent = AppendJsonString(hasContent);
807 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
808 }
809 return hasContent;
810 }
811 int end = static_cast<int>(jsHclass->NumberOfProps());
812 if (end <= 0) {
813 return hasContent;
814 }
815 for (int i = 0; i < end; i++) {
816 LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject());
817 JSTaggedValue key = layoutInfo->GetKey(i);
818 if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) {
819 handleKey_.Update(key);
820 JSTaggedValue value;
821 int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key);
822 PropertyAttributes attr(layoutInfo->GetAttr(index));
823 ASSERT(static_cast<int>(attr.GetOffset()) == index);
824 value = attr.IsInlinedProps()
825 ? obj->GetPropertyInlinedPropsWithRep(static_cast<uint32_t>(index), attr)
826 : propertiesArr->Get(static_cast<uint32_t>(index) - jsHclass->GetInlinedProperties());
827 if (attr.IsInlinedProps() && value.IsHole()) {
828 continue;
829 }
830 if (UNLIKELY(value.IsAccessor())) {
831 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
832 JSHandle<JSTaggedValue>(obj));
833 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
834 }
835 handleValue_.Update(value);
836 hasContent = AppendJsonString(hasContent);
837 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
838 }
839 }
840 return hasContent;
841 }
842 if (obj->IsJSGlobalObject()) {
843 JSHandle<GlobalDictionary> globalDic(propertiesArr);
844 int size = globalDic->Size();
845 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
846 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
847 JSTaggedValue key = globalDic->GetKey(hashIndex);
848 if (!key.IsString()) {
849 continue;
850 }
851 PropertyAttributes attr = globalDic->GetAttributes(hashIndex);
852 if (!attr.IsEnumerable()) {
853 continue;
854 }
855 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
856 sortArr.emplace_back(pair);
857 }
858 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
859 for (const auto &entry : sortArr) {
860 JSTaggedValue entryKey = entry.first.GetTaggedValue();
861 handleKey_.Update(entryKey);
862 int index = globalDic->FindEntry(entryKey);
863 JSTaggedValue value = globalDic->GetValue(index);
864 if (UNLIKELY(value.IsAccessor())) {
865 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
866 JSHandle<JSTaggedValue>(obj));
867 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
868 }
869 handleValue_.Update(value);
870 hasContent = AppendJsonString(hasContent);
871 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
872 }
873 return hasContent;
874 }
875 JSHandle<NameDictionary> nameDic(propertiesArr);
876 int size = nameDic->Size();
877 CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
878 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
879 JSTaggedValue key = nameDic->GetKey(hashIndex);
880 if (!key.IsString()) {
881 continue;
882 }
883 PropertyAttributes attr = nameDic->GetAttributes(hashIndex);
884 if (!attr.IsEnumerable()) {
885 continue;
886 }
887 std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(JSHandle<JSTaggedValue>(thread_, key), attr);
888 sortArr.emplace_back(pair);
889 }
890 std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey);
891 for (const auto &entry : sortArr) {
892 JSTaggedValue entryKey = entry.first.GetTaggedValue();
893 handleKey_.Update(entryKey);
894 int index = nameDic->FindEntry(entryKey);
895 JSTaggedValue value = nameDic->GetValue(index);
896 if (UNLIKELY(value.IsAccessor())) {
897 value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()),
898 JSHandle<JSTaggedValue>(obj));
899 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
900 }
901 handleValue_.Update(value);
902 hasContent = AppendJsonString(hasContent);
903 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
904 }
905 return hasContent;
906 }
907
AppendJsonString(bool hasContent)908 bool FastJsonStringifier::AppendJsonString(bool hasContent)
909 {
910 if (handleValue_->IsECMAObject() || handleValue_->IsBigInt()) {
911 JSTaggedValue serializeValue = GetSerializeValue(handleKey_, handleValue_);
912 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
913 if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() ||
914 (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) {
915 return hasContent;
916 }
917 handleValue_.Update(serializeValue);
918 }
919 CString keyStr = SerializeObjectKey(handleKey_, hasContent);
920 JSTaggedValue res = SerializeJSONProperty(handleValue_);
921 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
922 if (!res.IsUndefined()) {
923 return true;
924 }
925 EraseKeyString(keyStr, hasContent);
926 return hasContent;
927 }
928
DefaultSerializeObject(const JSTaggedValue & object,uint32_t numOfKeys,uint32_t numOfElements)929 bool FastJsonStringifier::DefaultSerializeObject(const JSTaggedValue &object, uint32_t numOfKeys,
930 uint32_t numOfElements)
931 {
932 JSHandle<JSTaggedValue> value(thread_, object);
933 bool isContain = PushValue(value);
934 if (isContain) {
935 THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true);
936 }
937
938 result_ += "{";
939 bool hasContent = false;
940
941 JSHandle<JSObject> obj(value);
942 if (numOfElements > 0) {
943 hasContent = DefaultSerializeElements(obj, hasContent);
944 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
945 }
946 if (numOfKeys > 0) {
947 hasContent = DefaultSerializeKeys(obj, hasContent);
948 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
949 }
950
951 result_ += "}";
952 PopValue();
953 return true;
954 }
955 } // namespace panda::ecmascript::base
956