mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-07 08:03:29 +00:00
f97fdcfd6b
Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I9RIJP Signed-off-by: weng-xi <wengxi1@huawei.com> Change-Id: If2e5a6adc59b3214f835c34c0b6e874bf9964ae9
1124 lines
56 KiB
C++
1124 lines
56 KiB
C++
/*
|
|
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "ecmascript/jspandafile/class_info_extractor.h"
|
|
#include "ecmascript/global_env.h"
|
|
#include "ecmascript/js_function.h"
|
|
#include "ecmascript/jspandafile/program_object.h"
|
|
#include "ecmascript/jspandafile/method_literal.h"
|
|
#include "ecmascript/shared_objects/js_sendable_arraybuffer.h"
|
|
#include "ecmascript/shared_objects/js_shared_array.h"
|
|
#include "ecmascript/shared_objects/js_shared_map.h"
|
|
#include "ecmascript/shared_objects/js_shared_set.h"
|
|
#include "ecmascript/shared_objects/js_shared_typed_array.h"
|
|
#include "ecmascript/tagged_dictionary.h"
|
|
|
|
namespace panda::ecmascript {
|
|
void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle<ClassInfoExtractor> &extractor,
|
|
const JSHandle<TaggedArray> &literal,
|
|
uint32_t length,
|
|
ClassKind kind)
|
|
{
|
|
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
|
|
ASSERT(length <= literal->GetLength());
|
|
// non static properties number is hidden in the last index of Literal buffer
|
|
uint32_t nonStaticNum = 0;
|
|
if (length != 0) {
|
|
nonStaticNum = static_cast<uint32_t>(literal->Get(thread, length - 1).GetInt());
|
|
}
|
|
|
|
// Reserve sufficient length to prevent frequent creation.
|
|
JSHandle<TaggedArray> nonStaticKeys;
|
|
JSHandle<TaggedArray> nonStaticProperties;
|
|
factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
|
|
if (kind == ClassKind::SENDABLE) {
|
|
nonStaticKeys = factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
|
|
nonStaticProperties =
|
|
factory->NewSOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
|
|
} else {
|
|
nonStaticKeys = factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
|
|
nonStaticProperties =
|
|
factory->NewOldSpaceTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH);
|
|
}
|
|
|
|
nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString());
|
|
Method *method = Method::Cast(extractor->GetConstructorMethod().GetTaggedObject());
|
|
MethodLiteral *methodLiteral = method->GetMethodLiteral();
|
|
const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
|
|
EntityId methodId = method->GetMethodId();
|
|
if (nonStaticNum) {
|
|
ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr};
|
|
|
|
JSHandle<TaggedArray> nonStaticElements = factory->EmptyArray();
|
|
if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys,
|
|
nonStaticProperties, nonStaticElements, jsPandaFile))) {
|
|
extractor->SetNonStaticWithElements(true);
|
|
extractor->SetNonStaticElements(thread, nonStaticElements);
|
|
}
|
|
}
|
|
|
|
extractor->SetNonStaticKeys(thread, nonStaticKeys);
|
|
extractor->SetNonStaticProperties(thread, nonStaticProperties);
|
|
|
|
uint32_t staticNum = length == 0 ? 0 : (length - 1) / 2 - nonStaticNum;
|
|
|
|
// Reserve sufficient length to prevent frequent creation.
|
|
JSHandle<TaggedArray> staticKeys;
|
|
JSHandle<TaggedArray> staticProperties;
|
|
if (kind == ClassKind::SENDABLE) {
|
|
staticKeys = factory->NewSOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
|
|
staticProperties = factory->NewSOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
|
|
} else {
|
|
staticKeys = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
|
|
staticProperties = factory->NewOldSpaceTaggedArray(staticNum + STATIC_RESERVED_LENGTH);
|
|
}
|
|
|
|
staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString());
|
|
staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString());
|
|
staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString());
|
|
|
|
JSHandle<TaggedArray> staticElements = factory->EmptyArray();
|
|
|
|
if (staticNum) {
|
|
ExtractContentsDetail staticDetail {
|
|
nonStaticNum * 2,
|
|
length - 1,
|
|
STATIC_RESERVED_LENGTH,
|
|
methodLiteral
|
|
};
|
|
if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys,
|
|
staticProperties, staticElements, jsPandaFile))) {
|
|
extractor->SetStaticWithElements(true);
|
|
extractor->SetStaticElements(thread, staticElements);
|
|
}
|
|
} else {
|
|
// without static properties, set class name
|
|
std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
|
|
JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
|
|
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);
|
|
}
|
|
|
|
bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle<TaggedArray> &literal,
|
|
const ExtractContentsDetail &detail,
|
|
JSHandle<TaggedArray> &keys,
|
|
JSHandle<TaggedArray> &properties,
|
|
JSHandle<TaggedArray> &elements,
|
|
const JSPandaFile *jsPandaFile)
|
|
{
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
|
|
ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0);
|
|
|
|
uint32_t pos = detail.fillStartLoc;
|
|
bool withElementsFlag = false;
|
|
bool isStaticFlag = (detail.methodLiteral != nullptr);
|
|
bool keysHasNameFlag = false;
|
|
|
|
JSHandle<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::SetCapacityInOldSpace(thread, elements, elementsLength + 2); // 2: key-value pair
|
|
elements->Set(thread, elementsLength, firstValue);
|
|
elements->Set(thread, elementsLength + 1, secondValue);
|
|
withElementsFlag = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
keys->Set(thread, pos, firstValue);
|
|
properties->Set(thread, pos, secondValue);
|
|
pos++;
|
|
}
|
|
|
|
if (isStaticFlag) {
|
|
if (LIKELY(!keysHasNameFlag)) {
|
|
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
EntityId methodId = detail.methodLiteral->GetMethodId();
|
|
std::string clsName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
|
|
JSHandle<EcmaString> clsNameHandle = factory->NewFromStdString(clsName);
|
|
properties->Set(thread, NAME_INDEX, clsNameHandle);
|
|
} else {
|
|
// class has static name property, reserved length bigger 1 than actual, need trim
|
|
uint32_t trimOneLength = keys->GetLength() - 1;
|
|
keys->Trim(thread, trimOneLength);
|
|
properties->Trim(thread, trimOneLength);
|
|
}
|
|
}
|
|
|
|
if (UNLIKELY(withElementsFlag)) {
|
|
ASSERT(pos + elements->GetLength() / 2 == properties->GetLength()); // 2: half
|
|
keys->Trim(thread, pos);
|
|
properties->Trim(thread, pos);
|
|
}
|
|
|
|
return withElementsFlag;
|
|
}
|
|
|
|
JSHandle<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_FAST_PROPS_CAPACITY)) {
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
|
|
for (uint32_t index = 0; index < length; ++index) {
|
|
key.Update(keys->Get(index));
|
|
ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
|
|
PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); // non-enumerable
|
|
|
|
if (UNLIKELY(properties->Get(index).IsAccessor())) {
|
|
attributes.SetIsAccessor(true);
|
|
}
|
|
|
|
attributes.SetIsInlinedProps(true);
|
|
attributes.SetRepresentation(Representation::TAGGED);
|
|
attributes.SetOffset(index);
|
|
layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
|
|
}
|
|
|
|
hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, length);
|
|
// Not need set proto here
|
|
hclass->SetLayout(thread, layout);
|
|
hclass->SetNumberOfProps(length);
|
|
} else {
|
|
// dictionary mode
|
|
hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0); // without in-obj
|
|
hclass->SetIsDictionaryMode(true);
|
|
hclass->SetNumberOfProps(0);
|
|
}
|
|
|
|
hclass->SetClassPrototype(true);
|
|
hclass->SetIsPrototype(true);
|
|
return hclass;
|
|
}
|
|
|
|
JSHandle<JSHClass> ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
|
|
JSHandle<TaggedArray> &keys,
|
|
JSHandle<TaggedArray> &properties)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
|
|
uint32_t length = keys->GetLength();
|
|
if (!thread->GetEcmaVM()->IsEnablePGOProfiler()) {
|
|
// The class constructor of AOT is not shared, and PGO collect cannot be shared.
|
|
if (length == ClassInfoExtractor::STATIC_RESERVED_LENGTH && base->IsHole() &&
|
|
properties->Get(NAME_INDEX).IsString()) {
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
return JSHandle<JSHClass>(globalConst->GetHandledClassConstructorClass());
|
|
}
|
|
}
|
|
JSHandle<JSHClass> hclass;
|
|
if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
|
|
for (uint32_t index = 0; index < length; ++index) {
|
|
key.Update(keys->Get(index));
|
|
ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
|
|
PropertyAttributes attributes;
|
|
switch (index) {
|
|
case LENGTH_INDEX:
|
|
attributes = PropertyAttributes::Default(false, false, true);
|
|
break;
|
|
case NAME_INDEX:
|
|
if (LIKELY(properties->Get(NAME_INDEX).IsString())) {
|
|
attributes = PropertyAttributes::Default(false, false, true);
|
|
} else {
|
|
ASSERT(properties->Get(NAME_INDEX).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::TAGGED);
|
|
attributes.SetOffset(index);
|
|
layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
|
|
}
|
|
|
|
hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, length);
|
|
// Not need set proto here
|
|
hclass->SetLayout(thread, layout);
|
|
hclass->SetNumberOfProps(length);
|
|
} else {
|
|
// dictionary mode
|
|
hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0); // without in-obj
|
|
hclass->SetIsDictionaryMode(true);
|
|
hclass->SetNumberOfProps(0);
|
|
}
|
|
|
|
hclass->SetClassConstructor(true);
|
|
hclass->SetConstructor(true);
|
|
|
|
return hclass;
|
|
}
|
|
|
|
void ClassInfoExtractor::CorrectConstructorHClass(JSThread *thread,
|
|
JSHandle<TaggedArray> &properties,
|
|
JSHClass *constructorHClass)
|
|
{
|
|
if (LIKELY(!constructorHClass->IsDictionaryMode())) {
|
|
JSHandle<LayoutInfo> layout(thread, constructorHClass->GetLayout());
|
|
for (uint32_t index = 0; index < ClassInfoExtractor::STATIC_RESERVED_LENGTH; ++index) {
|
|
switch (index) {
|
|
case NAME_INDEX:
|
|
if (UNLIKELY(properties->Get(NAME_INDEX).IsJSFunction())) {
|
|
PropertyAttributes attr = layout->GetAttr(index);
|
|
attr.SetWritable(true);
|
|
layout->SetNormalAttr(thread, index, attr);
|
|
}
|
|
if (UNLIKELY(properties->Get(index).IsAccessor())) {
|
|
PropertyAttributes attr = layout->GetAttr(index);
|
|
attr.SetIsAccessor(true);
|
|
layout->SetNormalAttr(thread, index, attr);
|
|
}
|
|
break;
|
|
default:
|
|
if (UNLIKELY(properties->Get(index).IsAccessor())) {
|
|
PropertyAttributes attr = layout->GetAttr(index);
|
|
attr.SetIsAccessor(true);
|
|
layout->SetNormalAttr(thread, index, attr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
JSHandle<JSHClass> ClassInfoExtractor::CreateSendableHClass(JSThread *thread, JSHandle<TaggedArray> &keys,
|
|
JSHandle<TaggedArray> &properties, bool isProtoClass,
|
|
uint32_t extraLength)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
uint32_t length = keys->GetLength();
|
|
JSHandle<JSHClass> hclass;
|
|
uint32_t maxInline = isProtoClass ? JSSharedObject::MAX_INLINE : JSSharedFunction::MAX_INLINE;
|
|
if (LIKELY(length + extraLength <= maxInline)) {
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(length + extraLength);
|
|
for (uint32_t index = 0; index < length; ++index) {
|
|
key.Update(keys->Get(index));
|
|
ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
|
|
PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
|
|
if (UNLIKELY(properties->Get(index).IsAccessor())) {
|
|
attributes.SetIsAccessor(true);
|
|
}
|
|
attributes.SetIsInlinedProps(true);
|
|
attributes.SetRepresentation(Representation::TAGGED);
|
|
attributes.SetOffset(index);
|
|
layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
|
|
}
|
|
hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length) :
|
|
factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, length + extraLength);
|
|
hclass->SetLayout(thread, layout);
|
|
hclass->SetNumberOfProps(length);
|
|
} else {
|
|
// dictionary mode
|
|
hclass = isProtoClass ? factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0) :
|
|
factory->NewSEcmaHClass(JSSharedFunction::SIZE, JSType::JS_SHARED_FUNCTION, 0);
|
|
hclass->SetIsDictionaryMode(true);
|
|
hclass->SetNumberOfProps(0);
|
|
}
|
|
if (isProtoClass) {
|
|
hclass->SetClassPrototype(true);
|
|
hclass->SetIsPrototype(true);
|
|
} else {
|
|
hclass->SetClassConstructor(true);
|
|
hclass->SetConstructor(true);
|
|
}
|
|
return hclass;
|
|
}
|
|
|
|
JSHandle<JSFunction> ClassHelper::DefineClassFromExtractor(JSThread *thread, const JSHandle<JSTaggedValue> &base,
|
|
JSHandle<ClassInfoExtractor> &extractor,
|
|
const JSHandle<JSTaggedValue> &lexenv)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
|
|
JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
|
|
|
|
JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
|
|
JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
|
|
JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
|
|
nonStaticProperties);
|
|
|
|
JSHandle<JSObject> prototype = factory->NewOldSpaceJSObject(prototypeHClass);
|
|
JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
|
|
JSHandle<JSHClass> constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys,
|
|
staticProperties);
|
|
// Allocate to non-movable space for PGO
|
|
JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
|
|
MemSpaceType::NON_MOVABLE);
|
|
|
|
// non-static
|
|
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 = factory->CloneJSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, prototype);
|
|
propFunc->SetLexicalEnv(thread, lexenv);
|
|
propValue.Update(propFunc);
|
|
}
|
|
prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
|
|
ClassPropertyType::NON_STATIC, lexenv);
|
|
prototype->SetProperties(thread, dict);
|
|
}
|
|
|
|
// non-static elements
|
|
if (UNLIKELY(extractor->GetNonStaticWithElements())) {
|
|
JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
|
|
ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements);
|
|
}
|
|
|
|
// static
|
|
uint32_t staticLength = staticProperties->GetLength();
|
|
|
|
if (LIKELY(!constructorHClass->IsDictionaryMode())) {
|
|
for (uint32_t index = 0; index < staticLength; ++index) {
|
|
propValue.Update(staticProperties->Get(index));
|
|
if (propValue->IsJSFunction()) {
|
|
JSHandle<JSFunction> propFunc = factory->CloneJSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, constructor);
|
|
propFunc->SetLexicalEnv(thread, lexenv);
|
|
propValue.Update(propFunc);
|
|
}
|
|
JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
|
|
staticProperties, ClassPropertyType::STATIC, lexenv);
|
|
constructor->SetProperties(thread, dict);
|
|
}
|
|
|
|
// static elements
|
|
if (UNLIKELY(extractor->GetStaticWithElements())) {
|
|
JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
|
|
ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements);
|
|
}
|
|
|
|
PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
|
|
globalConst->GetHandledConstructorString(), ctorDesc);
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
|
|
constructor->SetHomeObject(thread, prototype);
|
|
constructor->SetProtoOrHClass(thread, prototype);
|
|
if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
|
|
thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
|
|
}
|
|
return constructor;
|
|
}
|
|
|
|
JSHandle<JSFunction> ClassHelper::DefineClassWithIHClass(JSThread *thread, const JSHandle<JSTaggedValue> &base,
|
|
JSHandle<ClassInfoExtractor> &extractor,
|
|
const JSHandle<JSTaggedValue> &lexenv,
|
|
const JSHandle<JSTaggedValue> &prototypeOrHClassVal,
|
|
const JSHandle<JSTaggedValue> &constructorHClassVal)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
|
|
JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
|
|
JSHandle<JSHClass> constructorHClass;
|
|
// When constructorHClassVal is undefined, it means that AOT has not generated the corresponding hclass (chc),
|
|
// then chc will be created through the interpreter.
|
|
if (constructorHClassVal->IsUndefined()) {
|
|
constructorHClass = ClassInfoExtractor::CreateConstructorHClass(thread, base, staticKeys, staticProperties);
|
|
} else {
|
|
constructorHClass = JSHandle<JSHClass>(constructorHClassVal);
|
|
ClassInfoExtractor::CorrectConstructorHClass(thread, staticProperties, *constructorHClass);
|
|
}
|
|
|
|
JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
|
|
JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
|
|
JSHandle<JSObject> prototype;
|
|
JSHandle<JSTaggedValue> prototypeOrHClass = prototypeOrHClassVal;
|
|
// When prototypeOrHClassVal is undefined, it means that AOT has not generated the corresponding hclass or
|
|
// prototype, then prototype will be created through the interpreter.
|
|
if (prototypeOrHClassVal->IsUndefined()) {
|
|
JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreatePrototypeHClass(thread, nonStaticKeys,
|
|
nonStaticProperties);
|
|
prototype = factory->NewOldSpaceJSObject(prototypeHClass);
|
|
prototypeOrHClass = JSHandle<JSTaggedValue>(prototype);
|
|
} else if (prototypeOrHClassVal->IsJSHClass()) {
|
|
JSHandle<JSHClass> ihclass(prototypeOrHClassVal);
|
|
prototype = JSHandle<JSObject>(thread, ihclass->GetProto());
|
|
} else {
|
|
prototype = JSHandle<JSObject>(prototypeOrHClassVal);
|
|
}
|
|
|
|
JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
|
|
JSHandle<JSFunction> constructor = factory->NewJSFunctionByHClass(method, constructorHClass,
|
|
MemSpaceType::NON_MOVABLE);
|
|
|
|
// non-static
|
|
nonStaticProperties->Set(thread, 0, constructor);
|
|
|
|
uint32_t nonStaticLength = nonStaticProperties->GetLength();
|
|
JSMutableHandle<JSTaggedValue> propValue(thread, JSTaggedValue::Undefined());
|
|
|
|
if (LIKELY(!prototype->GetJSHClass()->IsDictionaryMode())) {
|
|
for (uint32_t index = 0; index < nonStaticLength; ++index) {
|
|
propValue.Update(nonStaticProperties->Get(index));
|
|
if (propValue->IsJSFunction()) {
|
|
JSHandle<JSFunction> propFunc = factory->CloneJSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, prototype);
|
|
propFunc->SetLexicalEnv(thread, lexenv);
|
|
propValue.Update(propFunc);
|
|
}
|
|
prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, prototype, nonStaticKeys, nonStaticProperties,
|
|
ClassPropertyType::NON_STATIC, lexenv);
|
|
prototype->SetProperties(thread, dict);
|
|
}
|
|
|
|
// non-static elements
|
|
if (UNLIKELY(extractor->GetNonStaticWithElements())) {
|
|
JSHandle<TaggedArray> nonStaticElements(thread, extractor->GetNonStaticElements());
|
|
ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements);
|
|
}
|
|
|
|
// static
|
|
uint32_t staticLength = staticProperties->GetLength();
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
int correntIndex = 0;
|
|
if (LIKELY(!constructorHClass->IsDictionaryMode())) {
|
|
for (uint32_t index = 0; index < staticLength; ++index) {
|
|
propValue.Update(staticProperties->Get(index));
|
|
if (propValue->IsJSFunction()) {
|
|
JSHandle<JSFunction> propFunc = factory->CloneJSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, constructor);
|
|
propFunc->SetLexicalEnv(thread, lexenv);
|
|
propValue.Update(propFunc);
|
|
}
|
|
bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH;
|
|
if (needCorrentIndex) {
|
|
key.Update(staticKeys->Get(index));
|
|
correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue());
|
|
}
|
|
JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread,
|
|
needCorrentIndex ? static_cast<uint32_t>(correntIndex) : index, propValue.GetTaggedValue());
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> dict = BuildDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
|
|
staticProperties, ClassPropertyType::STATIC, lexenv);
|
|
constructor->SetProperties(thread, dict);
|
|
}
|
|
|
|
// static elements
|
|
if (UNLIKELY(extractor->GetStaticWithElements())) {
|
|
JSHandle<TaggedArray> staticElements(thread, extractor->GetStaticElements());
|
|
ClassHelper::HandleElementsProperties(thread, JSHandle<JSObject>(constructor), staticElements);
|
|
}
|
|
|
|
PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(constructor), true, false, true);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
|
|
globalConst->GetHandledConstructorString(), ctorDesc);
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread);
|
|
constructor->SetHomeObject(thread, prototype);
|
|
constructor->SetProtoOrHClass(thread, prototypeOrHClass);
|
|
|
|
if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
|
|
thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType());
|
|
}
|
|
return constructor;
|
|
}
|
|
|
|
JSHandle<NameDictionary> ClassHelper::BuildDictionaryProperties(JSThread *thread, const JSHandle<JSObject> &object,
|
|
JSHandle<TaggedArray> &keys,
|
|
JSHandle<TaggedArray> &properties,
|
|
ClassPropertyType type,
|
|
const JSHandle<JSTaggedValue> &lexenv)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
uint32_t length = keys->GetLength();
|
|
ASSERT(length > PropertyAttributes::MAX_FAST_PROPS_CAPACITY);
|
|
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 = factory->CloneJSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, object);
|
|
propFunc->SetLexicalEnv(thread, lexenv);
|
|
propValue.Update(propFunc);
|
|
}
|
|
JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
|
|
dict.Update(newDict);
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
bool ClassHelper::MatchFieldType(SharedFieldType fieldType, JSTaggedValue value)
|
|
{
|
|
uint32_t sharedFieldType = static_cast<uint32_t>(fieldType);
|
|
if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::NUMBER)) != 0 && value.IsNumber()) {
|
|
return true;
|
|
} else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::BOOLEAN)) != 0 && value.IsBoolean()) {
|
|
return true;
|
|
} else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::STRING)) != 0 &&
|
|
(value.IsString() || value.IsNull())) {
|
|
return true;
|
|
} else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::BIG_INT)) != 0 && value.IsBigInt()) {
|
|
return true;
|
|
} else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::SENDABLE)) != 0 &&
|
|
(value.IsJSShared() || value.IsNull())) {
|
|
return true;
|
|
} else if ((sharedFieldType == static_cast<uint32_t>(SharedFieldType::NONE) ||
|
|
(sharedFieldType & static_cast<uint32_t>(SharedFieldType::GENERIC)) != 0) &&
|
|
(value.IsJSShared() || !value.IsHeapObject())) {
|
|
// (none || generic) && (jsShared || !heapObject)
|
|
return true;
|
|
} else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::NULL_TYPE)) != 0 && value.IsNull()) {
|
|
return true;
|
|
} else if ((sharedFieldType & static_cast<uint32_t>(SharedFieldType::UNDEFINED)) != 0 && value.IsUndefined()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
|
|
JSHandle<TaggedArray> &elements)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
JSHandle<JSFunction> SendableClassDefiner::DefineSendableClassFromExtractor(JSThread *thread,
|
|
JSHandle<ClassInfoExtractor> &extractor, const JSHandle<TaggedArray> &staticFieldArray)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
JSHandle<TaggedArray> staticKeys(thread, extractor->GetStaticKeys());
|
|
JSHandle<TaggedArray> staticProperties(thread, extractor->GetStaticProperties());
|
|
SendableClassDefiner::FilterDuplicatedKeys(thread, staticKeys, staticProperties);
|
|
|
|
JSHandle<TaggedArray> nonStaticKeys(thread, extractor->GetNonStaticKeys());
|
|
JSHandle<TaggedArray> nonStaticProperties(thread, extractor->GetNonStaticProperties());
|
|
SendableClassDefiner::FilterDuplicatedKeys(thread, nonStaticKeys, nonStaticProperties);
|
|
JSHandle<JSHClass> prototypeHClass = ClassInfoExtractor::CreateSendableHClass(thread, nonStaticKeys,
|
|
nonStaticProperties, true);
|
|
JSHandle<JSObject> prototype = factory->NewSharedOldSpaceJSObject(prototypeHClass);
|
|
uint32_t length = staticFieldArray->GetLength();
|
|
uint32_t staticFields = length / 2; // 2: key-type
|
|
JSHandle<JSHClass> constructorHClass =
|
|
ClassInfoExtractor::CreateSendableHClass(thread, staticKeys, staticProperties, false, staticFields);
|
|
JSHandle<Method> method(thread, Method::Cast(extractor->GetConstructorMethod().GetTaggedObject()));
|
|
method->SetFunctionKind(FunctionKind::CLASS_CONSTRUCTOR);
|
|
if (!constructorHClass->IsDictionaryMode() && staticFields > 0) {
|
|
auto layout = JSHandle<LayoutInfo>(thread, constructorHClass->GetLayout());
|
|
AddFieldTypeToHClass(thread, staticFieldArray, length, layout, constructorHClass);
|
|
}
|
|
|
|
JSHandle<JSFunction> constructor = factory->NewSFunctionByHClass(method, constructorHClass);
|
|
|
|
// non-static
|
|
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));
|
|
// constructor don't need to clone
|
|
if (propValue->IsJSFunction() && index != ClassInfoExtractor::CONSTRUCTOR_INDEX) {
|
|
JSHandle<JSFunction> propFunc = factory->CloneSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, prototype);
|
|
propFunc->SetLexicalEnv(thread, constructor);
|
|
ASSERT(!propFunc->GetClass()->IsExtensible());
|
|
propValue.Update(propFunc);
|
|
} else if (propValue->IsAccessorData()) {
|
|
UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(prototype), constructor);
|
|
}
|
|
prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> dict = BuildSendableDictionaryProperties(thread, prototype, nonStaticKeys,
|
|
nonStaticProperties, ClassPropertyType::NON_STATIC, constructor);
|
|
prototype->SetProperties(thread, dict);
|
|
}
|
|
|
|
// non-static elements
|
|
if (UNLIKELY(extractor->GetNonStaticWithElements())) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support members with numerical key",
|
|
JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
|
|
}
|
|
|
|
// static
|
|
uint32_t staticLength = staticProperties->GetLength();
|
|
if (LIKELY(!constructorHClass->IsDictionaryMode())) {
|
|
for (uint32_t index = 0; index < staticLength; ++index) {
|
|
propValue.Update(staticProperties->Get(index));
|
|
if (propValue->IsJSFunction()) {
|
|
JSHandle<JSFunction> propFunc = factory->CloneSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, constructor);
|
|
propFunc->SetLexicalEnv(thread, constructor);
|
|
ASSERT(!propFunc->GetClass()->IsExtensible());
|
|
propValue.Update(propFunc);
|
|
} else if (propValue->IsAccessorData()) {
|
|
UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(constructor), constructor);
|
|
}
|
|
JSHandle<JSObject>::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue());
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> dict =
|
|
BuildSendableDictionaryProperties(thread, JSHandle<JSObject>(constructor), staticKeys,
|
|
staticProperties, ClassPropertyType::STATIC, constructor);
|
|
JSMutableHandle<NameDictionary> nameDict(thread, dict);
|
|
if (staticFields > 0) {
|
|
AddFieldTypeToDict(thread, staticFieldArray, length, nameDict,
|
|
PropertyAttributes::Default(true, true, false));
|
|
}
|
|
constructor->SetProperties(thread, nameDict);
|
|
}
|
|
|
|
// static elements
|
|
if (UNLIKELY(extractor->GetStaticWithElements())) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "Concurrent class don't support static members with numerical key",
|
|
JSHandle<JSFunction>(thread, JSTaggedValue::Exception()));
|
|
}
|
|
prototype->GetJSHClass()->SetExtensible(false);
|
|
constructor->SetHomeObject(thread, prototype);
|
|
constructor->SetProtoOrHClass(thread, prototype);
|
|
constructor->SetLexicalEnv(thread, constructor);
|
|
return constructor;
|
|
}
|
|
|
|
// Process duplicated key due to getter/setter.
|
|
void SendableClassDefiner::FilterDuplicatedKeys(JSThread *thread, const JSHandle<TaggedArray> &keys,
|
|
const JSHandle<TaggedArray> &properties)
|
|
{
|
|
auto attr = PropertyAttributes::Default();
|
|
uint32_t length = keys->GetLength();
|
|
uint32_t left = 0;
|
|
uint32_t right = 0;
|
|
JSMutableHandle<NameDictionary> dict(
|
|
thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length)));
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> existValue(thread, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> index(thread, JSTaggedValue::Undefined());
|
|
for (; right < length; right++) {
|
|
key.Update(keys->Get(right));
|
|
value.Update(properties->Get(right));
|
|
int entry = dict->FindEntry(key.GetTaggedValue());
|
|
if (entry == -1) {
|
|
TryUpdateValue(thread, value);
|
|
index.Update(JSTaggedValue(left));
|
|
JSHandle<NameDictionary> newDict =
|
|
NameDictionary::PutIfAbsent(thread, dict, key, index, attr);
|
|
dict.Update(newDict);
|
|
if (left < right) {
|
|
keys->Set(thread, left, key);
|
|
}
|
|
properties->Set(thread, left, value);
|
|
left++;
|
|
continue;
|
|
}
|
|
auto existIndex = static_cast<uint32_t>(dict->GetValue(entry).GetNumber());
|
|
existValue.Update(properties->Get(existIndex));
|
|
bool needUpdateValue = TryUpdateExistValue(thread, existValue, value);
|
|
if (needUpdateValue) {
|
|
properties->Set(thread, existIndex, value);
|
|
}
|
|
}
|
|
if (left < right) {
|
|
keys->Trim(thread, left);
|
|
properties->Trim(thread, left);
|
|
}
|
|
}
|
|
|
|
JSHandle<NameDictionary> SendableClassDefiner::BuildSendableDictionaryProperties(JSThread *thread,
|
|
const JSHandle<JSObject> &object, JSHandle<TaggedArray> &keys, JSHandle<TaggedArray> &properties,
|
|
ClassPropertyType type, const JSHandle<JSFunction> &ctor)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
uint32_t length = keys->GetLength();
|
|
ASSERT(keys->GetLength() == properties->GetLength());
|
|
|
|
JSMutableHandle<NameDictionary> dict(
|
|
thread, NameDictionary::CreateInSharedHeap(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 = PropertyAttributes::Default(false, false, false);
|
|
if (UNLIKELY(properties->Get(index).IsAccessor())) {
|
|
attributes.SetIsAccessor(true);
|
|
}
|
|
propKey.Update(keys->Get(index));
|
|
propValue.Update(properties->Get(index));
|
|
// constructor don't need to clone
|
|
if (index == ClassInfoExtractor::CONSTRUCTOR_INDEX && type == ClassPropertyType::NON_STATIC) {
|
|
JSHandle<NameDictionary> newDict =
|
|
NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
|
|
dict.Update(newDict);
|
|
continue;
|
|
}
|
|
if (propValue->IsJSFunction()) {
|
|
JSHandle<JSFunction> propFunc = factory->CloneSFunction(JSHandle<JSFunction>::Cast(propValue));
|
|
propFunc->SetHomeObject(thread, object);
|
|
propFunc->SetLexicalEnv(thread, ctor);
|
|
ASSERT(!propFunc->GetClass()->IsExtensible());
|
|
propValue.Update(propFunc);
|
|
} else if (propValue->IsAccessorData()) {
|
|
UpdateAccessorFunction(thread, propValue, JSHandle<JSTaggedValue>(object), ctor);
|
|
}
|
|
JSHandle<NameDictionary> newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes);
|
|
dict.Update(newDict);
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
|
|
uint32_t length, const JSHandle<LayoutInfo> &layout, const JSHandle<JSHClass> &hclass)
|
|
{
|
|
ASSERT(length <= fieldTypeArray->GetLength());
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
uint32_t index = static_cast<uint32_t>(layout->NumberOfElements());
|
|
for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
|
|
PropertyAttributes attributes = PropertyAttributes::Default(true, true, false);
|
|
key.Update(fieldTypeArray->Get(i));
|
|
ASSERT(key->IsString());
|
|
SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
|
|
int entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index);
|
|
if (entry != -1) {
|
|
attributes = layout->GetAttr(entry);
|
|
attributes.SetSharedFieldType(type);
|
|
layout->SetNormalAttr(thread, entry, attributes);
|
|
} else {
|
|
attributes.SetIsInlinedProps(true);
|
|
attributes.SetRepresentation(Representation::TAGGED);
|
|
attributes.SetSharedFieldType(type);
|
|
attributes.SetOffset(index);
|
|
layout->AddKey(thread, index++, key.GetTaggedValue(), attributes);
|
|
}
|
|
}
|
|
hclass->SetLayout(thread, layout);
|
|
hclass->SetNumberOfProps(index);
|
|
auto inlinedProps = hclass->GetInlinedProperties();
|
|
if (inlinedProps > index) {
|
|
// resize hclass due to duplicated key.
|
|
uint32_t duplicatedSize = (inlinedProps - index) * JSTaggedValue::TaggedTypeSize();
|
|
hclass->SetObjectSize(hclass->GetObjectSize() - duplicatedSize);
|
|
}
|
|
}
|
|
|
|
void SendableClassDefiner::AddFieldTypeToDict(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
|
|
uint32_t length, JSMutableHandle<NameDictionary> &dict, PropertyAttributes attributes)
|
|
{
|
|
ASSERT(length <= fieldTypeArray->GetLength());
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
|
|
JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
|
|
for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
|
|
key.Update(fieldTypeArray->Get(i));
|
|
ASSERT(key->IsString());
|
|
SharedFieldType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
|
|
attributes.SetSharedFieldType(type);
|
|
attributes.SetBoxType(PropertyBoxType::UNDEFINED);
|
|
JSHandle<NameDictionary> newDict = NameDictionary::Put(thread, dict, key, value, attributes);
|
|
dict.Update(newDict);
|
|
}
|
|
}
|
|
|
|
void SendableClassDefiner::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
|
|
uint32_t length, const JSHandle<NameDictionary> &nameDict, const JSHandle<JSHClass> &hclass)
|
|
{
|
|
JSMutableHandle<NameDictionary> dict(thread, nameDict);
|
|
AddFieldTypeToDict(thread, fieldTypeArray, length, dict);
|
|
hclass->SetLayout(thread, dict);
|
|
hclass->SetNumberOfProps(0);
|
|
hclass->SetIsDictionaryMode(true);
|
|
}
|
|
|
|
void SendableClassDefiner::DefineSendableInstanceHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
|
|
uint32_t length, const JSHandle<JSFunction> &ctor, const JSHandle<JSTaggedValue> &base)
|
|
{
|
|
ASSERT(ctor->GetClass()->IsJSSharedFunction());
|
|
JSHandle<JSObject> clsPrototype(thread, JSHandle<JSFunction>(ctor)->GetFunctionPrototype());
|
|
ASSERT(clsPrototype->GetClass()->IsJSSharedObject());
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
ASSERT(length <= fieldTypeArray->GetLength());
|
|
uint32_t fieldNum = length / 2; // 2: key-value pair;
|
|
JSHandle<JSHClass> iHClass;
|
|
if (base->IsHole() || base->IsNull()) {
|
|
if (fieldNum == 0) {
|
|
iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
|
|
} else if (LIKELY(fieldNum <= JSSharedObject::MAX_INLINE)) {
|
|
iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
|
|
JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(fieldNum);
|
|
AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass);
|
|
} else {
|
|
iHClass = factory->NewSEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
|
|
JSHandle<NameDictionary> dict =
|
|
NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(fieldNum));
|
|
AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass);
|
|
}
|
|
} else {
|
|
ASSERT(base->IsJSSharedFunction());
|
|
JSHandle<JSFunction> baseCtor = JSHandle<JSFunction>::Cast(base);
|
|
JSHandle<JSHClass> baseIHClass(thread, baseCtor->GetProtoOrHClass());
|
|
ASSERT(baseIHClass->IsJSShared());
|
|
JSType baseType = baseIHClass->GetObjectType();
|
|
const auto [baseSize, baseMaxInlineSize] = GetSizeAndMaxInlineByType(baseType);
|
|
if (LIKELY(!baseIHClass->IsDictionaryMode())) {
|
|
auto baseLength = baseIHClass->NumberOfProps();
|
|
JSHandle<LayoutInfo> baseLayout(thread, baseIHClass->GetLayout());
|
|
auto newLength = baseLength + fieldNum;
|
|
if (newLength == 0) {
|
|
iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
|
|
} else if (LIKELY(newLength <= baseMaxInlineSize)) {
|
|
iHClass = factory->NewSEcmaHClass(baseSize, baseType, newLength);
|
|
JSHandle<LayoutInfo> layout = factory->CopyAndReSortSLayoutInfo(baseLayout, baseLength, newLength);
|
|
AddFieldTypeToHClass(thread, fieldTypeArray, length, layout, iHClass);
|
|
} else {
|
|
iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
|
|
JSHandle<NameDictionary> dict =
|
|
NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength));
|
|
auto globalConst = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
|
|
JSHandle<JSTaggedValue> value = globalConst->GetHandledUndefined();
|
|
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
|
|
for (uint32_t i = 0; i < baseLength; i++) {
|
|
key.Update(baseLayout->GetKey(i));
|
|
PropertyAttributes attr = baseLayout->GetAttr(i);
|
|
attr.SetIsInlinedProps(false);
|
|
attr.SetBoxType(PropertyBoxType::UNDEFINED);
|
|
dict = NameDictionary::Put(thread, dict, key, value, attr);
|
|
}
|
|
AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass);
|
|
}
|
|
} else {
|
|
JSHandle<NameDictionary> baseDict(thread, baseIHClass->GetLayout());
|
|
auto baseLength = baseDict->EntriesCount();
|
|
auto newLength = fieldNum + static_cast<uint32_t>(baseLength);
|
|
JSHandle<NameDictionary> dict =
|
|
NameDictionary::CreateInSharedHeap(thread, NameDictionary::ComputeHashTableSize(newLength));
|
|
baseDict->Rehash(thread, *dict);
|
|
dict->SetNextEnumerationIndex(thread, baseDict->GetNextEnumerationIndex());
|
|
iHClass = factory->NewSEcmaHClass(baseSize, baseType, 0);
|
|
AddFieldTypeToHClass(thread, fieldTypeArray, length, dict, iHClass);
|
|
}
|
|
}
|
|
iHClass->SetPrototype(thread, JSHandle<JSTaggedValue>(clsPrototype));
|
|
iHClass->SetExtensible(false);
|
|
ctor->SetProtoOrHClass(thread, iHClass);
|
|
ctor->GetJSHClass()->SetExtensible(false);
|
|
}
|
|
|
|
JSHandle<TaggedArray> SendableClassDefiner::ExtractStaticFieldTypeArray(JSThread *thread,
|
|
const JSHandle<TaggedArray> &fieldTypeArray)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
uint32_t arrayLength = fieldTypeArray->GetLength();
|
|
ASSERT(arrayLength > 0);
|
|
auto instanceFieldNums = static_cast<uint32_t>(fieldTypeArray->Get(arrayLength - 1).GetInt());
|
|
uint32_t staticFieldBegin = instanceFieldNums * 2; // 2: key-type
|
|
if (staticFieldBegin >= arrayLength) {
|
|
LOG_ECMA(ERROR) << "ExtractStaticFieldTypeArray Failed, staticFieldBegin:" << staticFieldBegin
|
|
<< " should be less than totalLength:" << arrayLength;
|
|
return factory->EmptyArray();
|
|
}
|
|
uint32_t staticFieldLength = arrayLength - staticFieldBegin - 1;
|
|
JSHandle<TaggedArray> staticFieldArray = factory->NewTaggedArray(staticFieldLength);
|
|
for (uint32_t i = 0; i < staticFieldLength; i += 2) { // 2: key-type
|
|
staticFieldArray->Set(thread, i, fieldTypeArray->Get(staticFieldBegin + i));
|
|
staticFieldArray->Set(thread, i + 1, fieldTypeArray->Get(staticFieldBegin + i + 1));
|
|
}
|
|
return staticFieldArray;
|
|
}
|
|
|
|
void SendableClassDefiner::UpdateAccessorFunction(JSThread *thread, const JSMutableHandle<JSTaggedValue> &value,
|
|
const JSHandle<JSTaggedValue> &homeObject, const JSHandle<JSFunction> &ctor)
|
|
{
|
|
ASSERT(value->IsAccessorData());
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
JSHandle<AccessorData> accessor(value);
|
|
auto getter = accessor->GetGetter();
|
|
if (getter.IsJSFunction()) {
|
|
JSHandle<JSFunction> func(thread, getter);
|
|
JSHandle<JSFunction> propFunc = factory->CloneSFunction(func);
|
|
propFunc->SetHomeObject(thread, homeObject);
|
|
propFunc->SetLexicalEnv(thread, ctor);
|
|
ASSERT(!propFunc->GetClass()->IsExtensible());
|
|
accessor->SetGetter(thread, propFunc);
|
|
}
|
|
auto setter = accessor->GetSetter();
|
|
if (setter.IsJSFunction()) {
|
|
JSHandle<JSFunction> func(thread, setter);
|
|
JSHandle<JSFunction> propFunc = factory->CloneSFunction(func);
|
|
propFunc->SetHomeObject(thread, homeObject);
|
|
propFunc->SetLexicalEnv(thread, ctor);
|
|
ASSERT(!propFunc->GetClass()->IsExtensible());
|
|
accessor->SetSetter(thread, propFunc);
|
|
}
|
|
}
|
|
|
|
bool SendableClassDefiner::TryUpdateExistValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &existValue,
|
|
JSMutableHandle<JSTaggedValue> &value)
|
|
{
|
|
bool needUpdateValue = true;
|
|
if (existValue->IsAccessorData()) {
|
|
if (value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter()) {
|
|
JSHandle<AccessorData> accessor(existValue);
|
|
UpdateValueToAccessor(thread, value, accessor);
|
|
needUpdateValue = false;
|
|
}
|
|
} else {
|
|
if (value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter()) {
|
|
JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
|
|
UpdateValueToAccessor(thread, value, accessor);
|
|
}
|
|
}
|
|
return needUpdateValue;
|
|
}
|
|
|
|
void SendableClassDefiner::TryUpdateValue(JSThread *thread, JSMutableHandle<JSTaggedValue> &value)
|
|
{
|
|
if (value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter()) {
|
|
JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
|
|
UpdateValueToAccessor(thread, value, accessor);
|
|
}
|
|
}
|
|
|
|
void SendableClassDefiner::UpdateValueToAccessor(JSThread *thread, JSMutableHandle<JSTaggedValue> &value,
|
|
JSHandle<AccessorData> &accessor)
|
|
{
|
|
ASSERT(value->IsJSFunction() && JSHandle<JSFunction>(value)->IsGetterOrSetter());
|
|
if (JSHandle<JSFunction>(value)->IsGetter()) {
|
|
accessor->SetGetter(thread, value);
|
|
} else {
|
|
accessor->SetSetter(thread, value);
|
|
}
|
|
value.Update(accessor);
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> SendableClassDefiner::GetSizeAndMaxInlineByType(JSType type)
|
|
{
|
|
switch (type) {
|
|
case JSType::JS_SHARED_OBJECT:
|
|
return { JSSharedObject::SIZE, JSSharedObject::MAX_INLINE };
|
|
case JSType::JS_SHARED_ARRAY:
|
|
return { JSSharedArray::SIZE, JSSharedArray::MAX_INLINE };
|
|
case JSType::JS_SHARED_MAP:
|
|
return { JSSharedMap::SIZE, JSSharedMap::MAX_INLINE };
|
|
case JSType::JS_SHARED_SET:
|
|
return { JSSharedSet::SIZE, JSSharedSet::MAX_INLINE };
|
|
case JSType::JS_SENDABLE_ARRAY_BUFFER:
|
|
return { JSSendableArrayBuffer::SIZE, JSSendableArrayBuffer::MAX_INLINE };
|
|
default:
|
|
if (JSType::JS_SHARED_TYPED_ARRAY_FIRST < type && type <= JSType::JS_SHARED_TYPED_ARRAY_LAST) {
|
|
return { JSSharedTypedArray::SIZE, JSSharedTypedArray::MAX_INLINE };
|
|
}
|
|
LOG_ECMA(FATAL) << "this branch is unreachable, cannot get size for type: " << static_cast<uint32_t>(type);
|
|
UNREACHABLE();
|
|
return {};
|
|
}
|
|
}
|
|
} // namespace panda::ecmascript
|