1 /*
2 * Copyright (c) 2021-2024 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
18 #include "ecmascript/js_api/js_api_bitvector.h"
19 #include "ecmascript/js_object-inl.h"
20 #include "ecmascript/jspandafile/program_object.h"
21 #include "ecmascript/shared_objects/js_sendable_arraybuffer.h"
22 #include "ecmascript/shared_objects/js_shared_array.h"
23 #include "ecmascript/shared_objects/js_shared_map.h"
24 #include "ecmascript/shared_objects/js_shared_set.h"
25 #include "ecmascript/shared_objects/js_shared_typed_array.h"
26 #include "ecmascript/object_fast_operator-inl.h"
27
28 namespace panda::ecmascript {
BuildClassInfoExtractorFromLiteral(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & literal,uint32_t length,ClassKind kind)29 void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
30 const JSHandle<TaggedArray> &literal,
31 uint32_t length,
32 ClassKind kind)
33 {
34 [[maybe_unused]] EcmaHandleScope handleScope(thread);
35 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
36 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
37
38 ASSERT(length <= literal->GetLength());
39 // non static properties number is hidden in the last index of Literal buffer
40 uint32_t nonStaticNum = 0;
41 if (length != 0) {
42 nonStaticNum = static_cast<uint32_t>(literal->Get(thread, length - 1).GetInt());
43 }
44
45 // Reserve sufficient length to prevent frequent creation.
46 JSHandle<TaggedArray> nonStaticKeys;
47 JSHandle<TaggedArray> nonStaticProperties;
48 factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
49 if (kind == ClassKind::SENDABLE) {
50 nonStaticKeys = factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
51 nonStaticProperties =
52 factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
53 } else {
54 nonStaticKeys = factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
55 nonStaticProperties =
56 factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
57 }
58
59 nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString());
60 Method *method = Method::Cast(extractor->GetConstructorMethod().GetTaggedObject());
61 MethodLiteral *methodLiteral = method->GetMethodLiteral();
62 const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
63 EntityId methodId = method->GetMethodId();
64 if (nonStaticNum) {
65 ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr};
66
67 JSHandle<TaggedArray> nonStaticElements = factory->EmptyArray();
68 if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys,
69 nonStaticProperties, nonStaticElements, jsPandaFile))) {
70 extractor->SetNonStaticWithElements(true);
71 extractor->SetNonStaticElements(thread, nonStaticElements);
72 }
73 }
74
75 extractor->SetNonStaticKeys(thread, nonStaticKeys);
76 extractor->SetNonStaticProperties(thread, nonStaticProperties);
77
78 uint32_t staticNum = length == 0 ? 0 : (length - 1) / 2 - nonStaticNum;
79
80 // Reserve sufficient length to prevent frequent creation.
81 JSHandle<TaggedArray> staticKeys;
82 JSHandle<TaggedArray> staticProperties;
83 if (kind == ClassKind::SENDABLE) {
84 staticKeys = factory->NewSOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
85 staticProperties = factory->NewSOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
86 } else {
87 staticKeys = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
88 staticProperties = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
89 }
90
91 staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString());
92 staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString());
93 staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString());
94
95 JSHandle<TaggedArray> staticElements = factory->EmptyArray();
96
97 if (staticNum) {
98 ExtractContentsDetail staticDetail {
99 nonStaticNum * 2,
100 length - 1,
101 STATIC_RESERVED_LENGTH,
102 methodLiteral
103 };
104 if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys,
105 staticProperties, staticElements, jsPandaFile))) {
106 extractor->SetStaticWithElements(true);
107 extractor->SetStaticElements(thread, staticElements);
108 }
109 } else {
110 // without static properties, set class name
111 std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
112 JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
113 staticProperties->Set(thread, NAME_INDEX, clsNameHandle);
114 }
115
116 // set prototype internal accessor
117 JSHandle<JSTaggedValue> prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor();
118 staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor);
119
120 extractor->SetStaticKeys(thread, staticKeys);
121 extractor->SetStaticProperties(thread, staticProperties);
122 }
123
ExtractAndReturnWhetherWithElements(JSThread * thread,const JSHandle<TaggedArray> & literal,const ExtractContentsDetail & detail,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,JSHandle<TaggedArray> & elements,const JSPandaFile * jsPandaFile)124 bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle<TaggedArray> &literal,
125 const ExtractContentsDetail &detail,
126 JSHandle<TaggedArray> &keys,
127 JSHandle<TaggedArray> &properties,
128 JSHandle<TaggedArray> &elements,
129 const JSPandaFile *jsPandaFile)
130 {
131 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
132
133 ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0);
134
135 uint32_t pos = detail.fillStartLoc;
136 bool withElementsFlag = false;
137 bool isStaticFlag = (detail.methodLiteral != nullptr);
138 bool keysHasNameFlag = false;
139
140 JSHandle<JSTaggedValue> nameString = globalConst->GetHandledNameString();
141 JSMutableHandle<JSTaggedValue> firstValue(thread, JSTaggedValue::Undefined());
142 JSMutableHandle<JSTaggedValue> secondValue(thread, JSTaggedValue::Undefined());
143 for (uint32_t index = detail.extractBegin; index < detail.extractEnd; index += 2) { // 2: key-value pair
144 firstValue.Update(literal->Get(index));
145 secondValue.Update(literal->Get(index + 1));
146 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(firstValue), "Key is not a property key");
147
148 if (LIKELY(firstValue->IsString())) {
149 if (isStaticFlag && !keysHasNameFlag && JSTaggedValue::SameValue(firstValue, nameString)) {
150 properties->Set(thread, NAME_INDEX, secondValue);
151 keysHasNameFlag = true;
152 continue;
153 }
154
155 // front-end can do better: write index in class literal directly.
156 uint32_t elementIndex = 0;
157 if (JSTaggedValue::StringToElementIndex(firstValue.GetTaggedValue(), &elementIndex)) {
158 ASSERT(elementIndex < JSObject::MAX_ELEMENT_INDEX);
159 uint32_t elementsLength = elements->GetLength();
160 elements =
161 TaggedArray::SetCapacityInOldSpace(thread, elements, elementsLength + 2); // 2: key-value pair
162 elements->Set(thread, elementsLength, firstValue);
163 elements->Set(thread, elementsLength + 1, secondValue);
164 withElementsFlag = true;
165 continue;
166 }
167 }
168
169 keys->Set(thread, pos, firstValue);
170 properties->Set(thread, pos, secondValue);
171 pos++;
172 }
173
174 if (isStaticFlag) {
175 if (LIKELY(!keysHasNameFlag)) {
176 [[maybe_unused]] EcmaHandleScope handleScope(thread);
177 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
178 EntityId methodId = detail.methodLiteral->GetMethodId();
179 std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
180 JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
181 properties->Set(thread, NAME_INDEX, clsNameHandle);
182 } else {
183 // class has static name property, reserved length bigger 1 than actual, need trim
184 uint32_t trimOneLength = keys->GetLength() - 1;
185 keys->Trim(thread, trimOneLength);
186 properties->Trim(thread, trimOneLength);
187 }
188 }
189
190 if (UNLIKELY(withElementsFlag)) {
191 ASSERT(pos + elements->GetLength() / 2 == properties->GetLength()); // 2: half
192 keys->Trim(thread, pos);
193 properties->Trim(thread, pos);
194 }
195
196 return withElementsFlag;
197 }
198
CreatePrototypeHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)199 JSHandle<JSHClass> ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread,
200 JSHandle<TaggedArray> &keys,
201 JSHandle<TaggedArray> &properties)
202 {
203 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
204
205 uint32_t length = keys->GetLength();
206 JSHandle<JSHClass> hclass;
207 if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
208 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
209 JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
210 for (uint32_t index = 0; index < length; ++index) {
211 key.Update(keys->Get(index));
212 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
213 PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); // non-enumerable
214
215 if (UNLIKELY(properties->Get(index).IsAccessor())) {
216 attributes.SetIsAccessor(true);
217 }
218
219 attributes.SetIsInlinedProps(true);
220 attributes.SetRepresentation(Representation::TAGGED);
221 attributes.SetOffset(index);
222 layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
223 }
224
225 hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, length);
226 // Not need set proto here
227 hclass->SetLayout(thread, layout);
228 hclass->SetNumberOfProps(length);
229 } else {
230 // dictionary mode
231 hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0); // without in-obj
232 hclass->SetIsDictionaryMode(true);
233 hclass->SetNumberOfProps(0);
234 }
235
236 hclass->SetClassPrototype(true);
237 hclass->SetIsPrototype(true);
238 return hclass;
239 }
240
CreateConstructorHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties)241 JSHandle<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
242 JSHandle<TaggedArray> &keys,
243 JSHandle<TaggedArray> &properties)
244 {
245 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
246
247 uint32_t length = keys->GetLength();
248 if (!thread->GetEcmaVM()->IsEnablePGOProfiler()) {
249 // The class constructor of AOT is not shared, and PGO collect cannot be shared.
250 if (length == ClassInfoExtractor::STATIC_RESERVED_LENGTH && base->IsHole() &&
251 properties->Get(NAME_INDEX).IsString()) {
252 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
253 return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorClass());
254 }
255 }
256 JSHandle<JSHClass> hclass;
257 if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
258 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
259 JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
260 for (uint32_t index = 0; index < length; ++index) {
261 key.Update(keys->Get(index));
262 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
263 PropertyAttributes attributes;
264 switch (index) {
265 case LENGTH_INDEX:
266 attributes = PropertyAttributes::Default(false, false, true);
267 break;
268 case NAME_INDEX:
269 if (LIKELY(properties->Get(NAME_INDEX).IsString())) {
270 attributes = PropertyAttributes::Default(false, false, true);
271 } else {
272 ASSERT(properties->Get(NAME_INDEX).IsFunctionTemplate());
273 attributes = PropertyAttributes::Default(true, false, true);
274 }
275 break;
276 case PROTOTYPE_INDEX:
277 attributes = PropertyAttributes::DefaultAccessor(false, false, false);
278 break;
279 default:
280 attributes = PropertyAttributes::Default(true, false, true);
281 break;
282 }
283
284 if (UNLIKELY(properties->Get(index).IsAccessor())) {
285 attributes.SetIsAccessor(true);
286 }
287
288 attributes.SetIsInlinedProps(true);
289 attributes.SetRepresentation(Representation::TAGGED);
290 attributes.SetOffset(index);
291 layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
292 }
293
294 hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, length);
295 // Not need set proto here
296 hclass->SetLayout(thread, layout);
297 hclass->SetNumberOfProps(length);
298 } else {
299 // dictionary mode
300 hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0); // without in-obj
301 hclass->SetIsDictionaryMode(true);
302 hclass->SetNumberOfProps(0);
303 }
304
305 hclass->SetClassConstructor(true);
306 hclass->SetConstructor(true);
307
308 return hclass;
309 }
310
CorrectConstructorHClass(JSThread * thread,JSHandle<TaggedArray> & properties,JSHClass * constructorHClass)311 void ClassInfoExtractor::CorrectConstructorHClass(JSThread *thread,
312 JSHandle<TaggedArray> &properties,
313 JSHClass *constructorHClass)
314 {
315 if (constructorHClass->IsDictionaryMode()) {
316 return;
317 }
318 JSHandle<LayoutInfo> layout(thread, constructorHClass->GetLayout());
319 for (uint32_t index = 0; index < ClassInfoExtractor::STATIC_RESERVED_LENGTH; ++index) {
320 if (index == NAME_INDEX) {
321 if (UNLIKELY(properties->Get(NAME_INDEX).IsFunctionTemplate())) {
322 PropertyAttributes attr = layout->GetAttr(index);
323 attr.SetWritable(true);
324 layout->SetNormalAttr(thread, index, attr);
325 }
326 if (UNLIKELY(properties->Get(index).IsAccessor())) {
327 PropertyAttributes attr = layout->GetAttr(index);
328 attr.SetIsAccessor(true);
329 layout->SetNormalAttr(thread, index, attr);
330 }
331 } else {
332 if (UNLIKELY(properties->Get(index).IsAccessor())) {
333 PropertyAttributes attr = layout->GetAttr(index);
334 attr.SetIsAccessor(true);
335 layout->SetNormalAttr(thread, index, attr);
336 }
337 }
338 }
339 }
340
CreateSendableHClass(JSThread * thread,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,bool isProtoClass,uint32_t extraLength)341 JSHandle<JSHClass> ClassInfoExtractor::CreateSendableHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
342 JSHandle<TaggedArray> &properties, bool isProtoClass,
343 uint32_t extraLength)
344 {
345 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
346 uint32_t length = keys->GetLength();
347 JSHandle<JSHClass> hclass;
348 uint32_t maxInline = isProtoClass ? JSSharedObject::MAX_INLINE : JSSharedFunction::MAX_INLINE;
349 if (LIKELY(length + extraLength <= maxInline)) {
350 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
351 JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(length + extraLength);
352 for (uint32_t index = 0; index < length; ++index) {
353 key.Update(keys->Get(index));
354 ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
355 PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
356 if (UNLIKELY(properties->Get(index).IsAccessor())) {
357 attributes.SetIsAccessor(true);
358 }
359 attributes.SetIsInlinedProps(true);
360 attributes.SetRepresentation(Representation::TAGGED);
361 attributes.SetOffset(index);
362 layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
363 }
364 hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length) :
365 factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length + extraLength);
366 hclass->SetLayout(thread, layout);
367 hclass->SetNumberOfProps(length);
368 } else {
369 // dictionary mode
370 hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0) :
371 factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, 0);
372 hclass->SetIsDictionaryMode(true);
373 hclass->SetNumberOfProps(0);
374 }
375 if (isProtoClass) {
376 hclass->SetClassPrototype(true);
377 hclass->SetIsPrototype(true);
378 } else {
379 hclass->SetClassConstructor(true);
380 hclass->SetConstructor(true);
381 }
382 return hclass;
383 }
384
DefineClassFromExtractor(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv)385 JSHandle<JSFunction> ClassHelper::DefineClassFromExtractor(JSThread *thread, const JSHandle<JSTaggedValue> &base,
386 JSHandle<ClassInfoExtractor> &extractor,
387 const JSHandle<JSTaggedValue> &lexenv)
388 {
389 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
390 JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
391 JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
392
393 JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
394 JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
395 JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
396 nonStaticProperties);
397
398 JSHandle<JSObject> prototype = factory->NewOldSpaceJSObject(prototypeHClass);
399 JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
400 JSHandle<JSHClass> constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys,
401 staticProperties);
402 // Allocate to non-movable space for PGO
403 JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
404 MemSpaceType::NON_MOVABLE);
405
406 // non-static
407 nonStaticProperties->Set(thread, 0, constructor);
408
409 uint32_t nonStaticLength = nonStaticProperties->GetLength();
410 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
411
412 if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
413 for (uint32_t index = 0; index < nonStaticLength; ++index) {
414 propValue.Update(nonStaticProperties->Get(index));
415 if (propValue->IsFunctionTemplate()) {
416 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
417 propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, prototype, lexenv));
418 }
419 prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
420 }
421 } else {
422 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
423 ClassPropertyType::NON_STATIC, lexenv);
424 prototype->SetProperties(thread, dict);
425 }
426
427 // non-static elements
428 if (UNLIKELY(extractor->GetNonStaticWithElements())) {
429 JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
430 ClassHelper::HandleElementsProperties(thread, prototype, lexenv, nonStaticElements);
431 }
432
433 // static
434 uint32_t staticLength = staticProperties->GetLength();
435
436 if (LIKELY(!constructorHClass->IsDictionaryMode())) {
437 for (uint32_t index = 0; index < staticLength; ++index) {
438 propValue.Update(staticProperties->Get(index));
439 if (propValue->IsFunctionTemplate()) {
440 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
441 propValue.Update(
442 CreateJSFunctionFromTemplate(thread, literalFunc, JSHandle<JSObject>(constructor), lexenv));
443 }
444 JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
445 }
446 } else {
447 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
448 staticProperties, ClassPropertyType::STATIC, lexenv);
449 constructor->SetProperties(thread, dict);
450 }
451
452 // static elements
453 if (UNLIKELY(extractor->GetStaticWithElements())) {
454 JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
455 ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), lexenv, staticElements);
456 }
457
458 PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
459 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
460 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
461 globalConst->GetHandledConstructorString(), ctorDesc);
462 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
463 constructor->SetHomeObject(thread, prototype);
464 constructor->SetProtoOrHClass(thread, prototype);
465 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
466 thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
467 }
468 return constructor;
469 }
470
DefineClassWithIHClass(JSThread * thread,const JSHandle<JSTaggedValue> & base,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<JSTaggedValue> & lexenv,const JSHandle<JSTaggedValue> & prototypeOrHClassVal,const JSHandle<JSTaggedValue> & constructorHClassVal)471 JSHandle<JSFunction> ClassHelper::DefineClassWithIHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
472 JSHandle<ClassInfoExtractor> &extractor,
473 const JSHandle<JSTaggedValue> &lexenv,
474 const JSHandle<JSTaggedValue> &prototypeOrHClassVal,
475 const JSHandle<JSTaggedValue> &constructorHClassVal)
476 {
477 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
478 JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
479 JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
480 JSHandle<JSHClass> constructorHClass;
481 // When constructorHClassVal is undefined, it means that AOT has not generated the corresponding hclass (chc),
482 // then chc will be created through the interpreter.
483 if (constructorHClassVal->IsUndefined()) {
484 constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys, staticProperties);
485 } else {
486 constructorHClass = JSHandle<JSHClass>(constructorHClassVal);
487 ClassInfoExtractor::CorrectConstructorHClass(thread, staticProperties, *constructorHClass);
488 }
489
490 JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
491 JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
492 JSHandle<JSObject> prototype;
493 JSHandle<JSTaggedValue> prototypeOrHClass = prototypeOrHClassVal;
494 // When prototypeOrHClassVal is undefined, it means that AOT has not generated the corresponding hclass or
495 // prototype, then prototype will be created through the interpreter.
496 if (prototypeOrHClassVal->IsUndefined()) {
497 JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
498 nonStaticProperties);
499 prototype = factory->NewOldSpaceJSObject(prototypeHClass);
500 prototypeOrHClass = JSHandle<JSTaggedValue>(prototype);
501 } else if (prototypeOrHClassVal->IsJSHClass()) {
502 JSHandle<JSHClass> ihclass(prototypeOrHClassVal);
503 prototype = JSHandle<JSObject>(thread, ihclass->GetProto());
504 } else {
505 prototype = JSHandle<JSObject>(prototypeOrHClassVal);
506 }
507
508 JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
509 JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
510 MemSpaceType::NON_MOVABLE);
511
512 // non-static
513 nonStaticProperties->Set(thread, 0, constructor);
514
515 uint32_t nonStaticLength = nonStaticProperties->GetLength();
516 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
517
518 if (LIKELY(!prototype->GetJSHClass()->IsDictionaryMode())) {
519 for (uint32_t index = 0; index < nonStaticLength; ++index) {
520 propValue.Update(nonStaticProperties->Get(index));
521 if (propValue->IsFunctionTemplate()) {
522 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
523 propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, prototype, lexenv));
524 }
525 prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
526 }
527 } else {
528 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
529 ClassPropertyType::NON_STATIC, lexenv);
530 prototype->SetProperties(thread, dict);
531 }
532
533 // non-static elements
534 if (UNLIKELY(extractor->GetNonStaticWithElements())) {
535 JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
536 ClassHelper::HandleElementsProperties(thread, prototype, lexenv, nonStaticElements);
537 }
538
539 // static
540 uint32_t staticLength = staticProperties->GetLength();
541 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
542 int correntIndex = 0;
543 if (LIKELY(!constructorHClass->IsDictionaryMode())) {
544 for (uint32_t index = 0; index < staticLength; ++index) {
545 propValue.Update(staticProperties->Get(index));
546 if (propValue->IsFunctionTemplate()) {
547 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
548 propValue.Update(
549 CreateJSFunctionFromTemplate(thread, literalFunc, JSHandle<JSObject>(constructor), lexenv));
550 }
551 bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH;
552 if (needCorrentIndex) {
553 key.Update(staticKeys->Get(index));
554 correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue());
555 }
556 JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread,
557 needCorrentIndex ? static_cast<uint32_t>(correntIndex) : index, propValue.GetTaggedValue());
558 }
559 } else {
560 JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
561 staticProperties, ClassPropertyType::STATIC, lexenv);
562 constructor->SetProperties(thread, dict);
563 }
564
565 // static elements
566 if (UNLIKELY(extractor->GetStaticWithElements())) {
567 JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
568 ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), lexenv, staticElements);
569 }
570
571 PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
572 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
573 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
574 globalConst->GetHandledConstructorString(), ctorDesc);
575 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
576 constructor->SetHomeObject(thread, prototype);
577 constructor->SetProtoOrHClass(thread, prototypeOrHClass);
578
579 if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
580 thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
581 }
582 return constructor;
583 }
584
CreateJSFunctionFromTemplate(JSThread * thread,const JSHandle<FunctionTemplate> & funcTemp,const JSHandle<JSObject> & homeObject,const JSHandle<JSTaggedValue> & lexenv)585 JSHandle<JSFunction> ClassHelper::CreateJSFunctionFromTemplate(JSThread *thread,
586 const JSHandle<FunctionTemplate> &funcTemp,
587 const JSHandle<JSObject> &homeObject,
588 const JSHandle<JSTaggedValue> &lexenv)
589 {
590 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
591 JSHandle<JSFunction> propFunc = factory->CreateJSFunctionFromTemplate(funcTemp);
592 JSFunction::UpdateProfileTypeInfoCell(thread, funcTemp, propFunc);
593 propFunc->SetHomeObject(thread, homeObject);
594 propFunc->SetLexicalEnv(thread, lexenv);
595 return propFunc;
596 }
597
BuildDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSTaggedValue> & lexenv)598 JSHandle<NameDictionary> ClassHelper::BuildDictionaryProperties(JSThread *thread, const JSHandle<JSObject> &object,
599 JSHandle<TaggedArray> &keys,
600 JSHandle<TaggedArray> &properties,
601 ClassPropertyType type,
602 const JSHandle<JSTaggedValue> &lexenv)
603 {
604 uint32_t length = keys->GetLength();
605 ASSERT(length > PropertyAttributes::MAX_FAST_PROPS_CAPACITY);
606 ASSERT(keys->GetLength() == properties->GetLength());
607
608 JSMutableHandle<NameDictionary> dict(
609 thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
610 JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
611 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
612 for (uint32_t index = 0; index < length; index++) {
613 PropertyAttributes attributes;
614 if (type == ClassPropertyType::STATIC) {
615 switch (index) {
616 case ClassInfoExtractor::LENGTH_INDEX:
617 attributes = PropertyAttributes::Default(false, false, true);
618 break;
619 case ClassInfoExtractor::NAME_INDEX:
620 if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) {
621 attributes = PropertyAttributes::Default(false, false, true);
622 } else {
623 ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsFunctionTemplate());
624 attributes = PropertyAttributes::Default(true, false, true);
625 }
626 break;
627 case ClassInfoExtractor::PROTOTYPE_INDEX:
628 attributes = PropertyAttributes::DefaultAccessor(false, false, false);
629 break;
630 default:
631 attributes = PropertyAttributes::Default(true, false, true);
632 break;
633 }
634 } else {
635 attributes = PropertyAttributes::Default(true, false, true); // non-enumerable
636 }
637 propKey.Update(keys->Get(index));
638 propValue.Update(properties->Get(index));
639 if (propValue->IsFunctionTemplate()) {
640 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
641 propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, object, lexenv));
642 }
643 JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
644 dict.Update(newDict);
645 }
646 return dict;
647 }
648
MatchFieldType(SharedFieldType fieldType,JSTaggedValue value)649 bool ClassHelper::MatchFieldType(SharedFieldType fieldType, JSTaggedValue value)
650 {
651 // all sendable types can be set to undefined
652 if (value.IsUndefined()) {
653 return true;
654 }
655 uint32_t sharedFieldType = static_cast<uint32_t>(fieldType);
656 if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::NUMBER)) != 0 && value.IsNumber()) {
657 return true;
658 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::BOOLEAN)) != 0 && value.IsBoolean()) {
659 return true;
660 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::STRING)) != 0 &&
661 (value.IsString() || value.IsNull())) {
662 return true;
663 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::BIG_INT)) != 0 && value.IsBigInt()) {
664 return true;
665 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::SENDABLE)) != 0 &&
666 (value.IsJSShared() || value.IsNull())) {
667 return true;
668 } else if ((sharedFieldType == static_cast<uint32_t>(SharedFieldType::NONE) ||
669 (sharedFieldType & static_cast<uint32_t>(SharedFieldType::GENERIC)) != 0) &&
670 (value.IsJSShared() || !value.IsHeapObject())) {
671 // (none || generic) && (jsShared || !heapObject)
672 return true;
673 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::NULL_TYPE)) != 0 && value.IsNull()) {
674 return true;
675 } else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::UNDEFINED)) != 0 && value.IsUndefined()) {
676 return true;
677 }
678 return false;
679 }
680
HandleElementsProperties(JSThread * thread,const JSHandle<JSObject> & object,const JSHandle<JSTaggedValue> & lexenv,JSHandle<TaggedArray> & elements)681 void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
682 const JSHandle<JSTaggedValue> &lexenv, JSHandle<TaggedArray> &elements)
683 {
684 JSMutableHandle<JSTaggedValue> elementsKey(thread, JSTaggedValue::Undefined());
685 JSMutableHandle<JSTaggedValue> elementsValue(thread, JSTaggedValue::Undefined());
686 for (uint32_t index = 0; index < elements->GetLength(); index += 2) { // 2: key-value pair
687 elementsKey.Update(elements->Get(index));
688 elementsValue.Update(elements->Get(index + 1));
689 if (elementsValue->IsFunctionTemplate()) {
690 auto literalFunc = JSHandle<FunctionTemplate>::Cast(elementsValue);
691 elementsValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, object, lexenv));
692 }
693
694 // class property attribute is not default, will transition to dictionary directly.
695 JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true);
696 }
697 }
698
DefineSendableClassFromExtractor(JSThread * thread,JSHandle<ClassInfoExtractor> & extractor,const JSHandle<TaggedArray> & staticFieldArray)699 JSHandle<JSFunction> SendableClassDefiner::DefineSendableClassFromExtractor(JSThread *thread,
700 JSHandle<ClassInfoExtractor> &extractor, const JSHandle<TaggedArray> &staticFieldArray)
701 {
702 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
703 JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
704 JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
705 SendableClassDefiner::FilterDuplicatedKeys(thread, staticKeys, staticProperties);
706
707 JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
708 JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
709 SendableClassDefiner::FilterDuplicatedKeys(thread, nonStaticKeys, nonStaticProperties);
710 JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreateSendableHClass(thread, nonStaticKeys,
711 nonStaticProperties, true);
712 JSHandle<JSObject> prototype = factory->NewSharedOldSpaceJSObject(prototypeHClass);
713 uint32_t length = staticFieldArray->GetLength();
714 uint32_t staticFields = length / 2; // 2: key-type
715 JSHandle<JSHClass> constructorHClass =
716 ClassInfoExtractor::CreateSendableHClass(thread, staticKeys, staticProperties, false, staticFields);
717 JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
718 /*
719 * Method::SetFunctionKind can't be called here, because method will set kind when set inheritance relationship,
720 * so there is a multi-threading problem with multi-threads define sendable DERIVED class at the same time.
721 * Scenario:
722 * A thread: define DERIVED class X [X's kind = DEFAULT --> BASE CLASS --> DERIVED CLASS], new X()
723 * B thread: define DERIVED class X [X's kind = DEFAULT --> BASE CLASS --> DERIVED CLASS], new X()
724 * Issue:
725 * When A thread new DERIVED class X, X's kind maybe set to BASE CLASS at B thread,
726 * and A thread will throw error when call super().
727 */
728 if (!constructorHClass->IsDictionaryMode() && staticFields > 0) {
729 auto layout = JSHandle<LayoutInfo>(thread, constructorHClass->GetLayout());
730 AddFieldTypeToHClass(thread, staticFieldArray, length, layout, constructorHClass, ~0U);
731 }
732
733 JSHandle<JSFunction> constructor = factory->NewSFunctionByHClass(method, constructorHClass);
734
735 // non-static
736 nonStaticProperties->Set(thread, 0, constructor);
737
738 uint32_t nonStaticLength = nonStaticProperties->GetLength();
739 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
740
741 if (LIKELY(!prototypeHClass->IsDictionaryMode())) {
742 for (uint32_t index = 0; index < nonStaticLength; ++index) {
743 propValue.Update(nonStaticProperties->Get(index));
744 // constructor don't need to clone
745 if (propValue->IsFunctionTemplate() && index != ClassInfoExtractor::CONSTRUCTOR_INDEX) {
746 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
747 propValue.Update(
748 CreateSFunctionFromTemplate(thread, literalFunc, prototype, JSHandle<JSTaggedValue>(constructor)));
749 } else if (propValue->IsAccessorData()) {
750 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(prototype), constructor);
751 }
752 prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
753 }
754 } else {
755 JSHandle<NameDictionary> dict = BuildSendableDictionaryProperties(thread, prototype, nonStaticKeys,
756 nonStaticProperties, ClassPropertyType::NON_STATIC, constructor);
757 prototype->SetProperties(thread, dict);
758 }
759
760 // non-static elements
761 if (UNLIKELY(extractor->GetNonStaticWithElements())) {
762 THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support members with numerical key",
763 JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
764 }
765
766 // static
767 uint32_t staticLength = staticProperties->GetLength();
768 if (LIKELY(!constructorHClass->IsDictionaryMode())) {
769 for (uint32_t index = 0; index < staticLength; ++index) {
770 propValue.Update(staticProperties->Get(index));
771 if (propValue->IsFunctionTemplate()) {
772 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
773 propValue.Update(CreateSFunctionFromTemplate(
774 thread, literalFunc, JSHandle<JSObject>(constructor), JSHandle<JSTaggedValue>(constructor)));
775 } else if (propValue->IsAccessorData()) {
776 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(constructor), constructor);
777 }
778 JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
779 }
780 } else {
781 JSHandle<NameDictionary> dict =
782 BuildSendableDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
783 staticProperties, ClassPropertyType::STATIC, constructor);
784 JSMutableHandle<NameDictionary> nameDict(thread, dict);
785 if (staticFields > 0) {
786 AddFieldTypeToDict(thread, staticFieldArray, length, nameDict,
787 PropertyAttributes::Default(true, true, false));
788 }
789 constructor->SetProperties(thread, nameDict);
790 }
791
792 // static elements
793 if (UNLIKELY(extractor->GetStaticWithElements())) {
794 THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support static members with numerical key",
795 JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
796 }
797 prototype->GetJSHClass()->SetExtensible(false);
798 constructor->SetHomeObject(thread, prototype);
799 constructor->SetProtoOrHClass(thread, prototype);
800 constructor->SetLexicalEnv(thread, constructor);
801 return constructor;
802 }
803
804 // Process duplicated key due to getter/setter.
FilterDuplicatedKeys(JSThread * thread,const JSHandle<TaggedArray> & keys,const JSHandle<TaggedArray> & properties)805 void SendableClassDefiner::FilterDuplicatedKeys(JSThread *thread, const JSHandle<TaggedArray> &keys,
806 const JSHandle<TaggedArray> &properties)
807 {
808 auto attr = PropertyAttributes::Default();
809 uint32_t length = keys->GetLength();
810 uint32_t left = 0;
811 uint32_t right = 0;
812 JSMutableHandle<NameDictionary> dict(
813 thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
814 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
815 JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
816 JSMutableHandle<JSTaggedValue> existValue(thread, JSTaggedValue::Undefined());
817 JSMutableHandle<JSTaggedValue> index(thread, JSTaggedValue::Undefined());
818 for (; right < length; right++) {
819 key.Update(keys->Get(right));
820 value.Update(properties->Get(right));
821 int entry = dict->FindEntry(key.GetTaggedValue());
822 if (entry == -1) {
823 TryUpdateValue(thread, value);
824 index.Update(JSTaggedValue(left));
825 JSHandle<NameDictionary> newDict =
826 NameDictionary::PutIfAbsent(thread, dict, key, index, attr);
827 dict.Update(newDict);
828 if (left < right) {
829 keys->Set(thread, left, key);
830 }
831 properties->Set(thread, left, value);
832 left++;
833 continue;
834 }
835 auto existIndex = static_cast<uint32_t>(dict->GetValue(entry).GetNumber());
836 existValue.Update(properties->Get(existIndex));
837 bool needUpdateValue = TryUpdateExistValue(thread, existValue, value);
838 if (needUpdateValue) {
839 properties->Set(thread, existIndex, value);
840 }
841 }
842 if (left < right) {
843 keys->Trim(thread, left);
844 properties->Trim(thread, left);
845 }
846 }
847
BuildSendableDictionaryProperties(JSThread * thread,const JSHandle<JSObject> & object,JSHandle<TaggedArray> & keys,JSHandle<TaggedArray> & properties,ClassPropertyType type,const JSHandle<JSFunction> & ctor)848 JSHandle<NameDictionary> SendableClassDefiner::BuildSendableDictionaryProperties(JSThread *thread,
849 const JSHandle<JSObject> &object, JSHandle<TaggedArray> &keys, JSHandle<TaggedArray> &properties,
850 ClassPropertyType type, const JSHandle<JSFunction> &ctor)
851 {
852 uint32_t length = keys->GetLength();
853 ASSERT(keys->GetLength() == properties->GetLength());
854
855 JSMutableHandle<NameDictionary> dict(
856 thread, NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(length)));
857 JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
858 JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
859 for (uint32_t index = 0; index < length; index++) {
860 PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
861 if (UNLIKELY(properties->Get(index).IsAccessor())) {
862 attributes.SetIsAccessor(true);
863 }
864 propKey.Update(keys->Get(index));
865 propValue.Update(properties->Get(index));
866 // constructor don't need to clone
867 if (index == ClassInfoExtractor::CONSTRUCTOR_INDEX && type == ClassPropertyType::NON_STATIC) {
868 JSHandle<NameDictionary> newDict =
869 NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
870 dict.Update(newDict);
871 continue;
872 }
873 if (propValue->IsFunctionTemplate()) {
874 auto literalFunc = JSHandle<FunctionTemplate>::Cast(propValue);
875 propValue.Update(CreateSFunctionFromTemplate(thread, literalFunc, object, JSHandle<JSTaggedValue>(ctor)));
876 } else if (propValue->IsAccessorData()) {
877 UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(object), ctor);
878 }
879 JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
880 dict.Update(newDict);
881 }
882 return dict;
883 }
884
CreateSFunctionFromTemplate(JSThread * thread,const JSHandle<FunctionTemplate> & funcTemp,const JSHandle<JSObject> & homeObject,const JSHandle<JSTaggedValue> & lexenv)885 JSHandle<JSFunction> SendableClassDefiner::CreateSFunctionFromTemplate(JSThread *thread,
886 const JSHandle<FunctionTemplate> &funcTemp,
887 const JSHandle<JSObject> &homeObject,
888 const JSHandle<JSTaggedValue> &lexenv)
889 {
890 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
891 JSHandle<JSFunction> propFunc = factory->CreateSFunctionFromTemplate(funcTemp);
892 propFunc->SetHomeObject(thread, homeObject);
893 propFunc->SetLexicalEnv(thread, lexenv);
894 ASSERT(!propFunc->GetClass()->IsExtensible());
895 return propFunc;
896 }
897
AddFieldTypeToDict(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,JSMutableHandle<NameDictionary> & dict,PropertyAttributes attributes)898 void SendableClassDefiner::AddFieldTypeToDict(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
899 uint32_t length, JSMutableHandle<NameDictionary> &dict, PropertyAttributes attributes)
900 {
901 ASSERT(length <= fieldTypeArray->GetLength());
902 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
903 auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
904 JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
905 for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
906 key.Update(fieldTypeArray->Get(i));
907 ASSERT(key->IsString());
908 SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
909 attributes.SetSharedFieldType(type);
910 attributes.SetBoxType(PropertyBoxType::UNDEFINED);
911 JSHandle<NameDictionary> newDict = NameDictionary::Put(thread, dict, key, value, attributes);
912 dict.Update(newDict);
913 }
914 }
915
AddFieldTypeToHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,const JSHandle<LayoutInfo> & layout,const JSHandle<JSHClass> & hclass,size_t start,const JSHandle<NumberDictionary> & elementsDic,std::vector<JSHandle<JSTaggedValue>> && propertyList)916 void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
917 uint32_t length, const JSHandle<LayoutInfo> &layout,
918 const JSHandle<JSHClass> &hclass, size_t start,
919 const JSHandle<NumberDictionary> &elementsDic,
920 std::vector<JSHandle<JSTaggedValue>> &&propertyList)
921 {
922 ASSERT(length <= fieldTypeArray->GetLength());
923 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
924 uint32_t index = static_cast<uint32_t>(layout->NumberOfElements());
925 JSMutableHandle<NumberDictionary> elementsDicUpdate(thread, elementsDic);
926 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
927 JSHandle<JSTaggedValue> undefinedVal(thread, globalConst->GetUndefined());
928 JSMutableHandle<JSTaggedValue> eleIndexKey(thread, JSTaggedValue::Undefined());
929 for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
930 PropertyAttributes attributes = PropertyAttributes::Default(true, true, false);
931 key.Update(fieldTypeArray->Get(i));
932 ASSERT(key->IsString());
933 SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
934 int entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index);
935 if (entry != -1) {
936 attributes = layout->GetAttr(entry);
937 attributes.SetSharedFieldType(type);
938 layout->SetNormalAttr(thread, entry, attributes);
939 if (start != ~0U && propertyList.size() > 0) {
940 propertyList[start + (static_cast<uint32_t>(entry) << 1)] = propertyList[start + i];
941 propertyList[start + (static_cast<uint32_t>(entry) << 1) + 1] = propertyList[start + i + 1];
942 }
943 } else {
944 if (start != ~0U && propertyList.size() > 0) {
945 propertyList[start + (index << 1)] = propertyList[start + i];
946 propertyList[start + (index << 1) + 1] = propertyList[start + i + 1];
947 }
948 attributes.SetIsInlinedProps(true);
949 attributes.SetRepresentation(Representation::TAGGED);
950 attributes.SetSharedFieldType(type);
951 attributes.SetOffset(index);
952 layout->AddKey(thread, index++, key.GetTaggedValue(), attributes);
953 int64_t eleIndex = ObjectFastOperator::TryToElementsIndex(key.GetTaggedValue());
954 if (eleIndex >= 0 && !elementsDic.IsEmpty()) {
955 eleIndexKey.Update(JSTaggedValue(eleIndex));
956 JSHandle<NumberDictionary> newElementsDic = NumberDictionary::Put(
957 thread, elementsDic, eleIndexKey, undefinedVal, attributes);
958 elementsDicUpdate.Update(newElementsDic);
959 }
960 }
961 }
962 hclass->SetLayout(thread, layout);
963 hclass->SetNumberOfProps(index);
964 auto inlinedProps = hclass->GetInlinedProperties();
965 if (inlinedProps > index) {
966 // resize hclass due to duplicated key.
967 uint32_t duplicatedSize = (inlinedProps - index) * JSTaggedValue::TaggedTypeSize();
968 hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize);
969 }
970 }
971
AddFieldTypeToHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,const JSHandle<NameDictionary> & nameDict,const JSHandle<JSHClass> & hclass)972 void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
973 uint32_t length, const JSHandle<NameDictionary> &nameDict, const JSHandle<JSHClass> &hclass)
974 {
975 JSMutableHandle<NameDictionary> dict(thread, nameDict);
976 AddFieldTypeToDict(thread, fieldTypeArray, length, dict);
977 hclass->SetLayout(thread, dict);
978 hclass->SetNumberOfProps(0);
979 hclass->SetIsDictionaryMode(true);
980 }
981
DefineSendableInstanceHClass(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray,uint32_t length,const JSHandle<JSFunction> & ctor,const JSHandle<JSTaggedValue> & base)982 void SendableClassDefiner::DefineSendableInstanceHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
983 uint32_t length, const JSHandle<JSFunction> &ctor, const JSHandle<JSTaggedValue> &base)
984 {
985 ASSERT(ctor->GetClass()->IsJSSharedFunction());
986 JSHandle<JSObject> clsPrototype(thread, JSHandle<JSFunction>(ctor)->GetFunctionPrototype());
987 ASSERT(clsPrototype->GetClass()->IsJSSharedObject());
988 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
989 ASSERT(length <= fieldTypeArray->GetLength());
990 uint32_t fieldNum = length / 2; // 2: key-value pair;
991 JSHandle<JSHClass> iHClass;
992 if (base->IsHole() || base->IsNull()) {
993 if (fieldNum == 0) {
994 iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
995 } else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) {
996 iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
997 JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(fieldNum);
998 AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass, ~0U);
999 } else {
1000 iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
1001 JSHandle<NameDictionary> dict =
1002 NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(fieldNum));
1003 AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass);
1004 }
1005 } else {
1006 ASSERT(base->IsJSSharedFunction());
1007 JSHandle<JSFunction> baseCtor = JSHandle<JSFunction>::Cast(base);
1008 JSHandle<JSHClass> baseIHClass(thread, baseCtor->GetProtoOrHClass());
1009 ASSERT(baseIHClass->IsJSShared());
1010 JSType baseType = baseIHClass->GetObjectType();
1011 const auto [baseSize, baseMaxInlineSize] = GetSizeAndMaxInlineByType(baseType);
1012 if (LIKELY(!baseIHClass->IsDictionaryMode())) {
1013 auto baseLength = baseIHClass->NumberOfProps();
1014 JSHandle<LayoutInfo> baseLayout(thread, baseIHClass->GetLayout());
1015 auto newLength = baseLength + fieldNum;
1016 if (newLength == 0) {
1017 iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
1018 } else if (LIKELY(newLength <= baseMaxInlineSize)) {
1019 iHClass = factory->NewSEcmaHClass(baseSize, baseType, newLength);
1020 JSHandle<LayoutInfo> layout = factory->CopyAndReSortSLayoutInfo(baseLayout, baseLength, newLength);
1021 AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass, ~0U);
1022 } else {
1023 iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
1024 JSHandle<NameDictionary> dict =
1025 NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength));
1026 auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
1027 JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
1028 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1029 for (uint32_t i = 0; i < baseLength; i++) {
1030 key.Update(baseLayout->GetKey(i));
1031 PropertyAttributes attr = baseLayout->GetAttr(i);
1032 attr.SetIsInlinedProps(false);
1033 attr.SetBoxType(PropertyBoxType::UNDEFINED);
1034 dict = NameDictionary::Put(thread, dict, key, value, attr);
1035 }
1036 AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass);
1037 }
1038 } else {
1039 JSHandle<NameDictionary> baseDict(thread, baseIHClass->GetLayout());
1040 auto baseLength = baseDict->EntriesCount();
1041 auto newLength = fieldNum + static_cast<uint32_t>(baseLength);
1042 JSHandle<NameDictionary> dict =
1043 NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength));
1044 baseDict->Rehash(thread, *dict);
1045 dict->SetNextEnumerationIndex(thread, baseDict->GetNextEnumerationIndex());
1046 iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
1047 AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass);
1048 }
1049 }
1050 iHClass->SetPrototype(thread, JSHandle<JSTaggedValue>(clsPrototype));
1051 iHClass->SetExtensible(false);
1052 ctor->SetProtoOrHClass(thread, iHClass);
1053 ctor->GetJSHClass()->SetExtensible(false);
1054 }
1055
ExtractStaticFieldTypeArray(JSThread * thread,const JSHandle<TaggedArray> & fieldTypeArray)1056 JSHandle<TaggedArray> SendableClassDefiner::ExtractStaticFieldTypeArray(JSThread *thread,
1057 const JSHandle<TaggedArray> &fieldTypeArray)
1058 {
1059 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1060 uint32_t arrayLength = fieldTypeArray->GetLength();
1061 ASSERT(arrayLength > 0);
1062 auto instanceFieldNums = static_cast<uint32_t>(fieldTypeArray->Get(arrayLength - 1).GetInt());
1063 uint32_t staticFieldBegin = instanceFieldNums * 2; // 2: key-type
1064 if (staticFieldBegin >= arrayLength) {
1065 LOG_ECMA(ERROR) << "ExtractStaticFieldTypeArray Failed, staticFieldBegin:" << staticFieldBegin
1066 << " should be less than totalLength:" << arrayLength;
1067 return factory->EmptyArray();
1068 }
1069 uint32_t staticFieldLength = arrayLength - staticFieldBegin - 1;
1070 JSHandle<TaggedArray> staticFieldArray = factory->NewTaggedArray(staticFieldLength);
1071 for (uint32_t i = 0; i < staticFieldLength; i += 2) { // 2: key-type
1072 staticFieldArray->Set(thread, i, fieldTypeArray->Get(staticFieldBegin + i));
1073 staticFieldArray->Set(thread, i + 1, fieldTypeArray->Get(staticFieldBegin + i + 1));
1074 }
1075 return staticFieldArray;
1076 }
1077
UpdateAccessorFunction(JSThread * thread,const JSMutableHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & homeObject,const JSHandle<JSFunction> & ctor)1078 void SendableClassDefiner::UpdateAccessorFunction(JSThread *thread, const JSMutableHandle<JSTaggedValue> &value,
1079 const JSHandle<JSTaggedValue> &homeObject, const JSHandle<JSFunction> &ctor)
1080 {
1081 ASSERT(value->IsAccessorData());
1082 JSHandle<AccessorData> accessor(value);
1083 auto getter = accessor->GetGetter();
1084 if (getter.IsFunctionTemplate()) {
1085 auto funcTemp = JSHandle<FunctionTemplate>(thread, getter);
1086 auto propFunc = CreateSFunctionFromTemplate(
1087 thread, funcTemp, JSHandle<JSObject>(homeObject), JSHandle<JSTaggedValue>(ctor));
1088 accessor->SetGetter(thread, propFunc);
1089 }
1090 auto setter = accessor->GetSetter();
1091 if (setter.IsFunctionTemplate()) {
1092 auto funcTemp = JSHandle<FunctionTemplate>(thread, setter);
1093 auto propFunc = CreateSFunctionFromTemplate(
1094 thread, funcTemp, JSHandle<JSObject>(homeObject), JSHandle<JSTaggedValue>(ctor));
1095 accessor->SetSetter(thread, propFunc);
1096 }
1097 }
1098
TryUpdateExistValue(JSThread * thread,JSMutableHandle<JSTaggedValue> & existValue,JSMutableHandle<JSTaggedValue> & value)1099 bool SendableClassDefiner::TryUpdateExistValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &existValue,
1100 JSMutableHandle<JSTaggedValue> &value)
1101 {
1102 bool needUpdateValue = true;
1103 if (existValue->IsAccessorData()) {
1104 if (value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter()) {
1105 JSHandle<AccessorData> accessor(existValue);
1106 UpdateValueToAccessor(thread, value, accessor);
1107 needUpdateValue = false;
1108 }
1109 } else {
1110 if (value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter()) {
1111 JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
1112 UpdateValueToAccessor(thread, value, accessor);
1113 }
1114 }
1115 return needUpdateValue;
1116 }
1117
TryUpdateValue(JSThread * thread,JSMutableHandle<JSTaggedValue> & value)1118 void SendableClassDefiner::TryUpdateValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &value)
1119 {
1120 if (value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter()) {
1121 JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
1122 UpdateValueToAccessor(thread, value, accessor);
1123 }
1124 }
1125
UpdateValueToAccessor(JSThread * thread,JSMutableHandle<JSTaggedValue> & value,JSHandle<AccessorData> & accessor)1126 void SendableClassDefiner::UpdateValueToAccessor(JSThread *thread, JSMutableHandle<JSTaggedValue> &value,
1127 JSHandle<AccessorData> &accessor)
1128 {
1129 ASSERT(value->IsFunctionTemplate() && JSHandle<FunctionTemplate>(value)->IsGetterOrSetter());
1130 if (JSHandle<FunctionTemplate>(value)->IsGetter()) {
1131 accessor->SetGetter(thread, value);
1132 } else {
1133 accessor->SetSetter(thread, value);
1134 }
1135 value.Update(accessor);
1136 }
1137
GetSizeAndMaxInlineByType(JSType type)1138 std::pair<uint32_t, uint32_t> SendableClassDefiner::GetSizeAndMaxInlineByType(JSType type)
1139 {
1140 switch (type) {
1141 case JSType::JS_SHARED_OBJECT:
1142 return { JSSharedObject::SIZE, JSSharedObject::MAX_INLINE };
1143 case JSType::JS_SHARED_ARRAY:
1144 return { JSSharedArray::SIZE, JSSharedArray::MAX_INLINE };
1145 case JSType::JS_SHARED_MAP:
1146 return { JSSharedMap::SIZE, JSSharedMap::MAX_INLINE };
1147 case JSType::JS_SHARED_SET:
1148 return { JSSharedSet::SIZE, JSSharedSet::MAX_INLINE };
1149 case JSType::JS_SENDABLE_ARRAY_BUFFER:
1150 return { JSSendableArrayBuffer::SIZE, JSSendableArrayBuffer::MAX_INLINE };
1151 case JSType::JS_API_BITVECTOR:
1152 return { JSAPIBitVector::SIZE, JSAPIBitVector::MAX_INLINE };
1153 default:
1154 if (JSType::JS_SHARED_TYPED_ARRAY_FIRST < type && type <= JSType::JS_SHARED_TYPED_ARRAY_LAST) {
1155 return { JSSharedTypedArray::SIZE, JSSharedTypedArray::MAX_INLINE };
1156 }
1157 LOG_ECMA(FATAL) << "this branch is unreachable, cannot get size for type: " << static_cast<uint32_t>(type);
1158 UNREACHABLE();
1159 return {};
1160 }
1161 }
1162 } // namespace panda::ecmascript
1163