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