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