diff --git a/ecmascript/base/json_stringifier.cpp b/ecmascript/base/json_stringifier.cpp index b851655eeb..7dc1bbc737 100644 --- a/ecmascript/base/json_stringifier.cpp +++ b/ecmascript/base/json_stringifier.cpp @@ -715,7 +715,6 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl JSHandle jsHclass(thread_, obj->GetJSHClass()); JSTaggedValue enumCache = jsHclass->GetEnumCache(); if (!enumCache.IsNull()) { - int propsNumber = static_cast(jsHclass->NumberOfProps()); JSHandle cache(thread_, enumCache); uint32_t length = cache->GetLength(); for (uint32_t i = 0; i < length; i++) { @@ -726,7 +725,7 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl handleKey_.Update(key); JSTaggedValue value; LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()); - int index = layoutInfo->FindElementWithCache(thread_, *jsHclass, key, propsNumber); + int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key); PropertyAttributes attr(layoutInfo->GetAttr(index)); ASSERT(static_cast(attr.GetOffset()) == index); value = attr.IsInlinedProps() @@ -746,14 +745,13 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl if (end <= 0) { return hasContent; } - int propsNumber = static_cast(jsHclass->NumberOfProps()); for (int i = 0; i < end; i++) { LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()); JSTaggedValue key = layoutInfo->GetKey(i); if (key.IsString() && layoutInfo->GetAttr(i).IsEnumerable()) { handleKey_.Update(key); JSTaggedValue value; - int index = layoutInfo->FindElementWithCache(thread_, *jsHclass, key, propsNumber); + int index = JSHClass::FindPropertyEntry(thread_, *jsHclass, key); PropertyAttributes attr(layoutInfo->GetAttr(index)); ASSERT(static_cast(attr.GetOffset()) == index); value = attr.IsInlinedProps() diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index f24341e8a1..9b197b4919 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -3443,6 +3443,7 @@ JSHandle Builtins::InitializeArkTools(const JSHandle &env) SetFunction(env, tools, "dumpHClass", builtins::BuiltinsArkTools::DumpHClass, FunctionLength::ONE); SetFunction(env, tools, "isTSHClass", builtins::BuiltinsArkTools::IsTSHClass, FunctionLength::ONE); SetFunction(env, tools, "getHClass", builtins::BuiltinsArkTools::GetHClass, FunctionLength::ONE); + SetFunction(env, tools, "hasTSSubtyping", builtins::BuiltinsArkTools::HasTSSubtyping, FunctionLength::ONE); SetFunction(env, tools, "forceFullGC", builtins::BuiltinsArkTools::ForceFullGC, FunctionLength::ZERO); SetFunction(env, tools, "removeAOTFlag", builtins::BuiltinsArkTools::RemoveAOTFlag, FunctionLength::ONE); #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) diff --git a/ecmascript/builtins/builtins_ark_tools.cpp b/ecmascript/builtins/builtins_ark_tools.cpp index dbde74b9e4..70ac7c4e45 100644 --- a/ecmascript/builtins/builtins_ark_tools.cpp +++ b/ecmascript/builtins/builtins_ark_tools.cpp @@ -61,8 +61,8 @@ JSTaggedValue BuiltinsArkTools::CompareHClass(EcmaRuntimeCallInfo *info) JSHandle obj1 = GetCallArg(info, 0); JSHandle obj2 = GetCallArg(info, 1); - JSHClass* obj1Hclass = obj1->GetTaggedObject()->GetClass(); - JSHClass* obj2Hclass = obj2->GetTaggedObject()->GetClass(); + JSHClass *obj1Hclass = obj1->GetTaggedObject()->GetClass(); + JSHClass *obj2Hclass = obj2->GetTaggedObject()->GetClass(); std::ostringstream oss; obj1Hclass->Dump(oss); obj2Hclass->Dump(oss); @@ -80,7 +80,7 @@ JSTaggedValue BuiltinsArkTools::DumpHClass(EcmaRuntimeCallInfo *info) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle obj = GetCallArg(info, 0); - JSHClass* objHclass = obj->GetTaggedObject()->GetClass(); + JSHClass *objHclass = obj->GetTaggedObject()->GetClass(); std::ostringstream oss; objHclass->Dump(oss); @@ -96,7 +96,7 @@ JSTaggedValue BuiltinsArkTools::IsTSHClass(EcmaRuntimeCallInfo *info) ASSERT(info->GetArgsNumber() == 1); JSHandle object = GetCallArg(info, 0); - JSHClass* hclass = object->GetTaggedObject()->GetClass(); + JSHClass *hclass = object->GetTaggedObject()->GetClass(); bool isTSHClass = hclass->IsTS(); return GetTaggedBoolean(isTSHClass); } @@ -109,10 +109,22 @@ JSTaggedValue BuiltinsArkTools::GetHClass(EcmaRuntimeCallInfo *info) ASSERT(info->GetArgsNumber() == 1); JSHandle object = GetCallArg(info, 0); - JSHClass* hclass = object->GetTaggedObject()->GetClass(); + JSHClass *hclass = object->GetTaggedObject()->GetClass(); return JSTaggedValue(hclass); } +JSTaggedValue BuiltinsArkTools::HasTSSubtyping(EcmaRuntimeCallInfo *info) +{ + ASSERT(info); + JSThread *thread = info->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ASSERT(info->GetArgsNumber() == 1); + JSHandle object = GetCallArg(info, 0); + JSHClass *hclass = object->GetTaggedObject()->GetClass(); + return GetTaggedBoolean(hclass->HasTSSubtyping()); +} + JSTaggedValue BuiltinsArkTools::ForceFullGC(EcmaRuntimeCallInfo *info) { ASSERT(info); diff --git a/ecmascript/builtins/builtins_ark_tools.h b/ecmascript/builtins/builtins_ark_tools.h index 62ff809362..e1fefa3be8 100644 --- a/ecmascript/builtins/builtins_ark_tools.h +++ b/ecmascript/builtins/builtins_ark_tools.h @@ -35,6 +35,8 @@ public: static JSTaggedValue GetHClass(EcmaRuntimeCallInfo *info); + static JSTaggedValue HasTSSubtyping(EcmaRuntimeCallInfo *info); + static JSTaggedValue ForceFullGC(EcmaRuntimeCallInfo *info); static JSTaggedValue RemoveAOTFlag(EcmaRuntimeCallInfo *info); diff --git a/ecmascript/compiler/circuit.h b/ecmascript/compiler/circuit.h index b7a8348cdd..971ae0ef1d 100644 --- a/ecmascript/compiler/circuit.h +++ b/ecmascript/compiler/circuit.h @@ -123,6 +123,14 @@ public: GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ + const GateMetaData* NAME(uint64_t pcOffset) const \ + { \ + return metaBuilder_.NAME(pcOffset); \ + } + GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_META) +#undef DECLARE_GATE_META + const GateMetaData* Nop() { return metaBuilder_.Nop(); diff --git a/ecmascript/compiler/circuit_builder-inl.h b/ecmascript/compiler/circuit_builder-inl.h index 6b65671923..1697efc78f 100644 --- a/ecmascript/compiler/circuit_builder-inl.h +++ b/ecmascript/compiler/circuit_builder-inl.h @@ -510,6 +510,11 @@ GateRef CircuitBuilder::GetGlobalConstantString(ConstantIndex index) return PtrMul(IntPtr(sizeof(JSTaggedValue)), IntPtr(static_cast(index))); } +GateRef CircuitBuilder::LoadObjectFromWeakRef(GateRef x) +{ + return PtrAdd(x, IntPtr(-JSTaggedValue::TAG_WEAK)); +} + // object operation GateRef CircuitBuilder::LoadHClass(GateRef object) { @@ -1074,4 +1079,4 @@ GateRef CircuitBuilder::Int32OverflowCheck(GateRef gate) } } // namespace panda::ecmascript::kungfu -#endif \ No newline at end of file +#endif diff --git a/ecmascript/compiler/circuit_builder.cpp b/ecmascript/compiler/circuit_builder.cpp index 8cec70b50a..ce0ebe5a5e 100644 --- a/ecmascript/compiler/circuit_builder.cpp +++ b/ecmascript/compiler/circuit_builder.cpp @@ -635,25 +635,27 @@ GateRef CircuitBuilder::HeapAlloc(GateRef initialHClass, GateType type, RegionSp return ret; } -GateRef CircuitBuilder::LoadProperty(GateRef receiver, GateRef offset) +GateRef CircuitBuilder::LoadProperty(GateRef receiver, GateRef propertyLookupResult) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto ret = GetCircuit()->NewGate(circuit_->LoadProperty(), MachineType::I64, - { currentControl, currentDepend, receiver, offset }, GateType::AnyType()); + { currentControl, currentDepend, receiver, propertyLookupResult }, + GateType::AnyType()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; } -GateRef CircuitBuilder::StoreProperty(GateRef receiver, GateRef offset, GateRef value) +GateRef CircuitBuilder::StoreProperty(GateRef receiver, GateRef propertyLookupResult, GateRef value) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto ret = GetCircuit()->NewGate(circuit_->StoreProperty(), MachineType::I64, - { currentControl, currentDepend, receiver, offset, value }, GateType::AnyType()); + { currentControl, currentDepend, receiver, propertyLookupResult, value }, + GateType::AnyType()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; @@ -705,6 +707,38 @@ GateRef CircuitBuilder::TypedAotCall(GateRef hirGate, std::vector args) return callGate; } +GateRef CircuitBuilder::CallGetter(GateRef hirGate, GateRef receiver, GateRef propertyLookupResult) +{ + ASSERT(acc_.GetOpCode(hirGate) == OpCode::JS_BYTECODE); + uint64_t pcOffset = acc_.GetPcOffset(hirGate); + + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto callGate = GetCircuit()->NewGate(circuit_->CallGetter(pcOffset), MachineType::I64, + { currentControl, currentDepend, receiver, propertyLookupResult }, + GateType::AnyType()); + currentLabel->SetControl(callGate); + currentLabel->SetDepend(callGate); + return callGate; +} + +GateRef CircuitBuilder::CallSetter(GateRef hirGate, GateRef receiver, GateRef propertyLookupResult, GateRef value) +{ + ASSERT(acc_.GetOpCode(hirGate) == OpCode::JS_BYTECODE); + uint64_t pcOffset = acc_.GetPcOffset(hirGate); + + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto callGate = GetCircuit()->NewGate(circuit_->CallSetter(pcOffset), MachineType::I64, + { currentControl, currentDepend, receiver, propertyLookupResult, value }, + GateType::AnyType()); + currentLabel->SetControl(callGate); + currentLabel->SetDepend(callGate); + return callGate; +} + GateRef CircuitBuilder::HasPendingException(GateRef glue) { GateRef exceptionOffset = IntPtr(JSThread::GlueData::GetExceptionOffset(env_->IsArch32Bit())); diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index 80d8190302..caadd1aadb 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -412,6 +412,7 @@ public: GateRef TaggedIsString(GateRef obj); GateRef TaggedIsStringOrSymbol(GateRef obj); inline GateRef GetGlobalConstantString(ConstantIndex index); + inline GateRef LoadObjectFromWeakRef(GateRef x); GateRef IsJSHClass(GateRef obj); GateRef HasPendingException(GateRef glue); @@ -432,12 +433,14 @@ public: GateRef LoadElement(GateRef receiver, GateRef index); template GateRef StoreElement(GateRef receiver, GateRef index, GateRef value); - GateRef LoadProperty(GateRef receiver, GateRef offset); - GateRef StoreProperty(GateRef receiver, GateRef offset, GateRef value); + GateRef LoadProperty(GateRef receiver, GateRef propertyLookupResult); + GateRef StoreProperty(GateRef receiver, GateRef propertyLookupResult, GateRef value); GateRef LoadArrayLength(GateRef array); GateRef HeapAlloc(GateRef initialHClass, GateType type, RegionSpaceFlag flag); GateRef Construct(GateRef hirGate, std::vector args); GateRef TypedAotCall(GateRef hirGate, std::vector args); + GateRef CallGetter(GateRef hirGate, GateRef receiver, GateRef propertyLookupResult); + GateRef CallSetter(GateRef hirGate, GateRef receiver, GateRef propertyLookupResult, GateRef value); // Object Operations inline GateRef LoadHClass(GateRef object); @@ -484,6 +487,7 @@ public: GateRef GetObjectFromConstPool(GateRef glue, GateRef hirGate, GateRef jsFunc, GateRef index, ConstPoolType type); GateRef GetObjectFromConstPool(GateRef glue, GateRef hirGate, GateRef constPool, GateRef module, GateRef index, ConstPoolType type); + void SetEnvironment(Environment *env) { env_ = env; diff --git a/ecmascript/compiler/gate_accessor.cpp b/ecmascript/compiler/gate_accessor.cpp index e92f15a8d1..42a070cb90 100644 --- a/ecmascript/compiler/gate_accessor.cpp +++ b/ecmascript/compiler/gate_accessor.cpp @@ -186,6 +186,8 @@ uint32_t GateAccessor::GetPcOffset(GateRef gate) const case OpCode::TYPED_CALL: case OpCode::CONSTRUCT: case OpCode::TYPEDAOTCALL: + case OpCode::CALL_GETTER: + case OpCode::CALL_SETTER: return static_cast(gatePtr->GetOneParameterMetaData()->GetValue()); default: break; @@ -761,6 +763,13 @@ void GateAccessor::DeleteStateSplitAndFrameState(GateRef gate) void GateAccessor::ReplaceGate(GateRef gate, GateRef state, GateRef depend, GateRef value) { + if (value != Circuit::NullGate()) { + GateType type = GetGateType(gate); + if (!type.IsAnyType()) { + SetGateType(value, type); + } + } + auto uses = Uses(gate); for (auto useIt = uses.begin(); useIt != uses.end();) { if (IsStateIn(useIt)) { diff --git a/ecmascript/compiler/gate_meta_data.cpp b/ecmascript/compiler/gate_meta_data.cpp index f36f32589c..1534f13e28 100644 --- a/ecmascript/compiler/gate_meta_data.cpp +++ b/ecmascript/compiler/gate_meta_data.cpp @@ -52,6 +52,7 @@ std::string GateMetaData::Str(OpCode opcode) GATE_META_DATA_LIST_WITH_SIZE(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_ONE_PARAMETER(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_PC_OFFSET(GATE_NAME_MAP) + GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(GATE_NAME_MAP) #undef GATE_NAME_MAP #define GATE_NAME_MAP(OP) { OpCode::OP, #OP }, GATE_OPCODE_LIST(GATE_NAME_MAP) @@ -281,6 +282,16 @@ const GateMetaData* GateMetaBuilder::NAME(uint64_t value, uint64_t pcOffset) GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ +const GateMetaData* GateMetaBuilder::NAME(uint64_t pcOffset) const \ +{ \ + auto meta = new (chunk_) OneParameterMetaData(OpCode::OP, R, S, D, V, pcOffset); \ + meta->SetKind(GateMetaData::Kind::MUTABLE_ONE_PARAMETER); \ + return meta; \ +} +GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_META) +#undef DECLARE_GATE_META + const GateMetaData* GateMetaBuilder::Arg(uint64_t value) { switch (value) { diff --git a/ecmascript/compiler/gate_meta_data.h b/ecmascript/compiler/gate_meta_data.h index 88b631166e..dcce3d9f2c 100644 --- a/ecmascript/compiler/gate_meta_data.h +++ b/ecmascript/compiler/gate_meta_data.h @@ -81,7 +81,7 @@ enum class DeoptType : uint8_t { NOTARRAY, NOTSARRAY, NOTF32ARRAY, - WRONGHCLASS, + INCONSISTENTHCLASS, NOTNEWOBJ, NOTARRAYIDX, NOTF32ARRAYIDX, @@ -222,12 +222,16 @@ std::string MachineTypeToStr(MachineType machineType); V(DebuggerBytecodeCall, DEBUGGER_BYTECODE_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ V(BuiltinsCallWithArgv, BUILTINS_CALL_WITH_ARGV, GateFlags::NONE_FLAG, 0, 1, value) \ V(BuiltinsCall, BUILTINS_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(SaveRegister, SAVE_REGISTER, GateFlags::NONE_FLAG, 0, 1, value) \ + V(SaveRegister, SAVE_REGISTER, GateFlags::NONE_FLAG, 0, 1, value) #define GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \ V(TypedCall, TYPED_CALL, GateFlags::NONE_FLAG, 1, 1, value) \ V(Construct, CONSTRUCT, GateFlags::NONE_FLAG, 1, 1, value) \ - V(TypedAotCall, TYPEDAOTCALL, GateFlags::NONE_FLAG, 1, 1, value) \ + V(TypedAotCall, TYPEDAOTCALL, GateFlags::NONE_FLAG, 1, 1, value) + +#define GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(V) \ + V(CallGetter, CALL_GETTER, GateFlags::NONE_FLAG, 1, 1, 2) \ + V(CallSetter, CALL_SETTER, GateFlags::NONE_FLAG, 1, 1, 3) #define GATE_META_DATA_LIST_WITH_SIZE(V) \ V(Merge, MERGE, GateFlags::CONTROL, value, 0, 0) \ @@ -242,7 +246,7 @@ std::string MachineTypeToStr(MachineType machineType); V(IndexCheck, INDEX_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ V(Int32OverflowCheck, INT32_OVERFLOW_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ V(TypedUnaryOp, TYPED_UNARY_OP, GateFlags::NO_WRITE, 1, 1, 1) \ - V(TypedConvert, TYPE_CONVERT, GateFlags::NO_WRITE, 1, 1, 1) \ + V(TypedConvert, TYPE_CONVERT, GateFlags::NO_WRITE, 1, 1, 1) #define GATE_META_DATA_LIST_WITH_VALUE(V) \ V(Icmp, ICMP, GateFlags::NONE_FLAG, 0, 0, 2) \ @@ -275,6 +279,7 @@ enum class OpCode : uint8_t { GATE_META_DATA_LIST_WITH_SIZE(DECLARE_GATE_OPCODE) GATE_META_DATA_LIST_WITH_ONE_PARAMETER(DECLARE_GATE_OPCODE) GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_OPCODE) #undef DECLARE_GATE_OPCODE #define DECLARE_GATE_OPCODE(NAME) NAME, GATE_OPCODE_LIST(DECLARE_GATE_OPCODE) diff --git a/ecmascript/compiler/gate_meta_data_builder.h b/ecmascript/compiler/gate_meta_data_builder.h index bfd0a7355c..c9a308a142 100644 --- a/ecmascript/compiler/gate_meta_data_builder.h +++ b/ecmascript/compiler/gate_meta_data_builder.h @@ -112,6 +112,11 @@ public: GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ + const GateMetaData* NAME(uint64_t pcOffset) const; + GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_META) +#undef DECLARE_GATE_META + explicit GateMetaBuilder(Chunk* chunk); const GateMetaData* JSBytecode(size_t valuesIn, EcmaOpcode opcode, uint32_t pcOffset, GateFlags flags) { @@ -146,4 +151,4 @@ private: Chunk* chunk_; }; } // namespace panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_GATE_META_DATA_CACHE_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_GATE_META_DATA_CACHE_H diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 3389c1a968..e6343e1477 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -1683,7 +1683,7 @@ inline GateRef StubBuilder::GetDoubleOfTNumber(GateRef x) inline GateRef StubBuilder::LoadObjectFromWeakRef(GateRef x) { - return env_->GetBuilder()->PtrAdd(x, IntPtr(-JSTaggedValue::TAG_WEAK)); + return env_->GetBuilder()->LoadObjectFromWeakRef(x); } inline GateRef StubBuilder::ExtFloat32ToDouble(GateRef x) diff --git a/ecmascript/compiler/ts_type_lowering.cpp b/ecmascript/compiler/ts_type_lowering.cpp index e70a5858da..c0bf6574ff 100644 --- a/ecmascript/compiler/ts_type_lowering.cpp +++ b/ecmascript/compiler/ts_type_lowering.cpp @@ -648,10 +648,14 @@ void TSTypeLowering::LowerTypedLdObjByName(GateRef gate) acc_.DeleteStateSplitAndFrameState(gate); return; } - JSTaggedValue hclass = tsManager_->GetHClassFromCache(hclassIndex); + JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); + if (!hclass->HasTSSubtyping()) { // slowpath + acc_.DeleteStateSplitAndFrameState(gate); + return; + } - auto propertyOffset = tsManager_->GetPropertyOffset(hclass, prop); - if (propertyOffset == -1) { // slowpath + PropertyLookupResult plr = JSHClass::LookupProperty(thread, hclass, prop); + if (!plr.IsFound()) { // slowpath acc_.DeleteStateSplitAndFrameState(gate); return; } @@ -659,11 +663,16 @@ void TSTypeLowering::LowerTypedLdObjByName(GateRef gate) AddProfiling(gate); GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); - GateRef propertyOffsetGate = builder_.IntPtr(propertyOffset); builder_.ObjectTypeCheck(receiverType, receiver, hclassIndexGate); ASSERT(acc_.GetOpCode(acc_.GetDep(gate)) == OpCode::STATE_SPLIT); - GateRef result = builder_.LoadProperty(receiver, propertyOffsetGate); + GateRef pfrGate = builder_.Int32(plr.GetData()); + GateRef result = Circuit::NullGate(); + if (LIKELY(!plr.IsAccessor())) { + result = builder_.LoadProperty(receiver, pfrGate); + } else { + result = builder_.CallGetter(gate, receiver, pfrGate); + } acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); } @@ -695,10 +704,14 @@ void TSTypeLowering::LowerTypedStObjByName(GateRef gate, bool isThis) acc_.DeleteStateSplitAndFrameState(gate); return; } - JSTaggedValue hclass = tsManager_->GetHClassFromCache(hclassIndex); + JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); + if (!hclass->HasTSSubtyping()) { // slowpath + acc_.DeleteStateSplitAndFrameState(gate); + return; + } - auto propertyOffset = tsManager_->GetPropertyOffset(hclass, prop); - if (propertyOffset == -1) { // slowpath + PropertyLookupResult plr = JSHClass::LookupProperty(thread, hclass, prop); + if (!plr.IsFound() || plr.IsFunction()) { // slowpath acc_.DeleteStateSplitAndFrameState(gate); return; } @@ -706,11 +719,15 @@ void TSTypeLowering::LowerTypedStObjByName(GateRef gate, bool isThis) AddProfiling(gate); GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); - GateRef propertyOffsetGate = builder_.IntPtr(propertyOffset); builder_.ObjectTypeCheck(receiverType, receiver, hclassIndexGate); ASSERT(acc_.GetOpCode(acc_.GetDep(gate)) == OpCode::STATE_SPLIT); - builder_.StoreProperty(receiver, propertyOffsetGate, value); + GateRef pfrGate = builder_.Int32(plr.GetData()); + if (LIKELY(plr.IsLocal())) { + builder_.StoreProperty(receiver, pfrGate, value); + } else { + builder_.CallSetter(gate, receiver, pfrGate, value); + } acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); } @@ -1076,7 +1093,7 @@ void TSTypeLowering::LowerTypedCallrange(GateRef gate) const size_t callTargetIndex = 1; // acc size_t argc = numArgs - callTargetIndex; GateRef func = acc_.GetValueIn(gate, argc); - + GateType funcType = acc_.GetGateType(func); if (!tsManager_->IsFunctionTypeKind(funcType)) { acc_.DeleteStateSplitAndFrameState(gate); diff --git a/ecmascript/compiler/type_lowering.cpp b/ecmascript/compiler/type_lowering.cpp index 38df64387d..10935f72ea 100644 --- a/ecmascript/compiler/type_lowering.cpp +++ b/ecmascript/compiler/type_lowering.cpp @@ -19,6 +19,7 @@ #include "ecmascript/deoptimizer/deoptimizer.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_native_pointer.h" +#include "ecmascript/vtable.h" namespace panda::ecmascript::kungfu { void TypeLowering::RunTypeLowering() @@ -82,11 +83,17 @@ void TypeLowering::LowerType(GateRef gate) LowerTypedUnaryOp(gate); break; case OpCode::LOAD_PROPERTY: - LowerLoadProperty(gate, glue); + LowerLoadProperty(gate); + break; + case OpCode::CALL_GETTER: + LowerCallGetter(gate, glue); break; case OpCode::STORE_PROPERTY: LowerStoreProperty(gate, glue); break; + case OpCode::CALL_SETTER: + LowerCallSetter(gate, glue); + break; case OpCode::LOAD_ARRAY_LENGTH: LowerLoadArrayLength(gate); break; @@ -258,25 +265,53 @@ void TypeLowering::LowerObjectTypeCheck(GateRef gate) Environment env(gate, circuit_, &builder_); auto type = acc_.GetParamGateType(gate); if (tsManager_->IsClassInstanceTypeKind(type)) { - LowerClassInstanceCheck(gate); + LowerTSSubtypingCheck(gate); } else { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } -void TypeLowering::LowerClassInstanceCheck(GateRef gate) +void TypeLowering::LowerTSSubtypingCheck(GateRef gate) { GateRef frameState = GetFrameState(gate); ArgumentAccessor argAcc(circuit_); GateRef jsFunc = argAcc.GetCommonArgGate(CommonArgIdx::FUNC); - auto receiver = acc_.GetValueIn(gate, 0); - auto receiverHClass = builder_.LoadHClass(receiver); - auto hclassOffset = acc_.GetValueIn(gate, 1); - GateRef hclass = GetObjectFromConstPool(jsFunc, hclassOffset); - GateRef check = builder_.Equal(receiverHClass, hclass); - builder_.DeoptCheck(check, frameState, DeoptType::WRONGHCLASS); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef aotHCIndex = acc_.GetValueIn(gate, 1); + + Label receiverIsHeapObject(&builder_); + Label exit(&builder_); + + DEFVAlUE(check, (&builder_), VariableType::BOOL(), builder_.False()); + builder_.Branch(builder_.TaggedIsHeapObject(receiver), &receiverIsHeapObject, &exit); + + builder_.Bind(&receiverIsHeapObject); + { + JSTaggedValue aotHC = tsManager_->GetHClassFromCache(acc_.TryGetValue(aotHCIndex)); + ASSERT(aotHC.IsJSHClass()); + + int32_t level = JSHClass::Cast(aotHC.GetTaggedObject())->GetLevel(); + ASSERT(level >= 0); + GateRef levelGate = builder_.Int32(level); + + GateRef receiverHC = builder_.LoadHClass(receiver); + GateRef supers = LoadSupers(receiverHC); + GateRef length = GetLengthFromSupers(supers); + + // Nextly, consider remove level check by guaranteeing not read illegal addresses. + Label levelValid(&builder_); + builder_.Branch(builder_.Int32LessThan(levelGate, length), &levelValid, &exit); + builder_.Bind(&levelValid); + { + GateRef aotHCGate = GetObjectFromConstPool(jsFunc, aotHCIndex); + check = builder_.Equal(aotHCGate, GetValueFromSupers(supers, levelGate)); + builder_.Jump(&exit); + } + } + builder_.Bind(&exit); + builder_.DeoptCheck(*check, frameState, DeoptType::INCONSISTENTHCLASS); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } @@ -466,36 +501,111 @@ GateRef TypeLowering::GetObjectFromConstPool(GateRef jsFunc, GateRef index) return builder_.GetValueFromTaggedArray(constPool, index); } -void TypeLowering::LowerLoadProperty(GateRef gate, [[maybe_unused]] GateRef glue) +void TypeLowering::LowerLoadProperty(GateRef gate) { Environment env(gate, circuit_, &builder_); - Label hole(&builder_); - Label exit(&builder_); + ASSERT(acc_.GetNumValueIn(gate) == 2); // 2: receiver, plr DEFVAlUE(result, (&builder_), VariableType::JS_ANY(), builder_.HoleConstant()); GateRef receiver = acc_.GetValueIn(gate, 0); - GateRef offset = acc_.GetValueIn(gate, 1); - result = builder_.Load(VariableType::JS_ANY(), receiver, offset); - // simplify the process, need to query the vtable to complete the whole process later - builder_.Branch(builder_.IsSpecial(*result, JSTaggedValue::VALUE_HOLE), &hole, &exit); - builder_.Bind(&hole); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsLocal() || plr.IsFunction()); + GateRef offset = builder_.IntPtr(plr.GetOffset()); + + if (plr.IsLocal()) { + Label returnUndefined(&builder_); + Label exit(&builder_); + result = builder_.Load(VariableType::JS_ANY(), receiver, offset); + builder_.Branch(builder_.IsSpecial(*result, JSTaggedValue::VALUE_HOLE), &returnUndefined, &exit); + builder_.Bind(&returnUndefined); + { + result = builder_.UndefineConstant(); + builder_.Jump(&exit); + } + builder_.Bind(&exit); + } else { + GateRef vtable = LoadVTable(receiver); + GateRef itemOwner = GetOwnerFromVTable(vtable, offset); + GateRef itemOffset = GetOffsetFromVTable(vtable, offset); + result = builder_.Load(VariableType::JS_ANY(), itemOwner, itemOffset); + } + + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), *result); +} + +void TypeLowering::LowerCallGetter(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + ASSERT(acc_.GetNumValueIn(gate) == 2); // 2: receiver, plr + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsAccessor()); + GateRef offset = builder_.IntPtr(plr.GetOffset()); + GateRef vtable = LoadVTable(receiver); + GateRef itemOwner = GetOwnerFromVTable(vtable, offset); + GateRef itemOffset = GetOffsetFromVTable(vtable, offset); + + DEFVAlUE(result, (&builder_), VariableType::JS_ANY(), builder_.UndefineConstant()); + Label callGetter(&builder_); + Label exit(&builder_); + GateRef accessor = builder_.Load(VariableType::JS_ANY(), itemOwner, itemOffset); + GateRef getter = builder_.Load(VariableType::JS_ANY(), accessor, + builder_.IntPtr(AccessorData::GETTER_OFFSET)); + builder_.Branch(builder_.IsSpecial(getter, JSTaggedValue::VALUE_UNDEFINED), &exit, &callGetter); + builder_.Bind(&callGetter); { - result = builder_.UndefineConstant(); + result = CallAccessor(glue, gate, getter, receiver, AccessorMode::GETTER); builder_.Jump(&exit); } builder_.Bind(&exit); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), *result); + ReplaceHirWithPendingException(gate, glue, builder_.GetState(), builder_.GetDepend(), *result); } void TypeLowering::LowerStoreProperty(GateRef gate, GateRef glue) { Environment env(gate, circuit_, &builder_); + ASSERT(acc_.GetNumValueIn(gate) == 3); // 3: receiver, plr, value GateRef receiver = acc_.GetValueIn(gate, 0); - GateRef offset = acc_.GetValueIn(gate, 1); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); GateRef value = acc_.GetValueIn(gate, 2); + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsLocal()); + GateRef offset = builder_.IntPtr(plr.GetOffset()); builder_.Store(VariableType::JS_ANY(), glue, receiver, offset, value); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } +void TypeLowering::LowerCallSetter(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + ASSERT(acc_.GetNumValueIn(gate) == 3); // 3: receiver, plr, value + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + GateRef value = acc_.GetValueIn(gate, 2); + + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsAccessor()); + GateRef offset = builder_.IntPtr(plr.GetOffset()); + GateRef vtable = LoadVTable(receiver); + GateRef itemOwner = GetOwnerFromVTable(vtable, offset); + GateRef itemOffset = GetOffsetFromVTable(vtable, offset); + + Label callSetter(&builder_); + Label exit(&builder_); + GateRef accessor = builder_.Load(VariableType::JS_ANY(), itemOwner, itemOffset); + GateRef setter = builder_.Load(VariableType::JS_ANY(), accessor, builder_.IntPtr(AccessorData::SETTER_OFFSET)); + builder_.Branch(builder_.IsSpecial(setter, JSTaggedValue::VALUE_UNDEFINED), &exit, &callSetter); + builder_.Bind(&callSetter); + { + CallAccessor(glue, gate, setter, receiver, AccessorMode::SETTER, value); + builder_.Jump(&exit); + } + builder_.Bind(&exit); + ReplaceHirWithPendingException(gate, glue, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + void TypeLowering::LowerLoadArrayLength(GateRef gate) { Environment env(gate, circuit_, &builder_); @@ -2972,4 +3082,71 @@ void TypeLowering::LowerGetSuperConstructor(GateRef gate) GateRef superCtor = builder_.Load(VariableType::JS_ANY(), hclass, protoOffset); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), superCtor); } + +GateRef TypeLowering::LoadVTable(GateRef object) +{ + GateRef hclass = builder_.LoadHClass(object); + return builder_.Load(VariableType::JS_ANY(), hclass, builder_.IntPtr(JSHClass::VTABLE_OFFSET)); +} + +GateRef TypeLowering::GetOwnerFromVTable(GateRef vtable, GateRef offset) +{ + GateRef dataOffset = builder_.PtrAdd(offset, builder_.IntPtr(VTable::TupleItem::OWNER)); + return builder_.GetValueFromTaggedArray(vtable, dataOffset); +} + +GateRef TypeLowering::GetOffsetFromVTable(GateRef vtable, GateRef offset) +{ + GateRef dataOffset = builder_.PtrAdd(offset, builder_.IntPtr(VTable::TupleItem::OFFSET)); + return builder_.TaggedGetInt(builder_.GetValueFromTaggedArray(vtable, dataOffset)); +} + +GateRef TypeLowering::LoadSupers(GateRef hclass) +{ + return builder_.Load(VariableType::JS_ANY(), hclass, builder_.IntPtr(JSHClass::SUPERS_OFFSET)); +} + +GateRef TypeLowering::GetLengthFromSupers(GateRef supers) +{ + GateRef length = builder_.GetValueFromTaggedArray(supers, builder_.Int32(WeakVector::END_INDEX)); + return builder_.TaggedGetInt(length); +} + +GateRef TypeLowering::GetValueFromSupers(GateRef supers, GateRef index) +{ + GateRef val = builder_.GetValueFromTaggedArray(supers, + builder_.Int32Add(index, builder_.Int32(WeakVector::ELEMENTS_START_INDEX))); + return builder_.LoadObjectFromWeakRef(val); +} + +GateRef TypeLowering::CallAccessor(GateRef glue, GateRef gate, GateRef function, GateRef receiver, AccessorMode mode, + GateRef value) +{ + const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(JSCall)); + GateRef target = builder_.IntPtr(RTSTUB_ID(JSCall)); + GateRef envArg = builder_.Undefined(); + GateRef newTarget = builder_.Undefined(); + GateRef argc = builder_.Int64(NUM_MANDATORY_JSFUNC_ARGS + (mode == AccessorMode::SETTER ? 1 : 0)); // 1: value + std::vector args { glue, envArg, argc, function, newTarget, receiver }; + if (mode == AccessorMode::SETTER) { + args.emplace_back(value); + } + + return builder_.Call(cs, glue, target, builder_.GetDepend(), args, gate); +} + +void TypeLowering::ReplaceHirWithPendingException(GateRef hirGate, GateRef glue, GateRef state, GateRef depend, + GateRef value) +{ + auto condition = builder_.HasPendingException(glue); + GateRef ifBranch = builder_.Branch(state, condition); + GateRef ifTrue = builder_.IfTrue(ifBranch); + GateRef ifFalse = builder_.IfFalse(ifBranch); + GateRef eDepend = builder_.DependRelay(ifTrue, depend); + GateRef sDepend = builder_.DependRelay(ifFalse, depend); + + StateDepend success(ifFalse, sDepend); + StateDepend exception(ifTrue, eDepend); + acc_.ReplaceHirWithIfBranch(hirGate, success, exception, value); +} } // namespace panda::ecmascript diff --git a/ecmascript/compiler/type_lowering.h b/ecmascript/compiler/type_lowering.h index 2e5fdc1149..3f301320f4 100644 --- a/ecmascript/compiler/type_lowering.h +++ b/ecmascript/compiler/type_lowering.h @@ -179,15 +179,17 @@ private: void LowerTypedDecOverflowCheck(GateRef gate); void LowerTypedNegOverflowCheck(GateRef gate); void LowerObjectTypeCheck(GateRef gate); - void LowerClassInstanceCheck(GateRef gate); + void LowerTSSubtypingCheck(GateRef gate); void LowerFloat32ArrayCheck(GateRef gate, GateRef glue); void LowerArrayCheck(GateRef gate, GateRef glue); void LowerStableArrayCheck(GateRef gate, GateRef glue); void LowerTypedArrayCheck(GateRef gate, GateRef glue); void LowerFloat32ArrayIndexCheck(GateRef gate); void LowerArrayIndexCheck(GateRef gate); - void LowerLoadProperty(GateRef gate, GateRef glue); + void LowerLoadProperty(GateRef gate); + void LowerCallGetter(GateRef gate, GateRef glue); void LowerStoreProperty(GateRef gate, GateRef glue); + void LowerCallSetter(GateRef gate, GateRef glue); void LowerLoadArrayLength(GateRef gate); void LowerStoreElement(GateRef gate, GateRef glue); void LowerLoadElement(GateRef gate); @@ -208,6 +210,15 @@ private: GateRef LowerCallRuntime(GateRef glue, GateRef hirGate, int index, const std::vector &args, bool useLabel = false); + enum AccessorMode { + GETTER, + SETTER, + }; + + GateRef CallAccessor(GateRef glue, GateRef gate, GateRef function, GateRef receiver, AccessorMode mode, + GateRef value = Circuit::NullGate()); + void ReplaceHirWithPendingException(GateRef hirGate, GateRef glue, GateRef state, GateRef depend, GateRef value); + template GateRef CalculateNumbers(GateRef left, GateRef right, GateType leftType, GateType rightType); template @@ -247,6 +258,13 @@ private: return acc_.GetFrameState(gate); } + GateRef LoadVTable(GateRef object); + GateRef GetOwnerFromVTable(GateRef vtable, GateRef offset); + GateRef GetOffsetFromVTable(GateRef vtable, GateRef offset); + GateRef LoadSupers(GateRef hclass); + GateRef GetLengthFromSupers(GateRef supers); + GateRef GetValueFromSupers(GateRef supers, GateRef index); + Circuit *circuit_; GateAccessor acc_; CircuitBuilder builder_; diff --git a/ecmascript/deoptimizer/deoptimizer.cpp b/ecmascript/deoptimizer/deoptimizer.cpp index 966128a39f..f4174e9efc 100644 --- a/ecmascript/deoptimizer/deoptimizer.cpp +++ b/ecmascript/deoptimizer/deoptimizer.cpp @@ -262,8 +262,8 @@ std::string Deoptimizier::DisplayItems(kungfu::DeoptType type) return "NOT SARRAY"; case kungfu::DeoptType::NOTF32ARRAY: return "NOT F32ARRAY"; - case kungfu::DeoptType::WRONGHCLASS: - return "WRONG HCLASS"; + case kungfu::DeoptType::INCONSISTENTHCLASS: + return "INCONSISTENT HCLASS"; case kungfu::DeoptType::NOTNEWOBJ: return "NOT NEWOBJ TYPE"; case kungfu::DeoptType::NOTARRAYIDX: @@ -335,4 +335,4 @@ JSTaggedType Deoptimizier::ConstructAsmInterpretFrame(kungfu::DeoptType type) frameWriter.PushRawValue(outputCount); return reinterpret_cast(frameWriter.GetTop()); } -} // namespace panda::ecmascript \ No newline at end of file +} // namespace panda::ecmascript diff --git a/ecmascript/js_hclass-inl.h b/ecmascript/js_hclass-inl.h index 3e511bfdb6..6b12225f14 100644 --- a/ecmascript/js_hclass-inl.h +++ b/ecmascript/js_hclass-inl.h @@ -224,6 +224,15 @@ inline void JSHClass::Copy(const JSThread *thread, const JSHClass *jshclass) SetBitField(jshclass->GetBitField()); SetNumberOfProps(jshclass->NumberOfProps()); } + +inline int JSHClass::FindPropertyEntry(const JSThread *thread, JSHClass *hclass, JSTaggedValue key) +{ + DISALLOW_GARBAGE_COLLECTION; + LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + uint32_t propsNumber = hclass->NumberOfProps(); + int entry = layout->FindElementWithCache(thread, hclass, key, propsNumber); + return entry; +} } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_HCLASS_INL_H diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index d0950d34b4..3d9ab87994 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -24,6 +24,7 @@ #include "ecmascript/js_object-inl.h" #include "ecmascript/js_symbol.h" #include "ecmascript/mem/c_containers.h" +#include "ecmascript/subtyping_operator.h" #include "ecmascript/tagged_array-inl.h" #include "ecmascript/weak_vector.h" @@ -273,6 +274,11 @@ void JSHClass::AddProperty(const JSThread *thread, const JSHandle &obj JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass); #endif obj->SetClass(*newJsHClass); + + // Maintaining subtyping is no longer required when transition succeeds. + if (jshclass->HasTSSubtyping()) { + SubtypingOperator::TryMaintainTSSubtyping(thread, jshclass, newJsHClass, key); + } } JSHandle JSHClass::TransitionExtension(const JSThread *thread, const JSHandle &jshclass) @@ -522,14 +528,18 @@ JSHandle JSHClass::GetProtoChangeDetails(const JSThread *thr return GetProtoChangeDetails(thread, jshclass); } -void JSHClass::MarkProtoChanged([[maybe_unused]] const JSThread *thread, const JSHandle &jshclass) +void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle &jshclass) { - ASSERT(jshclass->IsPrototype() || jshclass->HasTSInheritInfo()); + ASSERT(jshclass->IsPrototype() || jshclass->HasTSSubtyping()); JSTaggedValue markerValue = jshclass->GetProtoChangeMarker(); if (markerValue.IsProtoChangeMarker()) { ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject()); protoChangeMarker->SetHasChanged(true); } + + if (jshclass->HasTSSubtyping()) { + jshclass->InitTSInheritInfo(thread); + } } void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle &jshclass) @@ -570,7 +580,7 @@ void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle &ol } } -bool JSHClass::HasTSInheritInfo() const +bool JSHClass::HasTSSubtyping() const { // if fill TS inherit info, supers must not be empty WeakVector *supers = WeakVector::Cast(GetSupers().GetTaggedObject()); @@ -579,6 +589,56 @@ bool JSHClass::HasTSInheritInfo() const bool JSHClass::IsTSIHCWithInheritInfo() const { - return IsTS() && !IsPrototype() && HasTSInheritInfo(); + return IsTS() && !IsPrototype() && HasTSSubtyping(); +} + +PropertyLookupResult JSHClass::LookupProperty(const JSThread *thread, JSHClass *hclass, JSTaggedValue key) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT(hclass->IsTS()); + + PropertyLookupResult result; + int entry = JSHClass::FindPropertyEntry(thread, hclass, key); + + // found in local + if (entry != -1) { + result.SetIsFound(true); + result.SetIsLocal(true); + uint32_t offset = hclass->GetInlinedPropertiesOffset(entry); + result.SetOffset(offset); + return result; + } + + // found in vtable + JSHandle vtable(thread, hclass->GetVTable()); + entry = vtable->GetTupleIndexByName(key); + if (entry != -1) { + result.SetIsVtable(); + uint32_t offset = entry * VTable::TUPLE_SIZE; + result.SetOffset(offset); + if (vtable->IsAccessor(entry)) { + result.SetIsAccessor(true); + } + return result; + } + + // not fuond + result.SetIsFound(false); + return result; +} + +void JSHClass::CopyTSInheritInfo(const JSThread *thread, const JSHandle &oldHClass, + JSHandle &newHClass) +{ + JSHandle supers(thread, oldHClass->GetSupers()); + JSHandle copySupers = WeakVector::Copy(thread, supers); + newHClass->SetSupers(thread, copySupers); + + uint8_t level = oldHClass->GetLevel(); + newHClass->SetLevel(level); + + JSHandle vtable(thread, oldHClass->GetVTable()); + JSHandle copyVtable = VTable::Copy(thread, vtable); + newHClass->SetVTable(thread, copyVtable); } } // namespace panda::ecmascript diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 9a8d7fce22..8631da1e0b 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -60,6 +60,7 @@ */ namespace panda::ecmascript { class ProtoChangeDetails; +class PropertyLookupResult; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define JSTYPE_DECL /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -369,10 +370,13 @@ public: void InitTSInheritInfo(const JSThread *thread); - bool HasTSInheritInfo() const; + bool HasTSSubtyping() const; bool IsTSIHCWithInheritInfo() const; + static void CopyTSInheritInfo(const JSThread *thread, const JSHandle &oldHClass, + JSHandle &newHClass); + inline void ClearBitField() { SetBitField(0UL); @@ -1577,6 +1581,11 @@ public: uint32_t bits = GetBitField1(); return HasDeletePropertyBit::Decode(bits); } + + inline static int FindPropertyEntry(const JSThread *thread, JSHClass *hclass, JSTaggedValue key); + + static PropertyLookupResult LookupProperty(const JSThread *thread, JSHClass *hclass, JSTaggedValue key); + static constexpr size_t PROTOTYPE_OFFSET = TaggedObjectSize(); ACCESSORS(Proto, PROTOTYPE_OFFSET, LAYOUT_OFFSET); ACCESSORS(Layout, LAYOUT_OFFSET, TRANSTIONS_OFFSET); @@ -1630,6 +1639,84 @@ private: friend class RuntimeStubs; }; static_assert(JSHClass::BIT_FIELD_OFFSET % static_cast(MemAlignment::MEM_ALIGN_OBJECT) == 0); + +// record property look up info in local and vtable +class PropertyLookupResult { +public: + using IsFoundBit = BitField; + using IsLocalBit = IsFoundBit::NextFlag; + using IsAccessorBit = IsLocalBit::NextFlag; + using OffsetBits = IsAccessorBit::NextField; + + explicit PropertyLookupResult(uint32_t data = 0) : data_(data) {} + ~PropertyLookupResult() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyLookupResult); + DEFAULT_COPY_SEMANTIC(PropertyLookupResult); + + inline bool IsFound() const + { + return IsFoundBit::Get(data_); + } + + inline void SetIsFound(bool flag) + { + IsFoundBit::Set(flag, &data_); + } + + inline bool IsLocal() const + { + return IsLocalBit::Get(data_); + } + + inline void SetIsLocal(bool flag) + { + IsLocalBit::Set(flag, &data_); + } + + inline bool IsVtable() const + { + return IsFound() && !IsLocal(); + } + + inline void SetIsVtable() + { + SetIsFound(true); + SetIsLocal(false); + } + + inline bool IsAccessor() const + { + return IsAccessorBit::Get(data_); + } + + inline void SetIsAccessor(bool flag) + { + IsAccessorBit::Set(flag, &data_); + } + + inline bool IsFunction() const + { + return IsVtable() && !IsAccessor(); + } + + inline uint32_t GetOffset() const + { + return OffsetBits::Get(data_); + } + + inline void SetOffset(uint32_t offset) + { + OffsetBits::Set(offset, &data_); + } + + inline uint32_t GetData() const + { + return data_; + } + +private: + uint32_t data_ {0}; +}; } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_HCLASS_H diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index 849d1911b2..da2fc87f49 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -162,6 +162,11 @@ JSHandle JSObject::TransitionToDictionary(const JSThread *thread if (i < numberInlinedProps) { value = receiver->GetPropertyInlinedProps(i); + // If delete a property in hclass which has subtyping info and not prototype, only set value as hole and + // not remove. When transition to dictionary, exclude it. + if (value.IsHole()) { + continue; + } } else { value = array->Get(i - numberInlinedProps); } @@ -282,7 +287,9 @@ void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle } if (!array->IsDictionaryMode()) { - if (obj->GetJSHClass()->IsTS()) { + JSHClass *hclass = obj->GetJSHClass(); + // To maintain TS inherit info, not change hclass, just set hole. + if (hclass->HasTSSubtyping() && !hclass->IsPrototype()) { obj->SetPropertyInlinedProps(thread, index, JSTaggedValue::Hole()); return; } diff --git a/ecmascript/module/js_module_source_text.cpp b/ecmascript/module/js_module_source_text.cpp index a157b9e207..d879c72731 100644 --- a/ecmascript/module/js_module_source_text.cpp +++ b/ecmascript/module/js_module_source_text.cpp @@ -161,7 +161,7 @@ JSHandle SourceTextModule::ResolveCjsExport(JSThread *thread, con // Get layoutInfo and compare the input and output names of files JSHandle layoutInfo(thread, jsHclass->GetLayout()); if (layoutInfo->NumberOfElements() != 0) { - JSHandle resolution = ResolveCjsLocalExport(thread, layoutInfo, exportName, module); + JSHandle resolution = ResolveCjsLocalExport(thread, jsHclass, exportName, module); if (!resolution->IsUndefined()) { return resolution; } @@ -393,7 +393,7 @@ int SourceTextModule::InnerModuleInstantiation(JSThread *thread, const JSHandle< ASSERT(moduleRecordName.IsString()); JSHandle requiredVal = SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, required); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX); requiredModule.Update(JSHandle::Cast(requiredVal)); requestedModules->Set(thread, idx, requiredModule->GetEcmaModuleRecordName()); } @@ -1215,13 +1215,12 @@ void SourceTextModule::AddExportName(JSThread *thread, const JSTaggedValue &expo } JSHandle SourceTextModule::ResolveCjsLocalExport(JSThread *thread, - JSHandle layoutInfo, + const JSHandle &hclass, const JSHandle &exportName, const JSHandle &module) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - int propertiesNumber = layoutInfo->NumberOfElements(); - int idx = layoutInfo->FindElementWithCache(thread, nullptr, exportName.GetTaggedValue(), propertiesNumber); + int idx = JSHClass::FindPropertyEntry(thread, *hclass, exportName.GetTaggedValue()); if (idx != -1) { return JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, idx)); } diff --git a/ecmascript/module/js_module_source_text.h b/ecmascript/module/js_module_source_text.h index d763a34e91..f0ef9c7d69 100644 --- a/ecmascript/module/js_module_source_text.h +++ b/ecmascript/module/js_module_source_text.h @@ -156,7 +156,7 @@ private: const JSHandle &exportName, const JSHandle &module); static JSHandle ResolveCjsLocalExport(JSThread *thread, - JSHandle layoutInfo, + const JSHandle &hclass, const JSHandle &exportName, const JSHandle &module); static bool CheckCircularImport(const JSHandle &module, diff --git a/ecmascript/object_fast_operator-inl.h b/ecmascript/object_fast_operator-inl.h index a0ae1ebb5f..6a4d171e17 100644 --- a/ecmascript/object_fast_operator-inl.h +++ b/ecmascript/object_fast_operator-inl.h @@ -67,10 +67,9 @@ JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedVa if (LIKELY(!hclass->IsDictionaryMode())) { ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); - LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); - uint32_t propsNumber = hclass->NumberOfProps(); - int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber); + int entry = JSHClass::FindPropertyEntry(thread, hclass, key); if (entry != -1) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); PropertyAttributes attr(layoutInfo->GetAttr(entry)); ASSERT(static_cast(attr.GetOffset()) == entry); auto value = JSObject::Cast(holder)->GetProperty(hclass, attr); @@ -138,11 +137,10 @@ JSTaggedValue ObjectFastOperator::SetPropertyByName(JSThread *thread, JSTaggedVa if (LIKELY(!hclass->IsDictionaryMode())) { ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); - LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); - uint32_t propsNumber = hclass->NumberOfProps(); - int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber); + int entry = JSHClass::FindPropertyEntry(thread, hclass, key); if (entry != -1) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); PropertyAttributes attr(layoutInfo->GetAttr(entry)); ASSERT(static_cast(attr.GetOffset()) == entry); if (UNLIKELY(attr.IsAccessor())) { @@ -798,4 +796,4 @@ bool ObjectFastOperator::GetNumFromString(const char *str, int len, int *index, return true; } } -#endif // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H \ No newline at end of file +#endif // ECMASCRIPT_OBJECT_FAST_OPERATOR_INL_H diff --git a/ecmascript/object_operator.cpp b/ecmascript/object_operator.cpp index dfe1613284..42ce0436cf 100644 --- a/ecmascript/object_operator.cpp +++ b/ecmascript/object_operator.cpp @@ -335,13 +335,12 @@ void ObjectOperator::LookupPropertyInlinedProps(const JSHandle &obj) TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); if (!array->IsDictionaryMode()) { JSHClass *jshclass = obj->GetJSHClass(); - JSTaggedValue attrs = jshclass->GetLayout(); - LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); - uint32_t propsNumber = jshclass->NumberOfProps(); - int entry = layoutInfo->FindElementWithCache(thread_, jshclass, key_.GetTaggedValue(), propsNumber); + int entry = JSHClass::FindPropertyEntry(thread_, jshclass, key_.GetTaggedValue()); if (entry == -1) { return; } + JSTaggedValue attrs = jshclass->GetLayout(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); PropertyAttributes attr(layoutInfo->GetAttr(entry)); ASSERT(entry == static_cast(attr.GetOffset())); JSTaggedValue value; diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 3c2446ed34..63fff72204 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -47,6 +47,7 @@ #include "ecmascript/message_string.h" #include "ecmascript/object_factory.h" #include "ecmascript/pgo_profiler/pgo_profiler.h" +#include "ecmascript/subtyping_operator.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tagged_node.h" #include "ecmascript/ts_types/ts_manager.h" @@ -345,6 +346,10 @@ DEF_RUNTIME_STUBS(UpdateLayOutAndAddTransition) // 5. Add newClass to old hclass's transitions. JSHClass::AddTransitions(thread, oldHClassHandle, newHClassHandle, keyHandle, attrValue); + + if (oldHClassHandle->HasTSSubtyping()) { + SubtypingOperator::TryMaintainTSSubtyping(thread, oldHClassHandle, newHClassHandle, keyHandle); + } return JSTaggedValue::Hole().GetRawData(); } diff --git a/ecmascript/subtyping_operator.cpp b/ecmascript/subtyping_operator.cpp index 9ffb423be7..49499d0b32 100644 --- a/ecmascript/subtyping_operator.cpp +++ b/ecmascript/subtyping_operator.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/global_env.h" +#include "ecmascript/ic/proto_change_details.h" #include "ecmascript/subtyping_operator-inl.h" #include "ecmascript/vtable.h" @@ -196,4 +197,41 @@ void SubtypingOperator::AddSuper(const JSThread *thread, const JSHandleSetSupers(thread, newSupers); } -} // namespace panda::ecmascript \ No newline at end of file + +// when add property in local, try maintain. +void SubtypingOperator::TryMaintainTSSubtyping(const JSThread *thread, const JSHandle &oldHClass, + JSHandle &newHClass, const JSHandle &key) +{ + if (!key->IsString()) { // symbol + return; + } + + ASSERT(!oldHClass->IsPrototype()); // normal object hclass + JSHandle vtable(thread, oldHClass->GetVTable()); + ASSERT(vtable->GetNumberOfTuples() > 0); // there have default key 'constructor' at least + + if (vtable->Find(key.GetTaggedValue())) { // new key shadows vtable property + LOG_ECMA(DEBUG) << "TryMaintainTSSubtyping failed, key: " + << ConvertToString(EcmaString::Cast(key->GetTaggedObject())); + return; + } + + // Add newHClass to phc's listeners + JSHandle prototype(thread, oldHClass->GetPrototype()); + ASSERT(prototype->IsClassPrototype()); + JSHandle phc(thread, prototype->GetTaggedObject()->GetClass()); + // If hclass has inherit info, it had been registered on proto chain, details and listeners must not be Undefined. + JSHandle details(thread, phc->GetProtoChangeDetails()); + JSHandle listeners(thread, details->GetChangeListener()); + uint32_t registerIndex = 0; + JSHandle newListeners = ChangeListener::Add(thread, listeners, newHClass, ®isterIndex); + if (UNLIKELY(registerIndex == TaggedArray::MAX_ARRAY_INDEX)) { + return; + } + + // maintaining succeeds + details->SetChangeListener(thread, newListeners); + + JSHClass::CopyTSInheritInfo(thread, oldHClass, newHClass); +} +} // namespace panda::ecmascript diff --git a/ecmascript/subtyping_operator.h b/ecmascript/subtyping_operator.h index f2bf4c7228..f339256f54 100644 --- a/ecmascript/subtyping_operator.h +++ b/ecmascript/subtyping_operator.h @@ -32,6 +32,9 @@ public: static void GenVTable(const JSThread *thread, const JSHandle &ihcHandle, const JSHandle &phcHandle, const JSHandle &eIhcHandle); + + static void TryMaintainTSSubtyping(const JSThread *thread, const JSHandle &oldHClass, + JSHandle &newHClass, const JSHandle &key); private: static constexpr uint8_t MAX_LEVEL = 1 << JSHClass::LEVEL_BTTFIELD_NUM; @@ -80,4 +83,4 @@ private: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_SUBTYPING_OPERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_SUBTYPING_OPERATOR_H diff --git a/ecmascript/ts_types/ts_manager.cpp b/ecmascript/ts_types/ts_manager.cpp index 42ef3eb847..940b289aa6 100644 --- a/ecmascript/ts_types/ts_manager.cpp +++ b/ecmascript/ts_types/ts_manager.cpp @@ -142,21 +142,6 @@ JSTaggedValue TSManager::GetHClassFromCache(uint32_t index) return JSTaggedValue(hcVec[index - constantPool->GetCacheLength()]); } -int TSManager::GetPropertyOffset(JSTaggedValue hclass, JSTaggedValue key) -{ - JSHClass *hc = JSHClass::Cast(hclass.GetTaggedObject()); - LayoutInfo *layoutInfo = LayoutInfo::Cast(hc->GetLayout().GetTaggedObject()); - uint32_t propsNumber = hc->NumberOfProps(); - int entry = layoutInfo->FindElementWithCache(thread_, hc, key, propsNumber); - if (entry == -1) { - return entry; - } - - int offset = hc->GetInlinedPropertiesOffset(entry); - return offset; -} - - JSHandle TSManager::GetExtendedClassType(JSHandle classType) const { ASSERT(classType.GetTaggedValue().IsTSClassType()); diff --git a/ecmascript/ts_types/ts_manager.h b/ecmascript/ts_types/ts_manager.h index 6875ef8b63..aa251f729d 100644 --- a/ecmascript/ts_types/ts_manager.h +++ b/ecmascript/ts_types/ts_manager.h @@ -522,9 +522,6 @@ public: (l == static_cast(TSRuntimeType::ITERATOR_RESULT)); } - // not consider [[prototype]] properties and accessor, -1: not find - int PUBLIC_API GetPropertyOffset(JSTaggedValue hclass, JSTaggedValue key); - void PUBLIC_API SetCurConstantPool(const JSPandaFile *jsPandaFile, uint32_t methodOffset); JSHandle PUBLIC_API GetConstantPool() const diff --git a/ecmascript/weak_vector.h b/ecmascript/weak_vector.h index 663d0b542b..f152dcebb9 100644 --- a/ecmascript/weak_vector.h +++ b/ecmascript/weak_vector.h @@ -35,6 +35,8 @@ public: static constexpr uint32_t DEFAULT_CAPACITY = 4; static constexpr uint32_t DEFAULT_GROW_SIZE = 5; + static constexpr uint32_t END_INDEX = 0; + static constexpr uint32_t ELEMENTS_START_INDEX = 1; static JSHandle Create(const JSThread *thread, uint32_t capacity = DEFAULT_CAPACITY); static JSHandle Grow(const JSThread *thread, const JSHandle &old, uint32_t newCapacity); static JSHandle Append(const JSThread *thread, const JSHandle &vec, @@ -89,8 +91,6 @@ public: private: static const uint32_t MIN_CAPACITY = 2; - static const uint32_t END_INDEX = 0; - static const uint32_t ELEMENTS_START_INDEX = 1; static const uint32_t MAX_VECTOR_INDEX = TaggedArray::MAX_ARRAY_INDEX - ELEMENTS_START_INDEX; inline static constexpr uint32_t VectorToArrayIndex(uint32_t index) diff --git a/test/aottest/aot_compatibility_test/BUILD.gn b/test/aottest/aot_compatibility_test/BUILD.gn index 4587a0be68..e9f45399a3 100644 --- a/test/aottest/aot_compatibility_test/BUILD.gn +++ b/test/aottest/aot_compatibility_test/BUILD.gn @@ -16,11 +16,10 @@ group("aot_compatibility_test") { test_list = [ "base_verification", + "builtins_api", + "property_operation", "prototype_accessor", "prototype_base_verification", - - #"builtins_api", - #"property_operation", ] deps = [] diff --git a/test/aottest/aot_compatibility_test/property_operation/expect_output.txt b/test/aottest/aot_compatibility_test/property_operation/expect_output.txt index b5992c94f4..7dc4798f95 100644 --- a/test/aottest/aot_compatibility_test/property_operation/expect_output.txt +++ b/test/aottest/aot_compatibility_test/property_operation/expect_output.txt @@ -26,4 +26,4 @@ true true true xxxx -TypeError: CallObj is NonCallable +true diff --git a/test/aottest/aot_compatibility_test/property_operation/property_operation.ts b/test/aottest/aot_compatibility_test/property_operation/property_operation.ts index 2dc42a34dd..6e79e787ed 100644 --- a/test/aottest/aot_compatibility_test/property_operation/property_operation.ts +++ b/test/aottest/aot_compatibility_test/property_operation/property_operation.ts @@ -77,6 +77,6 @@ declare function print(arg:any):string; try { b5.foo(); } catch(e) { - print(e); + print(e instanceof TypeError); } }