1 /*
2 * Copyright (c) 2021-2024 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/jspandafile/class_info_extractor.h"
17
18 #include "ecmascript/js_object-inl.h"
19 #include "ecmascript/jspandafile/program_object.h"
20 #include "ecmascript/shared_objects/js_sendable_arraybuffer.h"
21 #include "ecmascript/shared_objects/js_shared_map.h"
22 #include "ecmascript/object_fast_operator-inl.h"
23
24 namespace panda::ecmascript {
BuildClassInfoExtractorFromLiteral(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & literal,uint32_t length,ClassKind kind)25 void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
26 const JSHandle<TaggedArray> &literal,
27 uint32_t length,
28 ClassKind kind)
29 {
30 [[maybe_unused]] EcmaHandleScope handleScope(thread);
31 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
32 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33
34 ASSERT(length <= literal->GetLength());
35 // non static properties number is hidden in the last index of Literal buffer
36 uint32_t nonStaticNum = 0;
37 if (length != 0) {
38 nonStaticNum = static_cast<uint32_t>(literal->Get(thread, length - 1).GetInt());
39 }
40
41 // Reserve sufficient length to prevent frequent creation.
42 JSHandle<TaggedArray> nonStaticKeys;
43 JSHandle<TaggedArray> nonStaticProperties;
44 if (kind == ClassKind::SENDABLE) {
45 nonStaticKeys = factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
46 nonStaticProperties =
47 factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
48 } else {
49 nonStaticKeys = factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
50 nonStaticProperties =
51 factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
52 }
53
54 nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString());
55 Method *method = Method::Cast(extractor->GetConstructorMethod().GetTaggedObject());
56 MethodLiteral *methodLiteral = method->GetMethodLiteral();
57 const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
58 EntityId methodId = method->GetMethodId();
59 if (nonStaticNum) {
60 ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr};
61
62 JSHandle<TaggedArray> nonStaticElements = factory->EmptyArray();
63 if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys,
64 nonStaticProperties, nonStaticElements, jsPandaFile))) {
65 extractor->SetNonStaticWithElements(true);
66 extractor->SetNonStaticElements(thread, nonStaticElements);
67 }
68 }
69
70 extractor->SetNonStaticKeys(thread, nonStaticKeys);
71 extractor->SetNonStaticProperties(thread, nonStaticProperties);
72
73 uint32_t staticNum = length == 0 ? 0 : (length - 1) / 2 - nonStaticNum;
74
75 // Reserve sufficient length to prevent frequent creation.
76 JSHandle<TaggedArray> staticKeys;
77 JSHandle<TaggedArray> staticProperties;
78 if (kind == ClassKind::SENDABLE) {
79 staticKeys = factory->NewSOldSpaceTaggedArray(staticNum + SENDABLE_STATIC_RESERVED_LENGTH);
80 staticProperties = factory->NewSOldSpaceTaggedArray(staticNum + SENDABLE_STATIC_RESERVED_LENGTH);
81 } else {
82 staticKeys = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
83 staticProperties = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
84 }
85
86 staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString());
87 staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString());
88 staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString());
89 if (kind == ClassKind::SENDABLE) {
90 staticKeys->Set(thread, SENDABLE_ELEMENTS_INDEX, globalConst->GetSendableElementsSymbol());
91 }
92
93 JSHandle<TaggedArray> staticElements = factory->EmptyArray();
94
95 if (staticNum) {
96 ExtractContentsDetail staticDetail {};
97
98 if (kind == ClassKind::SENDABLE) {
99 staticDetail = { nonStaticNum * 2, length - 1, SENDABLE_STATIC_RESERVED_LENGTH, methodLiteral };
100 } else {
101 staticDetail = { nonStaticNum * 2, length - 1, STATIC_RESERVED_LENGTH, methodLiteral };
102 }
103
104 if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys,
105 staticProperties, staticElements, jsPandaFile))) {
106 extractor->SetStaticWithElements(true);
107 extractor->SetStaticElements(thread, staticElements);
108 }
109 } else {
110 // without static properties, set class name
111 std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
112 JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
113 staticProperties->Set(thread, NAME_INDEX, clsNameHandle);
114 }
115
116 // set prototype internal accessor
117 JSHandle<JSTaggedValue> prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor();
118 staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor);
119 if (kind == ClassKind::SENDABLE) {
120 staticProperties->Set(thread, SENDABLE_ELEMENTS_INDEX, JSTaggedValue::Undefined());
121 }
122
123 extractor->SetStaticKeys(thread, staticKeys);
124 extractor->SetStaticProperties(thread, staticProperties);
125 }
126
ExtractAndReturnWhetherWithElements(JSThread * thread,const JSHandle<TaggedArray> & literal,const ExtractContentsDetail & detail,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,JSHandle<TaggedArray> & elements,const JSPandaFile * jsPandaFile)127 bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle<TaggedArray> &literal,
128 const ExtractContentsDetail &detail,
129 JSHandle<TaggedArray> &keys,
130 JSHandle<TaggedArray> &properties,
131 JSHandle<TaggedArray> &elements,
132 const JSPandaFile *jsPandaFile)
133 {
134 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
135
136 ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0);
137
138 uint32_t pos = detail.fillStartLoc;
139 bool withElementsFlag = false;
140 bool isStaticFlag = (detail.methodLiteral != nullptr);
141 bool keysHasNameFlag = false;
142
143 JSHandle<JSTaggedValue> nameString = globalConst->GetHandledNameString();
144 JSMutableHandle<JSTaggedValue> firstValue(thread, JSTaggedValue::Undefined());
145 JSMutableHandle<JSTaggedValue> secondValue(thread, JSTaggedValue::Undefined());
146 for (uint32_t index = detail.extractBegin; index < detail.extractEnd; index += 2) { // 2: key-value pair
147 firstValue.Update(literal->Get(index));
148 secondValue.Update(literal->Get(index + 1));
149 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(firstValue), "Key is not a property key");
150
151 if (LIKELY(firstValue->IsString())) {
152 if (isStaticFlag && !keysHasNameFlag && JSTaggedValue::SameValue(firstValue, nameString)) {
153 properties->Set(thread, NAME_INDEX, secondValue);
154 keysHasNameFlag = true;
155 continue;
156 }
157
158 // front-end can do better: write index in class literal directly.
159 uint32_t elementIndex = 0;
160 if (JSTaggedValue::StringToElementIndex(firstValue.GetTaggedValue(), &elementIndex)) {
161 ASSERT(elementIndex < JSObject::MAX_ELEMENT_INDEX);
162 uint32_t elementsLength = elements->GetLength();
163 elements =
164 TaggedArray::SetCapacityInOldSpace(thread, elements, elementsLength + 2); // 2: key-value pair
165 elements->Set(thread, elementsLength, firstValue);
166 elements->Set(thread, elementsLength + 1, secondValue);
167 withElementsFlag = true;
168 continue;
169 }
170 }
171
172 keys->Set(thread, pos, firstValue);
173 properties->Set(thread, pos, secondValue);
174 pos++;
175 }
176
177 if (isStaticFlag) {
178 if (LIKELY(!keysHasNameFlag)) {
179 [[maybe_unused]] EcmaHandleScope handleScope(thread);
180 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
181 EntityId methodId = detail.methodLiteral->GetMethodId();
182 std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
183 JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
184 properties->Set(thread, NAME_INDEX, clsNameHandle);
185 } else {
186 // class has static name property, reserved length bigger 1 than actual, need trim
187 uint32_t trimOneLength = keys->GetLength() - 1;
188 keys->Trim(thread, trimOneLength);
189 properties->Trim(thread, trimOneLength);
190 }
191 }
192
193 if (UNLIKELY(withElementsFlag)) {
194 ASSERT(pos + elements->GetLength() / 2 == properties->GetLength()); // 2: half
195 keys->Trim(thread, pos);
196 properties->Trim(thread, pos);
197 }
198
199 return withElementsFlag;
200 }
201
CreatePrototypeHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)202 JSHandle<JSHClass> ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread,
203 JSHandle<TaggedArray> &keys,
204 JSHandle<TaggedArray> &properties)
205 {
206 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
207
208 uint32_t length = keys->GetLength();
209 JSHandle<JSHClass> hclass;
210 if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
211 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
212 JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
213 for (uint32_t index = 0; index < length; ++index) {
214 key.Update(keys->Get(index));
215 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
216 PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); // non-enumerable
217
218 if (UNLIKELY(properties->Get(index).IsAccessor())) {
219 attributes.SetIsAccessor(true);
220 }
221
222 attributes.SetIsInlinedProps(true);
223 attributes.SetRepresentation(Representation::TAGGED);
224 attributes.SetOffset(index);
225 layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
226 }
227
228 hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, length);
229 // Not need set proto here
230 hclass->SetLayout(thread, layout);
231 hclass->SetNumberOfProps(length);
232 } else {
233 // dictionary mode
234 hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0); // without in-obj
235 hclass->SetIsDictionaryMode(true);
236 hclass->SetNumberOfProps(0);
237 }
238
239 hclass->SetClassPrototype(true);
240 hclass->SetIsPrototype(true);
241 return hclass;
242 }
243
CreateConstructorHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)244 JSHandle<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
245 JSHandle<TaggedArray> &keys,
246 JSHandle<TaggedArray> &properties)
247 {
248 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
249
250 uint32_t length = keys->GetLength();
251 if (!thread->GetEcmaVM()->IsEnablePGOProfiler()) {
252 // The class constructor of AOT is not shared, and PGO collect cannot be shared.
253 if (length == ClassInfoExtractor::STATIC_RESERVED_LENGTH && base->IsHole() &&
254 properties->Get(NAME_INDEX).IsString()) {
255 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
256 return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorClass());
257 }
258 }
259 JSHandle<JSHClass> hclass;
260 if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
261 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
262 JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
263 for (uint32_t index = 0; index < length; ++index) {
264 key.Update(keys->Get(index));
265 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
266 PropertyAttributes attributes;
267 switch (index) {
268 case LENGTH_INDEX:
269 attributes = PropertyAttributes::Default(false, false, true);
270 break;
271 case NAME_INDEX:
272 if (LIKELY(properties->Get(NAME_INDEX).IsString())) {
273 attributes = PropertyAttributes::Default(false, false, true);
274 } else {
275 ASSERT(properties->Get(NAME_INDEX).IsFunctionTemplate());
276 attributes = PropertyAttributes::Default(true, false, true);
277 }
278 break;
279 case PROTOTYPE_INDEX:
280 attributes = PropertyAttributes::DefaultAccessor(false, false, false);
281 break;
282 default:
283 attributes = PropertyAttributes::Default(true, false, true);
284 break;
285 }
286
287 if (UNLIKELY(properties->Get(index).IsAccessor())) {
288 attributes.SetIsAccessor(true);
289 }
290
291 attributes.SetIsInlinedProps(true);
292 attributes.SetRepresentation(Representation::TAGGED);
293 attributes.SetOffset(index);
294 layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
295 }
296
297 hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, length);
298 // Not need set proto here
299 hclass->SetLayout(thread, layout);
300 hclass->SetNumberOfProps(length);
301 } else {
302 // dictionary mode
303 hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0); // without in-obj
304 hclass->SetIsDictionaryMode(true);
305 hclass->SetNumberOfProps(0);
306 }
307
308 hclass->SetClassConstructor(true);
309 hclass->SetConstructor(true);
310
311 return hclass;
312 }
313
CorrectConstructorHClass(JSThread * thread,JSHandle<TaggedArray> & properties,JSHClass * constructorHClass)314 void ClassInfoExtractor::CorrectConstructorHClass(JSThread *thread,
315 JSHandle<TaggedArray> &properties,
316 JSHClass *constructorHClass)
317 {
318 if (constructorHClass->IsDictionaryMode()) {
319 return;
320 }
321 JSHandle<LayoutInfo> layout(thread, constructorHClass->GetLayout());
322 for (uint32_t index = 0; index < ClassInfoExtractor::STATIC_RESERVED_LENGTH; ++index) {
323 if (index == NAME_INDEX) {
324 if (UNLIKELY(properties->Get(NAME_INDEX).IsFunctionTemplate())) {
325 PropertyAttributes attr = layout->GetAttr(index);
326 attr.SetWritable(true);
327 layout->SetNormalAttr(thread, index, attr);
328 }
329 if (UNLIKELY(properties->Get(index).IsAccessor())) {
330 PropertyAttributes attr = layout->GetAttr(index);
331 attr.SetIsAccessor(true);
332 layout->SetNormalAttr(thread, index, attr);
333 }
334 } else {
335 if (UNLIKELY(properties->Get(index).IsAccessor())) {
336 PropertyAttributes attr = layout->GetAttr(index);
337 attr.SetIsAccessor(true);
338 layout->SetNormalAttr(thread, index, attr);
339 }
340 }
341 }
342 }
343
CreateSendableHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,bool isProtoClass,uint32_t extraLength)344 JSHandle<JSHClass> ClassInfoExtractor::CreateSendableHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
345 JSHandle<TaggedArray> &properties, bool isProtoClass,
346 uint32_t extraLength)
347 {
348 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
349 uint32_t length = keys->GetLength();
350 JSHandle<JSHClass> hclass;
351 uint32_t maxInline = isProtoClass ? JSSharedObject::MAX_INLINE : JSSharedFunction::MAX_INLINE;
352 if (LIKELY(length + extraLength <= maxInline)) {
353 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
354 JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(length + extraLength);
355 for (uint32_t index = 0; index < length; ++index) {
356 key.Update(keys->Get(index));
357 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
358 PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
359 if (UNLIKELY(properties->Get(index).IsAccessor())) {
360 attributes.SetIsAccessor(true);
361 }
362 attributes.SetIsInlinedProps(true);
363 attributes.SetRepresentation(Representation::TAGGED);
364 attributes.SetOffset(index);
365 layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
366 }
367 hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length) :
368 factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length + extraLength);
369 hclass->SetLayout(thread, layout);
370 hclass->SetNumberOfProps(length);
371 } else {
372 // dictionary mode
373 hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0) :
374 factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, 0);
375 hclass->SetIsDictionaryMode(true);
376 hclass->SetNumberOfProps(0);
377 }
378 if (isProtoClass) {
379 hclass->SetClassPrototype(true);
380 hclass->SetIsPrototype(true);
381 } else {
382 hclass->SetClassConstructor(true);
383 hclass->SetConstructor(true);
384 }
385 return hclass;
386 }
387
DefineClassFromExtractor(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv)388 JSHandle<JSFunction> ClassHelper::DefineClassFromExtractor(JSThread *thread, const JSHandle<JSTaggedValue> &base,
389 JSHandle<ClassInfoExtractor> &extractor,
390 const JSHandle<JSTaggedValue> &lexenv)
391 {
392 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
393 JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
394 JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
395
396 JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
397 JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
398 JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
399 nonStaticProperties);
400
401 JSHandle<JSObject> prototype = factory->NewOldSpaceJSObject(prototypeHClass);
402 JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
403 JSHandle<JSHClass> constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys,
404 staticProperties);
405 // Allocate to non-movable space for PGO
406 JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
407 MemSpaceType::NON_MOVABLE);
408
409 // non-static
410 nonStaticProperties->Set(thread, 0, constructor);
411
412 uint32_t nonStaticLength = nonStaticProperties->GetLength();
413 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
414
415 if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
416 for (uint32_t index = 0; index < nonStaticLength; ++index) {
417 propValue.Update(nonStaticProperties->Get(index));
418 if (propValue->IsFunctionTemplate()) {
419 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
420 propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, prototype, lexenv));
421 }
422 prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
423 }
424 } else {
425 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
426 ClassPropertyType::NON_STATIC, lexenv);
427 prototype->SetProperties(thread, dict);
428 }
429
430 // non-static elements
431 if (UNLIKELY(extractor->GetNonStaticWithElements())) {
432 JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
433 ClassHelper::HandleElementsProperties(thread, prototype, lexenv, nonStaticElements);
434 }
435
436 // static
437 uint32_t staticLength = staticProperties->GetLength();
438
439 if (LIKELY(!constructorHClass->IsDictionaryMode())) {
440 for (uint32_t index = 0; index < staticLength; ++index) {
441 propValue.Update(staticProperties->Get(index));
442 if (propValue->IsFunctionTemplate()) {
443 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
444 propValue.Update(
445 CreateJSFunctionFromTemplate(thread, literalFunc, JSHandle<JSObject>(constructor), lexenv));
446 }
447 JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
448 }
449 } else {
450 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
451 staticProperties, ClassPropertyType::STATIC, lexenv);
452 constructor->SetProperties(thread, dict);
453 }
454
455 // static elements
456 if (UNLIKELY(extractor->GetStaticWithElements())) {
457 JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
458 ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), lexenv, staticElements);
459 }
460
461 PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
462 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
463 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
464 globalConst->GetHandledConstructorString(), ctorDesc);
465 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
466 constructor->SetHomeObject(thread, prototype);
467 constructor->SetProtoOrHClass(thread, prototype);
468 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
469 thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
470 }
471 return constructor;
472 }
473
DefineClassWithIHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv,const JSHandle<JSTaggedValue> & prototypeOrHClassVal,const JSHandle<JSTaggedValue> & constructorHClassVal)474 JSHandle<JSFunction> ClassHelper::DefineClassWithIHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
475 JSHandle<ClassInfoExtractor> &extractor,
476 const JSHandle<JSTaggedValue> &lexenv,
477 const JSHandle<JSTaggedValue> &prototypeOrHClassVal,
478 const JSHandle<JSTaggedValue> &constructorHClassVal)
479 {
480 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
481 JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
482 JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
483 JSHandle<JSHClass> constructorHClass;
484 // When constructorHClassVal is undefined, it means that AOT has not generated the corresponding hclass (chc),
485 // then chc will be created through the interpreter.
486 if (constructorHClassVal->IsUndefined()) {
487 constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys, staticProperties);
488 } else {
489 constructorHClass = JSHandle<JSHClass>(constructorHClassVal);
490 ClassInfoExtractor::CorrectConstructorHClass(thread, staticProperties, *constructorHClass);
491 }
492
493 JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
494 JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
495 JSHandle<JSObject> prototype;
496 JSHandle<JSTaggedValue> prototypeOrHClass = prototypeOrHClassVal;
497 // When prototypeOrHClassVal is undefined, it means that AOT has not generated the corresponding hclass or
498 // prototype, then prototype will be created through the interpreter.
499 if (prototypeOrHClassVal->IsUndefined()) {
500 JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
501 nonStaticProperties);
502 prototype = factory->NewOldSpaceJSObject(prototypeHClass);
503 prototypeOrHClass = JSHandle<JSTaggedValue>(prototype);
504 } else if (prototypeOrHClassVal->IsJSHClass()) {
505 JSHandle<JSHClass> ihclass(prototypeOrHClassVal);
506 prototype = JSHandle<JSObject>(thread, ihclass->GetProto());
507 } else {
508 prototype = JSHandle<JSObject>(prototypeOrHClassVal);
509 }
510
511 JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
512 JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
513 MemSpaceType::NON_MOVABLE);
514
515 // non-static
516 nonStaticProperties->Set(thread, 0, constructor);
517
518 uint32_t nonStaticLength = nonStaticProperties->GetLength();
519 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
520
521 if (LIKELY(!prototype->GetJSHClass()->IsDictionaryMode())) {
522 for (uint32_t index = 0; index < nonStaticLength; ++index) {
523 propValue.Update(nonStaticProperties->Get(index));
524 if (propValue->IsFunctionTemplate()) {
525 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
526 propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, prototype, lexenv));
527 }
528 prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
529 }
530 } else {
531 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
532 ClassPropertyType::NON_STATIC, lexenv);
533 prototype->SetProperties(thread, dict);
534 }
535
536 // non-static elements
537 if (UNLIKELY(extractor->GetNonStaticWithElements())) {
538 JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
539 ClassHelper::HandleElementsProperties(thread, prototype, lexenv, nonStaticElements);
540 }
541
542 // static
543 uint32_t staticLength = staticProperties->GetLength();
544 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
545 int correntIndex = 0;
546 if (LIKELY(!constructorHClass->IsDictionaryMode())) {
547 for (uint32_t index = 0; index < staticLength; ++index) {
548 propValue.Update(staticProperties->Get(index));
549 if (propValue->IsFunctionTemplate()) {
550 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
551 propValue.Update(
552 CreateJSFunctionFromTemplate(thread, literalFunc, JSHandle<JSObject>(constructor), lexenv));
553 }
554 bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH;
555 if (needCorrentIndex) {
556 key.Update(staticKeys->Get(index));
557 correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue());
558 }
559 JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread,
560 needCorrentIndex ? static_cast<uint32_t>(correntIndex) : index, propValue.GetTaggedValue());
561 }
562 } else {
563 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
564 staticProperties, ClassPropertyType::STATIC, lexenv);
565 constructor->SetProperties(thread, dict);
566 }
567
568 // static elements
569 if (UNLIKELY(extractor->GetStaticWithElements())) {
570 JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
571 ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), lexenv, staticElements);
572 }
573
574 PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
575 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
576 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
577 globalConst->GetHandledConstructorString(), ctorDesc);
578 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
579 constructor->SetHomeObject(thread, prototype);
580 constructor->SetProtoOrHClass(thread, prototypeOrHClass);
581
582 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
583 thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
584 }
585 return constructor;
586 }
587
CreateJSFunctionFromTemplate(JSThread * thread,const JSHandle<FunctionTemplate> & funcTemp,const JSHandle<JSObject> & homeObject,const JSHandle<JSTaggedValue> & lexenv)588 JSHandle<JSFunction> ClassHelper::CreateJSFunctionFromTemplate(JSThread *thread,
589 const JSHandle<FunctionTemplate> &funcTemp,
590 const JSHandle<JSObject> &homeObject,
591 const JSHandle<JSTaggedValue> &lexenv)
592 {
593 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
594 JSHandle<JSFunction> propFunc = factory->CreateJSFunctionFromTemplate(funcTemp);
595 JSFunction::UpdateProfileTypeInfoCell(thread, funcTemp, propFunc);
596 propFunc->SetHomeObject(thread, homeObject);
597 propFunc->SetLexicalEnv(thread, lexenv);
598 return propFunc;
599 }
600
BuildDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSTaggedValue> & lexenv)601 JSHandle<NameDictionary> ClassHelper::BuildDictionaryProperties(JSThread *thread, const JSHandle<JSObject> &object,
602 JSHandle<TaggedArray> &keys,
603 JSHandle<TaggedArray> &properties,
604 ClassPropertyType type,
605 const JSHandle<JSTaggedValue> &lexenv)
606 {
607 uint32_t length = keys->GetLength();
608 ASSERT(length > PropertyAttributes::MAX_FAST_PROPS_CAPACITY);
609 ASSERT(keys->GetLength() == properties->GetLength());
610
611 JSMutableHandle<NameDictionary> dict(
612 thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
613 JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
614 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
615 for (uint32_t index = 0; index < length; index++) {
616 PropertyAttributes attributes;
617 if (type == ClassPropertyType::STATIC) {
618 switch (index) {
619 case ClassInfoExtractor::LENGTH_INDEX:
620 attributes = PropertyAttributes::Default(false, false, true);
621 break;
622 case ClassInfoExtractor::NAME_INDEX:
623 if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) {
624 attributes = PropertyAttributes::Default(false, false, true);
625 } else {
626 ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsFunctionTemplate());
627 attributes = PropertyAttributes::Default(true, false, true);
628 }
629 break;
630 case ClassInfoExtractor::PROTOTYPE_INDEX:
631 attributes = PropertyAttributes::DefaultAccessor(false, false, false);
632 break;
633 default:
634 attributes = PropertyAttributes::Default(true, false, true);
635 break;
636 }
637 } else {
638 attributes = PropertyAttributes::Default(true, false, true); // non-enumerable
639 }
640 propKey.Update(keys->Get(index));
641 propValue.Update(properties->Get(index));
642 if (propValue->IsFunctionTemplate()) {
643 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
644 propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, object, lexenv));
645 }
646 JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
647 dict.Update(newDict);
648 }
649 return dict;
650 }
651
MatchFieldType(SharedFieldType fieldType,JSTaggedValue value)652 bool ClassHelper::MatchFieldType(SharedFieldType fieldType, JSTaggedValue value)
653 {
654 // all sendable types can be set to undefined
655 if (value.IsUndefined()) {
656 return true;
657 }
658 uint32_t sharedFieldType = static_cast<uint32_t>(fieldType);
659 if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::NUMBER)) != 0 && value.IsNumber()) {
660 return true;
661 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::BOOLEAN)) != 0 && value.IsBoolean()) {
662 return true;
663 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::STRING)) != 0 &&
664 (value.IsString() || value.IsNull())) {
665 return true;
666 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::BIG_INT)) != 0 && value.IsBigInt()) {
667 return true;
668 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::SENDABLE)) != 0 &&
669 (value.IsJSShared() || value.IsNull())) {
670 return true;
671 } else if ((sharedFieldType == static_cast<uint32_t>(SharedFieldType::NONE) ||
672 (sharedFieldType & static_cast<uint32_t>(SharedFieldType::GENERIC)) != 0) &&
673 (value.IsJSShared() || !value.IsHeapObject())) {
674 // (none || generic) && (jsShared || !heapObject)
675 return true;
676 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::NULL_TYPE)) != 0 && value.IsNull()) {
677 return true;
678 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::UNDEFINED)) != 0 && value.IsUndefined()) {
679 return true;
680 }
681 std::stringstream oss;
682 value.DumpTaggedValueType(oss);
683 LOG_ECMA(ERROR) << "Sendable obj Match field type fail. expected type: " <<
684 StaticFieldTypeToString(sharedFieldType) << ", actual type: " << oss.str();
685 return false;
686 }
687
StaticFieldTypeToString(uint32_t fieldType)688 CString ClassHelper::StaticFieldTypeToString(uint32_t fieldType)
689 {
690 switch (fieldType) {
691 case static_cast<uint32_t>(SharedFieldType::NONE):
692 return "[None]";
693 case static_cast<uint32_t>(SharedFieldType::NUMBER):
694 return "[Number]";
695 case static_cast<uint32_t>(SharedFieldType::STRING):
696 return "[String]";
697 case static_cast<uint32_t>(SharedFieldType::BOOLEAN):
698 return "[Boolean]";
699 case static_cast<uint32_t>(SharedFieldType::SENDABLE):
700 return "[Sendable Object]";
701 case static_cast<uint32_t>(SharedFieldType::BIG_INT):
702 return "[BigInt]";
703 case static_cast<uint32_t>(SharedFieldType::GENERIC):
704 return "[Generic]";
705 case static_cast<uint32_t>(SharedFieldType::NULL_TYPE):
706 return "[Null]";
707 case static_cast<uint32_t>(SharedFieldType::UNDEFINED):
708 return "[Undefined]";
709 default: {
710 CString ret = "unknown type ";
711 return ret.append(std::to_string(fieldType));
712 }
713 }
714 }
715
HandleElementsProperties(JSThread * thread,const JSHandle<JSObject> & object,const JSHandle<JSTaggedValue> & lexenv,JSHandle<TaggedArray> & elements)716 void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
717 const JSHandle<JSTaggedValue> &lexenv, JSHandle<TaggedArray> &elements)
718 {
719 JSMutableHandle<JSTaggedValue> elementsKey(thread, JSTaggedValue::Undefined());
720 JSMutableHandle<JSTaggedValue> elementsValue(thread, JSTaggedValue::Undefined());
721 for (uint32_t index = 0; index < elements->GetLength(); index += 2) { // 2: key-value pair
722 elementsKey.Update(elements->Get(index));
723 elementsValue.Update(elements->Get(index + 1));
724 if (elementsValue->IsFunctionTemplate()) {
725 auto literalFunc = JSHandle<FunctionTemplate>::Cast(elementsValue);
726 elementsValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, object, lexenv));
727 }
728
729 // class property attribute is not default, will transition to dictionary directly.
730 JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true);
731 }
732 }
733
DefineSendableClassFromExtractor(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & staticFieldArray)734 JSHandle<JSFunction> SendableClassDefiner::DefineSendableClassFromExtractor(JSThread *thread,
735 JSHandle<ClassInfoExtractor> &extractor, const JSHandle<TaggedArray> &staticFieldArray)
736 {
737 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
738 JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
739 JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
740 SendableClassDefiner::FilterDuplicatedKeys(thread, staticKeys, staticProperties);
741
742 JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
743 JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
744 SendableClassDefiner::FilterDuplicatedKeys(thread, nonStaticKeys, nonStaticProperties);
745 JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreateSendableHClass(thread, nonStaticKeys,
746 nonStaticProperties, true);
747 JSHandle<JSObject> prototype = factory->NewSharedOldSpaceJSObject(prototypeHClass);
748 uint32_t length = staticFieldArray->GetLength();
749 uint32_t staticFields = length / 2; // 2: key-type
750 JSHandle<JSHClass> constructorHClass =
751 ClassInfoExtractor::CreateSendableHClass(thread, staticKeys, staticProperties, false, staticFields);
752 JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
753 /*
754 * Method::SetFunctionKind can't be called here, because method will set kind when set inheritance relationship,
755 * so there is a multi-threading problem with multi-threads define sendable DERIVED class at the same time.
756 * Scenario:
757 * A thread: define DERIVED class X [X's kind = DEFAULT --> BASE CLASS --> DERIVED CLASS], new X()
758 * B thread: define DERIVED class X [X's kind = DEFAULT --> BASE CLASS --> DERIVED CLASS], new X()
759 * Issue:
760 * When A thread new DERIVED class X, X's kind maybe set to BASE CLASS at B thread,
761 * and A thread will throw error when call super().
762 */
763
764 JSHandle<NumberDictionary> elementsDic = NumberDictionary::CreateInSharedHeap(thread);
765 bool hasElement = false;
766 if (!constructorHClass->IsDictionaryMode() && staticFields > 0) {
767 auto layout = JSHandle<LayoutInfo>(thread, constructorHClass->GetLayout());
768 AddFieldTypeToHClass(
769 thread, staticFieldArray, length, layout, constructorHClass, elementsDic, hasElement, ~0U);
770 }
771
772 JSHandle<JSFunction> constructor = factory->NewSFunctionByHClass(method, constructorHClass);
773
774 // non-static
775 nonStaticProperties->Set(thread, 0, constructor);
776
777 uint32_t nonStaticLength = nonStaticProperties->GetLength();
778 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
779
780 if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
781 for (uint32_t index = 0; index < nonStaticLength; ++index) {
782 propValue.Update(nonStaticProperties->Get(index));
783 // constructor don't need to clone
784 if (propValue->IsFunctionTemplate() && index != ClassInfoExtractor::CONSTRUCTOR_INDEX) {
785 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
786 propValue.Update(
787 CreateSFunctionFromTemplate(thread, literalFunc, prototype, JSHandle<JSTaggedValue>(constructor)));
788 } else if (propValue->IsAccessorData()) {
789 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(prototype), constructor);
790 }
791 prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
792 }
793 } else {
794 JSHandle<NameDictionary> dict = BuildSendableDictionaryProperties(thread, prototype, nonStaticKeys,
795 nonStaticProperties, ClassPropertyType::NON_STATIC, constructor);
796 prototype->SetProperties(thread, dict);
797 }
798
799 // non-static elements
800 if (UNLIKELY(extractor->GetNonStaticWithElements())) {
801 THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support members with numerical key",
802 JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
803 }
804
805 // static
806 uint32_t staticLength = staticProperties->GetLength();
807 if (LIKELY(!constructorHClass->IsDictionaryMode())) {
808 for (uint32_t index = 0; index < staticLength; ++index) {
809 propValue.Update(staticProperties->Get(index));
810 if (propValue->IsFunctionTemplate()) {
811 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
812 propValue.Update(CreateSFunctionFromTemplate(
813 thread, literalFunc, JSHandle<JSObject>(constructor), JSHandle<JSTaggedValue>(constructor)));
814 } else if (propValue->IsAccessorData()) {
815 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(constructor), constructor);
816 }
817 JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
818 }
819 } else {
820 JSHandle<NameDictionary> dict =
821 BuildSendableDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
822 staticProperties, ClassPropertyType::STATIC, constructor);
823 JSMutableHandle<NameDictionary> nameDict(thread, dict);
824 if (staticFields > 0) {
825 AddFieldTypeToDict(thread, staticFieldArray, length, nameDict, elementsDic, hasElement,
826 PropertyAttributes::Default(true, true, false));
827 }
828 constructor->SetProperties(thread, nameDict);
829 }
830 if (hasElement) {
831 constructor->SetElements(thread, elementsDic);
832 constructorHClass->SetIsDictionaryElement(true);
833 }
834
835 // static elements
836 if (UNLIKELY(extractor->GetStaticWithElements())) {
837 THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support static members with numerical key",
838 JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
839 }
840 prototype->GetJSHClass()->SetExtensible(false);
841 constructor->SetHomeObject(thread, prototype);
842 constructor->SetProtoOrHClass(thread, prototype);
843 constructor->SetLexicalEnv(thread, constructor);
844 return constructor;
845 }
846
847 // Process duplicated key due to getter/setter.
FilterDuplicatedKeys(JSThread * thread,const JSHandle<TaggedArray> & keys,const JSHandle<TaggedArray> & properties)848 void SendableClassDefiner::FilterDuplicatedKeys(JSThread *thread, const JSHandle<TaggedArray> &keys,
849 const JSHandle<TaggedArray> &properties)
850 {
851 auto attr = PropertyAttributes::Default();
852 uint32_t length = keys->GetLength();
853 uint32_t left = 0;
854 uint32_t right = 0;
855 JSMutableHandle<NameDictionary> dict(
856 thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
857 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
858 JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
859 JSMutableHandle<JSTaggedValue> existValue(thread, JSTaggedValue::Undefined());
860 JSMutableHandle<JSTaggedValue> index(thread, JSTaggedValue::Undefined());
861 for (; right < length; right++) {
862 key.Update(keys->Get(right));
863 value.Update(properties->Get(right));
864 int entry = dict->FindEntry(key.GetTaggedValue());
865 if (entry == -1) {
866 TryUpdateValue(thread, value);
867 index.Update(JSTaggedValue(left));
868 JSHandle<NameDictionary> newDict =
869 NameDictionary::PutIfAbsent(thread, dict, key, index, attr);
870 dict.Update(newDict);
871 if (left < right) {
872 keys->Set(thread, left, key);
873 }
874 properties->Set(thread, left, value);
875 left++;
876 continue;
877 }
878 auto existIndex = static_cast<uint32_t>(dict->GetValue(entry).GetNumber());
879 existValue.Update(properties->Get(existIndex));
880 bool needUpdateValue = TryUpdateExistValue(thread, existValue, value);
881 if (needUpdateValue) {
882 properties->Set(thread, existIndex, value);
883 }
884 }
885 if (left < right) {
886 keys->Trim(thread, left);
887 properties->Trim(thread, left);
888 }
889 }
890
BuildSendableDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSFunction> & ctor)891 JSHandle<NameDictionary> SendableClassDefiner::BuildSendableDictionaryProperties(JSThread *thread,
892 const JSHandle<JSObject> &object, JSHandle<TaggedArray> &keys, JSHandle<TaggedArray> &properties,
893 ClassPropertyType type, const JSHandle<JSFunction> &ctor)
894 {
895 uint32_t length = keys->GetLength();
896 ASSERT(keys->GetLength() == properties->GetLength());
897
898 JSMutableHandle<NameDictionary> dict(
899 thread, NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(length)));
900 JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
901 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
902 for (uint32_t index = 0; index < length; index++) {
903 PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
904 if (UNLIKELY(properties->Get(index).IsAccessor())) {
905 attributes.SetIsAccessor(true);
906 }
907 propKey.Update(keys->Get(index));
908 propValue.Update(properties->Get(index));
909 // constructor don't need to clone
910 if (index == ClassInfoExtractor::CONSTRUCTOR_INDEX && type == ClassPropertyType::NON_STATIC) {
911 JSHandle<NameDictionary> newDict =
912 NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
913 dict.Update(newDict);
914 continue;
915 }
916 if (propValue->IsFunctionTemplate()) {
917 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
918 propValue.Update(CreateSFunctionFromTemplate(thread, literalFunc, object, JSHandle<JSTaggedValue>(ctor)));
919 } else if (propValue->IsAccessorData()) {
920 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(object), ctor);
921 }
922 JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
923 dict.Update(newDict);
924 }
925 return dict;
926 }
927
CreateSFunctionFromTemplate(JSThread * thread,const JSHandle<FunctionTemplate> & funcTemp,const JSHandle<JSObject> & homeObject,const JSHandle<JSTaggedValue> & lexenv)928 JSHandle<JSFunction> SendableClassDefiner::CreateSFunctionFromTemplate(JSThread *thread,
929 const JSHandle<FunctionTemplate> &funcTemp,
930 const JSHandle<JSObject> &homeObject,
931 const JSHandle<JSTaggedValue> &lexenv)
932 {
933 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
934 JSHandle<JSFunction> propFunc = factory->CreateSFunctionFromTemplate(funcTemp);
935 propFunc->SetHomeObject(thread, homeObject);
936 propFunc->SetLexicalEnv(thread, lexenv);
937 ASSERT(!propFunc->GetClass()->IsExtensible());
938 return propFunc;
939 }
940
AddFieldTypeToDict(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,JSMutableHandle<NameDictionary> & dict,JSHandle<NumberDictionary> & elementsDic,bool & hasElement,PropertyAttributes attributes)941 void SendableClassDefiner::AddFieldTypeToDict(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
942 uint32_t length, JSMutableHandle<NameDictionary> &dict,
943 JSHandle<NumberDictionary> &elementsDic, bool &hasElement,
944 PropertyAttributes attributes)
945 {
946 ASSERT(length <= fieldTypeArray->GetLength());
947 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
948 auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
949 JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
950
951 JSMutableHandle<NumberDictionary> elementsDicUpdate(thread, elementsDic);
952 JSHandle<JSTaggedValue> undefinedVal = thread->GlobalConstants()->GetHandledUndefined();
953 JSMutableHandle<JSTaggedValue> eleIndexKey(thread, JSTaggedValue::Undefined());
954
955 for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
956 key.Update(fieldTypeArray->Get(i));
957 ASSERT(key->IsString());
958 SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
959 attributes.SetSharedFieldType(type);
960 attributes.SetBoxType(PropertyBoxType::UNDEFINED);
961 int64_t eleIndex = ObjectFastOperator::TryToElementsIndex(key.GetTaggedValue());
962 if (eleIndex >= 0) {
963 eleIndexKey.Update(JSTaggedValue(eleIndex));
964 JSHandle<NumberDictionary> newElementsDic = NumberDictionary::Put(
965 thread, elementsDic, eleIndexKey, undefinedVal, attributes);
966 elementsDicUpdate.Update(newElementsDic);
967 if (!hasElement) {
968 hasElement = true;
969 }
970 } else {
971 JSHandle<NameDictionary> newDict = NameDictionary::Put(thread, dict, key, value, attributes);
972 dict.Update(newDict);
973 }
974 }
975 }
976
AddFieldTypeToHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,const JSHandle<LayoutInfo> & layout,const JSHandle<JSHClass> & hclass,JSHandle<NumberDictionary> & elementsDic,bool & hasElement,size_t start,std::vector<JSHandle<JSTaggedValue>> && propertyList)977 void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
978 uint32_t length, const JSHandle<LayoutInfo> &layout,
979 const JSHandle<JSHClass> &hclass,
980 JSHandle<NumberDictionary> &elementsDic,
981 bool &hasElement, size_t start,
982 std::vector<JSHandle<JSTaggedValue>> &&propertyList)
983 {
984 ASSERT(length <= fieldTypeArray->GetLength());
985 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
986 uint32_t index = static_cast<uint32_t>(layout->NumberOfElements());
987
988 JSMutableHandle<NumberDictionary> elementsDicUpdate(thread, elementsDic);
989 JSHandle<JSTaggedValue> undefinedVal = thread->GlobalConstants()->GetHandledUndefined();
990 JSMutableHandle<JSTaggedValue> eleIndexKey(thread, JSTaggedValue::Undefined());
991 for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
992 PropertyAttributes attributes = PropertyAttributes::Default(true, true, false);
993 key.Update(fieldTypeArray->Get(i));
994 ASSERT(key->IsString());
995 SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
996 int entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index);
997 if (entry != -1) {
998 attributes = layout->GetAttr(entry);
999 attributes.SetSharedFieldType(type);
1000 layout->SetNormalAttr(thread, entry, attributes);
1001 if (start != ~0U && propertyList.size() > 0) {
1002 propertyList[start + (static_cast<uint32_t>(entry) << 1)] = propertyList[start + i];
1003 propertyList[start + (static_cast<uint32_t>(entry) << 1) + 1] = propertyList[start + i + 1];
1004 }
1005 } else {
1006 int64_t eleIndex = ObjectFastOperator::TryToElementsIndex(key.GetTaggedValue());
1007 if (eleIndex < 0 && start != ~0U && propertyList.size() > 0) {
1008 propertyList[start + (index << 1)] = propertyList[start + i];
1009 propertyList[start + (index << 1) + 1] = propertyList[start + i + 1];
1010 }
1011 attributes.SetIsInlinedProps(true);
1012 attributes.SetRepresentation(Representation::TAGGED);
1013 attributes.SetSharedFieldType(type);
1014 attributes.SetOffset(index);
1015 if (eleIndex >= 0) {
1016 if (propertyList.size() > 0) {
1017 undefinedVal = propertyList[start + i + 1];
1018 }
1019 eleIndexKey.Update(JSTaggedValue(eleIndex));
1020 JSHandle<NumberDictionary> newElementsDic = NumberDictionary::Put(
1021 thread, elementsDic, eleIndexKey, undefinedVal, attributes);
1022 elementsDicUpdate.Update(newElementsDic);
1023 if (!hasElement) {
1024 hasElement = true;
1025 }
1026 } else {
1027 layout->AddKey(thread, index++, key.GetTaggedValue(), attributes);
1028 }
1029 }
1030 }
1031 hclass->SetLayout(thread, layout);
1032 hclass->SetNumberOfProps(index);
1033 auto inlinedProps = hclass->GetInlinedProperties();
1034 if (inlinedProps > index) {
1035 // resize hclass due to duplicated key.
1036 uint32_t duplicatedSize = (inlinedProps - index) * JSTaggedValue::TaggedTypeSize();
1037 hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize);
1038 }
1039 }
1040
AddFieldTypeToHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,const JSHandle<NameDictionary> & nameDict,const JSHandle<JSHClass> & hclass,JSHandle<NumberDictionary> & elementsDic,bool & hasElement)1041 void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
1042 uint32_t length, const JSHandle<NameDictionary> &nameDict,
1043 const JSHandle<JSHClass> &hclass,
1044 JSHandle<NumberDictionary> &elementsDic,
1045 bool &hasElement)
1046 {
1047 JSMutableHandle<NameDictionary> dict(thread, nameDict);
1048 AddFieldTypeToDict(thread, fieldTypeArray, length, dict, elementsDic, hasElement);
1049 hclass->SetLayout(thread, dict);
1050 hclass->SetNumberOfProps(0);
1051 hclass->SetIsDictionaryMode(true);
1052 }
1053
DefineSendableInstanceHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,const JSHandle<JSFunction> & ctor,const JSHandle<JSTaggedValue> & base)1054 void SendableClassDefiner::DefineSendableInstanceHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
1055 uint32_t length, const JSHandle<JSFunction> &ctor, const JSHandle<JSTaggedValue> &base)
1056 {
1057 ASSERT(ctor->GetClass()->IsJSSharedFunction());
1058 JSHandle<JSObject> clsPrototype(thread, JSHandle<JSFunction>(ctor)->GetFunctionPrototype());
1059 ASSERT(clsPrototype->GetClass()->IsJSSharedObject());
1060 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1061 ASSERT(length <= fieldTypeArray->GetLength());
1062 uint32_t fieldNum = length / 2; // 2: key-value pair;
1063 JSHandle<JSHClass> iHClass;
1064 JSHandle<NumberDictionary> elementsDic = NumberDictionary::CreateInSharedHeap(thread);
1065 bool hasElement = false;
1066 if (base->IsHole() || base->IsNull()) {
1067 if (fieldNum == 0) {
1068 iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
1069 } else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) {
1070 iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
1071 JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(fieldNum);
1072 AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass, elementsDic, hasElement, ~0U);
1073 } else {
1074 iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
1075 JSHandle<NameDictionary> dict =
1076 NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(fieldNum));
1077 AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass, elementsDic, hasElement);
1078 }
1079 } else {
1080 ASSERT(base->IsJSSharedFunction());
1081 JSHandle<JSFunction> baseCtor = JSHandle<JSFunction>::Cast(base);
1082 JSHandle<JSHClass> baseIHClass(thread, baseCtor->GetProtoOrHClass());
1083 ASSERT(baseIHClass->IsJSShared());
1084 JSType baseType = baseIHClass->GetObjectType();
1085 const auto [baseSize, baseMaxInlineSize] = GetSizeAndMaxInlineByType(baseType);
1086 if (LIKELY(!baseIHClass->IsDictionaryMode())) {
1087 auto baseLength = baseIHClass->NumberOfProps();
1088 JSHandle<LayoutInfo> baseLayout(thread, baseIHClass->GetLayout());
1089 auto newLength = baseLength + fieldNum;
1090 if (newLength == 0) {
1091 iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
1092 } else if (LIKELY(newLength <= baseMaxInlineSize)) {
1093 iHClass = factory->NewSEcmaHClass(baseSize, baseType, newLength);
1094 JSHandle<LayoutInfo> layout = factory->CopyAndReSortSLayoutInfo(baseLayout, baseLength, newLength);
1095 AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass, elementsDic, hasElement, ~0U);
1096 } else {
1097 iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
1098 JSHandle<NameDictionary> dict =
1099 NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength));
1100 auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
1101 JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
1102 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1103 for (uint32_t i = 0; i < baseLength; i++) {
1104 key.Update(baseLayout->GetKey(i));
1105 PropertyAttributes attr = baseLayout->GetAttr(i);
1106 attr.SetIsInlinedProps(false);
1107 attr.SetBoxType(PropertyBoxType::UNDEFINED);
1108 dict = NameDictionary::Put(thread, dict, key, value, attr);
1109 }
1110 AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass, elementsDic, hasElement);
1111 }
1112 } else {
1113 JSHandle<NameDictionary> baseDict(thread, baseIHClass->GetLayout());
1114 auto baseLength = baseDict->EntriesCount();
1115 auto newLength = fieldNum + static_cast<uint32_t>(baseLength);
1116 JSHandle<NameDictionary> dict =
1117 NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength));
1118 baseDict->Rehash(thread, *dict);
1119 dict->SetNextEnumerationIndex(thread, baseDict->GetNextEnumerationIndex());
1120 iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
1121 AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass, elementsDic, hasElement);
1122 }
1123 }
1124 if (hasElement) {
1125 JSHandle<JSObject>::Cast(ctor)->SetPropertyInlinedProps(
1126 thread, ClassInfoExtractor::SENDABLE_ELEMENTS_INDEX, elementsDic.GetTaggedValue());
1127 }
1128 iHClass->SetPrototype(thread, JSHandle<JSTaggedValue>(clsPrototype));
1129 iHClass->SetExtensible(false);
1130 ctor->SetProtoOrHClass(thread, iHClass);
1131 ctor->GetJSHClass()->SetExtensible(false);
1132 }
1133
ExtractStaticFieldTypeArray(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray)1134 JSHandle<TaggedArray> SendableClassDefiner::ExtractStaticFieldTypeArray(JSThread *thread,
1135 const JSHandle<TaggedArray> &fieldTypeArray)
1136 {
1137 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1138 uint32_t arrayLength = fieldTypeArray->GetLength();
1139 ASSERT(arrayLength > 0);
1140 auto instanceFieldNums = static_cast<uint32_t>(fieldTypeArray->Get(arrayLength - 1).GetInt());
1141 uint32_t staticFieldBegin = instanceFieldNums * 2; // 2: key-type
1142 if (staticFieldBegin >= arrayLength) {
1143 LOG_ECMA(ERROR) << "ExtractStaticFieldTypeArray Failed, staticFieldBegin:" << staticFieldBegin
1144 << " should be less than totalLength:" << arrayLength;
1145 return factory->EmptyArray();
1146 }
1147 uint32_t staticFieldLength = arrayLength - staticFieldBegin - 1;
1148 JSHandle<TaggedArray> staticFieldArray = factory->NewTaggedArray(staticFieldLength);
1149 for (uint32_t i = 0; i < staticFieldLength; i += 2) { // 2: key-type
1150 staticFieldArray->Set(thread, i, fieldTypeArray->Get(staticFieldBegin + i));
1151 staticFieldArray->Set(thread, i + 1, fieldTypeArray->Get(staticFieldBegin + i + 1));
1152 }
1153 return staticFieldArray;
1154 }
1155
UpdateAccessorFunction(JSThread * thread,const JSMutableHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & homeObject,const JSHandle<JSFunction> & ctor)1156 void SendableClassDefiner::UpdateAccessorFunction(JSThread *thread, const JSMutableHandle<JSTaggedValue> &value,
1157 const JSHandle<JSTaggedValue> &homeObject, const JSHandle<JSFunction> &ctor)
1158 {
1159 ASSERT(value->IsAccessorData());
1160 JSHandle<AccessorData> accessor(value);
1161 auto getter = accessor->GetGetter();
1162 if (getter.IsFunctionTemplate()) {
1163 auto funcTemp = JSHandle<FunctionTemplate>(thread, getter);
1164 auto propFunc = CreateSFunctionFromTemplate(
1165 thread, funcTemp, JSHandle<JSObject>(homeObject), JSHandle<JSTaggedValue>(ctor));
1166 accessor->SetGetter(thread, propFunc);
1167 }
1168 auto setter = accessor->GetSetter();
1169 if (setter.IsFunctionTemplate()) {
1170 auto funcTemp = JSHandle<FunctionTemplate>(thread, setter);
1171 auto propFunc = CreateSFunctionFromTemplate(
1172 thread, funcTemp, JSHandle<JSObject>(homeObject), JSHandle<JSTaggedValue>(ctor));
1173 accessor->SetSetter(thread, propFunc);
1174 }
1175 }
1176
TryUpdateExistValue(JSThread * thread,JSMutableHandle<JSTaggedValue> & existValue,JSMutableHandle<JSTaggedValue> & value)1177 bool SendableClassDefiner::TryUpdateExistValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &existValue,
1178 JSMutableHandle<JSTaggedValue> &value)
1179 {
1180 bool needUpdateValue = true;
1181 if (existValue->IsAccessorData()) {
1182 if (value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter()) {
1183 JSHandle<AccessorData> accessor(existValue);
1184 UpdateValueToAccessor(thread, value, accessor);
1185 needUpdateValue = false;
1186 }
1187 } else {
1188 if (value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter()) {
1189 JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
1190 UpdateValueToAccessor(thread, value, accessor);
1191 }
1192 }
1193 return needUpdateValue;
1194 }
1195
TryUpdateValue(JSThread * thread,JSMutableHandle<JSTaggedValue> & value)1196 void SendableClassDefiner::TryUpdateValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &value)
1197 {
1198 if (value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter()) {
1199 JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
1200 UpdateValueToAccessor(thread, value, accessor);
1201 }
1202 }
1203
UpdateValueToAccessor(JSThread * thread,JSMutableHandle<JSTaggedValue> & value,JSHandle<AccessorData> & accessor)1204 void SendableClassDefiner::UpdateValueToAccessor(JSThread *thread, JSMutableHandle<JSTaggedValue> &value,
1205 JSHandle<AccessorData> &accessor)
1206 {
1207 ASSERT(value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter());
1208 if (JSHandle<FunctionTemplate>(value)->IsGetter()) {
1209 accessor->SetGetter(thread, value);
1210 } else {
1211 accessor->SetSetter(thread, value);
1212 }
1213 value.Update(accessor);
1214 }
1215
GetSizeAndMaxInlineByType(JSType type)1216 std::pair<uint32_t, uint32_t> SendableClassDefiner::GetSizeAndMaxInlineByType(JSType type)
1217 {
1218 switch (type) {
1219 case JSType::JS_SHARED_OBJECT:
1220 return { JSSharedObject::SIZE, JSSharedObject::MAX_INLINE };
1221 case JSType::JS_SHARED_ARRAY:
1222 return { JSSharedArray::SIZE, JSSharedArray::MAX_INLINE };
1223 case JSType::JS_SHARED_MAP:
1224 return { JSSharedMap::SIZE, JSSharedMap::MAX_INLINE };
1225 case JSType::JS_SHARED_SET:
1226 return { JSSharedSet::SIZE, JSSharedSet::MAX_INLINE };
1227 case JSType::JS_SENDABLE_ARRAY_BUFFER:
1228 return { JSSendableArrayBuffer::SIZE, JSSendableArrayBuffer::MAX_INLINE };
1229 case JSType::JS_API_BITVECTOR:
1230 return { JSAPIBitVector::SIZE, JSAPIBitVector::MAX_INLINE };
1231 default:
1232 if (JSType::JS_SHARED_TYPED_ARRAY_FIRST < type && type <= JSType::JS_SHARED_TYPED_ARRAY_LAST) {
1233 return { JSSharedTypedArray::SIZE, JSSharedTypedArray::MAX_INLINE };
1234 }
1235 LOG_ECMA(FATAL) << "this branch is unreachable, cannot get size for type: " << static_cast<uint32_t>(type);
1236 UNREACHABLE();
1237 return {};
1238 }
1239 }
1240 } // namespace panda::ecmascript
1241