Supprt define instance hclass

Support non-static&public field of sendable class

Signed-off-by: lukai <lukai25@huawei.com>
Change-Id: I66ca75e4fe031c5b39f184268509cad8160113b8
This commit is contained in:
lukai 2023-12-16 16:38:35 +08:00 committed by hzzhouzebin
parent 5b838d0963
commit f5457927f9
9 changed files with 233 additions and 119 deletions

View File

@ -551,6 +551,8 @@ static void DumpAttr(const PropertyAttributes &attr, bool fastMode, std::ostream
if (attr.IsConfigurable()) {
os << "C";
}
os << (int)(attr.GetTrackType());
os << ")";
os << " InlinedProps: " << attr.IsInlinedProps();
@ -720,6 +722,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os)
break;
case JSType::ACCESSOR_DATA:
break;
case JSType::JS_SHARED_FUNCTION:
case JSType::JS_FUNCTION:
needDumpHClass = true;
JSFunction::Cast(obj)->Dump(os);

View File

@ -494,9 +494,9 @@ JSHandle<JSFunction> ClassHelper::DefineSendableClassFromExtractor(JSThread *thr
// JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(prototype),
// globalConst->GetHandledConstructorString(), ctorDesc);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
auto value = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(prototype), globalConst->GetHandledConstructorString()).GetValue();
[[maybe_unused]]auto value = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(prototype), globalConst->GetHandledConstructorString()).GetValue();
ASSERT(JSTaggedValue::Equal(thread, value, JSHandle<JSTaggedValue>(constructor)));
ECMAObject::InitializeExtRefAndOwner(thread->GetEcmaVM(), JSHandle<JSObject>(constructor));
constructor->SetHomeObject(thread, prototype);
constructor->SetProtoOrHClass(thread, prototype);
constructor->SetLexicalEnv(thread, lexenv);
@ -757,87 +757,127 @@ void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle<JSOb
}
}
// todo , need ihc information from abc
void ClassHelper::DefineSendableInstanceHClass(JSThread *thread, const JSHandle<JSFunction> &ctor, bool isbaseCase)
void ClassHelper::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
const JSHandle<LayoutInfo> &layout, const JSHandle<JSHClass> &hclass)
{
uint32_t length = fieldTypeArray->GetLength();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
uint32_t index = layout->NumberOfElements();
PropertyAttributes attributes = PropertyAttributes::Default(true, true, true);
attributes.SetIsInlinedProps(true);
attributes.SetRepresentation(Representation::TAGGED);
for (uint32_t i = 0; i < length; i += 2) { // 2: key-value pair;
key.Update(fieldTypeArray->Get(i));
TrackType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
int entry = layout->FindElementWithCache(thread, *hclass, key.GetTaggedValue(), index);
if (entry != -1) {
std::stringstream ss;
key->Dump(ss);
LOG_FULL(ERROR) << "defineSendableClass with same filed " << ss.str(); // TODO DELETE THIS LOG WHEN PUBLISH THIS PR.
attributes = layout->GetAttr(entry);
attributes.SetTrackType(type);
layout->SetNormalAttr(thread, entry, attributes);
} else {
attributes.SetTrackType(type);
attributes.SetOffset(index);
layout->AddKey<true>(thread, index++, key.GetTaggedValue(), attributes); // todo check duplicated only for test
}
}
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 ClassHelper::AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
const JSHandle<NameDictionary> &nameDict, const JSHandle<JSHClass> &hclass)
{
uint32_t length = fieldTypeArray->GetLength();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<NameDictionary> dict(thread, nameDict);
PropertyAttributes attributes = PropertyAttributes::Default(true, true, true);
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));
TrackType type = FromFieldType(FieldType(fieldTypeArray->Get(i + 1).GetInt()));
attributes.SetTrackType(type);
attributes.SetBoxType(PropertyBoxType::UNDEFINED);
JSHandle<NameDictionary> newDict = NameDictionary::Put(thread, dict, key, value, attributes);
dict.Update(newDict);
}
ASSERT(hclass->GetInlinedProperties() == 0);
// HClass's layout field is reused as a NameDictionary
hclass->SetLayout(thread, dict);
hclass->SetNumberOfProps(0);
hclass->SetIsDictionaryMode(true);
}
void ClassHelper::DefineSendableInstanceHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
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();
// todo define ihc
// for test , i create ihc with such fields. Should match your .js with such fields.
// -------- base case ---------
// x : string
// y : number
// z : boolean
// -------- derived case --------
// a : sobj
// b : boolean
JSHandle<JSHClass> hclass;
if (isbaseCase) {
int length = 3;
std::string keys[] = {"x", "y", "z"};
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
for (int i = 0; i < length; i++) {
key.Update(factory->NewFromUtf8(keys[i]));
PropertyAttributes attributes = PropertyAttributes::Default(true, true, true);
attributes.SetIsInlinedProps(true);
attributes.SetRepresentation(Representation::TAGGED);
switch (i) {
case 0: {
attributes.SetTrackType(TrackType::STRING);
break;
}
case 1: {
attributes.SetTrackType(TrackType::NUMBER);
break;
}
case 2: {
attributes.SetTrackType(TrackType::BOOLEAN);
break;
}
default:
break;
}
attributes.SetOffset(i);
layout->AddKey(thread, i, key.GetTaggedValue(), attributes);
uint32_t length = fieldTypeArray->GetLength();
uint32_t fieldNum = length / 2; // 2: key-value pair;
JSHandle<JSHClass> iHClass;
if (base->IsHole() || base->IsNull()) {
if (LIKELY(fieldNum <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, fieldNum);
JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(fieldNum, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
AddFieldTypeToHClass(thread, fieldTypeArray, layout, iHClass);
} else {
iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
JSHandle<NameDictionary> dict =
NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(fieldNum));
AddFieldTypeToHClass(thread, fieldTypeArray, dict, iHClass);
}
hclass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length);
hclass->SetLayout(thread, layout);
hclass->SetNumberOfProps(length);
} else {
int length = 2;
std::string keys[] = {"a", "b"};
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP);
for (int i = 0; i < length; i++) {
key.Update(factory->NewFromUtf8(keys[i]));
PropertyAttributes attributes = PropertyAttributes::Default(true, true, true);
attributes.SetIsInlinedProps(true);
attributes.SetRepresentation(Representation::TAGGED);
switch (i) {
case 0: {
attributes.SetTrackType(TrackType::SENDABLE);
break;
ASSERT(base->IsJSSharedFunction());
JSHandle<JSFunction> baseCtor = JSHandle<JSFunction>::Cast(base);
JSHandle<JSHClass> baseIHClass(thread, baseCtor->GetProtoOrHClass());
ASSERT(baseIHClass->IsJSSharedObject());
if (LIKELY(!baseIHClass->IsDictionaryMode())) {
auto baseLength = baseIHClass->NumberOfProps();
JSHandle<LayoutInfo> baseLayout(thread, baseIHClass->GetLayout());
auto newLength = baseLength + fieldNum;
if (newLength <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY) {
iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, newLength);
JSHandle<LayoutInfo> layout = factory->ExtendLayoutInfo(baseLayout, newLength);
AddFieldTypeToHClass(thread, fieldTypeArray, layout, iHClass);
} else {
iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
JSHandle<NameDictionary> dict =
NameDictionary::Create(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.SetBoxType(PropertyBoxType::UNDEFINED);
dict = NameDictionary::Put(thread, dict, key, value, attr);
}
case 1: {
attributes.SetTrackType(TrackType::BOOLEAN);
break;
}
default:
break;
AddFieldTypeToHClass(thread, fieldTypeArray, dict, iHClass);
}
attributes.SetOffset(i);
layout->AddKey(thread, i, key.GetTaggedValue(), attributes);
} else {
JSHandle<NameDictionary> baseDict(thread, baseIHClass->GetLayout());
auto baseLength = baseDict->EntriesCount();
JSHandle<NameDictionary> dict =
NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(fieldNum + baseLength));
baseDict->Rehash(thread, *dict);
iHClass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, 0);
AddFieldTypeToHClass(thread, fieldTypeArray, dict, iHClass);
}
hclass = factory->NewEcmaHClass(JSSharedObject::SIZE, JSType::JS_SHARED_OBJECT, length);
hclass->SetLayout(thread, layout);
hclass->SetNumberOfProps(length);
}
hclass->SetPrototype(thread, JSHandle<JSTaggedValue>(clsPrototype));
ctor->SetProtoOrHClass(thread, hclass);
// todo support dictionary mode?
iHClass->SetPrototype(thread, JSHandle<JSTaggedValue>(clsPrototype));
ctor->SetProtoOrHClass(thread, iHClass);
}
} // namespace panda::ecmascript

View File

@ -25,6 +25,14 @@ namespace panda::ecmascript {
// Attention: keys accessor stores the property key and properties accessor stores the property value, but elements
// accessor stores the key-value pair abuttally.
using EntityId = panda_file::File::EntityId;
enum class FieldType
{
NONE = 0,
NUMBER = (1 << 0),
STRING = (1 << 1),
BOOLEAN = (1 << 2),
TS_TYPE_REF = (1 << 3),
};
class ClassInfoExtractor : public TaggedObject {
public:
static constexpr uint8_t NON_STATIC_RESERVED_LENGTH = 1;
@ -106,7 +114,24 @@ public:
const JSHandle<JSTaggedValue> &lexenv,
const JSHandle<JSTaggedValue> &ihclass,
const JSHandle<JSHClass> &constructorHClass);
static void DefineSendableInstanceHClass(JSThread *thread, const JSHandle<JSFunction> &ctor, bool isbaseCase);
static void DefineSendableInstanceHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
const JSHandle<JSFunction> &ctor, const JSHandle<JSTaggedValue> &base);
static TrackType FromFieldType(FieldType type) {
switch (type) {
case FieldType::NONE:
return TrackType::NONE;
case FieldType::NUMBER:
return TrackType::NUMBER;
case FieldType::STRING:
return TrackType::STRING;
case FieldType::BOOLEAN:
return TrackType::BOOLEAN;
case FieldType::TS_TYPE_REF:
return TrackType::SENDABLE;
default:
UNREACHABLE();
}
}
private:
static JSHandle<NameDictionary> BuildDictionaryProperties(JSThread *thread, const JSHandle<JSObject> &object,
@ -122,6 +147,12 @@ private:
static void HandleElementsProperties(JSThread *thread, const JSHandle<JSObject> &object,
JSHandle<TaggedArray> &elements);
static void AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
const JSHandle<LayoutInfo> &layout, const JSHandle<JSHClass> &hclass);
static void AddFieldTypeToHClass(JSThread *thread, const JSHandle<TaggedArray> &fieldTypeArray,
const JSHandle<NameDictionary> &nameDict, const JSHandle<JSHClass> &hclass);
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JSPANDAFILE_CLASS_INFO_EXTRACTOR_H

View File

@ -504,6 +504,10 @@ JSHandle<TaggedArray> LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread,
jt = accessor.GetTaggedValue();
break;
}
case LiteralTag::LITERALARRAY: {
jt = JSTaggedValue(std::get<uint32_t>(value));
break;
}
case LiteralTag::NULLVALUE: {
break;
}

View File

@ -287,6 +287,37 @@ public:
return val;
}
static JSHandle<TaggedArray> GetFieldLiteral(JSThread *thread, JSHandle<ConstantPool> constpool,
uint32_t literal, CString entry)
{
// auto val = constpool->GetObjectFromCache(literal);
JSPandaFile *jsPandaFile = constpool->GetJSPandaFile();
// // For AOT
// bool isLoadedAOT = jsPandaFile->IsLoadedAOT();
JSHandle<AOTLiteralInfo> entryIndexes(thread, JSTaggedValue::Undefined());
// if (isLoadedAOT && val.IsAOTLiteralInfo()) {
// entryIndexes = JSHandle<AOTLiteralInfo>(thread, val);
// val = JSTaggedValue::Hole();
// }
// if (val.IsHole()) {
// EcmaVM *vm = thread->GetEcmaVM();
// ObjectFactory *factory = vm->GetFactory();
ASSERT(jsPandaFile->IsNewVersion());
panda_file::File::EntityId literalId(literal);
// bool needSetAotFlag = isLoadedAOT && !entryIndexes.GetTaggedValue().IsUndefined();
JSHandle<TaggedArray> literalArray = LiteralDataExtractor::GetDatasIgnoreType(
thread, jsPandaFile, literalId, constpool, entry, false, entryIndexes);
// JSHandle<ClassLiteral> classLiteral = factory->NewClassLiteral();
// classLiteral->SetArray(thread, literalArray);
// val = classLiteral.GetTaggedValue();
// constpool->SetObjectToCache(thread, literal, val);
// }
return literalArray;
}
template <ConstPoolType type>
static JSTaggedValue GetLiteralFromCache(JSThread *thread, JSTaggedValue constpool, uint32_t index, CString entry)
{

View File

@ -212,6 +212,7 @@ void LayoutInfo::AddKey(const JSThread *thread, [[maybe_unused]] int index, cons
return;
}
if (prevKey == key) {
std::abort();
THROW_TYPE_ERROR(const_cast<JSThread *>(thread), "property keys can not duplicate");
}
}

View File

@ -70,6 +70,7 @@ enum class PropertyBoxType {
* -----------------------------
* Slow | PropertyBoxTypeField(bit 8...9)
* | DictionaryOrderField(bit 10...29)
* | TrackTypeField(bit 30...32)
*/
class PropertyAttributes {
public:
@ -128,7 +129,8 @@ public:
static_assert(DictModeStartField::SIZE == CommonLastBitField::SIZE);
using PropertyBoxTypeField = DictModeStartField::NextField<PropertyBoxType, 2>; // 2: 2 bits, 8-9
using DictionaryOrderField = PropertyBoxTypeField::NextField<uint32_t, DICTIONARY_ORDER_NUM>; // 29
using DictModeLastField = DictionaryOrderField;
using DictTrackTypeField = DictionaryOrderField::NextField<TrackType, TRACK_TYPE_NUM>;
using DictModeLastField = DictTrackTypeField;
static_assert(
DictModeLastField::START_BIT + DictModeLastField::SIZE <= sizeof(uint32_t) * BITS_PER_BYTE, "Invalid");
@ -307,6 +309,16 @@ public:
TrackTypeField::Set(type, &value_);
}
inline void SetDictTrackType(TrackType type)
{
DictTrackTypeField::Set(type, &value_);
}
inline TrackType GetDictTrackType() const
{
return DictTrackTypeField::Get(value_);
}
inline void SetDictionaryOrder(uint32_t order)
{
DictionaryOrderField::Set<uint32_t>(order, &value_);

View File

@ -900,35 +900,26 @@ JSTaggedValue RuntimeStubs::RuntimeCreateSendableClass(JSThread *thread,
thread, constpool.GetTaggedValue(), module.GetTaggedValue(), methodId);
JSHandle<JSTaggedValue> method(thread, methodObj);
JSHandle<ConstantPool> constpoolHandle = JSHandle<ConstantPool>::Cast(constpool);
// JSHandle<JSFunction> cls;
// JSMutableHandle<JSTaggedValue> ihc(thread, JSTaggedValue::Undefined());
// JSMutableHandle<JSTaggedValue> chc(thread, JSTaggedValue::Undefined());
// JSTaggedValue val = constpoolHandle->GetObjectFromCache(literalId);
// if (val.IsAOTLiteralInfo()) {
// JSHandle<AOTLiteralInfo> aotLiteralInfo(thread, val);
// ihc.Update(aotLiteralInfo->GetIhc());
// chc.Update(aotLiteralInfo->GetChc());
// }
auto literalObj = ConstantPool::GetClassLiteralFromCache(thread, constpoolHandle, literalId, entry);
JSHandle<ClassLiteral> classLiteral(thread, literalObj);
JSHandle<TaggedArray> arrayHandle(thread, classLiteral->GetArray());
auto literalLength = arrayHandle->GetLength();
// fieldTypeId is the last element in literal buffer
auto fieldTypeId = static_cast<uint32_t>(arrayHandle->Get(literalLength - 1).GetInt());
arrayHandle->Trim(thread, literalLength - 1);
JSHandle<ClassInfoExtractor> extractor = factory->NewClassInfoExtractor(method);
ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread, extractor, arrayHandle);
// if (ShouldUseAOTHClass(ihc, chc, classLiteral)) {
// classLiteral->SetIsAOTUsed(true);
// JSHandle<JSHClass> chclass(chc);
// cls = ClassHelper::DefineClassWithIHClass(thread, extractor,
// lexenv, ihc, chclass);
// } else {
JSHandle<JSFunction> cls = ClassHelper::DefineSendableClassFromExtractor(thread, base, extractor, lexenv);
// }
RuntimeSetClassConstructorLength(thread, cls.GetTaggedValue(), JSTaggedValue(length));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
RuntimeSetClassInheritanceRelationship(thread, JSHandle<JSTaggedValue>(cls), base, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
bool isbaseClass = base->IsHole(); // todo for test
ClassHelper::DefineSendableInstanceHClass(thread, cls, isbaseClass); // todo need ihc information from abc
JSHandle<TaggedArray> fieldTypeArray = ConstantPool::GetFieldLiteral(thread, constpoolHandle, fieldTypeId, entry);
ClassHelper::DefineSendableInstanceHClass(thread, fieldTypeArray, cls, base);
return cls.GetTaggedValue();
}

View File

@ -295,6 +295,33 @@ public:
Set(thread, index, value);
}
// Rehash element to new_table
void Rehash(const JSThread *thread, Derived *newTable)
{
if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) {
return;
}
int currentSize = this->Size();
// Rehash elements to new table
for (int i = 0; i < currentSize; i++) {
int fromIndex = Derived::GetKeyIndex(i);
JSTaggedValue k = this->GetKey(i);
if (!IsKey(k)) {
continue;
}
int32_t hash = static_cast<int32_t>(Derived::Hash(k));
int insertionIndex = Derived::GetKeyIndex(newTable->FindInsertIndex(hash));
JSTaggedValue tv = Get(fromIndex);
newTable->Set(thread, insertionIndex, tv);
for (int j = 1; j < Derived::GetEntrySize(); j++) {
tv = Get(fromIndex + j);
newTable->Set(thread, insertionIndex + j, tv);
}
}
newTable->SetEntriesCount(thread, EntriesCount());
newTable->SetHoleEntriesCount(thread, 0);
}
static constexpr int MIN_SHRINK_SIZE = 16;
static constexpr int MIN_SIZE = 4;
static constexpr int NUMBER_OF_ENTRIES_INDEX = 0;
@ -354,33 +381,6 @@ protected:
this->SetValue(thread, entry, defaultValue);
this->IncreaseHoleEntriesCount(thread);
}
// Rehash element to new_table
void Rehash(const JSThread *thread, Derived *newTable)
{
if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) {
return;
}
int currentSize = this->Size();
// Rehash elements to new table
for (int i = 0; i < currentSize; i++) {
int fromIndex = Derived::GetKeyIndex(i);
JSTaggedValue k = this->GetKey(i);
if (!IsKey(k)) {
continue;
}
int32_t hash = static_cast<int32_t>(Derived::Hash(k));
int insertionIndex = Derived::GetKeyIndex(newTable->FindInsertIndex(hash));
JSTaggedValue tv = Get(fromIndex);
newTable->Set(thread, insertionIndex, tv);
for (int j = 1; j < Derived::GetEntrySize(); j++) {
tv = Get(fromIndex + j);
newTable->Set(thread, insertionIndex + j, tv);
}
}
newTable->SetEntriesCount(thread, EntriesCount());
newTable->SetHoleEntriesCount(thread, 0);
}
};
template<typename Derived>
@ -443,6 +443,7 @@ public:
PropertyAttributes attr(metaData);
attr.SetDictionaryOrder(enumIndex);
attr.SetRepresentation(Representation::TAGGED);
attr.SetDictTrackType(metaData.GetTrackType());
int entry = table->FindEntry(key.GetTaggedValue());
if (entry != -1) {
table->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr);