diff --git a/BUILD.gn b/BUILD.gn index b012140f..e0b5115e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -390,6 +390,7 @@ ecma_source = [ "ecmascript/vmstat/runtime_stat.cpp", "ecmascript/weak_vector.cpp", "ecmascript/compiler/llvm/llvm_stackmap_parser.cpp", + "ecmascript/class_info_extractor.cpp", ] ecma_source += [ diff --git a/ecmascript/class_info_extractor.cpp b/ecmascript/class_info_extractor.cpp new file mode 100644 index 00000000..77be6b2d --- /dev/null +++ b/ecmascript/class_info_extractor.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2021 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/class_info_extractor.h" + +#include "ecmascript/global_env.h" +#include "ecmascript/js_function.h" +#include "ecmascript/tagged_dictionary.h" + +namespace panda::ecmascript { +void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle &extractor, + const JSHandle &literal) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t literalBufferLength = literal->GetLength(); + // non static properties number is hidden in the last index of Literal buffer + uint32_t nonStaticNum = literal->Get(thread, literalBufferLength - 1).GetInt(); + + // Reserve sufficient length to prevent frequent creation. + JSHandle nonStaticKeys = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); + JSHandle nonStaticProperties = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); + + nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString()); + + JSHandle nonStaticElements = factory->EmptyArray(); + + if (nonStaticNum) { + ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr}; + + if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys, + nonStaticProperties, nonStaticElements))) { + extractor->SetNonStaticWithElements(); + extractor->SetNonStaticElements(thread, nonStaticElements); + } + } + + extractor->SetNonStaticKeys(thread, nonStaticKeys); + extractor->SetNonStaticProperties(thread, nonStaticProperties); + + JSHandle prototypeHClass = CreatePrototypeHClass(thread, nonStaticKeys, nonStaticProperties); + extractor->SetPrototypeHClass(thread, prototypeHClass); + + uint32_t staticNum = (literalBufferLength - 1) / 2 - nonStaticNum; + + // Reserve sufficient length to prevent frequent creation. + JSHandle staticKeys = factory->NewTaggedArray(staticNum + STATIC_RESERVED_LENGTH); + JSHandle staticProperties = factory->NewTaggedArray(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()); + + JSHandle staticElements = factory->EmptyArray(); + + if (staticNum) { + ExtractContentsDetail staticDetail { + nonStaticNum * 2, + literalBufferLength - 1, + STATIC_RESERVED_LENGTH, + extractor->GetConstructorMethod() + }; + + if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys, + staticProperties, staticElements))) { + extractor->SetStaticWithElements(); + extractor->SetStaticElements(thread, staticElements); + } + } else { + // without static properties, set class name + CString clsName = extractor->GetConstructorMethod()->ParseFunctionName(); + JSHandle clsNameHandle = factory->NewFromString(clsName.c_str()); + staticProperties->Set(thread, NAME_INDEX, clsNameHandle); + } + + // set prototype internal accessor + JSHandle prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor(); + staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor); + + extractor->SetStaticKeys(thread, staticKeys); + extractor->SetStaticProperties(thread, staticProperties); + + JSHandle ctorHClass = CreateConstructorHClass(thread, staticKeys, staticProperties); + extractor->SetConstructorHClass(thread, ctorHClass); +} + +bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle &literal, + const ExtractContentsDetail &detail, + JSHandle &keys, + JSHandle &properties, + JSHandle &elements) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0); + + uint32_t pos = detail.fillStartLoc; + bool withElemenstFlag = false; + bool isStaticFlag = detail.ctorMethod ? true : false; + 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::SetCapacity(thread, elements, elementsLength + 2); // 2: key-value pair + elements->Set(thread, elementsLength, firstValue); + elements->Set(thread, elementsLength + 1, secondValue); + withElemenstFlag = 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(); + CString clsName = detail.ctorMethod->ParseFunctionName(); + JSHandle clsNameHandle = factory->NewFromString(clsName.c_str()); + 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(withElemenstFlag)) { + ASSERT(pos + elements->GetLength() / 2 == properties->GetLength()); // 2: half + keys->Trim(thread, pos); + properties->Trim(thread, pos); + } + + return withElemenstFlag; +} + +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_CAPACITY_OF_PROPERTIES)) { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSHandle layout = factory->CreateLayoutInfo(length); + 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::MIXED); + attributes.SetOffset(index); + layout->AddKey(thread, index, key.GetTaggedValue(), attributes); + } + + hclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, length); + // Not need set proto here + hclass->SetLayout(thread, layout); + hclass->SetNumberOfProps(length); + } else { + // dictionary mode + hclass = factory->NewEcmaDynClass(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, JSHandle &keys, + JSHandle &properties) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t length = keys->GetLength(); + JSHandle hclass; + if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSHandle layout = factory->CreateLayoutInfo(length); + 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).IsJSFunction()); + 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::MIXED); + attributes.SetOffset(index); + layout->AddKey(thread, index, key.GetTaggedValue(), attributes); + } + + hclass = factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, length); + // Not need set proto here + hclass->SetLayout(thread, layout); + hclass->SetNumberOfProps(length); + } else { + // dictionary mode + hclass = factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0); // without in-obj + hclass->SetIsDictionaryMode(true); + hclass->SetNumberOfProps(0); + } + + hclass->SetClassConstructor(true); + hclass->SetConstructor(true); + + return hclass; +} + +JSHandle ClassHelper::DefineClassTemplate(JSThread *thread, JSHandle &extractor, + const JSHandle &constantpool) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle prototypeHClass(thread, extractor->GetPrototypeHClass()); + JSHandle prototype = factory->NewJSObject(prototypeHClass); + + JSHandle constructorHClass(thread, extractor->GetConstructorHClass()); + JSHandle constructor = factory->NewJSFunctionByDynClass(extractor->GetConstructorMethod(), + constructorHClass, + FunctionKind::CLASS_CONSTRUCTOR); + + // non-static + JSHandle nonStaticProperties(thread, extractor->GetNonStaticProperties()); + 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->IsJSFunction()) { + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, prototype); + propFunc->SetConstantPool(thread, constantpool); + } + prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); + } + } else { + JSHandle nonStaticKeys(thread, extractor->GetNonStaticKeys()); + JSHandle dict = BuildDictionaryPropeties(thread, prototype, nonStaticKeys, nonStaticProperties, + ClassPropertyType::NON_STATIC, constantpool); + prototype->SetProperties(thread, dict); + } + + // non-static elements + if (UNLIKELY(extractor->IsNonStaticWithElements())) { + JSHandle nonStaticElements(thread, extractor->GetNonStaticElements()); + ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements, constantpool); + } + + // static + JSHandle staticProperties(thread, extractor->GetStaticProperties()); + uint32_t staticLength = staticProperties->GetLength(); + + if (LIKELY(!constructorHClass->IsDictionaryMode())) { + for (uint32_t index = 0; index < staticLength; ++index) { + propValue.Update(staticProperties->Get(index)); + if (propValue->IsJSFunction()) { + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, constructor); + propFunc->SetConstantPool(thread, constantpool); + } + JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); + } + } else { + JSHandle staticKeys(thread, extractor->GetStaticKeys()); + JSHandle dict = BuildDictionaryPropeties(thread, JSHandle(constructor), staticKeys, + staticProperties, ClassPropertyType::STATIC, + constantpool); + constructor->SetProperties(thread, dict); + } + + // static elements + if (UNLIKELY(extractor->IsStaticWithElements())) { + JSHandle staticElements(thread, extractor->GetStaticElements()); + ClassHelper::HandleElementsProperties(thread, JSHandle(constructor), staticElements, constantpool); + } + + constructor->SetProtoOrDynClass(thread, prototype); + + return constructor; +} + +JSHandle ClassHelper::BuildDictionaryPropeties(JSThread *thread, const JSHandle &object, + JSHandle &keys, + JSHandle &properties, + ClassPropertyType type, + const JSHandle &constantpool) +{ + uint32_t length = keys->GetLength(); + ASSERT(length > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + 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).IsJSFunction()); + 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->IsJSFunction()) { + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, object); + propFunc->SetConstantPool(thread, constantpool); + } + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes); + dict.Update(newDict); + } + + return dict; +} + +void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle &object, + JSHandle &elements, const JSHandle &constantpool) +{ + 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)); + // class property attribute is not default, will transition to dictionary directly. + JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true); + + if (elementsValue->IsJSFunction()) { + JSHandle elementsFunc = JSHandle::Cast(elementsValue); + elementsFunc->SetHomeObject(thread, object); + elementsFunc->SetConstantPool(thread, constantpool); + } + } +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/class_info_extractor.h b/ecmascript/class_info_extractor.h new file mode 100644 index 00000000..b2d6f565 --- /dev/null +++ b/ecmascript/class_info_extractor.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef ECMASCRIPT_CLASS_INFO_EXTRACTOR_H +#define ECMASCRIPT_CLASS_INFO_EXTRACTOR_H + +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +// ClassInfoExtractor will analyze and extract the contents from class literal to keys, properties and elements(both +// non-static and static), later generate the complete hclass (both prototype and constructor) based on keys. +// Attention: keys accessor stores the property key and properties accessor stores the property value, but elements +// accessor stores the key-value pair abuttally. +class ClassInfoExtractor : public TaggedObject { +public: + static constexpr uint8_t NON_STATIC_RESERVED_LENGTH = 1; + static constexpr uint8_t STATIC_RESERVED_LENGTH = 3; + + static constexpr uint8_t CONSTRUCTOR_INDEX = 0; + static constexpr uint8_t LENGTH_INDEX = 0; + static constexpr uint8_t NAME_INDEX = 1; + static constexpr uint8_t PROTOTYPE_INDEX = 2; + + struct ExtractContentsDetail { + uint32_t extractBegin; + uint32_t extractEnd; + uint8_t fillStartLoc; + JSMethod *ctorMethod; + }; + + CAST_CHECK(ClassInfoExtractor, IsClassInfoExtractor); + + using NonStaticWithElementsBit = BitField; + using StaticWithElementsBit = NonStaticWithElementsBit::NextFlag; + + static void BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle &extractor, + const JSHandle &literal); + + inline void InitializeBitField() + { + SetBitField(0UL); + } + + inline void SetNonStaticWithElements() + { + uint8_t bits = GetBitField(); + uint8_t newVal = NonStaticWithElementsBit::Update(bits, true); + SetBitField(newVal); + } + + inline void SetStaticWithElements() + { + uint8_t bits = GetBitField(); + uint8_t newVal = StaticWithElementsBit::Update(bits, true); + SetBitField(newVal); + } + + inline bool IsNonStaticWithElements() const + { + return NonStaticWithElementsBit::Decode(GetBitField()); + } + + inline bool IsStaticWithElements() const + { + return StaticWithElementsBit::Decode(GetBitField()); + } + + static constexpr size_t BIT_FIELD_OFFSET = TaggedObjectSize(); + SET_GET_PRIMITIVE_FIELD(BitField, uint8_t, BIT_FIELD_OFFSET, CONSTRUCTOR_METHOD_OFFSET) + SET_GET_NATIVE_FIELD(ConstructorMethod, JSMethod, CONSTRUCTOR_METHOD_OFFSET, PROTOTYPE_HCLASS_OFFSET) + ACCESSORS(PrototypeHClass, PROTOTYPE_HCLASS_OFFSET, NON_STATIC_KEYS_OFFSET) + ACCESSORS(NonStaticKeys, NON_STATIC_KEYS_OFFSET, NON_STATIC_PROPERTIES_OFFSET) + ACCESSORS(NonStaticProperties, NON_STATIC_PROPERTIES_OFFSET, NON_STATIC_ELEMENTS_OFFSET) + ACCESSORS(NonStaticElements, NON_STATIC_ELEMENTS_OFFSET, CONSTRUCTOR_HCLASS_OFFSET) + ACCESSORS(ConstructorHClass, CONSTRUCTOR_HCLASS_OFFSET, STATIC_KEYS_OFFSET) + ACCESSORS(StaticKeys, STATIC_KEYS_OFFSET, STATIC_PROPERTIES_OFFSET) + ACCESSORS(StaticProperties, STATIC_PROPERTIES_OFFSET, STATIC_ELEMENTS_OFFSET) + ACCESSORS(StaticElements, STATIC_ELEMENTS_OFFSET, SIZE) + + DECL_VISIT_OBJECT(PROTOTYPE_HCLASS_OFFSET, SIZE) + DECL_DUMP() + +private: + static bool ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle &literal, + const ExtractContentsDetail &detail, + JSHandle &keys, JSHandle &properties, + JSHandle &elements); + + static JSHandle CreatePrototypeHClass(JSThread *thread, JSHandle &keys, + JSHandle &properties); + + static JSHandle CreateConstructorHClass(JSThread *thread, JSHandle &keys, + JSHandle &properties); +}; + +enum class ClassPropertyType : uint8_t { NON_STATIC = 0, STATIC }; + +class ClassHelper { +public: + static JSHandle DefineClassTemplate(JSThread *thread, JSHandle &extractor, + const JSHandle &constantpool); + +private: + static JSHandle BuildDictionaryPropeties(JSThread *thread, const JSHandle &object, + JSHandle &keys, + JSHandle &properties, ClassPropertyType type, + const JSHandle &constantpool); + + static void HandleElementsProperties(JSThread *thread, const JSHandle &object, + JSHandle &elements, const JSHandle &constantpool); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_CLASS_INFO_EXTRACTOR_H diff --git a/ecmascript/class_linker/panda_file_translator.cpp b/ecmascript/class_linker/panda_file_translator.cpp index c4bf2e63..f5606e2a 100644 --- a/ecmascript/class_linker/panda_file_translator.cpp +++ b/ecmascript/class_linker/panda_file_translator.cpp @@ -19,6 +19,7 @@ #include #include +#include "ecmascript/class_info_extractor.h" #include "ecmascript/class_linker/program_object-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/interpreter.h" @@ -138,7 +139,6 @@ Program *PandaFileTranslator::GenerateProgram(const panda_file::File &pf) JSHandle normalDynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); JSHandle asyncDynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); JSHandle generatorDynclass = JSHandle::Cast(env->GetGeneratorFunctionClass()); - JSHandle classDynclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); for (const auto &it : constpoolMap_) { ConstPoolValue value(it.second); @@ -202,10 +202,8 @@ Program *PandaFileTranslator::GenerateProgram(const panda_file::File &pf) panda_file::File::EntityId id(it.first); auto method = const_cast(FindMethods(it.first)); ASSERT(method != nullptr); - JSHandle jsFunc = - factory_->NewJSFunctionByDynClass(method, classDynclass, FunctionKind::CLASS_CONSTRUCTOR); - constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); - jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + JSHandle classInfoExtractor = factory_->NewClassInfoExtractor(method); + constpool->Set(thread_, value.GetConstpoolIndex(), classInfoExtractor.GetTaggedValue()); } else if (value.GetConstpoolType() == ConstPoolType::METHOD) { ASSERT(mainMethodIndex_ != it.first); panda_file::File::EntityId id(it.first); @@ -264,6 +262,7 @@ Program *PandaFileTranslator::GenerateProgram(const panda_file::File &pf) constpool->Set(thread_, constpoolIndex_, program.GetTaggedValue()); } + DefineClassInConstPool(constpool); return *program; } @@ -578,4 +577,29 @@ JSHandle PandaFileTranslator::DefineMethodInLiteral(JSThread *thread jsFunc->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(length)); return jsFunc; } + +void PandaFileTranslator::DefineClassInConstPool(const JSHandle &constpool) const +{ + array_size_t length = constpool->GetLength(); + array_size_t index = 0; + while (index < length - 1) { + JSTaggedValue value = constpool->Get(index); + if (!value.IsClassInfoExtractor()) { + index++; + continue; + } + + // Here, using a law: when inserting ctor in index of constantpool, the index + 1 location will be inserted by + // corresponding class literal. Because translator fixes ECMA_DEFINECLASSWITHBUFFER two consecutive times. + JSTaggedValue nextValue = constpool->Get(index + 1); + ASSERT(nextValue.IsTaggedArray()); + + JSHandle extractor(thread_, value); + JSHandle literal(thread_, nextValue); + ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread_, extractor, literal); + JSHandle cls = ClassHelper::DefineClassTemplate(thread_, extractor, constpool); + constpool->Set(thread_, index, cls); + index += 2; // 2: pair of extractor and literal + } +} } // namespace panda::ecmascript diff --git a/ecmascript/class_linker/panda_file_translator.h b/ecmascript/class_linker/panda_file_translator.h index bd6427fd..77483c79 100644 --- a/ecmascript/class_linker/panda_file_translator.h +++ b/ecmascript/class_linker/panda_file_translator.h @@ -31,20 +31,6 @@ class PandaFileTranslator { public: enum FixInstructionIndex : uint8_t { FIX_ONE = 1, FIX_TWO = 2, FIX_FOUR = 4 }; - enum SecondInstructionOfMidr : uint8_t { - DEFINE_FUNC_DYN = 0, - DEFINE_NC_FUNC_DYN, - DEFINE_GENERATOR_FUNC_DYN, - DEFINE_ASYNC_FUNC_DYN, - DEFINE_METHOD - }; - - enum SecondInstructionOfImm : uint8_t { - CREATE_OBJECT_WITH_BUFFER = 2, - CREATE_ARRAY_WITH_BUFFER, - CREATE_OBJECT_HAVING_METHOD - }; - explicit PandaFileTranslator(EcmaVM *vm); ~PandaFileTranslator() = default; NO_COPY_SEMANTIC(PandaFileTranslator); @@ -111,6 +97,7 @@ private: void FixInstructionId32(const BytecodeInstruction &inst, uint32_t index, uint32_t fixOrder = 0) const; void FixOpcode(uint8_t *pc) const; void UpdateICOffset(JSMethod *method, uint8_t *pc) const; + void DefineClassInConstPool(const JSHandle &constpool) const; void SetMethods(Span methods, const uint32_t numMethods) { diff --git a/ecmascript/compiler/stub-inl.h b/ecmascript/compiler/stub-inl.h index 5ca208c3..dd405808 100644 --- a/ecmascript/compiler/stub-inl.h +++ b/ecmascript/compiler/stub-inl.h @@ -940,16 +940,15 @@ GateRef Stub::NotBuiltinsConstructor(GateRef object) GateRef Stub::IsClassConstructor(GateRef object) { - GateRef functionInfoFlagOffset = GetArchRelateConstant(JSFunction::FUNCTION_INFO_FLAG_OFFSET); - GateRef functionInfoTaggedValue = Load(MachineType::UINT64, object, functionInfoFlagOffset); - GateRef functionInfoInt32 = TaggedCastToInt32(functionInfoTaggedValue); - GateRef functionInfoFlag = ZExtInt32ToInt64(functionInfoInt32); + GateRef hClass = LoadHClass(object); + GateRef bitfieldOffset = GetArchRelateConstant(JSHClass::BIT_FIELD_OFFSET); + + GateRef bitfield = Load(MachineType::UINT32, hClass, bitfieldOffset); // decode - return Word64NotEqual( - Word64And( - Word64LSR(functionInfoFlag, GetWord64Constant(JSFunction::ClassConstructorBit::START_BIT)), - GetWord64Constant((1LLU << JSFunction::ClassConstructorBit::SIZE) - 1)), - GetWord64Constant(0)); + return Word32NotEqual( + Word32And(Word32LSR(bitfield, GetInt32Constant(JSHClass::ClassConstructorBit::START_BIT)), + GetInt32Constant((1LU << JSHClass::ClassConstructorBit::SIZE) - 1)), + GetInt32Constant(0)); } GateRef Stub::IsExtensible(GateRef object) diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 301c9bbb..f2a1dfcd 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -19,6 +19,7 @@ #include #include "ecmascript/accessor_data.h" +#include "ecmascript/class_info_extractor.h" #include "ecmascript/class_linker/program_object-inl.h" #include "ecmascript/ecma_module.h" #include "ecmascript/ecma_vm.h" @@ -251,6 +252,8 @@ CString JSHClass::DumpJSType(JSType type) return "MachineCode"; case JSType::ECMA_MODULE: return "EcmaModule"; + case JSType::CLASS_INFO_EXTRACTOR: + return "ClassInfoExtractor"; default: { CString ret = "unknown type "; return ret + static_cast(type); @@ -591,6 +594,9 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, std::ostream &os) case JSType::ECMA_MODULE: EcmaModule::Cast(obj)->Dump(thread, os); break; + case JSType::CLASS_INFO_EXTRACTOR: + ClassInfoExtractor::Cast(obj)->Dump(thread, os); + break; default: UNREACHABLE(); break; @@ -1890,6 +1896,34 @@ void EcmaModule::Dump(JSThread *thread, std::ostream &os) const os << "\n"; } +void ClassInfoExtractor::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - PrototypeHClass: "; + GetPrototypeHClass().D(); + os << "\n"; + os << " - NonStaticKeys: "; + GetNonStaticKeys().D(); + os << "\n"; + os << " - NonStaticProperties: "; + GetNonStaticProperties().D(); + os << "\n"; + os << " - NonStaticElements: "; + GetNonStaticElements().D(); + os << "\n"; + os << " - ConstructorHClass: "; + GetConstructorHClass().D(); + os << "\n"; + os << " - StaticKeys: "; + GetStaticKeys().D(); + os << "\n"; + os << " - StaticProperties: "; + GetStaticProperties().D(); + os << "\n"; + os << " - StaticElements: "; + GetStaticElements().D(); + os << "\n"; +} + // ######################################################################################## // Dump for Snapshot // ######################################################################################## @@ -2140,6 +2174,9 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, case JSType::PROTOTYPE_HANDLER: PrototypeHandler::Cast(obj)->DumpForSnapshot(thread, vec); return; + case JSType::CLASS_INFO_EXTRACTOR: + ClassInfoExtractor::Cast(obj)->DumpForSnapshot(thread, vec); + return; default: UNREACHABLE(); break; @@ -2958,4 +2995,16 @@ void EcmaModule::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.push_back(std::make_pair(CString("PrototypeHClass"), GetPrototypeHClass())); + vec.push_back(std::make_pair(CString("NonStaticKeys"), GetNonStaticKeys())); + vec.push_back(std::make_pair(CString("NonStaticProperties"), GetNonStaticProperties())); + vec.push_back(std::make_pair(CString("NonStaticElements"), GetNonStaticElements())); + vec.push_back(std::make_pair(CString("ConstructorHClass"), GetConstructorHClass())); + vec.push_back(std::make_pair(CString("StaticKeys"), GetStaticKeys())); + vec.push_back(std::make_pair(CString("StaticProperties"), GetStaticProperties())); + vec.push_back(std::make_pair(CString("StaticElements"), GetStaticElements())); +} } // namespace panda::ecmascript diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index 1eddffbe..7e7ff51f 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -18,6 +18,7 @@ #include "ecmascript/accessor_data.h" #include "ecmascript/builtins.h" #include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/class_info_extractor.h" #include "ecmascript/class_linker/program_object.h" #include "ecmascript/ecma_module.h" #include "ecmascript/ecma_vm.h" @@ -150,6 +151,9 @@ void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHCl factory->NewEcmaDynClass(dynClassClass, JSRealm::SIZE, JSType::JS_REALM).GetTaggedValue()); SetConstant(ConstantIndex::MACHINE_CODE_CLASS_INDEX, factory->NewEcmaDynClass(dynClassClass, 0, JSType::MACHINE_CODE_OBJECT).GetTaggedValue()); + SetConstant(ConstantIndex::CLASS_INFO_EXTRACTOR_HCLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ClassInfoExtractor::SIZE, JSType::CLASS_INFO_EXTRACTOR) + .GetTaggedValue()); } // NOLINTNEXTLINE(readability-function-size) diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 8aa060e5..dd1e716f 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -66,7 +66,8 @@ class JSThread; V(JSTaggedValue, JSProxyConstructClass, JS_PROXY_CONSTRUCT_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, JSRealmClass, JS_REALM_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, JSRegExpClass, JS_REGEXP_CLASS_INDEX, ecma_roots_class) \ - V(JSTaggedValue, MachineCodeClass, MACHINE_CODE_CLASS_INDEX, ecma_roots_class) + V(JSTaggedValue, MachineCodeClass, MACHINE_CODE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ClassInfoExtractorHClass, CLASS_INFO_EXTRACTOR_HCLASS_INDEX, ecma_roots_class) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GLOBAL_ENV_CONSTANT_SPECIAL(V) \ diff --git a/ecmascript/interpreter/interpreter-inl.h b/ecmascript/interpreter/interpreter-inl.h index 36350b1a..ec192a9a 100644 --- a/ecmascript/interpreter/interpreter-inl.h +++ b/ecmascript/interpreter/interpreter-inl.h @@ -3289,25 +3289,31 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, ConstantPool uint16_t v1 = READ_INST_8_8(); LOG_INST() << "intrinsics::defineclasswithbuffer" << " method id:" << methodId << " literal id:" << imm << " lexenv: v" << v0 << " parent: v" << v1; - JSFunction *cls = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); - ASSERT(cls != nullptr); - if (cls->IsResolved()) { - auto res = SlowRuntimeStub::NewClassFunc(thread, cls); - INTERPRETER_RETURN_IF_ABRUPT(res); - cls = JSFunction::Cast(res.GetTaggedObject()); - cls->SetConstantPool(thread, JSTaggedValue(constpool)); - } else { - cls->SetResolved(thread); - } - - cls->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(length)); - JSTaggedValue lexenv = GET_VREG_VALUE(v0); - cls->SetLexicalEnv(thread, lexenv); + JSFunction *classTemplate = JSFunction::Cast(constpool->GetObjectFromCache(methodId).GetTaggedObject()); + ASSERT(classTemplate != nullptr); TaggedArray *literalBuffer = TaggedArray::Cast(constpool->GetObjectFromCache(imm).GetTaggedObject()); + JSTaggedValue lexenv = GET_VREG_VALUE(v0); JSTaggedValue proto = GET_VREG_VALUE(v1); - JSTaggedValue res = SlowRuntimeStub::DefineClass(thread, cls, literalBuffer, proto, lexenv, constpool); + + JSTaggedValue res; + if (LIKELY(!classTemplate->IsResolved())) { + res = SlowRuntimeStub::ResolveClass(thread, JSTaggedValue(classTemplate), literalBuffer, + proto, lexenv, constpool); + } else { + res = SlowRuntimeStub::CloneClassFromTemplate(thread, JSTaggedValue(classTemplate), + proto, lexenv, constpool); + } + INTERPRETER_RETURN_IF_ABRUPT(res); + ASSERT(res.IsClassConstructor()); + JSFunction *cls = JSFunction::Cast(res.GetTaggedObject()); + + lexenv = GET_VREG_VALUE(v0); // slow runtime may gc + cls->SetLexicalEnv(thread, lexenv); + + SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); + SET_ACC(res); DISPATCH(BytecodeInstruction::Format::PREF_ID16_IMM16_IMM16_V8_V8); } diff --git a/ecmascript/interpreter/slow_runtime_stub.cpp b/ecmascript/interpreter/slow_runtime_stub.cpp index 6c96a43e..ae575dc0 100644 --- a/ecmascript/interpreter/slow_runtime_stub.cpp +++ b/ecmascript/interpreter/slow_runtime_stub.cpp @@ -1608,146 +1608,6 @@ JSTaggedValue SlowRuntimeStub::DefinefuncDyn(JSThread *thread, JSFunction *func) return jsFunc.GetTaggedValue(); } -JSTaggedValue SlowRuntimeStub::NewClassFunc(JSThread *thread, JSFunction *func) -{ - INTERPRETER_TRACE(thread, NewClassFunc); - [[maybe_unused]] EcmaHandleScope handleScope(thread); - auto method = func->GetCallTarget(); - - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); - JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::CLASS_CONSTRUCTOR); - ASSERT_NO_ABRUPT_COMPLETION(thread); - return jsFunc.GetTaggedValue(); -} - -JSTaggedValue SlowRuntimeStub::DefineClass(JSThread *thread, JSFunction *func, TaggedArray *literal, - JSTaggedValue proto, JSTaggedValue lexenv, ConstantPool *constpool) -{ - INTERPRETER_TRACE(thread, DefineClass); - [[maybe_unused]] EcmaHandleScope handleScope(thread); - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - - JSHandle cls(thread, func); - ASSERT(cls->IsJSFunction()); - JSHandle literalBuffer(thread, literal); - JSMutableHandle parent(thread, proto); - JSHandle lexicalEnv(thread, lexenv); - JSHandle constantpool(thread, constpool); - - cls->GetTaggedObject()->GetClass()->SetClassConstructor(true); - JSFunction::Cast(cls->GetTaggedObject())->SetClassConstructor(thread, true); - - /* - * set class __proto__ - * - * class A / class A extends null class A extends B - * a a - * | | - * | __proto__ | __proto__ - * | | - * A ----> A.prototype A ----> A.prototype - * | | | | - * | __proto__ | __proto__ | __proto__ | __proto__ - * | | | | - * Function.prototype Object.prototype / null B ----> B.prototype - */ - JSMutableHandle parentPrototype(thread, JSTaggedValue::Undefined()); - // hole means parent is not present - if (parent->IsHole()) { - JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR); - parentPrototype.Update(env->GetObjectFunctionPrototype().GetTaggedValue()); - parent.Update(env->GetFunctionPrototype().GetTaggedValue()); - } else if (parent->IsNull()) { - JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); - parentPrototype.Update(JSTaggedValue::Null()); - parent.Update(env->GetFunctionPrototype().GetTaggedValue()); - } else if (!parent->IsConstructor()) { - return ThrowTypeError(thread, "parent class is not constructor"); - } else { - JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); - parentPrototype.Update(JSTaggedValue::GetProperty(thread, parent, globalConst->GetHandledPrototypeString()) - .GetValue() - .GetTaggedValue()); - if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) { - return ThrowTypeError(thread, "parent class have no valid prototype"); - } - } - - // null cannot cast JSObject - JSHandle clsPrototype = JSObject::ObjectCreate(thread, JSHandle(parentPrototype)); - - bool success = true; - success = success && JSFunction::MakeClassConstructor(thread, cls, clsPrototype); - clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true); - - success = success && JSObject::SetPrototype(thread, JSHandle::Cast(cls), parent); - - uint32_t bufferLength = literalBuffer->GetLength(); - // static location is hidden in the last index of Literal buffer - uint32_t staticLoc = literalBuffer->Get(thread, bufferLength - 1).GetInt(); - - // set property on class.prototype and class - JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); - JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); - for (uint32_t i = 0; i < staticLoc * 2; i += 2) { // 2: Each literal buffer contains a pair of key-value. - // set non-static property on cls.prototype - propKey.Update(literalBuffer->Get(thread, i)); - propValue.Update(literalBuffer->Get(thread, i + 1)); - if (propValue->IsJSFunction()) { - propValue.Update( - factory->CloneJSFuction(JSHandle::Cast(propValue), FunctionKind::NORMAL_FUNCTION) - .GetTaggedValue()); - JSHandle propFunc = JSHandle::Cast(propValue); - propFunc->SetHomeObject(thread, clsPrototype); - propFunc->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); - propFunc->SetConstantPool(thread, constantpool.GetTaggedValue()); - } - PropertyDescriptor desc(thread, propValue, true, false, true); // non-enumerable - success = success && JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(clsPrototype), - propKey, desc); - } - for (uint32_t i = staticLoc * 2; i < bufferLength - 1; i += 2) { // 2: ditto - // set static property on cls - propKey.Update(literalBuffer->Get(thread, i)); - propValue.Update(literalBuffer->Get(thread, i + 1)); - if (propValue->IsJSFunction()) { - propValue.Update( - factory->CloneJSFuction(JSHandle::Cast(propValue), FunctionKind::NORMAL_FUNCTION) - .GetTaggedValue()); - JSHandle propFunc = JSHandle::Cast(propValue); - propFunc->SetHomeObject(thread, cls); - propFunc->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); - propFunc->SetConstantPool(thread, constantpool.GetTaggedValue()); - } - PropertyDescriptor desc(thread, propValue, true, false, true); // non-enumerable - success = success && JSTaggedValue::DefinePropertyOrThrow(thread, cls, propKey, desc); - } - - // ECMA2015 14.5.15: if class don't have a method which is name(), set the class a string name. - if (!JSTaggedValue::HasOwnProperty(thread, cls, globalConst->GetHandledNameString())) { - JSMethod *clsTarget = JSHandle::Cast(cls)->GetCallTarget(); - ASSERT(clsTarget != nullptr); - CString clsName = clsTarget->ParseFunctionName(); - if (!clsName.empty()) { - success = - success && JSFunction::SetFunctionName(thread, JSHandle(cls), - JSHandle(factory->NewFromString(clsName.c_str())), - globalConst->GetHandledUndefined()); - } - } - - if (!success) { - return JSTaggedValue::Exception(); - } - - ASSERT_NO_ABRUPT_COMPLETION(thread); - return cls.GetTaggedValue(); -} - JSTaggedValue SlowRuntimeStub::SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, uint16_t firstVRegIdx, uint16_t length) { @@ -1922,4 +1782,144 @@ JSTaggedValue SlowRuntimeStub::NotifyInlineCache(JSThread *thread, JSFunction *f } return JSTaggedValue::Undefined(); } -} // namespace panda::ecmascript + +JSTaggedValue SlowRuntimeStub::ResolveClass(JSThread *thread, JSTaggedValue ctor, TaggedArray *literal, + JSTaggedValue base, JSTaggedValue lexenv, ConstantPool *constpool) +{ + ASSERT(ctor.IsClassConstructor()); + JSHandle cls(thread, ctor); + JSHandle literalBuffer(thread, literal); + JSHandle lexicalEnv(thread, lexenv); + JSHandle constpoolHandle(thread, constpool); + + SetClassInheritanceRelationship(thread, ctor, base); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + uint32_t literalBufferLength = literalBuffer->GetLength(); + + // only traverse the value of key-value pair + for (uint32_t index = 1; index < literalBufferLength - 1; index += 2) { // 2: key-value pair + JSTaggedValue value = literalBuffer->Get(index); + if (LIKELY(value.IsJSFunction())) { + JSFunction::Cast(value.GetTaggedObject())->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + JSFunction::Cast(value.GetTaggedObject())->SetConstantPool(thread, constpoolHandle.GetTaggedValue()); + } + } + + cls->SetResolved(thread); + return cls.GetTaggedValue(); +} + +// clone class may need re-set inheritance relationship due to extends may be a variable. +JSTaggedValue SlowRuntimeStub::CloneClassFromTemplate(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base, + JSTaggedValue lexenv, ConstantPool *constpool) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + ASSERT(ctor.IsClassConstructor()); + JSHandle lexenvHandle(thread, lexenv); + JSHandle constpoolHandle(thread, JSTaggedValue(constpool)); + JSHandle baseHandle(thread, base); + + JSHandle cls(thread, ctor); + + JSHandle clsPrototype(thread, cls->GetFunctionPrototype()); + + bool canShareHClass = false; + if (cls->GetClass()->GetProto() == baseHandle.GetTaggedValue()) { + canShareHClass = true; + } + + JSHandle cloneClass = factory->CloneClassCtor(cls, lexenvHandle, canShareHClass); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle cloneClassPrototype = factory->CloneObjectLiteral(JSHandle(clsPrototype), lexenvHandle, + constpoolHandle, canShareHClass); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // After clone both, reset "constructor" and "prototype" properties. + cloneClass->SetFunctionPrototype(thread, cloneClassPrototype.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + PropertyDescriptor ctorDesc(thread, JSHandle(cloneClass), true, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(cloneClassPrototype), + globalConst->GetHandledConstructorString(), ctorDesc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + cloneClass->SetHomeObject(thread, cloneClassPrototype); + + if (!canShareHClass) { + SetClassInheritanceRelationship(thread, cloneClass.GetTaggedValue(), baseHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + return cloneClass.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SetClassInheritanceRelationship(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSHandle cls(thread, ctor); + ASSERT(cls->IsJSFunction()); + JSMutableHandle parent(thread, base); + + /* + * class A / class A extends null class A extends B + * a a + * | | + * | __proto__ | __proto__ + * | | + * A ----> A.prototype A ----> A.prototype + * | | | | + * | __proto__ | __proto__ | __proto__ | __proto__ + * | | | | + * Function.prototype Object.prototype / null B ----> B.prototype + */ + + JSHandle parentPrototype; + // hole means parent is not present + if (parent->IsHole()) { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR); + parentPrototype = env->GetObjectFunctionPrototype(); + parent.Update(env->GetFunctionPrototype().GetTaggedValue()); + } else if (parent->IsNull()) { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); + parentPrototype = JSHandle(thread, JSTaggedValue::Null()); + parent.Update(env->GetFunctionPrototype().GetTaggedValue()); + } else if (!parent->IsConstructor()) { + return ThrowTypeError(thread, "parent class is not constructor"); + } else { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); + parentPrototype = JSTaggedValue::GetProperty(thread, parent, + globalConst->GetHandledPrototypeString()).GetValue(); + if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) { + return ThrowTypeError(thread, "parent class have no valid prototype"); + } + } + + cls->GetTaggedObject()->GetClass()->SetPrototype(thread, parent); + + JSHandle clsPrototype(thread, JSHandle(cls)->GetFunctionPrototype()); + clsPrototype->GetClass()->SetPrototype(thread, parentPrototype); + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue SlowRuntimeStub::SetClassConstructorLength(JSThread *thread, JSTaggedValue ctor, JSTaggedValue length) +{ + ASSERT(ctor.IsClassConstructor()); + + JSFunction* cls = JSFunction::Cast(ctor.GetTaggedObject()); + if (LIKELY(!cls->GetClass()->IsDictionaryMode())) { + cls->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, length); + } else { + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + cls->UpdatePropertyInDictionary(thread, globalConst->GetLengthString(), length); + } + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/interpreter/slow_runtime_stub.h b/ecmascript/interpreter/slow_runtime_stub.h index 48bb088d..152fb25a 100644 --- a/ecmascript/interpreter/slow_runtime_stub.h +++ b/ecmascript/interpreter/slow_runtime_stub.h @@ -135,10 +135,7 @@ public: static JSTaggedValue DefineAsyncFunc(JSThread *thread, JSFunction *func); static JSTaggedValue DefineNCFuncDyn(JSThread *thread, JSFunction *func); static JSTaggedValue DefinefuncDyn(JSThread *thread, JSFunction *func); - static JSTaggedValue NewClassFunc(JSThread *thread, JSFunction *func); - static JSTaggedValue DefineClass(JSThread *thread, JSFunction *func, TaggedArray *literal, JSTaggedValue proto, - JSTaggedValue lexenv, ConstantPool *constpool); static JSTaggedValue SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, uint16_t firstVRegIdx, uint16_t length); static JSTaggedValue SuperCallSpread(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, @@ -149,12 +146,19 @@ public: JSTaggedValue thisFunc); static JSTaggedValue NotifyInlineCache(JSThread *thread, JSFunction *func, JSMethod *method); static JSTaggedValue ThrowReferenceError(JSThread *thread, JSTaggedValue prop, const char *desc); + + static JSTaggedValue ResolveClass(JSThread *thread, JSTaggedValue ctor, TaggedArray *literal, JSTaggedValue base, + JSTaggedValue lexenv, ConstantPool *constpool); + static JSTaggedValue CloneClassFromTemplate(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base, + JSTaggedValue lexenv, ConstantPool *constpool); + static JSTaggedValue SetClassConstructorLength(JSThread *thread, JSTaggedValue ctor, JSTaggedValue length); /* -------------- Common API End, Don't change those interface!!! ----------------- */ private: static JSTaggedValue ThrowTypeError(JSThread *thread, const char *message); static JSTaggedValue ThrowSyntaxError(JSThread *thread, const char *message); static JSTaggedValue GetCallSpreadArgs(JSThread *thread, JSTaggedValue array); + static JSTaggedValue SetClassInheritanceRelationship(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_INTERPRETER_SLOW_RUNTIME_STUB_H diff --git a/ecmascript/js_function.cpp b/ecmascript/js_function.cpp index e82b4291..56351f89 100644 --- a/ecmascript/js_function.cpp +++ b/ecmascript/js_function.cpp @@ -16,6 +16,7 @@ #include "js_function.h" #include "ecmascript/base/error_type.h" +#include "ecmascript/class_info_extractor.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/global_env.h" @@ -51,7 +52,6 @@ void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandleSetFunctionExtraInfo(thread, JSTaggedValue::Undefined()); func->SetFunctionInfoFlag(thread, JSTaggedValue(flag)); - ASSERT(!func->IsPropertiesDict()); auto globalConst = thread->GlobalConstants(); if (HasPrototype(kind)) { JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); @@ -59,9 +59,7 @@ void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandleSetPropertyInlinedProps(thread, PROTOTYPE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); accessor = globalConst->GetHandledFunctionNameAccessor(); func->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); - } else if (JSFunction::IsClassConstructor(kind)) { - func->SetPropertyInlinedProps(thread, CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); - } else { + } else if (!JSFunction::IsClassConstructor(kind)) { // class ctor do nothing PropertyDescriptor desc(thread, accessor, kind != FunctionKind::BUILTIN_CONSTRUCTOR, false, false); [[maybe_unused]] bool success = JSObject::DefineOwnProperty(thread, JSHandle(func), globalConst->GetHandledPrototypeString(), desc); @@ -247,21 +245,6 @@ bool JSFunction::MakeConstructor(JSThread *thread, const JSHandle &f return status; } -// set property constructor and prototype for class -bool JSFunction::MakeClassConstructor(JSThread *thread, const JSHandle &cls, - const JSHandle &clsPrototype) -{ - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - bool status = true; - PropertyDescriptor ctorDesc(thread, cls, true, false, true); - status = status && JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(clsPrototype), - globalConst->GetHandledConstructorString(), ctorDesc); - - JSHandle::Cast(cls)->SetFunctionPrototype(thread, clsPrototype.GetTaggedValue()); - JSHandle::Cast(cls)->SetHomeObject(thread, clsPrototype.GetTaggedValue()); - return status; -} - JSTaggedValue JSFunction::Call(JSThread *thread, const JSHandle &func, const JSHandle &thisArg, uint32_t argc, const JSTaggedType argv[]) { @@ -378,12 +361,6 @@ JSTaggedValue JSFunction::ConstructInternal(JSThread *thread, const JSHandle &func, - const JSHandle &homeObject) -{ - func->SetHomeObject(thread, homeObject); -} - JSHandle JSFunctionBase::GetFunctionName(JSThread *thread, const JSHandle &func) { JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); @@ -602,6 +579,7 @@ JSHandle JSFunction::GetOrCreateDerivedJSHClass(JSThread *thread, JSHa JSHandle newJSHClass = JSHClass::Clone(thread, ctorInitialJSHClass); // guarante derived has function prototype JSHandle prototype(thread, derived->GetProtoOrDynClass()); + ASSERT(!prototype->IsHole()); newJSHClass->SetPrototype(thread, prototype); derived->SetProtoOrDynClass(thread, newJSHClass); return newJSHClass; diff --git a/ecmascript/js_function.h b/ecmascript/js_function.h index d7b5127c..fd30c234 100644 --- a/ecmascript/js_function.h +++ b/ecmascript/js_function.h @@ -94,10 +94,6 @@ public: static bool AddRestrictedFunctionProperties(const JSHandle &func, const JSHandle &realm); static bool MakeConstructor(JSThread *thread, const JSHandle &func, const JSHandle &proto, bool writable = true); - static bool MakeClassConstructor(JSThread *thread, const JSHandle &cls, - const JSHandle &clsPrototype); - static void MakeMethod(const JSThread *thread, const JSHandle &func, - const JSHandle &homeObject); static bool SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg = true); static JSHandle NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, @@ -171,7 +167,7 @@ public: inline bool IsDerivedConstructor() const { FunctionKind kind = GetFunctionKind(); - return kind == FunctionKind::DERIVED_CONSTRUCTOR || kind == FunctionKind::DEFAULT_DERIVED_CONSTRUCTOR; + return kind == FunctionKind::DERIVED_CONSTRUCTOR; } inline void SetFunctionKind(const JSThread *thread, FunctionKind kind) @@ -186,12 +182,6 @@ public: SetFunctionInfoFlag(thread, JSTaggedValue(StrictBit::Update(oldValue, flag))); } - inline void SetClassConstructor(const JSThread *thread, bool flag) - { - JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); - SetFunctionInfoFlag(thread, JSTaggedValue(ClassConstructorBit::Update(oldValue, flag))); - } - inline void SetResolved(const JSThread *thread) { TaggedType oldValue = GetFunctionInfoFlag().GetRawData(); @@ -219,11 +209,6 @@ public: return StrictBit::Decode(GetFunctionInfoFlag().GetInt()); } - inline bool IsClassConstructor() const - { - return ClassConstructorBit::Decode(GetFunctionInfoFlag().GetInt()); - } - inline FunctionMode GetFunctionMode() const { return ThisModeBit::Decode(GetFunctionInfoFlag().GetInt()); @@ -236,7 +221,7 @@ public: inline static bool IsClassConstructor(FunctionKind kind) { - return (kind >= CLASS_CONSTRUCTOR) && (kind <= DERIVED_CONSTRUCTOR); + return (kind == CLASS_CONSTRUCTOR) || (kind == DERIVED_CONSTRUCTOR); } inline static bool IsConstructorKind(FunctionKind kind) @@ -259,6 +244,16 @@ public: return kind >= NORMAL_FUNCTION && kind <= ASYNC_FUNCTION; } + inline bool IsClassConstructor() const + { + return GetClass()->IsClassConstructor(); + } + + inline void SetClassConstructor(bool flag) + { + GetClass()->SetClassConstructor(flag); + } + /* -------------- Common API End, Don't change those interface!!! ----------------- */ static void InitializeJSFunction(JSThread *thread, const JSHandle &func, FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); @@ -278,9 +273,8 @@ public: static constexpr uint32_t FUNCTION_KIND_BIT_NUM = 5; using FunctionKindBit = BitField; using StrictBit = FunctionKindBit::NextFlag; - using ClassConstructorBit = StrictBit::NextFlag; - using ResolvedBit = ClassConstructorBit::NextFlag; + using ResolvedBit = StrictBit::NextFlag; using ThisModeBit = ResolvedBit::NextField; // 2: means this flag occupies two digits. DECL_DUMP() diff --git a/ecmascript/js_function_kind.h b/ecmascript/js_function_kind.h index 5df2362e..53c535a3 100644 --- a/ecmascript/js_function_kind.h +++ b/ecmascript/js_function_kind.h @@ -38,11 +38,8 @@ enum FunctionKind : uint8_t { // END base constructors // BEGIN class constructors CLASS_CONSTRUCTOR, - // BEGIN derived constructors - DEFAULT_DERIVED_CONSTRUCTOR, // END default constructors DERIVED_CONSTRUCTOR, - // END derived constructors // END class constructors GENERATOR_FUNCTION, // END generators diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index add7d490..b3ef03ea 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -287,6 +287,7 @@ void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto) } SetProto(thread, proto); } + void JSHClass::SetPrototype(const JSThread *thread, const JSHandle &proto) { SetPrototype(thread, proto.GetTaggedValue()); diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 2e60f588..a104cd79 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -159,7 +159,8 @@ class ProtoChangeDetails; COMPLETION_RECORD, /* JS_RECORD_END /////////////////////////////////////////////////////////////////////// */ \ MACHINE_CODE_OBJECT, \ ECMA_MODULE, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ - JS_TYPE_LAST = ECMA_MODULE, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + CLASS_INFO_EXTRACTOR, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPE_LAST = CLASS_INFO_EXTRACTOR, /* ///////////////////////////////////////////////////////////-PADDING */ \ \ JS_FUNCTION_BEGIN = JS_FUNCTION, /* ///////////////////////////////////////////////////////////////-PADDING */ \ JS_FUNCTION_END = JS_ASYNC_AWAIT_STATUS_FUNCTION, /* //////////////////////////////////////////////-PADDING */ \ @@ -686,6 +687,11 @@ public: return GetObjectType() == JSType::ECMA_MODULE; } + inline bool IsClassInfoExtractor() const + { + return GetObjectType() == JSType::CLASS_INFO_EXTRACTOR; + } + inline bool IsLexicalFunction() const { return GetObjectType() == JSType::LEXICAL_FUNCTION; diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index 78fecaf4..5ea96e3b 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -1757,11 +1757,14 @@ JSHandle JSObject::EnumerateObjectProperties(JSThread *thread, } void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, - const JSHandle &key, const JSHandle &value) + const JSHandle &key, const JSHandle &value, + bool useForClass) { ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); - auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); + PropertyAttributes attr = useForClass ? PropertyAttributes::Default(true, false, true) + : PropertyAttributes::Default(); + if (value->IsAccessorData()) { attr.SetIsAccessor(true); } diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index 6fa2f4bc..baa92b62 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -552,7 +552,8 @@ public: bool IsTypedArray() const; static void DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, - const JSHandle &key, const JSHandle &value); + const JSHandle &key, const JSHandle &value, + bool useForClass = false); static void DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static void DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index bb5281e8..3fbd14d4 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -840,6 +840,11 @@ inline bool JSTaggedValue::IsMachineCodeObject() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsMachineCodeObject(); } +inline bool JSTaggedValue::IsClassInfoExtractor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassInfoExtractor(); +} + inline double JSTaggedValue::ExtractNumber() const { ASSERT(IsNumber()); diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index c1934610..27387745 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -319,6 +319,7 @@ public: bool IsProtoChangeMarker() const; bool IsProtoChangeDetails() const; bool IsMachineCodeObject() const; + bool IsClassInfoExtractor() const; static bool IsSameTypeOrHClass(JSTaggedValue x, JSTaggedValue y); static ComparisonResult Compare(JSThread *thread, const JSHandle &x, diff --git a/ecmascript/mem/object_xray-inl.h b/ecmascript/mem/object_xray-inl.h index 5b160ee8..580a948a 100644 --- a/ecmascript/mem/object_xray-inl.h +++ b/ecmascript/mem/object_xray-inl.h @@ -17,6 +17,7 @@ #define ECMASCRIPT_MEM_HEAP_ROOTS_INL_H #include +#include "ecmascript/class_info_extractor.h" #include "ecmascript/class_linker/program_object.h" #include "ecmascript/ecma_module.h" #include "ecmascript/ecma_vm.h" @@ -296,6 +297,9 @@ void ObjectXRay::VisitObjectBody(TaggedObject *object, JSHClass *klass, const Ec case JSType::MACHINE_CODE_OBJECT: MachineCode::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::CLASS_INFO_EXTRACTOR: + ClassInfoExtractor::Cast(object)->VisitRangeSlot(visitor); + break; default: UNREACHABLE(); } diff --git a/ecmascript/napi/jsnapi.cpp b/ecmascript/napi/jsnapi.cpp index 50d056a1..d65b55db 100644 --- a/ecmascript/napi/jsnapi.cpp +++ b/ecmascript/napi/jsnapi.cpp @@ -856,6 +856,12 @@ Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallbackWit vm->GetMethodForNativeFunction(reinterpret_cast(Callback::RegisterCallbackWithNewTarget)); JSHandle current = factory->NewJSFunctionByDynClass(method, dynclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR); + + auto globalConst = thread->GlobalConstants(); + JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); + current->SetPropertyInlinedProps(thread, JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX, + accessor.GetTaggedValue()); + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); JSHandle dataCaddress(thread, JSTaggedValue::Undefined()); if (deleter == nullptr) { @@ -867,14 +873,13 @@ Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallbackWit JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, dataCaddress)); current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); - JSHandle clsPrototype = - JSObject::ObjectCreate(thread, JSHandle(env->GetObjectFunctionPrototype())); + JSHandle clsPrototype = JSFunction::NewJSFunctionPrototype(thread, factory, current); clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true); JSHandle::Cast(current)->GetTaggedObject()->GetClass()->SetClassConstructor(true); - current->SetClassConstructor(thread, true); + current->SetClassConstructor(true); JSHandle parent = env->GetFunctionPrototype(); JSObject::SetPrototype(thread, JSHandle::Cast(current), parent); - JSFunction::MakeClassConstructor(thread, JSHandle::Cast(current), clsPrototype); + current->SetHomeObject(thread, clsPrototype); return JSNApiHelper::ToLocal(JSHandle(current)); } diff --git a/ecmascript/napi/test/jsnapi_tests.cpp b/ecmascript/napi/test/jsnapi_tests.cpp index d0282cca..8bdbcd5a 100644 --- a/ecmascript/napi/test/jsnapi_tests.cpp +++ b/ecmascript/napi/test/jsnapi_tests.cpp @@ -693,4 +693,17 @@ HWTEST_F_L0(JSNApiTests, InheritPrototype) JSHandle son1Handle = JSHandle::Cast(JSNApiHelper::ToJSHandle(son1)); ASSERT_TRUE(son1Handle->HasFunctionPrototype()); } + +HWTEST_F_L0(JSNApiTests, ClassFunction) +{ + LocalScope scope(vm_); + Local cls = FunctionRef::NewClassFunction(vm_, nullptr, nullptr, nullptr); + + JSHandle clsObj = JSNApiHelper::ToJSHandle(Local(cls)); + ASSERT_TRUE(clsObj->IsClassConstructor()); + + JSTaggedValue accessor = JSHandle(clsObj)->GetPropertyInlinedProps( + JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX); + ASSERT_TRUE(accessor.IsInternalAccessor()); +} } // namespace panda::test diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index 34405c24..a6262714 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -20,6 +20,7 @@ #include "ecmascript/builtins.h" #include "ecmascript/builtins/builtins_errors.h" #include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/class_info_extractor.h" #include "ecmascript/class_linker/program_object.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_module.h" @@ -152,6 +153,7 @@ void ObjectFactory::ObtainRootClass([[maybe_unused]] const JSHandle & functionExtraInfo_ = JSHClass::Cast(globalConst->GetFunctionExtraInfoClass().GetTaggedObject()); jsRealmClass_ = JSHClass::Cast(globalConst->GetJSRealmClass().GetTaggedObject()); machineCodeClass_ = JSHClass::Cast(globalConst->GetMachineCodeClass().GetTaggedObject()); + classInfoExtractorHClass_ = JSHClass::Cast(globalConst->GetClassInfoExtractorHClass().GetTaggedObject()); } void ObjectFactory::InitObjectFields(const TaggedObject *object) @@ -395,11 +397,15 @@ JSHandle ObjectFactory::CloneProperties(const JSHandle } JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object, const JSHandle &env, - const JSHandle &constpool) + const JSHandle &constpool, bool canShareHClass) { NewObjectHook(); auto klass = JSHandle(thread_, object->GetClass()); + if (!canShareHClass) { + klass = JSHClass::Clone(thread_, klass); + } + JSHandle cloneObject = NewJSObject(klass); JSHandle elements(thread_, object->GetElements()); @@ -444,6 +450,49 @@ JSHandle ObjectFactory::CloneJSFuction(JSHandle obj, Fun return cloneFunc; } +JSHandle ObjectFactory::CloneClassCtor(JSHandle ctor, const JSHandle &lexenv, + bool canShareHClass) +{ + NewObjectHook(); + JSHandle constpool(thread_, ctor->GetConstantPool()); + JSHandle hclass(thread_, ctor->GetClass()); + + if (!canShareHClass) { + hclass = JSHClass::Clone(thread_, hclass); + } + + FunctionKind kind = ctor->GetFunctionKind(); + ASSERT_PRINT(kind == FunctionKind::CLASS_CONSTRUCTOR || kind == FunctionKind::DERIVED_CONSTRUCTOR, + "cloned function is not class"); + JSHandle cloneCtor = NewJSFunctionByDynClass(ctor->GetCallTarget(), hclass, kind); + + for (uint32_t i = 0; i < hclass->GetInlinedProperties(); i++) { + JSTaggedValue value = ctor->GetPropertyInlinedProps(i); + if (!value.IsJSFunction()) { + cloneCtor->SetPropertyInlinedProps(thread_, i, value); + } else { + JSHandle valueHandle(thread_, value); + JSHandle newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind()); + newFunc->SetLexicalEnv(thread_, lexenv); + newFunc->SetHomeObject(thread_, cloneCtor); + newFunc->SetConstantPool(thread_, constpool); + cloneCtor->SetPropertyInlinedProps(thread_, i, newFunc.GetTaggedValue()); + } + } + + JSHandle elements(thread_, ctor->GetElements()); + auto newElements = CloneProperties(elements, lexenv, JSHandle(cloneCtor), constpool); + cloneCtor->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, ctor->GetProperties()); + auto newProperties = CloneProperties(properties, lexenv, JSHandle(cloneCtor), constpool); + cloneCtor->SetProperties(thread_, newProperties.GetTaggedValue()); + + cloneCtor->SetConstantPool(thread_, constpool); + + return cloneCtor; +} + JSHandle ObjectFactory::NewNonMovableJSObject(const JSHandle &jshclass) { JSHandle obj(thread_, @@ -1989,6 +2038,25 @@ JSHandle ObjectFactory::NewMachineCodeObject(size_t length, const u return codeObj; } +JSHandle ObjectFactory::NewClassInfoExtractor(JSMethod *ctorMethod) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(classInfoExtractorHClass_); + JSHandle obj(thread_, header); + obj->InitializeBitField(); + obj->SetConstructorMethod(ctorMethod); + JSHandle emptyArray = EmptyArray(); + obj->SetPrototypeHClass(thread_, JSTaggedValue::Undefined()); + obj->SetNonStaticKeys(thread_, emptyArray, SKIP_BARRIER); + obj->SetNonStaticProperties(thread_, emptyArray, SKIP_BARRIER); + obj->SetNonStaticElements(thread_, emptyArray, SKIP_BARRIER); + obj->SetConstructorHClass(thread_, JSTaggedValue::Undefined()); + obj->SetStaticKeys(thread_, emptyArray, SKIP_BARRIER); + obj->SetStaticProperties(thread_, emptyArray, SKIP_BARRIER); + obj->SetStaticElements(thread_, emptyArray, SKIP_BARRIER); + return obj; +} + // ----------------------------------- new string ---------------------------------------- JSHandle ObjectFactory::NewFromString(const CString &data) { diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index 0f689323..e9263cdc 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -95,6 +95,7 @@ class ProtoChangeMarker; class ProtoChangeDetails; class ProfileTypeInfo; class MachineCode; +class ClassInfoExtractor; enum class PrimitiveType : uint8_t; enum class IterationKind; @@ -282,10 +283,12 @@ public: JSHandle NewJSPromiseAllResolveElementFunction(const void *nativeFunc); JSHandle CloneObjectLiteral(JSHandle object, const JSHandle &env, - const JSHandle &constpool); + const JSHandle &constpool, bool canShareHClass = true); JSHandle CloneObjectLiteral(JSHandle object); JSHandle CloneArrayLiteral(JSHandle object); JSHandle CloneJSFuction(JSHandle obj, FunctionKind kind); + JSHandle CloneClassCtor(JSHandle ctor, const JSHandle &lexenv, + bool canShareHClass); void NewJSArrayBufferData(const JSHandle &array, int32_t length); @@ -328,6 +331,7 @@ public: uintptr_t NewSpaceBySnapShotAllocator(size_t size); JSHandle NewMachineCodeObject(size_t length, const uint8_t *data); + JSHandle NewClassInfoExtractor(JSMethod *ctorMethod); ~ObjectFactory() = default; @@ -407,6 +411,7 @@ private: JSHClass *programClass_ {nullptr}; JSHClass *machineCodeClass_ {nullptr}; JSHClass *ecmaModuleClass_ {nullptr}; + JSHClass *classInfoExtractorHClass_ {nullptr}; EcmaVM *vm_ {nullptr}; Heap *heap_ {nullptr}; @@ -462,6 +467,7 @@ private: friend class PandaFileTranslator; friend class LiteralDataExtractor; friend class RuntimeTrampolines; + friend class ClassInfoExtractor; }; class ClassLinkerFactory { diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index c7674e28..55e5bb05 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/accessor_data.h" +#include "ecmascript/class_info_extractor.h" #include "ecmascript/class_linker/program_object-inl.h" #include "ecmascript/ecma_module.h" #include "ecmascript/ecma_vm.h" @@ -614,6 +615,12 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) DUMP_FOR_HANDLE(ecmaModule) break; } + case JSType::CLASS_INFO_EXTRACTOR: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), ClassInfoExtractor::SIZE, 10) + JSHandle classInfoExtractor = factory->NewClassInfoExtractor(nullptr); + DUMP_FOR_HANDLE(classInfoExtractor) + break; + } default: LOG_ECMA_MEM(ERROR) << "JSType " << static_cast(type) << " cannot be dumped."; UNREACHABLE(); diff --git a/test/run_ark_executable.py b/test/run_ark_executable.py index 604917c8..6d1dcc04 100755 --- a/test/run_ark_executable.py +++ b/test/run_ark_executable.py @@ -47,7 +47,7 @@ def judge_output(args): subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, env={'LD_LIBRARY_PATH': str(input_args.env_path)}) try: - out, err = subp.communicate(timeout=120) # units: s + out, err = subp.communicate(timeout=300) # units: s except TimeoutExpired: subp.kill() out, err = subp.communicate()