/* * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/jspandafile/class_info_extractor.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/shared_objects/js_sendable_arraybuffer.h" #include "ecmascript/shared_objects/js_shared_map.h" #include "ecmascript/object_fast_operator-inl.h" namespace panda::ecmascript { void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle &extractor, const JSHandle &literal, uint32_t length, ClassKind kind) { [[maybe_unused]] EcmaHandleScope handleScope(thread); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); ASSERT(length <= literal->GetLength()); // non static properties number is hidden in the last index of Literal buffer uint32_t nonStaticNum = 0; if (length != 0) { nonStaticNum = static_cast(literal->Get(thread, length - 1).GetInt()); } // Reserve sufficient length to prevent frequent creation. JSHandle nonStaticKeys; JSHandle nonStaticProperties; if (kind == ClassKind::SENDABLE) { nonStaticKeys = factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); nonStaticProperties = factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); } else { nonStaticKeys = factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); nonStaticProperties = factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); } nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString()); Method *method = Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()); MethodLiteral *methodLiteral = method->GetMethodLiteral(); const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); EntityId methodId = method->GetMethodId(); if (nonStaticNum) { ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr}; JSHandle nonStaticElements = factory->EmptyArray(); if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys, nonStaticProperties, nonStaticElements, jsPandaFile))) { extractor->SetNonStaticWithElements(true); extractor->SetNonStaticElements(thread, nonStaticElements); } } extractor->SetNonStaticKeys(thread, nonStaticKeys); extractor->SetNonStaticProperties(thread, nonStaticProperties); uint32_t staticNum = length == 0 ? 0 : (length - 1) / 2 - nonStaticNum; // Reserve sufficient length to prevent frequent creation. JSHandle staticKeys; JSHandle staticProperties; if (kind == ClassKind::SENDABLE) { staticKeys = factory->NewSOldSpaceTaggedArray(staticNum + SENDABLE_STATIC_RESERVED_LENGTH); staticProperties = factory->NewSOldSpaceTaggedArray(staticNum + SENDABLE_STATIC_RESERVED_LENGTH); } else { staticKeys = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH); staticProperties = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH); } staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString()); staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString()); staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString()); if (kind == ClassKind::SENDABLE) { staticKeys->Set(thread, SENDABLE_ELEMENTS_INDEX, globalConst->GetSendableElementsSymbol()); } JSHandle staticElements = factory->EmptyArray(); if (staticNum) { ExtractContentsDetail staticDetail {}; if (kind == ClassKind::SENDABLE) { staticDetail = { nonStaticNum * 2, length - 1, SENDABLE_STATIC_RESERVED_LENGTH, methodLiteral }; } else { staticDetail = { nonStaticNum * 2, length - 1, STATIC_RESERVED_LENGTH, methodLiteral }; } if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys, staticProperties, staticElements, jsPandaFile))) { extractor->SetStaticWithElements(true); extractor->SetStaticElements(thread, staticElements); } } else { // without static properties, set class name std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId); JSHandle clsNameHandle = factory->NewFromStdString(clsName); staticProperties->Set(thread, NAME_INDEX, clsNameHandle); } // set prototype internal accessor JSHandle prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor(); staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor); if (kind == ClassKind::SENDABLE) { staticProperties->Set(thread, SENDABLE_ELEMENTS_INDEX, JSTaggedValue::Undefined()); } extractor->SetStaticKeys(thread, staticKeys); extractor->SetStaticProperties(thread, staticProperties); } bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle &literal, const ExtractContentsDetail &detail, JSHandle &keys, JSHandle &properties, JSHandle &elements, const JSPandaFile *jsPandaFile) { const GlobalEnvConstants *globalConst = thread->GlobalConstants(); ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0); uint32_t pos = detail.fillStartLoc; bool withElementsFlag = false; bool isStaticFlag = (detail.methodLiteral != nullptr); bool keysHasNameFlag = false; JSHandle nameString = globalConst->GetHandledNameString(); JSMutableHandle firstValue(thread, JSTaggedValue::Undefined()); JSMutableHandle secondValue(thread, JSTaggedValue::Undefined()); for (uint32_t index = detail.extractBegin; index < detail.extractEnd; index += 2) { // 2: key-value pair firstValue.Update(literal->Get(index)); secondValue.Update(literal->Get(index + 1)); ASSERT_PRINT(JSTaggedValue::IsPropertyKey(firstValue), "Key is not a property key"); if (LIKELY(firstValue->IsString())) { if (isStaticFlag && !keysHasNameFlag && JSTaggedValue::SameValue(firstValue, nameString)) { properties->Set(thread, NAME_INDEX, secondValue); keysHasNameFlag = true; continue; } // front-end can do better: write index in class literal directly. uint32_t elementIndex = 0; if (JSTaggedValue::StringToElementIndex(firstValue.GetTaggedValue(), &elementIndex)) { ASSERT(elementIndex < JSObject::MAX_ELEMENT_INDEX); uint32_t elementsLength = elements->GetLength(); elements = TaggedArray::SetCapacityInOldSpace(thread, elements, elementsLength + 2); // 2: key-value pair elements->Set(thread, elementsLength, firstValue); elements->Set(thread, elementsLength + 1, secondValue); withElementsFlag = true; continue; } } keys->Set(thread, pos, firstValue); properties->Set(thread, pos, secondValue); pos++; } if (isStaticFlag) { if (LIKELY(!keysHasNameFlag)) { [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); EntityId methodId = detail.methodLiteral->GetMethodId(); std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId); JSHandle clsNameHandle = factory->NewFromStdString(clsName); properties->Set(thread, NAME_INDEX, clsNameHandle); } else { // class has static name property, reserved length bigger 1 than actual, need trim uint32_t trimOneLength = keys->GetLength() - 1; keys->Trim(thread, trimOneLength); properties->Trim(thread, trimOneLength); } } if (UNLIKELY(withElementsFlag)) { ASSERT(pos + elements->GetLength() / 2 == properties->GetLength()); // 2: half keys->Trim(thread, pos); properties->Trim(thread, pos); } return withElementsFlag; } JSHandle ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, JSHandle &keys, JSHandle &properties) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t length = keys->GetLength(); JSHandle hclass; if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP); for (uint32_t index = 0; index < length; ++index) { key.Update(keys->Get(index)); ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); // non-enumerable if (UNLIKELY(properties->Get(index).IsAccessor())) { attributes.SetIsAccessor(true); } attributes.SetIsInlinedProps(true); attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, length); // Not need set proto here hclass->SetLayout(thread, layout); hclass->SetNumberOfProps(length); } else { // dictionary mode hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0); // without in-obj hclass->SetIsDictionaryMode(true); hclass->SetNumberOfProps(0); } hclass->SetClassPrototype(true); hclass->SetIsPrototype(true); return hclass; } JSHandle ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, const JSHandle &base, JSHandle &keys, JSHandle &properties) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t length = keys->GetLength(); if (!thread->GetEcmaVM()->IsEnablePGOProfiler()) { // The class constructor of AOT is not shared, and PGO collect cannot be shared. if (length == ClassInfoExtractor::STATIC_RESERVED_LENGTH && base->IsHole() && properties->Get(NAME_INDEX).IsString()) { const GlobalEnvConstants *globalConst = thread->GlobalConstants(); return JSHandle(globalConst->GetHandledClassConstructorClass()); } } JSHandle hclass; if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP); for (uint32_t index = 0; index < length; ++index) { key.Update(keys->Get(index)); ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); PropertyAttributes attributes; switch (index) { case LENGTH_INDEX: attributes = PropertyAttributes::Default(false, false, true); break; case NAME_INDEX: if (LIKELY(properties->Get(NAME_INDEX).IsString())) { attributes = PropertyAttributes::Default(false, false, true); } else { ASSERT(properties->Get(NAME_INDEX).IsFunctionTemplate()); attributes = PropertyAttributes::Default(true, false, true); } break; case PROTOTYPE_INDEX: attributes = PropertyAttributes::DefaultAccessor(false, false, false); break; default: attributes = PropertyAttributes::Default(true, false, true); break; } if (UNLIKELY(properties->Get(index).IsAccessor())) { attributes.SetIsAccessor(true); } attributes.SetIsInlinedProps(true); attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, length); // Not need set proto here hclass->SetLayout(thread, layout); hclass->SetNumberOfProps(length); } else { // dictionary mode hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0); // without in-obj hclass->SetIsDictionaryMode(true); hclass->SetNumberOfProps(0); } hclass->SetClassConstructor(true); hclass->SetConstructor(true); return hclass; } void ClassInfoExtractor::CorrectConstructorHClass(JSThread *thread, JSHandle &properties, JSHClass *constructorHClass) { if (constructorHClass->IsDictionaryMode()) { return; } JSHandle layout(thread, constructorHClass->GetLayout()); for (uint32_t index = 0; index < ClassInfoExtractor::STATIC_RESERVED_LENGTH; ++index) { if (index == NAME_INDEX) { if (UNLIKELY(properties->Get(NAME_INDEX).IsFunctionTemplate())) { PropertyAttributes attr = layout->GetAttr(index); attr.SetWritable(true); layout->SetNormalAttr(thread, index, attr); } if (UNLIKELY(properties->Get(index).IsAccessor())) { PropertyAttributes attr = layout->GetAttr(index); attr.SetIsAccessor(true); layout->SetNormalAttr(thread, index, attr); } } else { if (UNLIKELY(properties->Get(index).IsAccessor())) { PropertyAttributes attr = layout->GetAttr(index); attr.SetIsAccessor(true); layout->SetNormalAttr(thread, index, attr); } } } } JSHandle ClassInfoExtractor::CreateSendableHClass(JSThread *thread, JSHandle &keys, JSHandle &properties, bool isProtoClass, uint32_t extraLength) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t length = keys->GetLength(); JSHandle hclass; uint32_t maxInline = isProtoClass ? JSSharedObject::MAX_INLINE : JSSharedFunction::MAX_INLINE; if (LIKELY(length + extraLength <= maxInline)) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateSLayoutInfo(length + extraLength); for (uint32_t index = 0; index < length; ++index) { key.Update(keys->Get(index)); ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); if (UNLIKELY(properties->Get(index).IsAccessor())) { attributes.SetIsAccessor(true); } attributes.SetIsInlinedProps(true); attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length) : factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length + extraLength); hclass->SetLayout(thread, layout); hclass->SetNumberOfProps(length); } else { // dictionary mode hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0) : factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, 0); hclass->SetIsDictionaryMode(true); hclass->SetNumberOfProps(0); } if (isProtoClass) { hclass->SetClassPrototype(true); hclass->SetIsPrototype(true); } else { hclass->SetClassConstructor(true); hclass->SetConstructor(true); } return hclass; } JSHandle ClassHelper::DefineClassFromExtractor(JSThread *thread, const JSHandle &base, JSHandle &extractor, const JSHandle &lexenv) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle staticKeys(thread, extractor->GetStaticKeys()); JSHandle staticProperties(thread, extractor->GetStaticProperties()); JSHandle nonStaticKeys(thread, extractor->GetNonStaticKeys()); JSHandle nonStaticProperties(thread, extractor->GetNonStaticProperties()); JSHandle prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys, nonStaticProperties); JSHandle prototype = factory->NewOldSpaceJSObject(prototypeHClass); JSHandle method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject())); JSHandle constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys, staticProperties); // Allocate to non-movable space for PGO JSHandle constructor = factory->NewJSFunctionByHClass(method, constructorHClass, MemSpaceType::NON_MOVABLE); // non-static nonStaticProperties->Set(thread, 0, constructor); uint32_t nonStaticLength = nonStaticProperties->GetLength(); JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); if (LIKELY(!prototypeHClass->IsDictionaryMode())) { for (uint32_t index = 0; index < nonStaticLength; ++index) { propValue.Update(nonStaticProperties->Get(index)); if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, prototype, lexenv)); } prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties, ClassPropertyType::NON_STATIC, lexenv); prototype->SetProperties(thread, dict); } // non-static elements if (UNLIKELY(extractor->GetNonStaticWithElements())) { JSHandle nonStaticElements(thread, extractor->GetNonStaticElements()); ClassHelper::HandleElementsProperties(thread, prototype, lexenv, nonStaticElements); } // static uint32_t staticLength = staticProperties->GetLength(); if (LIKELY(!constructorHClass->IsDictionaryMode())) { for (uint32_t index = 0; index < staticLength; ++index) { propValue.Update(staticProperties->Get(index)); if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update( CreateJSFunctionFromTemplate(thread, literalFunc, JSHandle(constructor), lexenv)); } JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildDictionaryProperties(thread, JSHandle(constructor), staticKeys, staticProperties, ClassPropertyType::STATIC, lexenv); constructor->SetProperties(thread, dict); } // static elements if (UNLIKELY(extractor->GetStaticWithElements())) { JSHandle staticElements(thread, extractor->GetStaticElements()); ClassHelper::HandleElementsProperties(thread, JSHandle(constructor), lexenv, staticElements); } PropertyDescriptor ctorDesc(thread, JSHandle(constructor), true, false, true); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(prototype), globalConst->GetHandledConstructorString(), ctorDesc); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread); constructor->SetHomeObject(thread, prototype); constructor->SetProtoOrHClass(thread, prototype); if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType()); } return constructor; } JSHandle ClassHelper::DefineClassWithIHClass(JSThread *thread, const JSHandle &base, JSHandle &extractor, const JSHandle &lexenv, const JSHandle &prototypeOrHClassVal, const JSHandle &constructorHClassVal) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle staticKeys(thread, extractor->GetStaticKeys()); JSHandle staticProperties(thread, extractor->GetStaticProperties()); JSHandle constructorHClass; // When constructorHClassVal is undefined, it means that AOT has not generated the corresponding hclass (chc), // then chc will be created through the interpreter. if (constructorHClassVal->IsUndefined()) { constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys, staticProperties); } else { constructorHClass = JSHandle(constructorHClassVal); ClassInfoExtractor::CorrectConstructorHClass(thread, staticProperties, *constructorHClass); } JSHandle nonStaticKeys(thread, extractor->GetNonStaticKeys()); JSHandle nonStaticProperties(thread, extractor->GetNonStaticProperties()); JSHandle prototype; JSHandle prototypeOrHClass = prototypeOrHClassVal; // When prototypeOrHClassVal is undefined, it means that AOT has not generated the corresponding hclass or // prototype, then prototype will be created through the interpreter. if (prototypeOrHClassVal->IsUndefined()) { JSHandle prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys, nonStaticProperties); prototype = factory->NewOldSpaceJSObject(prototypeHClass); prototypeOrHClass = JSHandle(prototype); } else if (prototypeOrHClassVal->IsJSHClass()) { JSHandle ihclass(prototypeOrHClassVal); prototype = JSHandle(thread, ihclass->GetProto()); } else { prototype = JSHandle(prototypeOrHClassVal); } JSHandle method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject())); JSHandle constructor = factory->NewJSFunctionByHClass(method, constructorHClass, MemSpaceType::NON_MOVABLE); // non-static nonStaticProperties->Set(thread, 0, constructor); uint32_t nonStaticLength = nonStaticProperties->GetLength(); JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); if (LIKELY(!prototype->GetJSHClass()->IsDictionaryMode())) { for (uint32_t index = 0; index < nonStaticLength; ++index) { propValue.Update(nonStaticProperties->Get(index)); if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, prototype, lexenv)); } prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties, ClassPropertyType::NON_STATIC, lexenv); prototype->SetProperties(thread, dict); } // non-static elements if (UNLIKELY(extractor->GetNonStaticWithElements())) { JSHandle nonStaticElements(thread, extractor->GetNonStaticElements()); ClassHelper::HandleElementsProperties(thread, prototype, lexenv, nonStaticElements); } // static uint32_t staticLength = staticProperties->GetLength(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); int correntIndex = 0; if (LIKELY(!constructorHClass->IsDictionaryMode())) { for (uint32_t index = 0; index < staticLength; ++index) { propValue.Update(staticProperties->Get(index)); if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update( CreateJSFunctionFromTemplate(thread, literalFunc, JSHandle(constructor), lexenv)); } bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH; if (needCorrentIndex) { key.Update(staticKeys->Get(index)); correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue()); } JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, needCorrentIndex ? static_cast(correntIndex) : index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildDictionaryProperties(thread, JSHandle(constructor), staticKeys, staticProperties, ClassPropertyType::STATIC, lexenv); constructor->SetProperties(thread, dict); } // static elements if (UNLIKELY(extractor->GetStaticWithElements())) { JSHandle staticElements(thread, extractor->GetStaticElements()); ClassHelper::HandleElementsProperties(thread, JSHandle(constructor), lexenv, staticElements); } PropertyDescriptor ctorDesc(thread, JSHandle(constructor), true, false, true); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(prototype), globalConst->GetHandledConstructorString(), ctorDesc); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread); constructor->SetHomeObject(thread, prototype); constructor->SetProtoOrHClass(thread, prototypeOrHClass); if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType()); } return constructor; } JSHandle ClassHelper::CreateJSFunctionFromTemplate(JSThread *thread, const JSHandle &funcTemp, const JSHandle &homeObject, const JSHandle &lexenv) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle propFunc = factory->CreateJSFunctionFromTemplate(funcTemp); JSFunction::UpdateProfileTypeInfoCell(thread, funcTemp, propFunc); propFunc->SetHomeObject(thread, homeObject); propFunc->SetLexicalEnv(thread, lexenv); return propFunc; } JSHandle ClassHelper::BuildDictionaryProperties(JSThread *thread, const JSHandle &object, JSHandle &keys, JSHandle &properties, ClassPropertyType type, const JSHandle &lexenv) { uint32_t length = keys->GetLength(); ASSERT(length > PropertyAttributes::MAX_FAST_PROPS_CAPACITY); ASSERT(keys->GetLength() == properties->GetLength()); JSMutableHandle dict( thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length))); JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); for (uint32_t index = 0; index < length; index++) { PropertyAttributes attributes; if (type == ClassPropertyType::STATIC) { switch (index) { case ClassInfoExtractor::LENGTH_INDEX: attributes = PropertyAttributes::Default(false, false, true); break; case ClassInfoExtractor::NAME_INDEX: if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) { attributes = PropertyAttributes::Default(false, false, true); } else { ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsFunctionTemplate()); attributes = PropertyAttributes::Default(true, false, true); } break; case ClassInfoExtractor::PROTOTYPE_INDEX: attributes = PropertyAttributes::DefaultAccessor(false, false, false); break; default: attributes = PropertyAttributes::Default(true, false, true); break; } } else { attributes = PropertyAttributes::Default(true, false, true); // non-enumerable } propKey.Update(keys->Get(index)); propValue.Update(properties->Get(index)); if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, object, lexenv)); } JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes); dict.Update(newDict); } return dict; } bool ClassHelper::MatchFieldType(SharedFieldType fieldType, JSTaggedValue value) { // all sendable types can be set to undefined if (value.IsUndefined()) { return true; } uint32_t sharedFieldType = static_cast(fieldType); if ((sharedFieldType & static_cast(SharedFieldType::NUMBER)) != 0 && value.IsNumber()) { return true; } else if ((sharedFieldType & static_cast(SharedFieldType::BOOLEAN)) != 0 && value.IsBoolean()) { return true; } else if ((sharedFieldType & static_cast(SharedFieldType::STRING)) != 0 && (value.IsString() || value.IsNull())) { return true; } else if ((sharedFieldType & static_cast(SharedFieldType::BIG_INT)) != 0 && value.IsBigInt()) { return true; } else if ((sharedFieldType & static_cast(SharedFieldType::SENDABLE)) != 0 && (value.IsJSShared() || value.IsNull())) { return true; } else if ((sharedFieldType == static_cast(SharedFieldType::NONE) || (sharedFieldType & static_cast(SharedFieldType::GENERIC)) != 0) && (value.IsJSShared() || !value.IsHeapObject())) { // (none || generic) && (jsShared || !heapObject) return true; } else if ((sharedFieldType & static_cast(SharedFieldType::NULL_TYPE)) != 0 && value.IsNull()) { return true; } else if ((sharedFieldType & static_cast(SharedFieldType::UNDEFINED)) != 0 && value.IsUndefined()) { return true; } std::stringstream oss; value.DumpTaggedValueType(oss); LOG_ECMA(ERROR) << "Sendable obj Match field type fail. expected type: " << StaticFieldTypeToString(sharedFieldType) << ", actual type: " << oss.str(); return false; } CString ClassHelper::StaticFieldTypeToString(uint32_t fieldType) { switch (fieldType) { case static_cast(SharedFieldType::NONE): return "[None]"; case static_cast(SharedFieldType::NUMBER): return "[Number]"; case static_cast(SharedFieldType::STRING): return "[String]"; case static_cast(SharedFieldType::BOOLEAN): return "[Boolean]"; case static_cast(SharedFieldType::SENDABLE): return "[Sendable Object]"; case static_cast(SharedFieldType::BIG_INT): return "[BigInt]"; case static_cast(SharedFieldType::GENERIC): return "[Generic]"; case static_cast(SharedFieldType::NULL_TYPE): return "[Null]"; case static_cast(SharedFieldType::UNDEFINED): return "[Undefined]"; default: { CString ret = "unknown type "; return ret.append(std::to_string(fieldType)); } } } void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle &object, const JSHandle &lexenv, JSHandle &elements) { JSMutableHandle elementsKey(thread, JSTaggedValue::Undefined()); JSMutableHandle elementsValue(thread, JSTaggedValue::Undefined()); for (uint32_t index = 0; index < elements->GetLength(); index += 2) { // 2: key-value pair elementsKey.Update(elements->Get(index)); elementsValue.Update(elements->Get(index + 1)); if (elementsValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(elementsValue); elementsValue.Update(CreateJSFunctionFromTemplate(thread, literalFunc, object, lexenv)); } // class property attribute is not default, will transition to dictionary directly. JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true); } } JSHandle SendableClassDefiner::DefineSendableClassFromExtractor(JSThread *thread, JSHandle &extractor, const JSHandle &staticFieldArray) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle staticKeys(thread, extractor->GetStaticKeys()); JSHandle staticProperties(thread, extractor->GetStaticProperties()); SendableClassDefiner::FilterDuplicatedKeys(thread, staticKeys, staticProperties); JSHandle nonStaticKeys(thread, extractor->GetNonStaticKeys()); JSHandle nonStaticProperties(thread, extractor->GetNonStaticProperties()); SendableClassDefiner::FilterDuplicatedKeys(thread, nonStaticKeys, nonStaticProperties); JSHandle prototypeHClass = ClassInfoExtractor::CreateSendableHClass(thread, nonStaticKeys, nonStaticProperties, true); JSHandle prototype = factory->NewSharedOldSpaceJSObject(prototypeHClass); uint32_t length = staticFieldArray->GetLength(); uint32_t staticFields = length / 2; // 2: key-type JSHandle constructorHClass = ClassInfoExtractor::CreateSendableHClass(thread, staticKeys, staticProperties, false, staticFields); JSHandle method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject())); /* * Method::SetFunctionKind can't be called here, because method will set kind when set inheritance relationship, * so there is a multi-threading problem with multi-threads define sendable DERIVED class at the same time. * Scenario: * A thread: define DERIVED class X [X's kind = DEFAULT --> BASE CLASS --> DERIVED CLASS], new X() * B thread: define DERIVED class X [X's kind = DEFAULT --> BASE CLASS --> DERIVED CLASS], new X() * Issue: * When A thread new DERIVED class X, X's kind maybe set to BASE CLASS at B thread, * and A thread will throw error when call super(). */ JSHandle elementsDic = NumberDictionary::CreateInSharedHeap(thread); bool hasElement = false; if (!constructorHClass->IsDictionaryMode() && staticFields > 0) { auto layout = JSHandle(thread, constructorHClass->GetLayout()); AddFieldTypeToHClass( thread, staticFieldArray, length, layout, constructorHClass, elementsDic, hasElement, ~0U); } JSHandle constructor = factory->NewSFunctionByHClass(method, constructorHClass); // non-static nonStaticProperties->Set(thread, 0, constructor); uint32_t nonStaticLength = nonStaticProperties->GetLength(); JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); if (LIKELY(!prototypeHClass->IsDictionaryMode())) { for (uint32_t index = 0; index < nonStaticLength; ++index) { propValue.Update(nonStaticProperties->Get(index)); // constructor don't need to clone if (propValue->IsFunctionTemplate() && index != ClassInfoExtractor::CONSTRUCTOR_INDEX) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update( CreateSFunctionFromTemplate(thread, literalFunc, prototype, JSHandle(constructor))); } else if (propValue->IsAccessorData()) { UpdateAccessorFunction(thread, propValue, JSHandle(prototype), constructor); } prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildSendableDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties, ClassPropertyType::NON_STATIC, constructor); prototype->SetProperties(thread, dict); } // non-static elements if (UNLIKELY(extractor->GetNonStaticWithElements())) { THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support members with numerical key", JSHandle(thread, JSTaggedValue::Exception())); } // static uint32_t staticLength = staticProperties->GetLength(); if (LIKELY(!constructorHClass->IsDictionaryMode())) { for (uint32_t index = 0; index < staticLength; ++index) { propValue.Update(staticProperties->Get(index)); if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update(CreateSFunctionFromTemplate( thread, literalFunc, JSHandle(constructor), JSHandle(constructor))); } else if (propValue->IsAccessorData()) { UpdateAccessorFunction(thread, propValue, JSHandle(constructor), constructor); } JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildSendableDictionaryProperties(thread, JSHandle(constructor), staticKeys, staticProperties, ClassPropertyType::STATIC, constructor); JSMutableHandle nameDict(thread, dict); if (staticFields > 0) { AddFieldTypeToDict(thread, staticFieldArray, length, nameDict, elementsDic, hasElement, PropertyAttributes::Default(true, true, false)); } constructor->SetProperties(thread, nameDict); } if (hasElement) { constructor->SetElements(thread, elementsDic); constructorHClass->SetIsDictionaryElement(true); } // static elements if (UNLIKELY(extractor->GetStaticWithElements())) { THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support static members with numerical key", JSHandle(thread, JSTaggedValue::Exception())); } prototype->GetJSHClass()->SetExtensible(false); constructor->SetHomeObject(thread, prototype); constructor->SetProtoOrHClass(thread, prototype); constructor->SetLexicalEnv(thread, constructor); return constructor; } // Process duplicated key due to getter/setter. void SendableClassDefiner::FilterDuplicatedKeys(JSThread *thread, const JSHandle &keys, const JSHandle &properties) { auto attr = PropertyAttributes::Default(); uint32_t length = keys->GetLength(); uint32_t left = 0; uint32_t right = 0; JSMutableHandle dict( thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length))); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle value(thread, JSTaggedValue::Undefined()); JSMutableHandle existValue(thread, JSTaggedValue::Undefined()); JSMutableHandle index(thread, JSTaggedValue::Undefined()); for (; right < length; right++) { key.Update(keys->Get(right)); value.Update(properties->Get(right)); int entry = dict->FindEntry(key.GetTaggedValue()); if (entry == -1) { TryUpdateValue(thread, value); index.Update(JSTaggedValue(left)); JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, key, index, attr); dict.Update(newDict); if (left < right) { keys->Set(thread, left, key); } properties->Set(thread, left, value); left++; continue; } auto existIndex = static_cast(dict->GetValue(entry).GetNumber()); existValue.Update(properties->Get(existIndex)); bool needUpdateValue = TryUpdateExistValue(thread, existValue, value); if (needUpdateValue) { properties->Set(thread, existIndex, value); } } if (left < right) { keys->Trim(thread, left); properties->Trim(thread, left); } } JSHandle SendableClassDefiner::BuildSendableDictionaryProperties(JSThread *thread, const JSHandle &object, JSHandle &keys, JSHandle &properties, ClassPropertyType type, const JSHandle &ctor) { uint32_t length = keys->GetLength(); ASSERT(keys->GetLength() == properties->GetLength()); JSMutableHandle dict( thread, NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(length))); JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); for (uint32_t index = 0; index < length; index++) { PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); if (UNLIKELY(properties->Get(index).IsAccessor())) { attributes.SetIsAccessor(true); } propKey.Update(keys->Get(index)); propValue.Update(properties->Get(index)); // constructor don't need to clone if (index == ClassInfoExtractor::CONSTRUCTOR_INDEX && type == ClassPropertyType::NON_STATIC) { JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes); dict.Update(newDict); continue; } if (propValue->IsFunctionTemplate()) { auto literalFunc = JSHandle::Cast(propValue); propValue.Update(CreateSFunctionFromTemplate(thread, literalFunc, object, JSHandle(ctor))); } else if (propValue->IsAccessorData()) { UpdateAccessorFunction(thread, propValue, JSHandle(object), ctor); } JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes); dict.Update(newDict); } return dict; } JSHandle SendableClassDefiner::CreateSFunctionFromTemplate(JSThread *thread, const JSHandle &funcTemp, const JSHandle &homeObject, const JSHandle &lexenv) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle propFunc = factory->CreateSFunctionFromTemplate(funcTemp); propFunc->SetHomeObject(thread, homeObject); propFunc->SetLexicalEnv(thread, lexenv); ASSERT(!propFunc->GetClass()->IsExtensible()); return propFunc; } void SendableClassDefiner::AddFieldTypeToDict(JSThread *thread, const JSHandle &fieldTypeArray, uint32_t length, JSMutableHandle &dict, JSHandle &elementsDic, bool &hasElement, PropertyAttributes attributes) { ASSERT(length <= fieldTypeArray->GetLength()); JSMutableHandle key(thread, JSTaggedValue::Undefined()); auto globalConst = const_cast(thread->GlobalConstants()); JSHandle value = globalConst->GetHandledUndefined(); JSMutableHandle elementsDicUpdate(thread, elementsDic); JSHandle undefinedVal = thread->GlobalConstants()->GetHandledUndefined(); JSMutableHandle eleIndexKey(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair; key.Update(fieldTypeArray->Get(i)); ASSERT(key->IsString()); SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt())); attributes.SetSharedFieldType(type); attributes.SetBoxType(PropertyBoxType::UNDEFINED); int64_t eleIndex = ObjectFastOperator::TryToElementsIndex(key.GetTaggedValue()); if (eleIndex >= 0) { eleIndexKey.Update(JSTaggedValue(eleIndex)); JSHandle newElementsDic = NumberDictionary::Put( thread, elementsDic, eleIndexKey, undefinedVal, attributes); elementsDicUpdate.Update(newElementsDic); if (!hasElement) { hasElement = true; } } else { JSHandle newDict = NameDictionary::Put(thread, dict, key, value, attributes); dict.Update(newDict); } } } void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle &fieldTypeArray, uint32_t length, const JSHandle &layout, const JSHandle &hclass, JSHandle &elementsDic, bool &hasElement, size_t start, std::vector> &&propertyList) { ASSERT(length <= fieldTypeArray->GetLength()); JSMutableHandle key(thread, JSTaggedValue::Undefined()); uint32_t index = static_cast(layout->NumberOfElements()); JSMutableHandle elementsDicUpdate(thread, elementsDic); JSHandle undefinedVal = thread->GlobalConstants()->GetHandledUndefined(); JSMutableHandle eleIndexKey(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair; PropertyAttributes attributes = PropertyAttributes::Default(true, true, false); key.Update(fieldTypeArray->Get(i)); ASSERT(key->IsString()); SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt())); int entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index); if (entry != -1) { attributes = layout->GetAttr(entry); attributes.SetSharedFieldType(type); layout->SetNormalAttr(thread, entry, attributes); if (start != ~0U && propertyList.size() > 0) { propertyList[start + (static_cast(entry) << 1)] = propertyList[start + i]; propertyList[start + (static_cast(entry) << 1) + 1] = propertyList[start + i + 1]; } } else { int64_t eleIndex = ObjectFastOperator::TryToElementsIndex(key.GetTaggedValue()); if (eleIndex < 0 && start != ~0U && propertyList.size() > 0) { propertyList[start + (index << 1)] = propertyList[start + i]; propertyList[start + (index << 1) + 1] = propertyList[start + i + 1]; } attributes.SetIsInlinedProps(true); attributes.SetRepresentation(Representation::TAGGED); attributes.SetSharedFieldType(type); attributes.SetOffset(index); if (eleIndex >= 0) { if (propertyList.size() > 0) { undefinedVal = propertyList[start + i + 1]; } eleIndexKey.Update(JSTaggedValue(eleIndex)); JSHandle newElementsDic = NumberDictionary::Put( thread, elementsDic, eleIndexKey, undefinedVal, attributes); elementsDicUpdate.Update(newElementsDic); if (!hasElement) { hasElement = true; } } else { layout->AddKey(thread, index++, key.GetTaggedValue(), attributes); } } } hclass->SetLayout(thread, layout); hclass->SetNumberOfProps(index); auto inlinedProps = hclass->GetInlinedProperties(); if (inlinedProps > index) { // resize hclass due to duplicated key. uint32_t duplicatedSize = (inlinedProps - index) * JSTaggedValue::TaggedTypeSize(); hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize); } } void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle &fieldTypeArray, uint32_t length, const JSHandle &nameDict, const JSHandle &hclass, JSHandle &elementsDic, bool &hasElement) { JSMutableHandle dict(thread, nameDict); AddFieldTypeToDict(thread, fieldTypeArray, length, dict, elementsDic, hasElement); hclass->SetLayout(thread, dict); hclass->SetNumberOfProps(0); hclass->SetIsDictionaryMode(true); } void SendableClassDefiner::DefineSendableInstanceHClass(JSThread *thread, const JSHandle &fieldTypeArray, uint32_t length, const JSHandle &ctor, const JSHandle &base) { ASSERT(ctor->GetClass()->IsJSSharedFunction()); JSHandle clsPrototype(thread, JSHandle(ctor)->GetFunctionPrototype()); ASSERT(clsPrototype->GetClass()->IsJSSharedObject()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); ASSERT(length <= fieldTypeArray->GetLength()); uint32_t fieldNum = length / 2; // 2: key-value pair; JSHandle iHClass; JSHandle elementsDic = NumberDictionary::CreateInSharedHeap(thread); bool hasElement = false; if (base->IsHole() || base->IsNull()) { if (fieldNum == 0) { iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum); } else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) { iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum); JSHandle layout = factory->CreateSLayoutInfo(fieldNum); AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass, elementsDic, hasElement, ~0U); } else { iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0); JSHandle dict = NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(fieldNum)); AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass, elementsDic, hasElement); } } else { ASSERT(base->IsJSSharedFunction()); JSHandle baseCtor = JSHandle::Cast(base); JSHandle baseIHClass(thread, baseCtor->GetProtoOrHClass()); ASSERT(baseIHClass->IsJSShared()); JSType baseType = baseIHClass->GetObjectType(); const auto [baseSize, baseMaxInlineSize] = GetSizeAndMaxInlineByType(baseType); if (LIKELY(!baseIHClass->IsDictionaryMode())) { auto baseLength = baseIHClass->NumberOfProps(); JSHandle baseLayout(thread, baseIHClass->GetLayout()); auto newLength = baseLength + fieldNum; if (newLength == 0) { iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0); } else if (LIKELY(newLength <= baseMaxInlineSize)) { iHClass = factory->NewSEcmaHClass(baseSize, baseType, newLength); JSHandle layout = factory->CopyAndReSortSLayoutInfo(baseLayout, baseLength, newLength); AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass, elementsDic, hasElement, ~0U); } else { iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0); JSHandle dict = NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength)); auto globalConst = const_cast(thread->GlobalConstants()); JSHandle value = globalConst->GetHandledUndefined(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < baseLength; i++) { key.Update(baseLayout->GetKey(i)); PropertyAttributes attr = baseLayout->GetAttr(i); attr.SetIsInlinedProps(false); attr.SetBoxType(PropertyBoxType::UNDEFINED); dict = NameDictionary::Put(thread, dict, key, value, attr); } AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass, elementsDic, hasElement); } } else { JSHandle baseDict(thread, baseIHClass->GetLayout()); auto baseLength = baseDict->EntriesCount(); auto newLength = fieldNum + static_cast(baseLength); JSHandle dict = NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength)); baseDict->Rehash(thread, *dict); dict->SetNextEnumerationIndex(thread, baseDict->GetNextEnumerationIndex()); iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0); AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass, elementsDic, hasElement); } } if (hasElement) { JSHandle::Cast(ctor)->SetPropertyInlinedProps( thread, ClassInfoExtractor::SENDABLE_ELEMENTS_INDEX, elementsDic.GetTaggedValue()); } iHClass->SetPrototype(thread, JSHandle(clsPrototype)); iHClass->SetExtensible(false); ctor->SetProtoOrHClass(thread, iHClass); ctor->GetJSHClass()->SetExtensible(false); } JSHandle SendableClassDefiner::ExtractStaticFieldTypeArray(JSThread *thread, const JSHandle &fieldTypeArray) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t arrayLength = fieldTypeArray->GetLength(); ASSERT(arrayLength > 0); auto instanceFieldNums = static_cast(fieldTypeArray->Get(arrayLength - 1).GetInt()); uint32_t staticFieldBegin = instanceFieldNums * 2; // 2: key-type if (staticFieldBegin >= arrayLength) { LOG_ECMA(ERROR) << "ExtractStaticFieldTypeArray Failed, staticFieldBegin:" << staticFieldBegin << " should be less than totalLength:" << arrayLength; return factory->EmptyArray(); } uint32_t staticFieldLength = arrayLength - staticFieldBegin - 1; JSHandle staticFieldArray = factory->NewTaggedArray(staticFieldLength); for (uint32_t i = 0; i < staticFieldLength; i += 2) { // 2: key-type staticFieldArray->Set(thread, i, fieldTypeArray->Get(staticFieldBegin + i)); staticFieldArray->Set(thread, i + 1, fieldTypeArray->Get(staticFieldBegin + i + 1)); } return staticFieldArray; } void SendableClassDefiner::UpdateAccessorFunction(JSThread *thread, const JSMutableHandle &value, const JSHandle &homeObject, const JSHandle &ctor) { ASSERT(value->IsAccessorData()); JSHandle accessor(value); auto getter = accessor->GetGetter(); if (getter.IsFunctionTemplate()) { auto funcTemp = JSHandle(thread, getter); auto propFunc = CreateSFunctionFromTemplate( thread, funcTemp, JSHandle(homeObject), JSHandle(ctor)); accessor->SetGetter(thread, propFunc); } auto setter = accessor->GetSetter(); if (setter.IsFunctionTemplate()) { auto funcTemp = JSHandle(thread, setter); auto propFunc = CreateSFunctionFromTemplate( thread, funcTemp, JSHandle(homeObject), JSHandle(ctor)); accessor->SetSetter(thread, propFunc); } } bool SendableClassDefiner::TryUpdateExistValue(JSThread *thread, JSMutableHandle &existValue, JSMutableHandle &value) { bool needUpdateValue = true; if (existValue->IsAccessorData()) { if (value->IsFunctionTemplate() && JSHandle(value)->IsGetterOrSetter()) { JSHandle accessor(existValue); UpdateValueToAccessor(thread, value, accessor); needUpdateValue = false; } } else { if (value->IsFunctionTemplate() && JSHandle(value)->IsGetterOrSetter()) { JSHandle accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData(); UpdateValueToAccessor(thread, value, accessor); } } return needUpdateValue; } void SendableClassDefiner::TryUpdateValue(JSThread *thread, JSMutableHandle &value) { if (value->IsFunctionTemplate() && JSHandle(value)->IsGetterOrSetter()) { JSHandle accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData(); UpdateValueToAccessor(thread, value, accessor); } } void SendableClassDefiner::UpdateValueToAccessor(JSThread *thread, JSMutableHandle &value, JSHandle &accessor) { ASSERT(value->IsFunctionTemplate() && JSHandle(value)->IsGetterOrSetter()); if (JSHandle(value)->IsGetter()) { accessor->SetGetter(thread, value); } else { accessor->SetSetter(thread, value); } value.Update(accessor); } std::pair SendableClassDefiner::GetSizeAndMaxInlineByType(JSType type) { switch (type) { case JSType::JS_SHARED_OBJECT: return { JSSharedObject::SIZE, JSSharedObject::MAX_INLINE }; case JSType::JS_SHARED_ARRAY: return { JSSharedArray::SIZE, JSSharedArray::MAX_INLINE }; case JSType::JS_SHARED_MAP: return { JSSharedMap::SIZE, JSSharedMap::MAX_INLINE }; case JSType::JS_SHARED_SET: return { JSSharedSet::SIZE, JSSharedSet::MAX_INLINE }; case JSType::JS_SENDABLE_ARRAY_BUFFER: return { JSSendableArrayBuffer::SIZE, JSSendableArrayBuffer::MAX_INLINE }; case JSType::JS_API_BITVECTOR: return { JSAPIBitVector::SIZE, JSAPIBitVector::MAX_INLINE }; default: if (JSType::JS_SHARED_TYPED_ARRAY_FIRST < type && type <= JSType::JS_SHARED_TYPED_ARRAY_LAST) { return { JSSharedTypedArray::SIZE, JSSharedTypedArray::MAX_INLINE }; } LOG_ECMA(FATAL) << "this branch is unreachable, cannot get size for type: " << static_cast(type); UNREACHABLE(); return {}; } } } // namespace panda::ecmascript