template class

Signed-off-by: ding <dingding5@huawei.com>
This commit is contained in:
ding
2021-12-25 09:51:43 +08:00
parent 4d6423ad89
commit c4ffd76fd3
28 changed files with 957 additions and 246 deletions
+1
View File
@@ -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 += [
+422
View File
@@ -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<ClassInfoExtractor> &extractor,
const JSHandle<TaggedArray> &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<TaggedArray> nonStaticKeys = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
JSHandle<TaggedArray> nonStaticProperties = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString());
JSHandle<TaggedArray> 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<JSHClass> prototypeHClass = CreatePrototypeHClass(thread, nonStaticKeys, nonStaticProperties);
extractor->SetPrototypeHClass(thread, prototypeHClass);
uint32_t staticNum = (literalBufferLength - 1) / 2 - nonStaticNum;
// Reserve sufficient length to prevent frequent creation.
JSHandle<TaggedArray> staticKeys = factory->NewTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
JSHandle<TaggedArray> 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<TaggedArray> 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<EcmaString> clsNameHandle = factory->NewFromString(clsName.c_str());
staticProperties->Set(thread, NAME_INDEX, clsNameHandle);
}
// set prototype internal accessor
JSHandle<JSTaggedValue> prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor();
staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor);
extractor->SetStaticKeys(thread, staticKeys);
extractor->SetStaticProperties(thread, staticProperties);
JSHandle<JSHClass> ctorHClass = CreateConstructorHClass(thread, staticKeys, staticProperties);
extractor->SetConstructorHClass(thread, ctorHClass);
}
bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle<TaggedArray> &literal,
const ExtractContentsDetail &detail,
JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties,
JSHandle<TaggedArray> &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<JSTaggedValue> nameString = globalConst->GetHandledNameString();
JSMutableHandle<JSTaggedValue> firstValue(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> 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<EcmaString> 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<JSHClass> ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
uint32_t length = keys->GetLength();
JSHandle<JSHClass> hclass;
if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSHandle<LayoutInfo> 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<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
uint32_t length = keys->GetLength();
JSHandle<JSHClass> hclass;
if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSHandle<LayoutInfo> 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<JSFunction> ClassHelper::DefineClassTemplate(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
const JSHandle<ConstantPool> &constantpool)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSHClass> prototypeHClass(thread, extractor->GetPrototypeHClass());
JSHandle<JSObject> prototype = factory->NewJSObject(prototypeHClass);
JSHandle<JSHClass> constructorHClass(thread, extractor->GetConstructorHClass());
JSHandle<JSFunction> constructor = factory->NewJSFunctionByDynClass(extractor->GetConstructorMethod(),
constructorHClass,
FunctionKind::CLASS_CONSTRUCTOR);
// non-static
JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
nonStaticProperties->Set(thread, 0, constructor);
uint32_t nonStaticLength = nonStaticProperties->GetLength();
JSMutableHandle<JSTaggedValue> 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<JSFunction> propFunc = JSHandle<JSFunction>::Cast(propValue);
propFunc->SetHomeObject(thread, prototype);
propFunc->SetConstantPool(thread, constantpool);
}
prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
}
} else {
JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
JSHandle<NameDictionary> dict = BuildDictionaryPropeties(thread, prototype, nonStaticKeys, nonStaticProperties,
ClassPropertyType::NON_STATIC, constantpool);
prototype->SetProperties(thread, dict);
}
// non-static elements
if (UNLIKELY(extractor->IsNonStaticWithElements())) {
JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements, constantpool);
}
// static
JSHandle<TaggedArray> 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<JSFunction> propFunc = JSHandle<JSFunction>::Cast(propValue);
propFunc->SetHomeObject(thread, constructor);
propFunc->SetConstantPool(thread, constantpool);
}
JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
}
} else {
JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
JSHandle<NameDictionary> dict = BuildDictionaryPropeties(thread, JSHandle<JSObject>(constructor), staticKeys,
staticProperties, ClassPropertyType::STATIC,
constantpool);
constructor->SetProperties(thread, dict);
}
// static elements
if (UNLIKELY(extractor->IsStaticWithElements())) {
JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements, constantpool);
}
constructor->SetProtoOrDynClass(thread, prototype);
return constructor;
}
JSHandle<NameDictionary> ClassHelper::BuildDictionaryPropeties(JSThread *thread, const JSHandle<JSObject> &object,
JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties,
ClassPropertyType type,
const JSHandle<ConstantPool> &constantpool)
{
uint32_t length = keys->GetLength();
ASSERT(length > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES);
ASSERT(keys->GetLength() == properties->GetLength());
JSMutableHandle<NameDictionary> dict(
thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
JSMutableHandle<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> 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<JSFunction> propFunc = JSHandle<JSFunction>::Cast(propValue);
propFunc->SetHomeObject(thread, object);
propFunc->SetConstantPool(thread, constantpool);
}
JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
dict.Update(newDict);
}
return dict;
}
void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
JSHandle<TaggedArray> &elements, const JSHandle<ConstantPool> &constantpool)
{
JSMutableHandle<JSTaggedValue> elementsKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> 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<JSFunction> elementsFunc = JSHandle<JSFunction>::Cast(elementsValue);
elementsFunc->SetHomeObject(thread, object);
elementsFunc->SetConstantPool(thread, constantpool);
}
}
}
} // namespace panda::ecmascript
+125
View File
@@ -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<bool, 0, 1>;
using StaticWithElementsBit = NonStaticWithElementsBit::NextFlag;
static void BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
const JSHandle<TaggedArray> &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<TaggedArray> &literal,
const ExtractContentsDetail &detail,
JSHandle<TaggedArray> &keys, JSHandle<TaggedArray> &properties,
JSHandle<TaggedArray> &elements);
static JSHandle<JSHClass> CreatePrototypeHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties);
static JSHandle<JSHClass> CreateConstructorHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties);
};
enum class ClassPropertyType : uint8_t { NON_STATIC = 0, STATIC };
class ClassHelper {
public:
static JSHandle<JSFunction> DefineClassTemplate(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
const JSHandle<ConstantPool> &constantpool);
private:
static JSHandle<NameDictionary> BuildDictionaryPropeties(JSThread *thread, const JSHandle<JSObject> &object,
JSHandle<TaggedArray> &keys,
JSHandle<TaggedArray> &properties, ClassPropertyType type,
const JSHandle<ConstantPool> &constantpool);
static void HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
JSHandle<TaggedArray> &elements, const JSHandle<ConstantPool> &constantpool);
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_CLASS_INFO_EXTRACTOR_H
@@ -19,6 +19,7 @@
#include <string_view>
#include <vector>
#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<JSHClass> normalDynclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutProto());
JSHandle<JSHClass> asyncDynclass = JSHandle<JSHClass>::Cast(env->GetAsyncFunctionClass());
JSHandle<JSHClass> generatorDynclass = JSHandle<JSHClass>::Cast(env->GetGeneratorFunctionClass());
JSHandle<JSHClass> classDynclass = JSHandle<JSHClass>::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<JSMethod *>(FindMethods(it.first));
ASSERT(method != nullptr);
JSHandle<JSFunction> jsFunc =
factory_->NewJSFunctionByDynClass(method, classDynclass, FunctionKind::CLASS_CONSTRUCTOR);
constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue());
jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue());
JSHandle<ClassInfoExtractor> 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<JSFunction> PandaFileTranslator::DefineMethodInLiteral(JSThread *thread
jsFunc->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(length));
return jsFunc;
}
void PandaFileTranslator::DefineClassInConstPool(const JSHandle<ConstantPool> &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<ClassInfoExtractor> extractor(thread_, value);
JSHandle<TaggedArray> literal(thread_, nextValue);
ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread_, extractor, literal);
JSHandle<JSFunction> cls = ClassHelper::DefineClassTemplate(thread_, extractor, constpool);
constpool->Set(thread_, index, cls);
index += 2; // 2: pair of extractor and literal
}
}
} // namespace panda::ecmascript
@@ -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<ConstantPool> &constpool) const;
void SetMethods(Span<JSMethod> methods, const uint32_t numMethods)
{
+8 -9
View File
@@ -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)
+49
View File
@@ -19,6 +19,7 @@
#include <string>
#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<char>(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<std::pair<CString
{
vec.push_back(std::make_pair(CString("NameDictionary"), GetNameDictionary()));
}
void ClassInfoExtractor::DumpForSnapshot(JSThread *thread, std::vector<std::pair<CString, JSTaggedValue>> &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
+4
View File
@@ -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)
+2 -1
View File
@@ -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) \
+21 -15
View File
@@ -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);
}
+141 -141
View File
@@ -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<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSHClass> dynclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutName());
JSHandle<JSFunction> 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<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> cls(thread, func);
ASSERT(cls->IsJSFunction());
JSHandle<TaggedArray> literalBuffer(thread, literal);
JSMutableHandle<JSTaggedValue> parent(thread, proto);
JSHandle<JSTaggedValue> lexicalEnv(thread, lexenv);
JSHandle<ConstantPool> 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<JSTaggedValue> parentPrototype(thread, JSTaggedValue::Undefined());
// hole means parent is not present
if (parent->IsHole()) {
JSHandle<JSFunction>::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR);
parentPrototype.Update(env->GetObjectFunctionPrototype().GetTaggedValue());
parent.Update(env->GetFunctionPrototype().GetTaggedValue());
} else if (parent->IsNull()) {
JSHandle<JSFunction>::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<JSFunction>::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<JSObject> clsPrototype = JSObject::ObjectCreate(thread, JSHandle<JSObject>(parentPrototype));
bool success = true;
success = success && JSFunction::MakeClassConstructor(thread, cls, clsPrototype);
clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true);
success = success && JSObject::SetPrototype(thread, JSHandle<JSObject>::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<JSTaggedValue> propKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> 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<JSFunction>::Cast(propValue), FunctionKind::NORMAL_FUNCTION)
.GetTaggedValue());
JSHandle<JSFunction> propFunc = JSHandle<JSFunction>::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<JSTaggedValue>::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<JSFunction>::Cast(propValue), FunctionKind::NORMAL_FUNCTION)
.GetTaggedValue());
JSHandle<JSFunction> propFunc = JSHandle<JSFunction>::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<JSFunction>::Cast(cls)->GetCallTarget();
ASSERT(clsTarget != nullptr);
CString clsName = clsTarget->ParseFunctionName();
if (!clsName.empty()) {
success =
success && JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(cls),
JSHandle<JSTaggedValue>(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<JSFunction> cls(thread, ctor);
JSHandle<TaggedArray> literalBuffer(thread, literal);
JSHandle<JSTaggedValue> lexicalEnv(thread, lexenv);
JSHandle<ConstantPool> 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<JSTaggedValue> lexenvHandle(thread, lexenv);
JSHandle<JSTaggedValue> constpoolHandle(thread, JSTaggedValue(constpool));
JSHandle<JSTaggedValue> baseHandle(thread, base);
JSHandle<JSFunction> cls(thread, ctor);
JSHandle<JSObject> clsPrototype(thread, cls->GetFunctionPrototype());
bool canShareHClass = false;
if (cls->GetClass()->GetProto() == baseHandle.GetTaggedValue()) {
canShareHClass = true;
}
JSHandle<JSFunction> cloneClass = factory->CloneClassCtor(cls, lexenvHandle, canShareHClass);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> cloneClassPrototype = factory->CloneObjectLiteral(JSHandle<JSObject>(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<JSTaggedValue>(cloneClass), true, false, true);
JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(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<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> cls(thread, ctor);
ASSERT(cls->IsJSFunction());
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> parentPrototype;
// hole means parent is not present
if (parent->IsHole()) {
JSHandle<JSFunction>::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR);
parentPrototype = env->GetObjectFunctionPrototype();
parent.Update(env->GetFunctionPrototype().GetTaggedValue());
} else if (parent->IsNull()) {
JSHandle<JSFunction>::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR);
parentPrototype = JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null());
parent.Update(env->GetFunctionPrototype().GetTaggedValue());
} else if (!parent->IsConstructor()) {
return ThrowTypeError(thread, "parent class is not constructor");
} else {
JSHandle<JSFunction>::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<JSObject> clsPrototype(thread, JSHandle<JSFunction>(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
+7 -3
View File
@@ -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
+3 -25
View File
@@ -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 JSHandle<JSFunctio
func->SetFunctionExtraInfo(thread, JSTaggedValue::Undefined());
func->SetFunctionInfoFlag(thread, JSTaggedValue(flag));
ASSERT(!func->IsPropertiesDict());
auto globalConst = thread->GlobalConstants();
if (HasPrototype(kind)) {
JSHandle<JSTaggedValue> accessor = globalConst->GetHandledFunctionPrototypeAccessor();
@@ -59,9 +59,7 @@ void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandle<JSFunctio
func->SetPropertyInlinedProps(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<JSObject>(func),
globalConst->GetHandledPrototypeString(), desc);
@@ -247,21 +245,6 @@ bool JSFunction::MakeConstructor(JSThread *thread, const JSHandle<JSFunction> &f
return status;
}
// set property constructor and prototype for class
bool JSFunction::MakeClassConstructor(JSThread *thread, const JSHandle<JSTaggedValue> &cls,
const JSHandle<JSObject> &clsPrototype)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
bool status = true;
PropertyDescriptor ctorDesc(thread, cls, true, false, true);
status = status && JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>::Cast(clsPrototype),
globalConst->GetHandledConstructorString(), ctorDesc);
JSHandle<JSFunction>::Cast(cls)->SetFunctionPrototype(thread, clsPrototype.GetTaggedValue());
JSHandle<JSFunction>::Cast(cls)->SetHomeObject(thread, clsPrototype.GetTaggedValue());
return status;
}
JSTaggedValue JSFunction::Call(JSThread *thread, const JSHandle<JSTaggedValue> &func,
const JSHandle<JSTaggedValue> &thisArg, uint32_t argc, const JSTaggedType argv[])
{
@@ -378,12 +361,6 @@ JSTaggedValue JSFunction::ConstructInternal(JSThread *thread, const JSHandle<JSF
return obj.GetTaggedValue();
}
void JSFunction::MakeMethod(const JSThread *thread, const JSHandle<JSFunction> &func,
const JSHandle<JSObject> &homeObject)
{
func->SetHomeObject(thread, homeObject);
}
JSHandle<JSTaggedValue> JSFunctionBase::GetFunctionName(JSThread *thread, const JSHandle<JSFunctionBase> &func)
{
JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
@@ -602,6 +579,7 @@ JSHandle<JSHClass> JSFunction::GetOrCreateDerivedJSHClass(JSThread *thread, JSHa
JSHandle<JSHClass> newJSHClass = JSHClass::Clone(thread, ctorInitialJSHClass);
// guarante derived has function prototype
JSHandle<JSTaggedValue> prototype(thread, derived->GetProtoOrDynClass());
ASSERT(!prototype->IsHole());
newJSHClass->SetPrototype(thread, prototype);
derived->SetProtoOrDynClass(thread, newJSHClass);
return newJSHClass;
+13 -19
View File
@@ -94,10 +94,6 @@ public:
static bool AddRestrictedFunctionProperties(const JSHandle<JSFunction> &func, const JSHandle<JSTaggedValue> &realm);
static bool MakeConstructor(JSThread *thread, const JSHandle<JSFunction> &func,
const JSHandle<JSTaggedValue> &proto, bool writable = true);
static bool MakeClassConstructor(JSThread *thread, const JSHandle<JSTaggedValue> &cls,
const JSHandle<JSObject> &clsPrototype);
static void MakeMethod(const JSThread *thread, const JSHandle<JSFunction> &func,
const JSHandle<JSObject> &homeObject);
static bool SetFunctionLength(JSThread *thread, const JSHandle<JSFunction> &func, JSTaggedValue length,
bool cfg = true);
static JSHandle<JSObject> 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<JSFunction> &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<FunctionKind, 0, FUNCTION_KIND_BIT_NUM>;
using StrictBit = FunctionKindBit::NextFlag;
using ClassConstructorBit = StrictBit::NextFlag;
using ResolvedBit = ClassConstructorBit::NextFlag;
using ResolvedBit = StrictBit::NextFlag;
using ThisModeBit = ResolvedBit::NextField<FunctionMode, 2>; // 2: means this flag occupies two digits.
DECL_DUMP()
-3
View File
@@ -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
+1
View File
@@ -287,6 +287,7 @@ void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto)
}
SetProto(thread, proto);
}
void JSHClass::SetPrototype(const JSThread *thread, const JSHandle<JSTaggedValue> &proto)
{
SetPrototype(thread, proto.GetTaggedValue());
+7 -1
View File
@@ -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;
+5 -2
View File
@@ -1757,11 +1757,14 @@ JSHandle<JSForInIterator> JSObject::EnumerateObjectProperties(JSThread *thread,
}
void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &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);
}
+2 -1
View File
@@ -552,7 +552,8 @@ public:
bool IsTypedArray() const;
static void DefinePropertyByLiteral(JSThread *thread, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value);
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
bool useForClass = false);
static void DefineSetter(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value);
static void DefineGetter(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
+5
View File
@@ -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());
+1
View File
@@ -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<JSTaggedValue> &x,
+4
View File
@@ -17,6 +17,7 @@
#define ECMASCRIPT_MEM_HEAP_ROOTS_INL_H
#include <cstdint>
#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();
}
+9 -4
View File
@@ -856,6 +856,12 @@ Local<FunctionRef> FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallbackWit
vm->GetMethodForNativeFunction(reinterpret_cast<void *>(Callback::RegisterCallbackWithNewTarget));
JSHandle<JSFunction> current =
factory->NewJSFunctionByDynClass(method, dynclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR);
auto globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> accessor = globalConst->GetHandledFunctionPrototypeAccessor();
current->SetPropertyInlinedProps(thread, JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX,
accessor.GetTaggedValue());
JSHandle<JSNativePointer> funcCallback = factory->NewJSNativePointer(reinterpret_cast<void *>(nativeFunc));
JSHandle<JSNativePointer> dataCaddress(thread, JSTaggedValue::Undefined());
if (deleter == nullptr) {
@@ -867,14 +873,13 @@ Local<FunctionRef> FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallbackWit
JSHandle<JSFunctionExtraInfo> extraInfo(factory->NewFunctionExtraInfo(funcCallback, dataCaddress));
current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue());
JSHandle<JSObject> clsPrototype =
JSObject::ObjectCreate(thread, JSHandle<JSObject>(env->GetObjectFunctionPrototype()));
JSHandle<JSObject> clsPrototype = JSFunction::NewJSFunctionPrototype(thread, factory, current);
clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true);
JSHandle<JSTaggedValue>::Cast(current)->GetTaggedObject()->GetClass()->SetClassConstructor(true);
current->SetClassConstructor(thread, true);
current->SetClassConstructor(true);
JSHandle<JSTaggedValue> parent = env->GetFunctionPrototype();
JSObject::SetPrototype(thread, JSHandle<JSObject>::Cast(current), parent);
JSFunction::MakeClassConstructor(thread, JSHandle<JSTaggedValue>::Cast(current), clsPrototype);
current->SetHomeObject(thread, clsPrototype);
return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}
+13
View File
@@ -693,4 +693,17 @@ HWTEST_F_L0(JSNApiTests, InheritPrototype)
JSHandle<JSFunction> son1Handle = JSHandle<JSFunction>::Cast(JSNApiHelper::ToJSHandle(son1));
ASSERT_TRUE(son1Handle->HasFunctionPrototype());
}
HWTEST_F_L0(JSNApiTests, ClassFunction)
{
LocalScope scope(vm_);
Local<FunctionRef> cls = FunctionRef::NewClassFunction(vm_, nullptr, nullptr, nullptr);
JSHandle<JSTaggedValue> clsObj = JSNApiHelper::ToJSHandle(Local<JSValueRef>(cls));
ASSERT_TRUE(clsObj->IsClassConstructor());
JSTaggedValue accessor = JSHandle<JSFunction>(clsObj)->GetPropertyInlinedProps(
JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX);
ASSERT_TRUE(accessor.IsInternalAccessor());
}
} // namespace panda::test
+69 -1
View File
@@ -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<GlobalEnv> &
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<TaggedArray> ObjectFactory::CloneProperties(const JSHandle<TaggedArray>
}
JSHandle<JSObject> ObjectFactory::CloneObjectLiteral(JSHandle<JSObject> object, const JSHandle<JSTaggedValue> &env,
const JSHandle<JSTaggedValue> &constpool)
const JSHandle<JSTaggedValue> &constpool, bool canShareHClass)
{
NewObjectHook();
auto klass = JSHandle<JSHClass>(thread_, object->GetClass());
if (!canShareHClass) {
klass = JSHClass::Clone(thread_, klass);
}
JSHandle<JSObject> cloneObject = NewJSObject(klass);
JSHandle<TaggedArray> elements(thread_, object->GetElements());
@@ -444,6 +450,49 @@ JSHandle<JSFunction> ObjectFactory::CloneJSFuction(JSHandle<JSFunction> obj, Fun
return cloneFunc;
}
JSHandle<JSFunction> ObjectFactory::CloneClassCtor(JSHandle<JSFunction> ctor, const JSHandle<JSTaggedValue> &lexenv,
bool canShareHClass)
{
NewObjectHook();
JSHandle<JSTaggedValue> constpool(thread_, ctor->GetConstantPool());
JSHandle<JSHClass> 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<JSFunction> 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<JSFunction> valueHandle(thread_, value);
JSHandle<JSFunction> newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind());
newFunc->SetLexicalEnv(thread_, lexenv);
newFunc->SetHomeObject(thread_, cloneCtor);
newFunc->SetConstantPool(thread_, constpool);
cloneCtor->SetPropertyInlinedProps(thread_, i, newFunc.GetTaggedValue());
}
}
JSHandle<TaggedArray> elements(thread_, ctor->GetElements());
auto newElements = CloneProperties(elements, lexenv, JSHandle<JSObject>(cloneCtor), constpool);
cloneCtor->SetElements(thread_, newElements.GetTaggedValue());
JSHandle<TaggedArray> properties(thread_, ctor->GetProperties());
auto newProperties = CloneProperties(properties, lexenv, JSHandle<JSObject>(cloneCtor), constpool);
cloneCtor->SetProperties(thread_, newProperties.GetTaggedValue());
cloneCtor->SetConstantPool(thread_, constpool);
return cloneCtor;
}
JSHandle<JSObject> ObjectFactory::NewNonMovableJSObject(const JSHandle<JSHClass> &jshclass)
{
JSHandle<JSObject> obj(thread_,
@@ -1989,6 +2038,25 @@ JSHandle<MachineCode> ObjectFactory::NewMachineCodeObject(size_t length, const u
return codeObj;
}
JSHandle<ClassInfoExtractor> ObjectFactory::NewClassInfoExtractor(JSMethod *ctorMethod)
{
NewObjectHook();
TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(classInfoExtractorHClass_);
JSHandle<ClassInfoExtractor> obj(thread_, header);
obj->InitializeBitField();
obj->SetConstructorMethod(ctorMethod);
JSHandle<TaggedArray> 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<EcmaString> ObjectFactory::NewFromString(const CString &data)
{
+7 -1
View File
@@ -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<JSPromiseAllResolveElementFunction> NewJSPromiseAllResolveElementFunction(const void *nativeFunc);
JSHandle<JSObject> CloneObjectLiteral(JSHandle<JSObject> object, const JSHandle<JSTaggedValue> &env,
const JSHandle<JSTaggedValue> &constpool);
const JSHandle<JSTaggedValue> &constpool, bool canShareHClass = true);
JSHandle<JSObject> CloneObjectLiteral(JSHandle<JSObject> object);
JSHandle<JSArray> CloneArrayLiteral(JSHandle<JSArray> object);
JSHandle<JSFunction> CloneJSFuction(JSHandle<JSFunction> obj, FunctionKind kind);
JSHandle<JSFunction> CloneClassCtor(JSHandle<JSFunction> ctor, const JSHandle<JSTaggedValue> &lexenv,
bool canShareHClass);
void NewJSArrayBufferData(const JSHandle<JSArrayBuffer> &array, int32_t length);
@@ -328,6 +331,7 @@ public:
uintptr_t NewSpaceBySnapShotAllocator(size_t size);
JSHandle<MachineCode> NewMachineCodeObject(size_t length, const uint8_t *data);
JSHandle<ClassInfoExtractor> 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 {
+7
View File
@@ -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> classInfoExtractor = factory->NewClassInfoExtractor(nullptr);
DUMP_FOR_HANDLE(classInfoExtractor)
break;
}
default:
LOG_ECMA_MEM(ERROR) << "JSType " << static_cast<int>(type) << " cannot be dumped.";
UNREACHABLE();
+1 -1
View File
@@ -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()