• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 #include "ecmascript/global_env.h"
18 #include "ecmascript/js_function.h"
19 #include "ecmascript/jspandafile/program_object.h"
20 #include "ecmascript/jspandafile/method_literal.h"
21 #include "ecmascript/tagged_dictionary.h"
22 
23 namespace panda::ecmascript {
BuildClassInfoExtractorFromLiteral(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & literal)24 void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
25                                                             const JSHandle<TaggedArray> &literal)
26 {
27     [[maybe_unused]] EcmaHandleScope handleScope(thread);
28     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
29     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
30 
31     uint32_t literalBufferLength = literal->GetLength();
32     // non static properties number is hidden in the last index of Literal buffer
33     uint32_t nonStaticNum = 0;
34     if (literalBufferLength != 0) {
35         nonStaticNum = static_cast<uint32_t>(literal->Get(thread, literalBufferLength - 1).GetInt());
36     }
37 
38     // Reserve sufficient length to prevent frequent creation.
39     JSHandle<TaggedArray> nonStaticKeys = factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
40     JSHandle<TaggedArray> nonStaticProperties =
41         factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
42 
43     nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString());
44     Method *method = Method::Cast(extractor->GetConstructorMethod().GetTaggedObject());
45     MethodLiteral *methodLiteral = method->GetMethodLiteral();
46     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
47     EntityId methodId = method->GetMethodId();
48     if (nonStaticNum) {
49         ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr};
50 
51         JSHandle<TaggedArray> nonStaticElements = factory->EmptyArray();
52         if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys,
53                                                          nonStaticProperties, nonStaticElements, jsPandaFile))) {
54             extractor->SetNonStaticWithElements(true);
55             extractor->SetNonStaticElements(thread, nonStaticElements);
56         }
57     }
58 
59     extractor->SetNonStaticKeys(thread, nonStaticKeys);
60     extractor->SetNonStaticProperties(thread, nonStaticProperties);
61 
62     uint32_t staticNum = literalBufferLength == 0 ? 0 : (literalBufferLength - 1) / 2 - nonStaticNum;
63 
64     // Reserve sufficient length to prevent frequent creation.
65     JSHandle<TaggedArray> staticKeys = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
66     JSHandle<TaggedArray> staticProperties = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
67 
68     staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString());
69     staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString());
70     staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString());
71 
72     JSHandle<TaggedArray> staticElements = factory->EmptyArray();
73 
74     if (staticNum) {
75         ExtractContentsDetail staticDetail {
76             nonStaticNum * 2,
77             literalBufferLength - 1,
78             STATIC_RESERVED_LENGTH,
79             methodLiteral
80         };
81         if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys,
82                                                          staticProperties, staticElements, jsPandaFile))) {
83             extractor->SetStaticWithElements(true);
84             extractor->SetStaticElements(thread, staticElements);
85         }
86     } else {
87         // without static properties, set class name
88         std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
89         JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
90         staticProperties->Set(thread, NAME_INDEX, clsNameHandle);
91     }
92 
93     // set prototype internal accessor
94     JSHandle<JSTaggedValue> prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor();
95     staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor);
96 
97     extractor->SetStaticKeys(thread, staticKeys);
98     extractor->SetStaticProperties(thread, staticProperties);
99 }
100 
ExtractAndReturnWhetherWithElements(JSThread * thread,const JSHandle<TaggedArray> & literal,const ExtractContentsDetail & detail,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,JSHandle<TaggedArray> & elements,const JSPandaFile * jsPandaFile)101 bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle<TaggedArray> &literal,
102                                                              const ExtractContentsDetail &detail,
103                                                              JSHandle<TaggedArray> &keys,
104                                                              JSHandle<TaggedArray> &properties,
105                                                              JSHandle<TaggedArray> &elements,
106                                                              const JSPandaFile *jsPandaFile)
107 {
108     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
109 
110     ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0);
111 
112     uint32_t pos = detail.fillStartLoc;
113     bool withElementsFlag = false;
114     bool isStaticFlag = (detail.methodLiteral != nullptr);
115     bool keysHasNameFlag = false;
116 
117     JSHandle<JSTaggedValue> nameString = globalConst->GetHandledNameString();
118     JSMutableHandle<JSTaggedValue> firstValue(thread, JSTaggedValue::Undefined());
119     JSMutableHandle<JSTaggedValue> secondValue(thread, JSTaggedValue::Undefined());
120     for (uint32_t index = detail.extractBegin; index < detail.extractEnd; index += 2) {  // 2: key-value pair
121         firstValue.Update(literal->Get(index));
122         secondValue.Update(literal->Get(index + 1));
123         ASSERT_PRINT(JSTaggedValue::IsPropertyKey(firstValue), "Key is not a property key");
124 
125         if (LIKELY(firstValue->IsString())) {
126             if (isStaticFlag && !keysHasNameFlag && JSTaggedValue::SameValue(firstValue, nameString)) {
127                 properties->Set(thread, NAME_INDEX, secondValue);
128                 keysHasNameFlag = true;
129                 continue;
130             }
131 
132             // front-end can do better: write index in class literal directly.
133             uint32_t elementIndex = 0;
134             if (JSTaggedValue::StringToElementIndex(firstValue.GetTaggedValue(), &elementIndex)) {
135                 ASSERT(elementIndex < JSObject::MAX_ELEMENT_INDEX);
136                 uint32_t elementsLength = elements->GetLength();
137                 elements =
138                     TaggedArray::SetCapacityInOldSpace(thread, elements, elementsLength + 2); // 2: key-value pair
139                 elements->Set(thread, elementsLength, firstValue);
140                 elements->Set(thread, elementsLength + 1, secondValue);
141                 withElementsFlag = true;
142                 continue;
143             }
144         }
145 
146         keys->Set(thread, pos, firstValue);
147         properties->Set(thread, pos, secondValue);
148         pos++;
149     }
150 
151     if (isStaticFlag) {
152         if (LIKELY(!keysHasNameFlag)) {
153             [[maybe_unused]] EcmaHandleScope handleScope(thread);
154             ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
155             EntityId methodId = detail.methodLiteral->GetMethodId();
156             std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
157             JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
158             properties->Set(thread, NAME_INDEX, clsNameHandle);
159         } else {
160             // class has static name property, reserved length bigger 1 than actual, need trim
161             uint32_t trimOneLength = keys->GetLength() - 1;
162             keys->Trim(thread, trimOneLength);
163             properties->Trim(thread, trimOneLength);
164         }
165     }
166 
167     if (UNLIKELY(withElementsFlag)) {
168         ASSERT(pos + elements->GetLength() / 2 == properties->GetLength());  // 2: half
169         keys->Trim(thread, pos);
170         properties->Trim(thread, pos);
171     }
172 
173     return withElementsFlag;
174 }
175 
CreatePrototypeHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)176 JSHandle<JSHClass> ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
177                                                              JSHandle<TaggedArray> &keys,
178                                                              JSHandle<TaggedArray> &properties)
179 {
180     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
181 
182     uint32_t length = keys->GetLength();
183     if (length == ClassInfoExtractor::NON_STATIC_RESERVED_LENGTH && base->IsHole()) {
184         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
185         return JSHandle<JSHClass>(globalConst->GetHandledClassPrototypeClass());
186     }
187     JSHandle<JSHClass> hclass;
188     if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
189         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
190         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
191         for (uint32_t index = 0; index < length; ++index) {
192             key.Update(keys->Get(index));
193             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
194             PropertyAttributes attributes = PropertyAttributes::Default(true, false, true);  // non-enumerable
195 
196             if (UNLIKELY(properties->Get(index).IsAccessor())) {
197                 attributes.SetIsAccessor(true);
198             }
199 
200             attributes.SetIsInlinedProps(true);
201             attributes.SetRepresentation(Representation::TAGGED);
202             attributes.SetOffset(index);
203             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
204         }
205 
206         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, length);
207         // Not need set proto here
208         hclass->SetLayout(thread, layout);
209         hclass->SetNumberOfProps(length);
210     } else {
211         // dictionary mode
212         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
213         hclass->SetIsDictionaryMode(true);
214         hclass->SetNumberOfProps(0);
215     }
216 
217     hclass->SetClassPrototype(true);
218     hclass->SetIsPrototype(true);
219     return hclass;
220 }
221 
CreateConstructorHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,JSHandle<Method> & method)222 JSHandle<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
223                                                                JSHandle<TaggedArray> &keys,
224                                                                JSHandle<TaggedArray> &properties,
225                                                                JSHandle<Method> &method)
226 {
227     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
228 
229     uint32_t length = keys->GetLength();
230     if (length == ClassInfoExtractor::STATIC_RESERVED_LENGTH && base->IsHole() &&
231         properties->Get(NAME_INDEX).IsString()) {
232         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
233         if (method->IsAotWithCallField()) {
234             if (method->IsFastCall()) {
235                 return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorOptimizedWithFastCallClass());
236             } else {
237                 return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorOptimizedClass());
238             }
239         } else {
240             return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorClass());
241         }
242     }
243     JSHandle<JSHClass> hclass;
244     if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
245         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
246         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
247         for (uint32_t index = 0; index < length; ++index) {
248             key.Update(keys->Get(index));
249             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
250             PropertyAttributes attributes;
251             switch (index) {
252                 case LENGTH_INDEX:
253                     attributes = PropertyAttributes::Default(false, false, true);
254                     break;
255                 case NAME_INDEX:
256                     if (LIKELY(properties->Get(NAME_INDEX).IsString())) {
257                         attributes = PropertyAttributes::Default(false, false, true);
258                     } else {
259                         ASSERT(properties->Get(NAME_INDEX).IsJSFunction());
260                         attributes = PropertyAttributes::Default(true, false, true);
261                     }
262                     break;
263                 case PROTOTYPE_INDEX:
264                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
265                     break;
266                 default:
267                     attributes = PropertyAttributes::Default(true, false, true);
268                     break;
269             }
270 
271             if (UNLIKELY(properties->Get(index).IsAccessor())) {
272                 attributes.SetIsAccessor(true);
273             }
274 
275             attributes.SetIsInlinedProps(true);
276             attributes.SetRepresentation(Representation::TAGGED);
277             attributes.SetOffset(index);
278             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
279         }
280 
281         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, length);
282         // Not need set proto here
283         hclass->SetLayout(thread, layout);
284         hclass->SetNumberOfProps(length);
285     } else {
286         // dictionary mode
287         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0);  // without in-obj
288         hclass->SetIsDictionaryMode(true);
289         hclass->SetNumberOfProps(0);
290     }
291 
292     hclass->SetClassConstructor(true);
293     hclass->SetConstructor(true);
294 
295     return hclass;
296 }
297 
CorrectConstructorHClass(JSThread * thread,JSHandle<TaggedArray> & properties,JSHClass * constructorHClass)298 void ClassInfoExtractor::CorrectConstructorHClass(JSThread *thread,
299                                                   JSHandle<TaggedArray> &properties,
300                                                   JSHClass *constructorHClass)
301 {
302     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
303         JSHandle<LayoutInfo> layout(thread, constructorHClass->GetLayout());
304         for (uint32_t index = 0; index < ClassInfoExtractor::STATIC_RESERVED_LENGTH; ++index) {
305             switch (index) {
306                 case NAME_INDEX:
307                     if (UNLIKELY(properties->Get(NAME_INDEX).IsJSFunction())) {
308                         PropertyAttributes attr = layout->GetAttr(index);
309                         attr.SetWritable(true);
310                         layout->SetNormalAttr(thread, index, attr);
311                     }
312                     if (UNLIKELY(properties->Get(index).IsAccessor())) {
313                         PropertyAttributes attr = layout->GetAttr(index);
314                         attr.SetIsAccessor(true);
315                         layout->SetNormalAttr(thread, index, attr);
316                     }
317                     break;
318                 default:
319                     if (UNLIKELY(properties->Get(index).IsAccessor())) {
320                         PropertyAttributes attr = layout->GetAttr(index);
321                         attr.SetIsAccessor(true);
322                         layout->SetNormalAttr(thread, index, attr);
323                     }
324                     break;
325             }
326         }
327     }
328 }
329 
DefineClassFromExtractor(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv)330 JSHandle<JSFunction> ClassHelper::DefineClassFromExtractor(JSThread *thread, const JSHandle<JSTaggedValue> &base,
331                                                            JSHandle<ClassInfoExtractor> &extractor,
332                                                            const JSHandle<JSTaggedValue> &lexenv)
333 {
334     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
335     JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
336     JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
337 
338     JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
339     JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
340     JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, base, nonStaticKeys,
341                                                                                    nonStaticProperties);
342 
343     JSHandle<JSObject> prototype = factory->NewOldSpaceJSObject(prototypeHClass);
344     JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
345     JSHandle<JSHClass> constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys,
346                                                                                        staticProperties, method);
347     // Allocate to non-movable space for PGO
348     JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
349         MemSpaceType::NON_MOVABLE);
350 
351     // non-static
352     nonStaticProperties->Set(thread, 0, constructor);
353 
354     uint32_t nonStaticLength = nonStaticProperties->GetLength();
355     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
356 
357     if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
358         for (uint32_t index = 0; index < nonStaticLength; ++index) {
359             propValue.Update(nonStaticProperties->Get(index));
360             if (propValue->IsJSFunction()) {
361                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
362                 propFunc->SetHomeObject(thread, prototype);
363                 propFunc->SetLexicalEnv(thread, lexenv);
364                 propValue.Update(propFunc);
365             }
366             prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
367         }
368     } else {
369         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
370                                                                   ClassPropertyType::NON_STATIC, lexenv);
371         prototype->SetProperties(thread, dict);
372     }
373 
374     // non-static elements
375     if (UNLIKELY(extractor->GetNonStaticWithElements())) {
376         JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
377         ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements);
378     }
379 
380     // static
381     uint32_t staticLength = staticProperties->GetLength();
382 
383     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
384         for (uint32_t index = 0; index < staticLength; ++index) {
385             propValue.Update(staticProperties->Get(index));
386             if (propValue->IsJSFunction()) {
387                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
388                 propFunc->SetHomeObject(thread, constructor);
389                 propFunc->SetLexicalEnv(thread, lexenv);
390                 propValue.Update(propFunc);
391             }
392             JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
393         }
394     } else {
395         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
396                                                                   staticProperties, ClassPropertyType::STATIC, lexenv);
397         constructor->SetProperties(thread, dict);
398     }
399 
400     // static elements
401     if (UNLIKELY(extractor->GetStaticWithElements())) {
402         JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
403         ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements);
404     }
405 
406     PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
407     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
408     JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
409                                          globalConst->GetHandledConstructorString(), ctorDesc);
410     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
411     constructor->SetHomeObject(thread, prototype);
412     constructor->SetProtoOrHClass(thread, prototype);
413 
414     return constructor;
415 }
416 
DefineClassWithIHClass(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv,const JSHandle<JSHClass> & ihclass,const JSHandle<JSHClass> & constructorHClass)417 JSHandle<JSFunction> ClassHelper::DefineClassWithIHClass(JSThread *thread,
418                                                          JSHandle<ClassInfoExtractor> &extractor,
419                                                          const JSHandle<JSTaggedValue> &lexenv,
420                                                          const JSHandle<JSHClass> &ihclass,
421                                                          const JSHandle<JSHClass> &constructorHClass)
422 {
423     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
424     JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
425     JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
426     ClassInfoExtractor::CorrectConstructorHClass(thread,
427                                                  staticProperties, *constructorHClass);
428     JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
429     JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
430     JSHandle<JSObject> prototype(thread, ihclass->GetProto());
431 
432     JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
433     constructorHClass->SetIsOptimized(method->IsAotWithCallField());
434     constructorHClass->SetCanFastCall(method->IsFastCall());
435     JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
436         MemSpaceType::NON_MOVABLE);
437 
438     // non-static
439     nonStaticProperties->Set(thread, 0, constructor);
440 
441     uint32_t nonStaticLength = nonStaticProperties->GetLength();
442     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
443 
444     if (LIKELY(!prototype->GetJSHClass()->IsDictionaryMode())) {
445         for (uint32_t index = 0; index < nonStaticLength; ++index) {
446             propValue.Update(nonStaticProperties->Get(index));
447             if (propValue->IsJSFunction()) {
448                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
449                 propFunc->SetHomeObject(thread, prototype);
450                 propFunc->SetLexicalEnv(thread, lexenv);
451                 propValue.Update(propFunc);
452             }
453             prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
454         }
455     } else {
456         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
457                                                                   ClassPropertyType::NON_STATIC, lexenv);
458         prototype->SetProperties(thread, dict);
459     }
460 
461     // non-static elements
462     if (UNLIKELY(extractor->GetNonStaticWithElements())) {
463         JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
464         ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements);
465     }
466 
467     // static
468     uint32_t staticLength = staticProperties->GetLength();
469     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
470     int correntIndex = 0;
471     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
472         for (uint32_t index = 0; index < staticLength; ++index) {
473             propValue.Update(staticProperties->Get(index));
474             if (propValue->IsJSFunction()) {
475                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
476                 propFunc->SetHomeObject(thread, constructor);
477                 propFunc->SetLexicalEnv(thread, lexenv);
478                 propValue.Update(propFunc);
479             }
480             bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH;
481             if (needCorrentIndex) {
482                 key.Update(staticKeys->Get(index));
483                 correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue());
484             }
485             JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread,
486                 needCorrentIndex ? static_cast<uint32_t>(correntIndex) : index, propValue.GetTaggedValue());
487         }
488     } else {
489         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
490                                                                   staticProperties, ClassPropertyType::STATIC, lexenv);
491         constructor->SetProperties(thread, dict);
492     }
493 
494     // static elements
495     if (UNLIKELY(extractor->GetStaticWithElements())) {
496         JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
497         ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements);
498     }
499 
500     PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
501     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
502     JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
503                                          globalConst->GetHandledConstructorString(), ctorDesc);
504     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
505     constructor->SetHomeObject(thread, prototype);
506     constructor->SetProtoOrHClass(thread, ihclass);
507 
508     return constructor;
509 }
510 
BuildDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSTaggedValue> & lexenv)511 JSHandle<NameDictionary> ClassHelper::BuildDictionaryProperties(JSThread *thread, const JSHandle<JSObject> &object,
512                                                                 JSHandle<TaggedArray> &keys,
513                                                                 JSHandle<TaggedArray> &properties,
514                                                                 ClassPropertyType type,
515                                                                 const JSHandle<JSTaggedValue> &lexenv)
516 {
517     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
518     uint32_t length = keys->GetLength();
519     ASSERT(length > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES);
520     ASSERT(keys->GetLength() == properties->GetLength());
521 
522     JSMutableHandle<NameDictionary> dict(
523         thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
524     JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
525     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
526     for (uint32_t index = 0; index < length; index++) {
527         PropertyAttributes attributes;
528         if (type == ClassPropertyType::STATIC) {
529             switch (index) {
530                 case ClassInfoExtractor::LENGTH_INDEX:
531                     attributes = PropertyAttributes::Default(false, false, true);
532                     break;
533                 case ClassInfoExtractor::NAME_INDEX:
534                     if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) {
535                         attributes = PropertyAttributes::Default(false, false, true);
536                     } else {
537                         ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsJSFunction());
538                         attributes = PropertyAttributes::Default(true, false, true);
539                     }
540                     break;
541                 case ClassInfoExtractor::PROTOTYPE_INDEX:
542                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
543                     break;
544                 default:
545                     attributes = PropertyAttributes::Default(true, false, true);
546                     break;
547             }
548         } else {
549             attributes = PropertyAttributes::Default(true, false, true);  // non-enumerable
550         }
551         propKey.Update(keys->Get(index));
552         propValue.Update(properties->Get(index));
553         if (propValue->IsJSFunction()) {
554             JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
555             propFunc->SetHomeObject(thread, object);
556             propFunc->SetLexicalEnv(thread, lexenv);
557             propValue.Update(propFunc);
558         }
559         JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
560         dict.Update(newDict);
561     }
562     return dict;
563 }
564 
HandleElementsProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & elements)565 void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
566                                            JSHandle<TaggedArray> &elements)
567 {
568     JSMutableHandle<JSTaggedValue> elementsKey(thread, JSTaggedValue::Undefined());
569     JSMutableHandle<JSTaggedValue> elementsValue(thread, JSTaggedValue::Undefined());
570     for (uint32_t index = 0; index < elements->GetLength(); index += 2) {  // 2: key-value pair
571         elementsKey.Update(elements->Get(index));
572         elementsValue.Update(elements->Get(index + 1));
573         // class property attribute is not default, will transition to dictionary directly.
574         JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true);
575 
576         if (elementsValue->IsJSFunction()) {
577             JSHandle<JSFunction> elementsFunc = JSHandle<JSFunction>::Cast(elementsValue);
578             elementsFunc->SetHomeObject(thread, object);
579         }
580     }
581 }
582 }  // namespace panda::ecmascript
583