• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/class_info_extractor.h"
17 
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/tagged_dictionary.h"
21 
22 namespace panda::ecmascript {
BuildClassInfoExtractorFromLiteral(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & literal)23 void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
24                                                             const JSHandle<TaggedArray> &literal)
25 {
26     [[maybe_unused]] EcmaHandleScope handleScope(thread);
27     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
28     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
29 
30     uint32_t literalBufferLength = literal->GetLength();
31     // non static properties number is hidden in the last index of Literal buffer
32     uint32_t nonStaticNum = literal->Get(thread, literalBufferLength - 1).GetInt();
33 
34     // Reserve sufficient length to prevent frequent creation.
35     JSHandle<TaggedArray> nonStaticKeys = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
36     JSHandle<TaggedArray> nonStaticProperties = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
37 
38     nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString());
39 
40     JSHandle<TaggedArray> nonStaticElements = factory->EmptyArray();
41 
42     if (nonStaticNum) {
43         ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr};
44 
45         if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys,
46                                                          nonStaticProperties, nonStaticElements))) {
47             extractor->SetNonStaticWithElements(true);
48             extractor->SetNonStaticElements(thread, nonStaticElements);
49         }
50     }
51 
52     extractor->SetNonStaticKeys(thread, nonStaticKeys);
53     extractor->SetNonStaticProperties(thread, nonStaticProperties);
54 
55     JSHandle<JSHClass> prototypeHClass = CreatePrototypeHClass(thread, nonStaticKeys, nonStaticProperties);
56     extractor->SetPrototypeHClass(thread, prototypeHClass);
57 
58     uint32_t staticNum = (literalBufferLength - 1) / 2 - nonStaticNum;
59 
60     // Reserve sufficient length to prevent frequent creation.
61     JSHandle<TaggedArray> staticKeys = factory->NewTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
62     JSHandle<TaggedArray> staticProperties = factory->NewTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
63 
64     staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString());
65     staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString());
66     staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString());
67 
68     JSHandle<TaggedArray> staticElements = factory->EmptyArray();
69 
70     if (staticNum) {
71         ExtractContentsDetail staticDetail {
72             nonStaticNum * 2,
73             literalBufferLength - 1,
74             STATIC_RESERVED_LENGTH,
75             extractor->GetConstructorMethod()
76         };
77 
78         if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys,
79                                                          staticProperties, staticElements))) {
80             extractor->SetStaticWithElements(true);
81             extractor->SetStaticElements(thread, staticElements);
82         }
83     } else {
84         // without static properties, set class name
85         std::string clsName = extractor->GetConstructorMethod()->ParseFunctionName();
86         JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
87         staticProperties->Set(thread, NAME_INDEX, clsNameHandle);
88     }
89 
90     // set prototype internal accessor
91     JSHandle<JSTaggedValue> prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor();
92     staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor);
93 
94     extractor->SetStaticKeys(thread, staticKeys);
95     extractor->SetStaticProperties(thread, staticProperties);
96 
97     JSHandle<JSHClass> ctorHClass = CreateConstructorHClass(thread, staticKeys, staticProperties);
98     extractor->SetConstructorHClass(thread, ctorHClass);
99 }
100 
ExtractAndReturnWhetherWithElements(JSThread * thread,const JSHandle<TaggedArray> & literal,const ExtractContentsDetail & detail,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,JSHandle<TaggedArray> & elements)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 {
107     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
108 
109     ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0);
110 
111     uint32_t pos = detail.fillStartLoc;
112     bool withElemenstFlag = false;
113     bool isStaticFlag = detail.ctorMethod ? true : false;
114     bool keysHasNameFlag = false;
115 
116     JSHandle<JSTaggedValue> nameString = globalConst->GetHandledNameString();
117     JSMutableHandle<JSTaggedValue> firstValue(thread, JSTaggedValue::Undefined());
118     JSMutableHandle<JSTaggedValue> secondValue(thread, JSTaggedValue::Undefined());
119     for (uint32_t index = detail.extractBegin; index < detail.extractEnd; index += 2) {  // 2: key-value pair
120         firstValue.Update(literal->Get(index));
121         secondValue.Update(literal->Get(index + 1));
122         ASSERT_PRINT(JSTaggedValue::IsPropertyKey(firstValue), "Key is not a property key");
123 
124         if (LIKELY(firstValue->IsString())) {
125             if (isStaticFlag && !keysHasNameFlag && JSTaggedValue::SameValue(firstValue, nameString)) {
126                 properties->Set(thread, NAME_INDEX, secondValue);
127                 keysHasNameFlag = true;
128                 continue;
129             }
130 
131             // front-end can do better: write index in class literal directly.
132             uint32_t elementIndex = 0;
133             if (JSTaggedValue::StringToElementIndex(firstValue.GetTaggedValue(), &elementIndex)) {
134                 ASSERT(elementIndex < JSObject::MAX_ELEMENT_INDEX);
135                 uint32_t elementsLength = elements->GetLength();
136                 elements = TaggedArray::SetCapacity(thread, elements, elementsLength + 2);  // 2: key-value pair
137                 elements->Set(thread, elementsLength, firstValue);
138                 elements->Set(thread, elementsLength + 1, secondValue);
139                 withElemenstFlag = true;
140                 continue;
141             }
142         }
143 
144         keys->Set(thread, pos, firstValue);
145         properties->Set(thread, pos, secondValue);
146         pos++;
147     }
148 
149     if (isStaticFlag) {
150         if (LIKELY(!keysHasNameFlag)) {
151             [[maybe_unused]] EcmaHandleScope handleScope(thread);
152             ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
153             std::string clsName = detail.ctorMethod->ParseFunctionName();
154             JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
155             properties->Set(thread, NAME_INDEX, clsNameHandle);
156         } else {
157             // class has static name property, reserved length bigger 1 than actual, need trim
158             uint32_t trimOneLength = keys->GetLength() - 1;
159             keys->Trim(thread, trimOneLength);
160             properties->Trim(thread, trimOneLength);
161         }
162     }
163 
164     if (UNLIKELY(withElemenstFlag)) {
165         ASSERT(pos + elements->GetLength() / 2 == properties->GetLength());  // 2: half
166         keys->Trim(thread, pos);
167         properties->Trim(thread, pos);
168     }
169 
170     return withElemenstFlag;
171 }
172 
CreatePrototypeHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)173 JSHandle<JSHClass> ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
174                                                              JSHandle<TaggedArray> &properties)
175 {
176     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
177 
178     uint32_t length = keys->GetLength();
179     JSHandle<JSHClass> hclass;
180     if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
181         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
182         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length);
183         for (uint32_t index = 0; index < length; ++index) {
184             key.Update(keys->Get(index));
185             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
186             PropertyAttributes attributes = PropertyAttributes::Default(true, false, true);  // non-enumerable
187 
188             if (UNLIKELY(properties->Get(index).IsAccessor())) {
189                 attributes.SetIsAccessor(true);
190             }
191 
192             attributes.SetIsInlinedProps(true);
193             attributes.SetRepresentation(Representation::MIXED);
194             attributes.SetOffset(index);
195             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
196         }
197 
198         hclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, length);
199         // Not need set proto here
200         hclass->SetLayout(thread, layout);
201         hclass->SetNumberOfProps(length);
202     } else {
203         // dictionary mode
204         hclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
205         hclass->SetIsDictionaryMode(true);
206         hclass->SetNumberOfProps(0);
207     }
208 
209     hclass->SetClassPrototype(true);
210     hclass->SetIsPrototype(true);
211     return hclass;
212 }
213 
CreateConstructorHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)214 JSHandle<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
215                                                                JSHandle<TaggedArray> &properties)
216 {
217     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
218 
219     uint32_t length = keys->GetLength();
220     JSHandle<JSHClass> hclass;
221     if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
222         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
223         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length);
224         for (uint32_t index = 0; index < length; ++index) {
225             key.Update(keys->Get(index));
226             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
227             PropertyAttributes attributes;
228             switch (index) {
229                 case LENGTH_INDEX:
230                     attributes = PropertyAttributes::Default(false, false, true);
231                     break;
232                 case NAME_INDEX:
233                     if (LIKELY(properties->Get(NAME_INDEX).IsString())) {
234                         attributes = PropertyAttributes::Default(false, false, true);
235                     } else {
236                         ASSERT(properties->Get(NAME_INDEX).IsJSFunction());
237                         attributes = PropertyAttributes::Default(true, false, true);
238                     }
239                     break;
240                 case PROTOTYPE_INDEX:
241                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
242                     break;
243                 default:
244                     attributes = PropertyAttributes::Default(true, false, true);
245                     break;
246             }
247 
248             if (UNLIKELY(properties->Get(index).IsAccessor())) {
249                 attributes.SetIsAccessor(true);
250             }
251 
252             attributes.SetIsInlinedProps(true);
253             attributes.SetRepresentation(Representation::MIXED);
254             attributes.SetOffset(index);
255             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
256         }
257 
258         hclass = factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, length);
259         // Not need set proto here
260         hclass->SetLayout(thread, layout);
261         hclass->SetNumberOfProps(length);
262     } else {
263         // dictionary mode
264         hclass = factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0);  // without in-obj
265         hclass->SetIsDictionaryMode(true);
266         hclass->SetNumberOfProps(0);
267     }
268 
269     hclass->SetClassConstructor(true);
270     hclass->SetConstructor(true);
271 
272     return hclass;
273 }
274 
DefineClassTemplate(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<ConstantPool> & constantpool)275 JSHandle<JSFunction> ClassHelper::DefineClassTemplate(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
276                                                       const JSHandle<ConstantPool> &constantpool)
277 {
278     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
279 
280     JSHandle<JSHClass> prototypeHClass(thread, extractor->GetPrototypeHClass());
281     JSHandle<JSObject> prototype = factory->NewJSObject(prototypeHClass);
282 
283     JSHandle<JSHClass> constructorHClass(thread, extractor->GetConstructorHClass());
284     JSHandle<JSFunction> constructor = factory->NewJSFunctionByDynClass(extractor->GetConstructorMethod(),
285                                                                         constructorHClass,
286                                                                         FunctionKind::CLASS_CONSTRUCTOR);
287 
288     // non-static
289     JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
290     nonStaticProperties->Set(thread, 0, constructor);
291 
292     uint32_t nonStaticLength = nonStaticProperties->GetLength();
293     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
294 
295     if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
296         for (uint32_t index = 0; index < nonStaticLength; ++index) {
297             propValue.Update(nonStaticProperties->Get(index));
298             if (propValue->IsJSFunction()) {
299                 JSHandle<JSFunction> propFunc = JSHandle<JSFunction>::Cast(propValue);
300                 propFunc->SetHomeObject(thread, prototype);
301                 propFunc->SetConstantPool(thread, constantpool);
302             }
303             prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
304         }
305     } else {
306         JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
307         JSHandle<NameDictionary> dict = BuildDictionaryPropeties(thread, prototype, nonStaticKeys, nonStaticProperties,
308                                                                  ClassPropertyType::NON_STATIC, constantpool);
309         prototype->SetProperties(thread, dict);
310     }
311 
312     // non-static elements
313     if (UNLIKELY(extractor->GetNonStaticWithElements())) {
314         JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
315         ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements, constantpool);
316     }
317 
318     // static
319     JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
320     uint32_t staticLength = staticProperties->GetLength();
321 
322     if (LIKELY(!constructorHClass->IsDictionaryMode())) {
323         for (uint32_t index = 0; index < staticLength; ++index) {
324             propValue.Update(staticProperties->Get(index));
325             if (propValue->IsJSFunction()) {
326                 JSHandle<JSFunction> propFunc = JSHandle<JSFunction>::Cast(propValue);
327                 propFunc->SetHomeObject(thread, constructor);
328                 propFunc->SetConstantPool(thread, constantpool);
329             }
330             JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
331         }
332     } else {
333         JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
334         JSHandle<NameDictionary> dict = BuildDictionaryPropeties(thread, JSHandle<JSObject>(constructor), staticKeys,
335                                                                  staticProperties, ClassPropertyType::STATIC,
336                                                                  constantpool);
337         constructor->SetProperties(thread, dict);
338     }
339 
340     // static elements
341     if (UNLIKELY(extractor->GetStaticWithElements())) {
342         JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
343         ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements, constantpool);
344     }
345 
346     constructor->SetProtoOrDynClass(thread, prototype);
347 
348     return constructor;
349 }
350 
BuildDictionaryPropeties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<ConstantPool> & constantpool)351 JSHandle<NameDictionary> ClassHelper::BuildDictionaryPropeties(JSThread *thread, const JSHandle<JSObject> &object,
352                                                                JSHandle<TaggedArray> &keys,
353                                                                JSHandle<TaggedArray> &properties,
354                                                                ClassPropertyType type,
355                                                                const JSHandle<ConstantPool> &constantpool)
356 {
357     uint32_t length = keys->GetLength();
358     ASSERT(length > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES);
359     ASSERT(keys->GetLength() == properties->GetLength());
360 
361     JSMutableHandle<NameDictionary> dict(
362         thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
363     JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
364     JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
365     for (uint32_t index = 0; index < length; index++) {
366         PropertyAttributes attributes;
367         if (type == ClassPropertyType::STATIC) {
368             switch (index) {
369                 case ClassInfoExtractor::LENGTH_INDEX:
370                     attributes = PropertyAttributes::Default(false, false, true);
371                     break;
372                 case ClassInfoExtractor::NAME_INDEX:
373                     if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) {
374                         attributes = PropertyAttributes::Default(false, false, true);
375                     } else {
376                         ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsJSFunction());
377                         attributes = PropertyAttributes::Default(true, false, true);
378                     }
379                     break;
380                 case ClassInfoExtractor::PROTOTYPE_INDEX:
381                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
382                     break;
383                 default:
384                     attributes = PropertyAttributes::Default(true, false, true);
385                     break;
386             }
387         } else {
388             attributes = PropertyAttributes::Default(true, false, true);  // non-enumerable
389         }
390         propKey.Update(keys->Get(index));
391         propValue.Update(properties->Get(index));
392         if (propValue->IsJSFunction()) {
393             JSHandle<JSFunction> propFunc = JSHandle<JSFunction>::Cast(propValue);
394             propFunc->SetHomeObject(thread, object);
395             propFunc->SetConstantPool(thread, constantpool);
396         }
397         JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
398         dict.Update(newDict);
399     }
400 
401     return dict;
402 }
403 
HandleElementsProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & elements,const JSHandle<ConstantPool> & constantpool)404 void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
405                                            JSHandle<TaggedArray> &elements, const JSHandle<ConstantPool> &constantpool)
406 {
407     JSMutableHandle<JSTaggedValue> elementsKey(thread, JSTaggedValue::Undefined());
408     JSMutableHandle<JSTaggedValue> elementsValue(thread, JSTaggedValue::Undefined());
409     for (uint32_t index = 0; index < elements->GetLength(); index += 2) {  // 2: key-value pair
410         elementsKey.Update(elements->Get(index));
411         elementsValue.Update(elements->Get(index + 1));
412         // class property attribute is not default, will transition to dictionary directly.
413         JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true);
414 
415         if (elementsValue->IsJSFunction()) {
416             JSHandle<JSFunction> elementsFunc = JSHandle<JSFunction>::Cast(elementsValue);
417             elementsFunc->SetHomeObject(thread, object);
418             elementsFunc->SetConstantPool(thread, constantpool);
419         }
420     }
421 }
422 }  // namespace panda::ecmascript