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