• 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,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)176 JSHandle<JSHClass> ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread,
177                                                              JSHandle<TaggedArray> &keys,
178                                                              JSHandle<TaggedArray> &properties)
179 {
180     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
181 
182     uint32_t length = keys->GetLength();
183     JSHandle<JSHClass> hclass;
184     if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
185         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
186         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
187         for (uint32_t index = 0; index < length; ++index) {
188             key.Update(keys->Get(index));
189             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
190             PropertyAttributes attributes = PropertyAttributes::Default(true, false, true);  // non-enumerable
191 
192             if (UNLIKELY(properties->Get(index).IsAccessor())) {
193                 attributes.SetIsAccessor(true);
194             }
195 
196             attributes.SetIsInlinedProps(true);
197             attributes.SetRepresentation(Representation::TAGGED);
198             attributes.SetOffset(index);
199             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
200         }
201 
202         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, length);
203         // Not need set proto here
204         hclass->SetLayout(thread, layout);
205         hclass->SetNumberOfProps(length);
206     } else {
207         // dictionary mode
208         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
209         hclass->SetIsDictionaryMode(true);
210         hclass->SetNumberOfProps(0);
211     }
212 
213     hclass->SetClassPrototype(true);
214     hclass->SetIsPrototype(true);
215     return hclass;
216 }
217 
CreateConstructorHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)218 JSHandle<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
219                                                                JSHandle<TaggedArray> &keys,
220                                                                JSHandle<TaggedArray> &properties)
221 {
222     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
223 
224     uint32_t length = keys->GetLength();
225     if (!thread->GetEcmaVM()->IsEnablePGOProfiler()) {
226         // The class constructor of AOT is not shared, and PGO collect cannot be shared.
227         if (length == ClassInfoExtractor::STATIC_RESERVED_LENGTH && base->IsHole() &&
228             properties->Get(NAME_INDEX).IsString()) {
229             const GlobalEnvConstants *globalConst = thread->GlobalConstants();
230             return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorClass());
231         }
232     }
233     JSHandle<JSHClass> hclass;
234     if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
235         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
236         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
237         for (uint32_t index = 0; index < length; ++index) {
238             key.Update(keys->Get(index));
239             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
240             PropertyAttributes attributes;
241             switch (index) {
242                 case LENGTH_INDEX:
243                     attributes = PropertyAttributes::Default(false, false, true);
244                     break;
245                 case NAME_INDEX:
246                     if (LIKELY(properties->Get(NAME_INDEX).IsString())) {
247                         attributes = PropertyAttributes::Default(false, false, true);
248                     } else {
249                         ASSERT(properties->Get(NAME_INDEX).IsJSFunction());
250                         attributes = PropertyAttributes::Default(true, false, true);
251                     }
252                     break;
253                 case PROTOTYPE_INDEX:
254                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
255                     break;
256                 default:
257                     attributes = PropertyAttributes::Default(true, false, true);
258                     break;
259             }
260 
261             if (UNLIKELY(properties->Get(index).IsAccessor())) {
262                 attributes.SetIsAccessor(true);
263             }
264 
265             attributes.SetIsInlinedProps(true);
266             attributes.SetRepresentation(Representation::TAGGED);
267             attributes.SetOffset(index);
268             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
269         }
270 
271         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, length);
272         // Not need set proto here
273         hclass->SetLayout(thread, layout);
274         hclass->SetNumberOfProps(length);
275     } else {
276         // dictionary mode
277         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0);  // without in-obj
278         hclass->SetIsDictionaryMode(true);
279         hclass->SetNumberOfProps(0);
280     }
281 
282     hclass->SetClassConstructor(true);
283     hclass->SetConstructor(true);
284 
285     return hclass;
286 }
287 
CorrectConstructorHClass(JSThread * thread,JSHandle<TaggedArray> & properties,JSHClass * constructorHClass)288 void ClassInfoExtractor::CorrectConstructorHClass(JSThread *thread,
289                                                   JSHandle<TaggedArray> &properties,
290                                                   JSHClass *constructorHClass)
291 {
292     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
293         JSHandle<LayoutInfo> layout(thread, constructorHClass->GetLayout());
294         for (uint32_t index = 0; index < ClassInfoExtractor::STATIC_RESERVED_LENGTH; ++index) {
295             switch (index) {
296                 case NAME_INDEX:
297                     if (UNLIKELY(properties->Get(NAME_INDEX).IsJSFunction())) {
298                         PropertyAttributes attr = layout->GetAttr(index);
299                         attr.SetWritable(true);
300                         layout->SetNormalAttr(thread, index, attr);
301                     }
302                     if (UNLIKELY(properties->Get(index).IsAccessor())) {
303                         PropertyAttributes attr = layout->GetAttr(index);
304                         attr.SetIsAccessor(true);
305                         layout->SetNormalAttr(thread, index, attr);
306                     }
307                     break;
308                 default:
309                     if (UNLIKELY(properties->Get(index).IsAccessor())) {
310                         PropertyAttributes attr = layout->GetAttr(index);
311                         attr.SetIsAccessor(true);
312                         layout->SetNormalAttr(thread, index, attr);
313                     }
314                     break;
315             }
316         }
317     }
318 }
319 
CreateSendableHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,bool isProtoClass,uint32_t extraLength)320 JSHandle<JSHClass> ClassInfoExtractor::CreateSendableHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
321                                                             JSHandle<TaggedArray> &properties, bool isProtoClass,
322                                                             uint32_t extraLength)
323 {
324     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
325     uint32_t length = keys->GetLength();
326     JSHandle<JSHClass> hclass;
327     uint32_t maxInline = isProtoClass ? JSSharedObject::MAX_INLINE : JSSharedFunction::MAX_INLINE;
328     if (LIKELY(length + extraLength <= maxInline)) {
329         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
330         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length + extraLength, MemSpaceType::OLD_SPACE,
331                                                                 GrowMode::KEEP);
332         for (uint32_t index = 0; index < length; ++index) {
333             key.Update(keys->Get(index));
334             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
335             PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
336             if (UNLIKELY(properties->Get(index).IsAccessor())) {
337                 attributes.SetIsAccessor(true);
338             }
339             attributes.SetIsInlinedProps(true);
340             attributes.SetRepresentation(Representation::TAGGED);
341             attributes.SetOffset(index);
342             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
343         }
344         hclass = isProtoClass ? factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length) :
345             factory->NewEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length + extraLength);
346         hclass->SetLayout(thread, layout);
347         hclass->SetNumberOfProps(length);
348     } else {
349         // dictionary mode
350         hclass = isProtoClass ? factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0) :
351             factory->NewEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, 0);
352         hclass->SetIsDictionaryMode(true);
353         hclass->SetNumberOfProps(0);
354     }
355     if (isProtoClass) {
356         hclass->SetClassPrototype(true);
357         hclass->SetIsPrototype(true);
358     } else {
359         hclass->SetClassConstructor(true);
360         hclass->SetConstructor(true);
361     }
362     return hclass;
363 }
364 
DefineClassFromExtractor(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv)365 JSHandle<JSFunction> ClassHelper::DefineClassFromExtractor(JSThread *thread, const JSHandle<JSTaggedValue> &base,
366                                                            JSHandle<ClassInfoExtractor> &extractor,
367                                                            const JSHandle<JSTaggedValue> &lexenv)
368 {
369     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
370     JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
371     JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
372 
373     JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
374     JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
375     JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
376                                                                                    nonStaticProperties);
377 
378     JSHandle<JSObject> prototype = factory->NewOldSpaceJSObject(prototypeHClass);
379     JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
380     JSHandle<JSHClass> constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys,
381                                                                                        staticProperties);
382     // Allocate to non-movable space for PGO
383     JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
384         MemSpaceType::NON_MOVABLE);
385 
386     // non-static
387     nonStaticProperties->Set(thread, 0, constructor);
388 
389     uint32_t nonStaticLength = nonStaticProperties->GetLength();
390     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
391 
392     if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
393         for (uint32_t index = 0; index < nonStaticLength; ++index) {
394             propValue.Update(nonStaticProperties->Get(index));
395             if (propValue->IsJSFunction()) {
396                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
397                 propFunc->SetHomeObject(thread, prototype);
398                 propFunc->SetLexicalEnv(thread, lexenv);
399                 propValue.Update(propFunc);
400             }
401             prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
402         }
403     } else {
404         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
405                                                                   ClassPropertyType::NON_STATIC, lexenv);
406         prototype->SetProperties(thread, dict);
407     }
408 
409     // non-static elements
410     if (UNLIKELY(extractor->GetNonStaticWithElements())) {
411         JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
412         ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements);
413     }
414 
415     // static
416     uint32_t staticLength = staticProperties->GetLength();
417 
418     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
419         for (uint32_t index = 0; index < staticLength; ++index) {
420             propValue.Update(staticProperties->Get(index));
421             if (propValue->IsJSFunction()) {
422                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
423                 propFunc->SetHomeObject(thread, constructor);
424                 propFunc->SetLexicalEnv(thread, lexenv);
425                 propValue.Update(propFunc);
426             }
427             JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
428         }
429     } else {
430         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
431                                                                   staticProperties, ClassPropertyType::STATIC, lexenv);
432         constructor->SetProperties(thread, dict);
433     }
434 
435     // static elements
436     if (UNLIKELY(extractor->GetStaticWithElements())) {
437         JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
438         ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements);
439     }
440 
441     PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
442     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
443     JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
444                                          globalConst->GetHandledConstructorString(), ctorDesc);
445     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
446     constructor->SetHomeObject(thread, prototype);
447     constructor->SetProtoOrHClass(thread, prototype);
448     if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
449         thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
450     }
451     return constructor;
452 }
453 
DefineClassWithIHClass(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv,const JSHandle<JSTaggedValue> & prototypeOrHClass,const JSHandle<JSHClass> & constructorHClass)454 JSHandle<JSFunction> ClassHelper::DefineClassWithIHClass(JSThread *thread,
455                                                          JSHandle<ClassInfoExtractor> &extractor,
456                                                          const JSHandle<JSTaggedValue> &lexenv,
457                                                          const JSHandle<JSTaggedValue> &prototypeOrHClass,
458                                                          const JSHandle<JSHClass> &constructorHClass)
459 {
460     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
461     JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
462     JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
463     ClassInfoExtractor::CorrectConstructorHClass(thread,
464                                                  staticProperties, *constructorHClass);
465     JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
466     JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
467     JSHandle<JSObject> prototype;
468     if (prototypeOrHClass->IsJSHClass()) {
469         JSHandle<JSHClass> ihclass(prototypeOrHClass);
470         prototype = JSHandle<JSObject>(thread, ihclass->GetProto());
471     } else {
472         prototype = JSHandle<JSObject>(prototypeOrHClass);
473     }
474 
475     JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
476     JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
477         MemSpaceType::NON_MOVABLE);
478 
479     // non-static
480     nonStaticProperties->Set(thread, 0, constructor);
481 
482     uint32_t nonStaticLength = nonStaticProperties->GetLength();
483     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
484 
485     if (LIKELY(!prototype->GetJSHClass()->IsDictionaryMode())) {
486         for (uint32_t index = 0; index < nonStaticLength; ++index) {
487             propValue.Update(nonStaticProperties->Get(index));
488             if (propValue->IsJSFunction()) {
489                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
490                 propFunc->SetHomeObject(thread, prototype);
491                 propFunc->SetLexicalEnv(thread, lexenv);
492                 propValue.Update(propFunc);
493             }
494             prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
495         }
496     } else {
497         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
498                                                                   ClassPropertyType::NON_STATIC, lexenv);
499         prototype->SetProperties(thread, dict);
500     }
501 
502     // non-static elements
503     if (UNLIKELY(extractor->GetNonStaticWithElements())) {
504         JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
505         ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements);
506     }
507 
508     // static
509     uint32_t staticLength = staticProperties->GetLength();
510     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
511     int correntIndex = 0;
512     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
513         for (uint32_t index = 0; index < staticLength; ++index) {
514             propValue.Update(staticProperties->Get(index));
515             if (propValue->IsJSFunction()) {
516                 JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
517                 propFunc->SetHomeObject(thread, constructor);
518                 propFunc->SetLexicalEnv(thread, lexenv);
519                 propValue.Update(propFunc);
520             }
521             bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH;
522             if (needCorrentIndex) {
523                 key.Update(staticKeys->Get(index));
524                 correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue());
525             }
526             JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread,
527                 needCorrentIndex ? static_cast<uint32_t>(correntIndex) : index, propValue.GetTaggedValue());
528         }
529     } else {
530         JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
531                                                                   staticProperties, ClassPropertyType::STATIC, lexenv);
532         constructor->SetProperties(thread, dict);
533     }
534 
535     // static elements
536     if (UNLIKELY(extractor->GetStaticWithElements())) {
537         JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
538         ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements);
539     }
540 
541     PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
542     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
543     JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
544                                          globalConst->GetHandledConstructorString(), ctorDesc);
545     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
546     constructor->SetHomeObject(thread, prototype);
547     constructor->SetProtoOrHClass(thread, prototypeOrHClass);
548 
549     if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
550         thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
551     }
552     return constructor;
553 }
554 
BuildDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSTaggedValue> & lexenv)555 JSHandle<NameDictionary> ClassHelper::BuildDictionaryProperties(JSThread *thread, const JSHandle<JSObject> &object,
556                                                                 JSHandle<TaggedArray> &keys,
557                                                                 JSHandle<TaggedArray> &properties,
558                                                                 ClassPropertyType type,
559                                                                 const JSHandle<JSTaggedValue> &lexenv)
560 {
561     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
562     uint32_t length = keys->GetLength();
563     ASSERT(length > PropertyAttributes::MAX_FAST_PROPS_CAPACITY);
564     ASSERT(keys->GetLength() == properties->GetLength());
565 
566     JSMutableHandle<NameDictionary> dict(
567         thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
568     JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
569     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
570     for (uint32_t index = 0; index < length; index++) {
571         PropertyAttributes attributes;
572         if (type == ClassPropertyType::STATIC) {
573             switch (index) {
574                 case ClassInfoExtractor::LENGTH_INDEX:
575                     attributes = PropertyAttributes::Default(false, false, true);
576                     break;
577                 case ClassInfoExtractor::NAME_INDEX:
578                     if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) {
579                         attributes = PropertyAttributes::Default(false, false, true);
580                     } else {
581                         ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsJSFunction());
582                         attributes = PropertyAttributes::Default(true, false, true);
583                     }
584                     break;
585                 case ClassInfoExtractor::PROTOTYPE_INDEX:
586                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
587                     break;
588                 default:
589                     attributes = PropertyAttributes::Default(true, false, true);
590                     break;
591             }
592         } else {
593             attributes = PropertyAttributes::Default(true, false, true);  // non-enumerable
594         }
595         propKey.Update(keys->Get(index));
596         propValue.Update(properties->Get(index));
597         if (propValue->IsJSFunction()) {
598             JSHandle<JSFunction> propFunc = factory->CloneJSFuction(JSHandle<JSFunction>::Cast(propValue));
599             propFunc->SetHomeObject(thread, object);
600             propFunc->SetLexicalEnv(thread, lexenv);
601             propValue.Update(propFunc);
602         }
603         JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
604         dict.Update(newDict);
605     }
606     return dict;
607 }
608 
MatchTrackType(TrackType trackType,JSTaggedValue value)609 bool ClassHelper::MatchTrackType(TrackType trackType, JSTaggedValue value)
610 {
611     bool checkRet = false;
612     switch (trackType) {
613         case TrackType::NUMBER: {
614             checkRet = value.IsNumber();
615             break;
616         }
617         case TrackType::INT: {
618             checkRet = value.IsInt();
619             break;
620         }
621         case TrackType::DOUBLE: {
622             checkRet = value.IsDouble();
623             break;
624         }
625         case TrackType::BOOLEAN: {
626             checkRet = value.IsBoolean();
627             break;
628         }
629         case TrackType::STRING: {
630             checkRet = value.IsString() || value.IsNull();
631             break;
632         }
633         case TrackType::SENDABLE: {
634             checkRet = value.IsJSShared() || value.IsNull();
635             break;
636         }
637         case TrackType::NONE: {
638             checkRet = true;
639             break;
640         }
641         case TrackType::TAGGED:
642         default:
643             break;
644     }
645     return checkRet;
646 }
647 
HandleElementsProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & elements)648 void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
649                                            JSHandle<TaggedArray> &elements)
650 {
651     JSMutableHandle<JSTaggedValue> elementsKey(thread, JSTaggedValue::Undefined());
652     JSMutableHandle<JSTaggedValue> elementsValue(thread, JSTaggedValue::Undefined());
653     for (uint32_t index = 0; index < elements->GetLength(); index += 2) {  // 2: key-value pair
654         elementsKey.Update(elements->Get(index));
655         elementsValue.Update(elements->Get(index + 1));
656         // class property attribute is not default, will transition to dictionary directly.
657         JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true);
658 
659         if (elementsValue->IsJSFunction()) {
660             JSHandle<JSFunction> elementsFunc = JSHandle<JSFunction>::Cast(elementsValue);
661             elementsFunc->SetHomeObject(thread, object);
662         }
663     }
664 }
665 
DefineSendableClassFromExtractor(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & staticFieldArray)666 JSHandle<JSFunction> SendableClassDefiner::DefineSendableClassFromExtractor(JSThread *thread,
667     JSHandle<ClassInfoExtractor> &extractor, const JSHandle<TaggedArray> &staticFieldArray)
668 {
669     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
670     JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
671     JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
672     SendableClassDefiner::FilterDuplicatedKeys(thread, staticKeys, staticProperties);
673 
674     JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
675     JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
676     SendableClassDefiner::FilterDuplicatedKeys(thread, nonStaticKeys, nonStaticProperties);
677     JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreateSendableHClass(thread, nonStaticKeys,
678                                                                                   nonStaticProperties, true);
679     JSHandle<JSObject> prototype = factory->NewOldSpaceJSObject(prototypeHClass);
680     uint32_t length = staticFieldArray->GetLength();
681     uint32_t staticFields =  length / 2; // 2: key-type
682     JSHandle<JSHClass> constructorHClass =
683         ClassInfoExtractor::CreateSendableHClass(thread, staticKeys, staticProperties, false, staticFields);
684     JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
685     method->SetFunctionKind(FunctionKind::CLASS_CONSTRUCTOR);
686     if (!constructorHClass->IsDictionaryMode() && staticFields > 0) {
687         auto layout = JSHandle<LayoutInfo>(thread, constructorHClass->GetLayout());
688         AddFieldTypeToHClass(thread, staticFieldArray, layout, constructorHClass);
689     }
690     JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
691         MemSpaceType::NON_MOVABLE);
692 
693     // non-static
694     nonStaticProperties->Set(thread, 0, constructor);
695 
696     uint32_t nonStaticLength = nonStaticProperties->GetLength();
697     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
698 
699     if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
700         for (uint32_t index = 0; index < nonStaticLength; ++index) {
701             propValue.Update(nonStaticProperties->Get(index));
702             // constructor don't need to clone
703             if (propValue->IsJSFunction() && index != ClassInfoExtractor::CONSTRUCTOR_INDEX) {
704                 JSHandle<JSFunction> propFunc = factory->CloneSFunction(JSHandle<JSFunction>::Cast(propValue));
705                 propFunc->SetHomeObject(thread, prototype);
706                 propFunc->SetLexicalEnv(thread, constructor);
707                 ASSERT(!propFunc->GetClass()->IsExtensible());
708                 propValue.Update(propFunc);
709             } else if (propValue->IsAccessorData()) {
710                 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(prototype), constructor);
711             }
712             prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
713         }
714     } else {
715         JSHandle<NameDictionary> dict = BuildSendableDictionaryProperties(thread, prototype, nonStaticKeys,
716             nonStaticProperties, ClassPropertyType::NON_STATIC, constructor);
717         prototype->SetProperties(thread, dict);
718     }
719 
720     // non-static elements
721     if (UNLIKELY(extractor->GetNonStaticWithElements())) {
722         THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support members with numerical key",
723             JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
724     }
725 
726     // static
727     uint32_t staticLength = staticProperties->GetLength();
728     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
729         for (uint32_t index = 0; index < staticLength; ++index) {
730             propValue.Update(staticProperties->Get(index));
731             if (propValue->IsJSFunction()) {
732                 JSHandle<JSFunction> propFunc = factory->CloneSFunction(JSHandle<JSFunction>::Cast(propValue));
733                 propFunc->SetHomeObject(thread, constructor);
734                 propFunc->SetLexicalEnv(thread, constructor);
735                 ASSERT(!propFunc->GetClass()->IsExtensible());
736                 propValue.Update(propFunc);
737             } else if (propValue->IsAccessorData()) {
738                 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(constructor), constructor);
739             }
740             JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
741         }
742     } else {
743         JSHandle<NameDictionary> dict =
744             BuildSendableDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
745                                               staticProperties, ClassPropertyType::STATIC, constructor);
746         JSMutableHandle<NameDictionary> nameDict(thread, dict);
747         if (staticFields > 0) {
748             AddFieldTypeToDict(thread, staticFieldArray, nameDict,
749                                PropertyAttributes::Default(false, true, false));
750         }
751         constructor->SetProperties(thread, nameDict);
752     }
753 
754     // static elements
755     if (UNLIKELY(extractor->GetStaticWithElements())) {
756         THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support static members with numerical key",
757             JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
758     }
759     prototype->GetJSHClass()->SetExtensible(false);
760     constructor->SetHomeObject(thread, prototype);
761     constructor->SetProtoOrHClass(thread, prototype);
762     constructor->SetLexicalEnv(thread, constructor);
763     return constructor;
764 }
765 
766 // Process duplicated key due to getter/setter.
FilterDuplicatedKeys(JSThread * thread,const JSHandle<TaggedArray> & keys,const JSHandle<TaggedArray> & properties)767 void SendableClassDefiner::FilterDuplicatedKeys(JSThread *thread, const JSHandle<TaggedArray> &keys,
768                                                 const JSHandle<TaggedArray> &properties)
769 {
770     auto attr = PropertyAttributes::Default();
771     uint32_t length = keys->GetLength();
772     uint32_t left = 0;
773     uint32_t right = 0;
774     JSMutableHandle<NameDictionary> dict(
775         thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
776     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
777     JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
778     JSMutableHandle<JSTaggedValue> existValue(thread, JSTaggedValue::Undefined());
779     JSMutableHandle<JSTaggedValue> index(thread, JSTaggedValue::Undefined());
780     for (; right < length; right++) {
781         key.Update(keys->Get(right));
782         value.Update(properties->Get(right));
783         int entry = dict->FindEntry(key.GetTaggedValue());
784         if (entry == -1) {
785             TryUpdateValue(thread, value);
786             index.Update(JSTaggedValue(left));
787             JSHandle<NameDictionary> newDict =
788                 NameDictionary::PutIfAbsent(thread, dict, key, index, attr);
789             dict.Update(newDict);
790             if (left < right) {
791                 keys->Set(thread, left, key);
792             }
793             properties->Set(thread, left, value);
794             left++;
795             continue;
796         }
797         auto existIndex = static_cast<uint32_t>(dict->GetValue(entry).GetNumber());
798         existValue.Update(properties->Get(existIndex));
799         bool needUpdateValue = TryUpdateExistValue(thread, existValue, value);
800         if (needUpdateValue) {
801             properties->Set(thread, existIndex, value);
802         }
803     }
804     if (left < right) {
805         keys->Trim(thread, left);
806         properties->Trim(thread, left);
807     }
808 }
809 
BuildSendableDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSFunction> & ctor)810 JSHandle<NameDictionary> SendableClassDefiner::BuildSendableDictionaryProperties(JSThread *thread,
811     const JSHandle<JSObject> &object, JSHandle<TaggedArray> &keys, JSHandle<TaggedArray> &properties,
812     ClassPropertyType type, const JSHandle<JSFunction> &ctor)
813 {
814     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
815     uint32_t length = keys->GetLength();
816     ASSERT(keys->GetLength() == properties->GetLength());
817 
818     JSMutableHandle<NameDictionary> dict(
819         thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
820     JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
821     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
822     for (uint32_t index = 0; index < length; index++) {
823         PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
824         propKey.Update(keys->Get(index));
825         propValue.Update(properties->Get(index));
826         // constructor don't need to clone
827         if (index == ClassInfoExtractor::CONSTRUCTOR_INDEX && type == ClassPropertyType::NON_STATIC) {
828             JSHandle<NameDictionary> newDict =
829                 NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
830             dict.Update(newDict);
831             continue;
832         }
833         if (propValue->IsJSFunction()) {
834             JSHandle<JSFunction> propFunc = factory->CloneSFunction(JSHandle<JSFunction>::Cast(propValue));
835             propFunc->SetHomeObject(thread, object);
836             propFunc->SetLexicalEnv(thread, ctor);
837             ASSERT(!propFunc->GetClass()->IsExtensible());
838             propValue.Update(propFunc);
839         } else if (propValue->IsAccessorData()) {
840             UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(object), ctor);
841         }
842         JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
843         dict.Update(newDict);
844     }
845     return dict;
846 }
847 
AddFieldTypeToHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,const JSHandle<LayoutInfo> & layout,const JSHandle<JSHClass> & hclass)848 void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
849     const JSHandle<LayoutInfo> &layout, const JSHandle<JSHClass> &hclass)
850 {
851     uint32_t length = fieldTypeArray->GetLength();
852     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
853     uint32_t index = static_cast<uint32_t>(layout->NumberOfElements());
854     PropertyAttributes attributes = PropertyAttributes::Default(true, true, false);
855     attributes.SetIsInlinedProps(true);
856     attributes.SetRepresentation(Representation::TAGGED);
857     for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
858         key.Update(fieldTypeArray->Get(i));
859         ASSERT(key->IsString());
860         TrackType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
861         int entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index);
862         if (entry != -1) {
863             attributes = layout->GetAttr(entry);
864             attributes.SetTrackType(type);
865             layout->SetNormalAttr(thread, entry, attributes);
866         } else {
867             attributes.SetTrackType(type);
868             attributes.SetOffset(index);
869             layout->AddKey(thread, index++, key.GetTaggedValue(), attributes);
870         }
871     }
872     hclass->SetLayout(thread, layout);
873     hclass->SetNumberOfProps(index);
874     auto inlinedProps = hclass->GetInlinedProperties();
875     if (inlinedProps > index) {
876         // resize hclass due to duplicated key.
877         uint32_t duplicatedSize = (inlinedProps - index) * JSTaggedValue::TaggedTypeSize();
878         hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize);
879     }
880 }
881 
AddFieldTypeToDict(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,JSMutableHandle<NameDictionary> & dict,PropertyAttributes attributes)882 void SendableClassDefiner::AddFieldTypeToDict(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
883     JSMutableHandle<NameDictionary> &dict, PropertyAttributes attributes)
884 {
885     uint32_t length = fieldTypeArray->GetLength();
886     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
887     auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
888     JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
889     for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
890         key.Update(fieldTypeArray->Get(i));
891         ASSERT(key->IsString());
892         TrackType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
893         attributes.SetTrackType(type);
894         attributes.SetBoxType(PropertyBoxType::UNDEFINED);
895         JSHandle<NameDictionary> newDict = NameDictionary::Put(thread, dict, key, value, attributes);
896         dict.Update(newDict);
897     }
898 }
899 
AddFieldTypeToHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,const JSHandle<NameDictionary> & nameDict,const JSHandle<JSHClass> & hclass)900 void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
901     const JSHandle<NameDictionary> &nameDict, const JSHandle<JSHClass> &hclass)
902 {
903     JSMutableHandle<NameDictionary> dict(thread, nameDict);
904     AddFieldTypeToDict(thread, fieldTypeArray, dict);
905     hclass->SetLayout(thread, dict);
906     hclass->SetNumberOfProps(0);
907     hclass->SetIsDictionaryMode(true);
908 }
909 
DefineSendableInstanceHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,const JSHandle<JSFunction> & ctor,const JSHandle<JSTaggedValue> & base)910 void SendableClassDefiner::DefineSendableInstanceHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
911     const JSHandle<JSFunction> &ctor, const JSHandle<JSTaggedValue> &base)
912 {
913     ASSERT(ctor->GetClass()->IsJSSharedFunction());
914     JSHandle<JSObject> clsPrototype(thread, JSHandle<JSFunction>(ctor)->GetFunctionPrototype());
915     ASSERT(clsPrototype->GetClass()->IsJSSharedObject());
916     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
917     uint32_t length = fieldTypeArray->GetLength();
918     uint32_t fieldNum = length / 2; // 2: key-value pair;
919     JSHandle<JSHClass> iHClass;
920     if (base->IsHole() || base->IsNull()) {
921         if (fieldNum == 0) {
922             iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
923         } else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) {
924             iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
925             JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(fieldNum, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
926             AddFieldTypeToHClass(thread, fieldTypeArray, layout, iHClass);
927         } else {
928             iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
929             JSHandle<NameDictionary> dict =
930                 NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(fieldNum));
931             AddFieldTypeToHClass(thread, fieldTypeArray, dict, iHClass);
932         }
933     } else {
934         ASSERT(base->IsJSSharedFunction());
935         JSHandle<JSFunction> baseCtor = JSHandle<JSFunction>::Cast(base);
936         JSHandle<JSHClass> baseIHClass(thread, baseCtor->GetProtoOrHClass());
937         ASSERT(baseIHClass->IsJSSharedObject());
938         if (LIKELY(!baseIHClass->IsDictionaryMode())) {
939             auto baseLength = baseIHClass->NumberOfProps();
940             JSHandle<LayoutInfo> baseLayout(thread, baseIHClass->GetLayout());
941             auto newLength = baseLength + fieldNum;
942             if (fieldNum == 0) {
943                 iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
944             } else if (LIKELY(newLength <= JSSharedObject::MAX_INLINE)) {
945                 iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, newLength);
946                 JSHandle<LayoutInfo> layout = factory->CopyAndReSort(baseLayout, baseLength, newLength);
947                 AddFieldTypeToHClass(thread, fieldTypeArray, layout, iHClass);
948             } else {
949                 iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
950                 JSHandle<NameDictionary> dict =
951                     NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(newLength));
952                 auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
953                 JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
954                 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
955                 for (uint32_t i = 0; i < baseLength; i++) {
956                     key.Update(baseLayout->GetKey(i));
957                     PropertyAttributes attr = baseLayout->GetAttr(i);
958                     attr.SetIsInlinedProps(false);
959                     attr.SetBoxType(PropertyBoxType::UNDEFINED);
960                     dict = NameDictionary::Put(thread, dict, key, value, attr);
961                 }
962                 AddFieldTypeToHClass(thread, fieldTypeArray, dict, iHClass);
963             }
964         } else {
965             JSHandle<NameDictionary> baseDict(thread, baseIHClass->GetLayout());
966             auto baseLength = baseDict->EntriesCount();
967             JSHandle<NameDictionary> dict =
968                 NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(fieldNum + baseLength));
969             baseDict->Rehash(thread, *dict);
970             dict->SetNextEnumerationIndex(thread, baseDict->GetNextEnumerationIndex());
971             iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
972             AddFieldTypeToHClass(thread, fieldTypeArray, dict, iHClass);
973         }
974     }
975     iHClass->SetPrototype(thread, JSHandle<JSTaggedValue>(clsPrototype));
976     iHClass->SetExtensible(false);
977     ctor->SetProtoOrHClass(thread, iHClass);
978     ctor->GetJSHClass()->SetExtensible(false);
979 }
980 
ExtractStaticFieldTypeArray(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray)981 JSHandle<TaggedArray> SendableClassDefiner::ExtractStaticFieldTypeArray(JSThread *thread,
982     const JSHandle<TaggedArray> &fieldTypeArray)
983 {
984     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
985     uint32_t arrayLength = fieldTypeArray->GetLength();
986     auto instanceFieldNums = static_cast<uint32_t>(fieldTypeArray->Get(arrayLength - 1).GetInt());
987     uint32_t staticFieldBegin = instanceFieldNums * 2; // 2: key-type
988     if (staticFieldBegin >= arrayLength) {
989         LOG_ECMA(ERROR) << "ExtractStaticFieldTypeArray Failed, staticFieldBegin:" << staticFieldBegin
990                         << " should be less than totalLength:" << arrayLength;
991         return factory->EmptyArray();
992     }
993     uint32_t staticFieldLength = arrayLength - staticFieldBegin - 1;
994     JSHandle<TaggedArray> staticFieldArray = factory->NewTaggedArray(staticFieldLength);
995     for (uint32_t i = 0; i < staticFieldLength; i += 2) {  // 2: key-type
996         staticFieldArray->Set(thread, i, fieldTypeArray->Get(staticFieldBegin + i));
997         staticFieldArray->Set(thread, i + 1, fieldTypeArray->Get(staticFieldBegin + i + 1));
998     }
999     return staticFieldArray;
1000 }
1001 
UpdateAccessorFunction(JSThread * thread,const JSMutableHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & homeObject,const JSHandle<JSFunction> & ctor)1002 void SendableClassDefiner::UpdateAccessorFunction(JSThread *thread, const JSMutableHandle<JSTaggedValue> &value,
1003     const JSHandle<JSTaggedValue> &homeObject, const JSHandle<JSFunction> &ctor)
1004 {
1005     ASSERT(value->IsAccessorData());
1006     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1007     JSHandle<AccessorData> accessor(value);
1008     auto getter = accessor->GetGetter();
1009     if (getter.IsJSFunction()) {
1010         JSHandle<JSFunction> func(thread, getter);
1011         JSHandle<JSFunction> propFunc = factory->CloneSFunction(func);
1012         propFunc->SetHomeObject(thread, homeObject);
1013         propFunc->SetLexicalEnv(thread, ctor);
1014         ASSERT(!propFunc->GetClass()->IsExtensible());
1015         accessor->SetGetter(thread, propFunc);
1016     }
1017     auto setter = accessor->GetSetter();
1018     if (setter.IsJSFunction()) {
1019         JSHandle<JSFunction> func(thread, setter);
1020         JSHandle<JSFunction> propFunc = factory->CloneSFunction(func);
1021         propFunc->SetHomeObject(thread, homeObject);
1022         propFunc->SetLexicalEnv(thread, ctor);
1023         ASSERT(!propFunc->GetClass()->IsExtensible());
1024         accessor->SetSetter(thread, propFunc);
1025     }
1026 }
1027 
TryUpdateExistValue(JSThread * thread,JSMutableHandle<JSTaggedValue> & existValue,JSMutableHandle<JSTaggedValue> & value)1028 bool SendableClassDefiner::TryUpdateExistValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &existValue,
1029                                                JSMutableHandle<JSTaggedValue> &value)
1030 {
1031     bool needUpdateValue = true;
1032     if (existValue->IsAccessorData()) {
1033         if (value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter()) {
1034             JSHandle<AccessorData> accessor(existValue);
1035             UpdateValueToAccessor(thread, value, accessor);
1036             needUpdateValue = false;
1037         }
1038     } else {
1039         if (value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter()) {
1040             JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData();
1041             UpdateValueToAccessor(thread, value, accessor);
1042         }
1043     }
1044     return needUpdateValue;
1045 }
1046 
TryUpdateValue(JSThread * thread,JSMutableHandle<JSTaggedValue> & value)1047 void SendableClassDefiner::TryUpdateValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &value)
1048 {
1049     if (value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter()) {
1050         JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData();
1051         UpdateValueToAccessor(thread, value, accessor);
1052     }
1053 }
1054 
UpdateValueToAccessor(JSThread * thread,JSMutableHandle<JSTaggedValue> & value,JSHandle<AccessorData> & accessor)1055 void SendableClassDefiner::UpdateValueToAccessor(JSThread *thread, JSMutableHandle<JSTaggedValue> &value,
1056                                                  JSHandle<AccessorData> &accessor)
1057 {
1058     ASSERT(value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter());
1059     if (JSHandle<JSFunction>(value)->IsGetter()) {
1060         accessor->SetGetter(thread, value);
1061     } else {
1062         accessor->SetSetter(thread, value);
1063     }
1064     value.Update(accessor);
1065 }
1066 }  // namespace panda::ecmascript
1067