From aa35c2dab0fb1c67a5217c850ca1cdb8636e36d1 Mon Sep 17 00:00:00 2001 From: zhangyinlu Date: Tue, 1 Jul 2025 15:35:42 +0800 Subject: [PATCH] =?UTF-8?q?Proxy=20GetPrototype&HasProperty=20IR=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/ICJFSW Description:Proxy GetPrototype&HasProperty IR化 Change-Id: I1b770fdedb24b716d36e141cac1e91827e09f3aa Signed-off-by: zhangyinlu --- .../builtins/builtins_proxy_stub_builder.cpp | 169 +++++++++++++++ .../builtins/builtins_proxy_stub_builder.h | 2 + ecmascript/compiler/call_signature.cpp | 29 +++ ecmascript/compiler/call_signature.h | 2 + ecmascript/compiler/circuit_builder.cpp | 4 +- ecmascript/compiler/circuit_builder.h | 2 +- ecmascript/compiler/common_stub_csigns.h | 2 + ecmascript/compiler/common_stubs.cpp | 19 ++ .../compiler/object_operator_stub_builder.cpp | 2 +- ecmascript/compiler/slowpath_lowering.cpp | 10 +- ecmascript/compiler/stub_builder-inl.h | 4 +- ecmascript/compiler/stub_builder.cpp | 8 +- ecmascript/compiler/stub_builder.h | 2 +- ecmascript/compiler/typed_hcr_lowering.cpp | 2 +- .../compiler/typed_native_inline_lowering.cpp | 6 +- ecmascript/message_string.h | 8 + ecmascript/stubs/runtime_stub_list.h | 2 +- ecmascript/stubs/runtime_stubs.cpp | 4 +- test/moduletest/BUILD.gn | 2 + test/moduletest/proxygetprototype/BUILD.gn | 18 ++ .../proxygetprototype/expect_output.txt | 25 +++ .../proxygetprototype/proxygetprototype.js | 196 ++++++++++++++++++ test/moduletest/proxyhasproperty/BUILD.gn | 18 ++ .../proxyhasproperty/expect_output.txt | 29 +++ .../proxyhasproperty/proxyhasproperty.js | 154 ++++++++++++++ 25 files changed, 696 insertions(+), 23 deletions(-) create mode 100644 test/moduletest/proxygetprototype/BUILD.gn create mode 100644 test/moduletest/proxygetprototype/expect_output.txt create mode 100644 test/moduletest/proxygetprototype/proxygetprototype.js create mode 100644 test/moduletest/proxyhasproperty/BUILD.gn create mode 100644 test/moduletest/proxyhasproperty/expect_output.txt create mode 100644 test/moduletest/proxyhasproperty/proxyhasproperty.js diff --git a/ecmascript/compiler/builtins/builtins_proxy_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_proxy_stub_builder.cpp index 92dba9d4c8..3ce73a7f8e 100644 --- a/ecmascript/compiler/builtins/builtins_proxy_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_proxy_stub_builder.cpp @@ -304,4 +304,173 @@ GateRef BuiltinsProxyStubBuilder::SetProperty(GateRef proxy, GateRef key, GateRe auto ret = *result; return ret; } + +GateRef BuiltinsProxyStubBuilder::HasProperty(GateRef proxy, GateRef key) +{ + auto env = GetEnvironment(); + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + Label callExit(env); + Label exit(env); + Label handlerIsNull(env); + Label handlerIsNotNull(env); + Label slowPath(env); + Label trapIsCallable(env); + Label trapIsHeapObject(env); + Label booleanTrapResultFalse(env); + + GateRef handler = GetHandler(glue_, proxy); + BRANCH(TaggedIsNull(handler), &handlerIsNull, &handlerIsNotNull); + Bind(&handlerIsNull); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPropertyHandlerIsNull)); + CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + result = Exception(); + Jump(&exit); + } + Bind(&handlerIsNotNull); + { + GateRef target = GetTarget(glue_, proxy); + GateRef name = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, + ConstantIndex::HAS_STRING_INDEX); + GateRef trap = GetPropertyByName(glue_, handler, name); + BRANCH(TaggedIsHeapObject(trap), &trapIsHeapObject, &slowPath); + Bind(&trapIsHeapObject); + BRANCH(IsCallable(glue_, trap), &trapIsCallable, &slowPath); + Bind(&trapIsCallable); + { + JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG2_WITH_RETURN); + callArgs.callThisArg2WithReturnArgs = { handler, target, key }; + CallStubBuilder callBuilder(this, glue_, trap, + Int32(2), 0, nullptr, Circuit::NullGate(), callArgs); // 2 : two arg + result = FastToBoolean(glue_, callBuilder.JSCallDispatch()); + BRANCH(TaggedIsFalse(*result), &booleanTrapResultFalse, &exit); + Bind(&booleanTrapResultFalse); + { + Label isFound(env); + DEFVARIABLE(value, VariableType::JS_ANY(), Hole()); + DEFVARIABLE(attr, VariableType::INT64(), Int64(0)); + TryGetOwnProperty(glue_, target, key, Circuit::NullGate(), &value, &attr, + &isFound, &isFound, &exit, &slowPath); + Bind(&isFound); + { + Label trapResultTypeError(env); + Label targetExtensibleCheck(env); + Label targetNotExtensible(env); + GateRef rAttr = attr.ReadVariable(); + BRANCH(BoolNot(IsConfigable(rAttr)), &trapResultTypeError, &targetExtensibleCheck); + Bind(&trapResultTypeError); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPropertyHandlerIsNull)); + CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + result = Exception(); + Jump(&exit); + } + Bind(&targetExtensibleCheck); + BRANCH(BoolNot(IsExtensible(glue_, target)), &targetNotExtensible, &exit); + Bind(&targetNotExtensible); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPropertyHandlerIsNull)); + CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + result = Exception(); + Jump(&exit); + } + } + } + } + Bind(&slowPath); + { + result = CallRuntime(glue_, RTSTUB_ID(JSProxyHasProperty), { proxy, key }); + Jump(&exit); + } + } + Bind(&exit); + auto ret = *result; + return ret; +} + +GateRef BuiltinsProxyStubBuilder::GetPrototype(GateRef proxy) +{ + auto env = GetEnvironment(); + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + Label callExit(env); + Label checkGetTrapResult(env); + Label exit(env); + Label handlerIsNull(env); + Label handlerIsNotNull(env); + Label slowPath(env); + Label trapIsCallable(env); + Label trapIsHeapObject(env); + Label handlerProtoIsNotEcmaObjectOrNull(env); + Label handlerProtoIsEcmaObject(env); + Label checkHandlerProtoSameWithTargetProto(env); + Label handlerProtoIsNotSameWithTargetProto(env); + Label setResult(env); + + GateRef handler = GetHandler(glue_, proxy); + BRANCH(TaggedIsNull(handler), &handlerIsNull, &handlerIsNotNull); + Bind(&handlerIsNull); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPrototypeHandlerIsNull)); + CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + result = Exception(); + Jump(&exit); + } + Bind(&handlerIsNotNull); + { + GateRef target = GetTarget(glue_, proxy); + GateRef name = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, + ConstantIndex::GETPROTOTYPEOF_STRING_INDEX); + GateRef trap = GetPropertyByName(glue_, handler, name); + BRANCH(TaggedIsHeapObject(trap), &trapIsHeapObject, &slowPath); + Bind(&trapIsHeapObject); + BRANCH(IsCallable(glue_, trap), &trapIsCallable, &slowPath); + Bind(&trapIsCallable); + { + JSCallArgs callArgs(JSCallMode::CALL_SETTER); + callArgs.callSetterArgs = { handler, target }; + CallStubBuilder callBuilder(this, glue_, trap, Int32(1), 0, nullptr, Circuit::NullGate(), callArgs); + GateRef proto = callBuilder.JSCallDispatch(); + GateRef handlerProtoIsNotEcmaObject = BoolNot(LogicAndBuilder(env).And(TaggedIsHeapObject(proto)) + .And(TaggedObjectIsEcmaObject(glue_, proto)).Done()); + BRANCH(BitAnd(handlerProtoIsNotEcmaObject, TaggedIsNotNull(proto)), &handlerProtoIsNotEcmaObjectOrNull, + &handlerProtoIsEcmaObject) + Bind(&handlerProtoIsNotEcmaObjectOrNull); + { + auto taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPrototypeHandlerProtoIsNeitherObjectNorNull)); + CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + result = Exception(); + Jump(&exit); + } + Bind(&handlerProtoIsEcmaObject); + { + BRANCH(IsExtensible(glue_, target), &setResult, &checkHandlerProtoSameWithTargetProto); + Bind(&checkHandlerProtoSameWithTargetProto); + { + GateRef targetProto = StubBuilder::GetPrototype(glue_, target); + BRANCH(SameValue(glue_, proto, targetProto), &setResult, &handlerProtoIsNotSameWithTargetProto); + Bind(&handlerProtoIsNotSameWithTargetProto); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPrototypeHandlerProtoNotSame)); + CallRuntime(glue_, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + result = Exception(); + Jump(&exit); + } + } + } + Bind(&setResult); + { + result = proto; + Jump(&exit); + } + } + Bind(&slowPath); + { + result = CallRuntime(glue_, RTSTUB_ID(JSProxyGetPrototype), { proxy }); + Jump(&exit); + } + } + Bind(&exit); + auto ret = *result; + return ret; +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/builtins_proxy_stub_builder.h b/ecmascript/compiler/builtins/builtins_proxy_stub_builder.h index 0479547e8a..fdb21a9d88 100644 --- a/ecmascript/compiler/builtins/builtins_proxy_stub_builder.h +++ b/ecmascript/compiler/builtins/builtins_proxy_stub_builder.h @@ -35,6 +35,8 @@ public: void GenProxyConstructor(GateRef nativeCode, GateRef func, GateRef newTarget); GateRef GetProperty(GateRef proxy, GateRef key, GateRef receiver); GateRef SetProperty(GateRef proxy, GateRef key, GateRef value, GateRef receiver, bool mayThrow = true); + GateRef HasProperty(GateRef proxy, GateRef key); + GateRef GetPrototype(GateRef proxy); void CheckGetTrapResult(GateRef target, GateRef key, Variable *result, Label *exit); void CheckSetTrapResult(GateRef target, GateRef key, GateRef value, Variable *result, Label *exit); diff --git a/ecmascript/compiler/call_signature.cpp b/ecmascript/compiler/call_signature.cpp index 6bf93385a0..be792e3e1f 100644 --- a/ecmascript/compiler/call_signature.cpp +++ b/ecmascript/compiler/call_signature.cpp @@ -3836,6 +3836,35 @@ DEF_CALL_SIGNATURE(JSProxySetPropertyNoThrow) ; } +DEF_CALL_SIGNATURE(JSProxyHasProperty) +{ + constexpr size_t paramCount = 3; + CallSignature signature("JSProxyHasProperty", 0, paramCount, ArgumentsOrder::DEFAULT_ORDER, + VariableType::JS_ANY()); + *callSign = signature; + std::array params = { + VariableType::NATIVE_POINTER(), // glue + VariableType::JS_ANY(), // proxy + VariableType::JS_ANY(), // key + }; + callSign->SetParameters(params.data()); + callSign->SetCallConv(CallSignature::CallConv::CCallConv);; +} + +DEF_CALL_SIGNATURE(JSProxyGetPrototype) +{ + constexpr size_t paramCount = 2; + CallSignature signature("JSProxyGetPrototype", 0, paramCount, ArgumentsOrder::DEFAULT_ORDER, + VariableType::JS_ANY()); + *callSign = signature; + std::array params = { + VariableType::NATIVE_POINTER(), // glue + VariableType::JS_ANY(), // proxy + }; + callSign->SetParameters(params.data()); + callSign->SetCallConv(CallSignature::CallConv::CCallConv);; +} + DEF_CALL_SIGNATURE(FindPatchModule) { // 2 : 2 input parameters diff --git a/ecmascript/compiler/call_signature.h b/ecmascript/compiler/call_signature.h index 59c932acec..48aef3c645 100644 --- a/ecmascript/compiler/call_signature.h +++ b/ecmascript/compiler/call_signature.h @@ -608,6 +608,8 @@ private: V(JSProxyGetProperty) \ V(JSProxySetProperty) \ V(JSProxySetPropertyNoThrow) \ + V(JSProxyHasProperty) \ + V(JSProxyGetPrototype) \ V(GrowElementsCapacity) \ V(CreateJSTypedArrayEntries) \ V(CreateJSTypedArrayKeys) \ diff --git a/ecmascript/compiler/circuit_builder.cpp b/ecmascript/compiler/circuit_builder.cpp index 593f1e1166..f1120b323a 100644 --- a/ecmascript/compiler/circuit_builder.cpp +++ b/ecmascript/compiler/circuit_builder.cpp @@ -1410,7 +1410,7 @@ GateRef CircuitBuilder::ToObject(GateRef glue, GateRef obj) return ret; } -GateRef CircuitBuilder::GetPrototype(GateRef glue, GateRef object) +GateRef CircuitBuilder::GetPrototype(GateRef glue, GateRef object, GateRef hir) { Label entry(env_); env_->SubCfgEntry(&entry); @@ -1437,7 +1437,7 @@ GateRef CircuitBuilder::GetPrototype(GateRef glue, GateRef object) BRANCH(IsJsProxy(glue, object), &objectIsJsProxy, &objectNotIsJsProxy); Bind(&objectIsJsProxy); { - result = CallRuntime(glue, RTSTUB_ID(CallGetPrototype), Gate::InvalidGateRef, { object }, glue); + result = CallCommonStub(glue, hir, CommonStubCSigns::JSProxyGetPrototype, { glue, object }); Jump(&exit); } Bind(&objectNotIsJsProxy); diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index 65cf6fe1fc..2eb1f0eec7 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -782,7 +782,7 @@ public: GateRef BuildBigIntAsIntN(const GateMetaData* op, std::vector &&args); GateRef NewJSPrimitiveRef(GateRef glue, size_t index, GateRef obj); GateRef ToObject(GateRef glue, GateRef obj); - GateRef GetPrototype(GateRef glue, GateRef object); + GateRef GetPrototype(GateRef glue, GateRef object, GateRef hir = Circuit::NullGate()); GateRef GetGlobalConstantValue(VariableType type, GateRef glue, ConstantIndex index); GateRef TransProtoWithoutLayout(GateRef glue, GateRef hClass, GateRef proto); diff --git a/ecmascript/compiler/common_stub_csigns.h b/ecmascript/compiler/common_stub_csigns.h index e7d40eb11b..e185c24190 100644 --- a/ecmascript/compiler/common_stub_csigns.h +++ b/ecmascript/compiler/common_stub_csigns.h @@ -110,6 +110,8 @@ namespace panda::ecmascript::kungfu { V(JSProxyGetProperty) \ V(JSProxySetProperty) \ V(JSProxySetPropertyNoThrow) \ + V(JSProxyHasProperty) \ + V(JSProxyGetPrototype) \ V(CreateJSTypedArrayEntries) \ V(CreateJSTypedArrayKeys) \ V(CreateJSTypedArrayValues) \ diff --git a/ecmascript/compiler/common_stubs.cpp b/ecmascript/compiler/common_stubs.cpp index 42374a64eb..bcc8ca2a1b 100644 --- a/ecmascript/compiler/common_stubs.cpp +++ b/ecmascript/compiler/common_stubs.cpp @@ -1737,6 +1737,25 @@ void JSProxySetPropertyNoThrowStubBuilder::GenerateCircuit() Return(result); } +void JSProxyHasPropertyStubBuilder::GenerateCircuit() +{ + GateRef glue = PtrArgument(0); + GateRef holder = TaggedArgument(1); + GateRef key = TaggedArgument(2U); + BuiltinsProxyStubBuilder proxyStubBuilder(this, glue, GetGlobalEnv(glue)); + GateRef result = proxyStubBuilder.HasProperty(holder, key); + Return(result); +} + +void JSProxyGetPrototypeStubBuilder::GenerateCircuit() +{ + GateRef glue = PtrArgument(0); + GateRef proxy = TaggedArgument(1); + BuiltinsProxyStubBuilder proxyStubBuilder(this, glue, GetGlobalEnv(glue)); + GateRef result = proxyStubBuilder.GetPrototype(proxy); + Return(result); +} + void CreateJSTypedArrayEntriesStubBuilder::GenerateCircuit() { auto env = GetEnvironment(); diff --git a/ecmascript/compiler/object_operator_stub_builder.cpp b/ecmascript/compiler/object_operator_stub_builder.cpp index e2bbcc9aba..286ff30e81 100644 --- a/ecmascript/compiler/object_operator_stub_builder.cpp +++ b/ecmascript/compiler/object_operator_stub_builder.cpp @@ -568,7 +568,7 @@ void ObjectOperatorStubBuilder::TryLookupInProtoChain(GateRef glue, GateRef key, { Label noPendingException(env); Label notJSProxy(env); - GateRef proto = GetPrototype(glue, results.GetHolder()); + GateRef proto = GetPrototype(glue, results.GetHolder(), hir); BRANCH_UNLIKELY(HasPendingException(glue), &loopExit, &noPendingException); Bind(&noPendingException); diff --git a/ecmascript/compiler/slowpath_lowering.cpp b/ecmascript/compiler/slowpath_lowering.cpp index ba43fad24f..f0a92ddc1a 100644 --- a/ecmascript/compiler/slowpath_lowering.cpp +++ b/ecmascript/compiler/slowpath_lowering.cpp @@ -1907,7 +1907,7 @@ void SlowPathLowering::LowerSuperCall(GateRef gate) GateRef taggedLength = builder_.ToTaggedInt(builder_.Int64(length)); GateRef taggedArray = GetTaggedArrayFromValueIn(&env, gate, length); GateRef func = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::FUNC); - GateRef superFunc = objBuilder.GetPrototype(glue_, func); + GateRef superFunc = objBuilder.GetPrototype(glue_, func, gate); CheckSuperAndNewTarget(objBuilder, superFunc, newTarget, thisObj, fastPath, slowPath); builder_.Bind(&fastPath); @@ -1942,7 +1942,7 @@ void SlowPathLowering::LowerSuperCallForJIT(GateRef gate) GateRef taggedLength = builder_.ToTaggedInt(builder_.Int64(length)); GateRef taggedArray = GetTaggedArrayFromValueIn(&env, gate, length); GateRef func = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::FUNC); - GateRef superFunc = objBuilder.GetPrototype(glue_, func); + GateRef superFunc = objBuilder.GetPrototype(glue_, func, gate); GateRef ret = CheckSuperAndNewTargetForJIT(gate, superFunc, newTarget, fastPath, slowPath); builder_.Bind(&fastPath); @@ -1993,7 +1993,7 @@ void SlowPathLowering::LowerSuperCallSpread(GateRef gate) GateRef array = acc_.GetValueIn(gate, 0); GateRef func = acc_.GetValueIn(gate, 1); - GateRef superFunc = objBuilder.GetPrototype(glue_, func); + GateRef superFunc = objBuilder.GetPrototype(glue_, func, gate); CheckSuperAndNewTarget(objBuilder, superFunc, newTarget, thisObj, fastPath, slowPath); builder_.Bind(&fastPath); { @@ -2150,7 +2150,7 @@ void SlowPathLowering::LowerSuperCallForwardAllArgs(GateRef gate) GateRef func = acc_.GetValueIn(gate, 0); GateRef actualArgc = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::ACTUAL_ARGC); GateRef actualArgv = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::ACTUAL_ARGV); - GateRef super = objBuilder.GetPrototype(glue_, func); + GateRef super = objBuilder.GetPrototype(glue_, func, gate); Label fastPath(&builder_); Label fastPathWithArgv(&builder_); Label callExit(&builder_); @@ -3669,7 +3669,7 @@ void SlowPathLowering::LowerCallNewBuiltin(GateRef gate) args[i] = acc_.GetValueIn(gate, i); } ASSERT(num >= 3); // 3: skip argc argv newtarget - + GateRef ctor = acc_.GetValueIn(gate, static_cast(CommonArgIdx::FUNC)); if (g_isEnableCMCGC) { builder_.CallNGCRuntime(glue_, RTSTUB_ID(CopyCallTarget), Gate::InvalidGateRef, {glue_, ctor}, glue_); diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index a6df5cc620..b8d188b053 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -4417,9 +4417,9 @@ inline GateRef StubBuilder::ToObject(GateRef glue, GateRef obj) return env_->GetBuilder()->ToObject(glue, obj); } -inline GateRef StubBuilder::GetPrototype(GateRef glue, GateRef object) +inline GateRef StubBuilder::GetPrototype(GateRef glue, GateRef object, GateRef hir) { - return env_->GetBuilder()->GetPrototype(glue, object); + return env_->GetBuilder()->GetPrototype(glue, object, hir); } inline GateRef StubBuilder::GetLengthOfJSArray(GateRef array) diff --git a/ecmascript/compiler/stub_builder.cpp b/ecmascript/compiler/stub_builder.cpp index 1097851a39..f623899939 100644 --- a/ecmascript/compiler/stub_builder.cpp +++ b/ecmascript/compiler/stub_builder.cpp @@ -1059,7 +1059,7 @@ GateRef StubBuilder::JSObjectHasProperty(GateRef glue, GateRef obj, GateRef key, opStubBuilder.StartLookup( glue, key, &checkHolder, opOptions, opResult, hir); - // if holder is JSProxy, op's lookup will return. This will be handled by CallRuntime. + // if holder is JSProxy, op's lookup will return. This will be handled by CallRuntime. Bind(&checkHolder); { Label isJSProxy(env); @@ -1067,7 +1067,7 @@ GateRef StubBuilder::JSObjectHasProperty(GateRef glue, GateRef obj, GateRef key, BRANCH(TaggedIsJSProxy(glue, opResult.GetHolder()), &isJSProxy, &isFound); Bind(&isJSProxy); { - result = CallRuntime(glue, RTSTUB_ID(JSProxyHasProperty), {opResult.GetHolder(), key}); + result = CallRuntime(glue, RTSTUB_ID(JSProxyHasProperty), { opResult.GetHolder(), key }); Jump(&exit); } Bind(&isFound); @@ -8901,7 +8901,7 @@ GateRef StubBuilder::HasProperty(GateRef glue, GateRef obj, GateRef key, GateRef BRANCH(IsJsProxy(glue, obj), &isJSProxy, ¬JSProxy); Bind(&isJSProxy); { - result = CallRuntime(glue, RTSTUB_ID(JSProxyHasProperty), {obj, key}); + result = CallCommonStub(glue, CommonStubCSigns::JSProxyHasProperty, { glue, obj, key }, hir); Jump(&exit); } @@ -12509,7 +12509,7 @@ void StubBuilder::EndTraceLoadValue([[maybe_unused]]GateRef glue) #endif } -void StubBuilder::StartTraceCallDetail([[maybe_unused]] GateRef glue, [[maybe_unused]] GateRef profileTypeInfo, +void StubBuilder::StartTraceCallDetail([[maybe_unused]] GateRef glue, [[maybe_unused]] GateRef profileTypeInfo, [[maybe_unused]] GateRef slotId) { #if ECMASCRIPT_ENABLE_TRACE_CALL diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index 1065dc7836..a6c7df9ae6 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -548,7 +548,7 @@ public: GateRef GetStoreAOTHandlerHolder(GateRef glue, GateRef object); GateRef GetStoreAOTHandlerHandlerInfo(GateRef glue, GateRef object); inline GateRef GetLengthOfJSArray(GateRef array); - inline GateRef GetPrototype(GateRef glue, GateRef object); + inline GateRef GetPrototype(GateRef glue, GateRef object, GateRef hir = Circuit::NullGate()); inline GateRef GetHasChanged(GateRef object); inline GateRef GetNotFoundHasChanged(GateRef object); GateRef HclassIsPrototypeHandler(GateRef hClass); diff --git a/ecmascript/compiler/typed_hcr_lowering.cpp b/ecmascript/compiler/typed_hcr_lowering.cpp index 281c43bf59..a395a66760 100644 --- a/ecmascript/compiler/typed_hcr_lowering.cpp +++ b/ecmascript/compiler/typed_hcr_lowering.cpp @@ -2848,7 +2848,7 @@ void TypedHCRLowering::LowerOrdinaryHasInstance(GateRef gate, GateRef glue) BRANCH_CIR(builder_.IsJsProxy(glue, *object), &objectIsJsProxy, &objectNotIsJsProxy); builder_.Bind(&objectIsJsProxy); { - object = builder_.CallRuntime(glue, RTSTUB_ID(CallGetPrototype), Gate::InvalidGateRef, + object = builder_.CallRuntime(glue, RTSTUB_ID(JSProxyGetPrototype), Gate::InvalidGateRef, { *object }, gate); builder_.Jump(&shouldContinue); } diff --git a/ecmascript/compiler/typed_native_inline_lowering.cpp b/ecmascript/compiler/typed_native_inline_lowering.cpp index c55e501faf..9b6c432058 100644 --- a/ecmascript/compiler/typed_native_inline_lowering.cpp +++ b/ecmascript/compiler/typed_native_inline_lowering.cpp @@ -2511,7 +2511,7 @@ void TypedNativeInlineLowering::LowerObjectGetPrototypeOf(GateRef gate) result = builder_.GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv, index); } else { GateRef object = builder_.ToObject(glue, value); - result = builder_.GetPrototype(glue, object); + result = builder_.GetPrototype(glue, object, gate); } acc_.ReplaceGate(gate, builder_.GetStateDepend(), result); @@ -2587,7 +2587,7 @@ void TypedNativeInlineLowering::LowerObjectIsPrototypeOf(GateRef gate) BRANCH_CIR(builder_.TaggedIsNotNull(*proto), &compare, &loopExit); builder_.Bind(&compare); { - proto = builder_.GetPrototype(glue, *proto); + proto = builder_.GetPrototype(glue, *proto, gate); Label noPendingException1(&builder_); BRANCH_CIR(builder_.HasPendingException(glue, compilationEnv_), &returnException, &noPendingException1); @@ -2653,7 +2653,7 @@ void TypedNativeInlineLowering::LowerReflectGetPrototypeOf(GateRef gate) Environment env(gate, circuit_, &builder_); GateRef glue = glue_; GateRef value = acc_.GetValueIn(gate, 0); - GateRef result = builder_.GetPrototype(glue, value); // Do not ToObject + GateRef result = builder_.GetPrototype(glue, value, gate); // Do not ToObject acc_.ReplaceGate(gate, builder_.GetStateDepend(), result); } diff --git a/ecmascript/message_string.h b/ecmascript/message_string.h index 23abd8d258..f50556156e 100644 --- a/ecmascript/message_string.h +++ b/ecmascript/message_string.h @@ -81,6 +81,14 @@ namespace panda::ecmascript { V(ProxySetPropertyReturnFalse, "JSProxy::SetProperty: 'set' return false") \ V(ProxySetPropertyResultTypeError, "JSProxy::SetProperty: TypeError of trapResult") \ V(ProxySetPropertyResultNotAccessor, "JSProxy::SetProperty: TypeError of AccessorDescriptor") \ + V(ProxyHasPropertyHandlerIsNull, "JSProxy::HasProperty: handler is Null") \ + V(ProxyHasPropertyTargetDescTypeError, "JSProxy::HasProperty: TypeError of targetDesc") \ + V(ProxyHasPropertyNotExtensibleTarget, "JSProxy::HasProperty: extensibleTarget is false") \ + V(ProxyGetPrototypeHandlerIsNull, "JSProxy::GetPrototype: handler is null") \ + V(ProxyGetPrototypeHandlerProtoIsNeitherObjectNorNull, \ + "JSProxy::GetPrototype: Type(handlerProto) is neither Object nor Null") \ + V(ProxyGetPrototypeHandlerProtoNotSame, \ + "JSProxy::GetPrototype: SameValue(handlerProto, targetProto) is false") \ V(InOperatorOnNonObject, "Cannot use 'in' operator in Non-Object") \ V(CurrentModuleUndefined, "GetModuleValueOutter currentModule failed") \ V(MisstakenResolvedBinding, "Get module value failed, mistaken ResolvedBinding") \ diff --git a/ecmascript/stubs/runtime_stub_list.h b/ecmascript/stubs/runtime_stub_list.h index 3ac03e6eb0..e23527ea93 100644 --- a/ecmascript/stubs/runtime_stub_list.h +++ b/ecmascript/stubs/runtime_stub_list.h @@ -240,7 +240,7 @@ namespace panda::ecmascript { V(CallInternalGetter) \ V(CallInternalSetter) \ V(CallInternalSetterNoThrow) \ - V(CallGetPrototype) \ + V(JSProxyGetPrototype) \ V(RegularJSObjDeletePrototype) \ V(CallJSObjDeletePrototype) \ V(ToPropertyKey) \ diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index b990eee6be..bfa23665a4 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -862,9 +862,9 @@ DEF_RUNTIME_STUBS(Dec) return RuntimeDec(thread, value).GetRawData(); } -DEF_RUNTIME_STUBS(CallGetPrototype) +DEF_RUNTIME_STUBS(JSProxyGetPrototype) { - RUNTIME_STUBS_HEADER(CallGetPrototype); + RUNTIME_STUBS_HEADER(JSProxyGetPrototype); JSHandle proxy = GetHArg(argv, argc, 0); // 0: means the zeroth parameter return JSProxy::GetPrototype(thread, proxy).GetRawData(); } diff --git a/test/moduletest/BUILD.gn b/test/moduletest/BUILD.gn index 40c51f208e..2e7c9a3c65 100644 --- a/test/moduletest/BUILD.gn +++ b/test/moduletest/BUILD.gn @@ -430,6 +430,8 @@ group("ark_asm_assert_test") { "multiconstpoolfunc", "multiconstpoolobj", "proxyget", + "proxygetprototype", + "proxyhasproperty", "proxyrelease", "proxyset", ] diff --git a/test/moduletest/proxygetprototype/BUILD.gn b/test/moduletest/proxygetprototype/BUILD.gn new file mode 100644 index 0000000000..dd866a8967 --- /dev/null +++ b/test/moduletest/proxygetprototype/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2025 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_assert_action("proxygetprototype") { + deps = [] +} diff --git a/test/moduletest/proxygetprototype/expect_output.txt b/test/moduletest/proxygetprototype/expect_output.txt new file mode 100644 index 0000000000..139f4c76c9 --- /dev/null +++ b/test/moduletest/proxygetprototype/expect_output.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2025 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. + +===== Starting Proxy getPrototypeOf Trap Tests ===== +Basic: Correct prototype - true +Custom: Overridden prototype - true +NonExtensible: Correct TypeError - true +InvalidReturn: Correct TypeError - true +NullProto: Null prototype - true +Revocable: Active state - true +Revocable: Correct post-revocation error - true +Nested: Top-level prototype - true +MissingTrap: Falls through to target - true +ValidNonExt: Correct prototype - true +===== Tests Completed ===== \ No newline at end of file diff --git a/test/moduletest/proxygetprototype/proxygetprototype.js b/test/moduletest/proxygetprototype/proxygetprototype.js new file mode 100644 index 0000000000..b6552c0315 --- /dev/null +++ b/test/moduletest/proxygetprototype/proxygetprototype.js @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2025 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. + */ + +function runGetPrototypeTests() { + // Scenario 1: Basic functionality + function testBasicPrototypeAccess() { + const target = { name: 'Alice' }; + const proto = { greeting: 'Hello' }; + Object.setPrototypeOf(target, proto); + + const proxy = new Proxy(target, { + getPrototypeOf(t) { + return Reflect.getPrototypeOf(t); + } + }); + + print('Basic: Correct prototype - ' + + (Object.getPrototypeOf(proxy) === proto)); // true + } + + // Scenario 2: Custom prototype override + function testCustomPrototype() { + const target = {}; + const realProto = {}; + const customProto = { custom: true }; + Object.setPrototypeOf(target, realProto); + + const proxy = new Proxy(target, { + getPrototypeOf() { + return customProto; + } + }); + + print('Custom: Overridden prototype - ' + + (Object.getPrototypeOf(proxy) === customProto)); // true + } + + // Scenario 3: Non-extensible target validation + function testNonExtensibleTarget() { + const realProto = {}; + const target = Object.create(realProto); + Object.preventExtensions(target); + + const proxy = new Proxy(target, { + getPrototypeOf() { + return {}; // Wrong prototype + } + }); + + try { + Object.getPrototypeOf(proxy); + print('NonExtensible: ERROR - No error thrown'); + } catch (e) { + print('NonExtensible: Correct TypeError - ' + + (e instanceof TypeError)); // true + } + } + + // Scenario 4: Invalid return type handling + function testInvalidReturnValue() { + const proxy = new Proxy({}, { + getPrototypeOf() { + return 42; // Invalid return + } + }); + + try { + Object.getPrototypeOf(proxy); + print('InvalidReturn: ERROR - No error thrown'); + } catch (e) { + print('InvalidReturn: Correct TypeError - ' + + (e instanceof TypeError)); // true + } + } + + // Scenario 5: Null prototype handling + function testNullPrototype() { + const target = Object.create(null); + const proxy = new Proxy(target, { + getPrototypeOf() { + return null; + } + }); + + print('NullProto: Null prototype - ' + + (Object.getPrototypeOf(proxy) === null)); // true + } + + // Scenario 6: Revocable proxies + function testRevocableProxy() { + const target = {}; + const proto = {}; + Object.setPrototypeOf(target, proto); + + const { proxy, revoke } = Proxy.revocable(target, { + getPrototypeOf() { + return proto; + } + }); + + print('Revocable: Active state - ' + + (Object.getPrototypeOf(proxy) === proto)); // true + + revoke(); + try { + Object.getPrototypeOf(proxy); + print('Revocable: ERROR - No error after revocation'); + } catch (e) { + print('Revocable: Correct post-revocation error - ' + + (e instanceof TypeError)); // true + } + } + + // Scenario 7: Nested proxies + function testNestedProxies() { + const base = {}; + const midProto = {}; + const topProto = {}; + + const baseProxy = new Proxy(base, { + getPrototypeOf() { + return midProto; + } + }); + + const topProxy = new Proxy(baseProxy, { + getPrototypeOf() { + return topProto; + } + }); + + print('Nested: Top-level prototype - ' + + (Object.getPrototypeOf(topProxy) === topProto)); // true + } + + // Scenario 8: Handler without trap + function testMissingTrap() { + const target = {}; + const proto = {}; + Object.setPrototypeOf(target, proto); + + const proxy = new Proxy(target, {}); + print('MissingTrap: Falls through to target - ' + + (Object.getPrototypeOf(proxy) === proto)); // true + } + + // Scenario 9: Correct prototype for non-extensible + function testValidNonExtensible() { + const realProto = {}; + const target = Object.create(realProto); + Object.preventExtensions(target); + + const proxy = new Proxy(target, { + getPrototypeOf() { + return realProto; // Correct prototype + } + }); + + try { + const result = Object.getPrototypeOf(proxy) === realProto; + print('ValidNonExt: Correct prototype - ' + result); + } catch (e) { + print('ValidNonExt: Unexpected error - ' + e.message); + } + } + + // Execute all tests + print('===== Starting Proxy getPrototypeOf Trap Tests ====='); + testBasicPrototypeAccess(); + testCustomPrototype(); + testNonExtensibleTarget(); + testInvalidReturnValue(); + testNullPrototype(); + testRevocableProxy(); + testNestedProxies(); + testMissingTrap(); + testValidNonExtensible(); + print('===== Tests Completed ====='); +} + +// Run the test suite +runGetPrototypeTests(); + +test_end(); diff --git a/test/moduletest/proxyhasproperty/BUILD.gn b/test/moduletest/proxyhasproperty/BUILD.gn new file mode 100644 index 0000000000..9b3c5e8ad5 --- /dev/null +++ b/test/moduletest/proxyhasproperty/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2025 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_assert_action("proxyhasproperty") { + deps = [] +} diff --git a/test/moduletest/proxyhasproperty/expect_output.txt b/test/moduletest/proxyhasproperty/expect_output.txt new file mode 100644 index 0000000000..85fd63c07b --- /dev/null +++ b/test/moduletest/proxyhasproperty/expect_output.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2025 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. + +===== Starting Proxy has() Trap Tests ===== +Basic - Visible property: true +Basic - Hidden property: false +Basic - Non-existent property: false +Conditional - Short property: true +Conditional - Long property: false +Prototype - Blocked inherited: false +Prototype - Native method: true +Non-config - Correctly caught TypeError: JSProxy::GetProperty: handler is Null +Exception - Correctly caught Error: has trap exploded +Falsy - Test property: false +Symbol - Symbol property: false +Symbol - Regular property: false +Nested - Regular property: true +Nested - Blocked property: false +===== Tests Completed ===== \ No newline at end of file diff --git a/test/moduletest/proxyhasproperty/proxyhasproperty.js b/test/moduletest/proxyhasproperty/proxyhasproperty.js new file mode 100644 index 0000000000..59ed294049 --- /dev/null +++ b/test/moduletest/proxyhasproperty/proxyhasproperty.js @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2025 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. + */ + +function runHasPropertyTests() { + // Scenario 1: Basic functionality + function testBasicInterception() { + const target = { visible: true, _hidden: false }; + const proxy = new Proxy(target, { + has(target, prop) { + if (prop.startsWith('_')) return false; + return prop in target; + } + }); + + print('Basic - Visible property: ' + ('visible' in proxy)); // true + print('Basic - Hidden property: ' + ('_hidden' in proxy)); // false + print('Basic - Non-existent property: ' + ('missing' in proxy)); // false + } + + // Scenario 2: Conditional existence + function testConditionalExistence() { + const proxy = new Proxy({}, { + has(_, prop) { + return prop.length < 5; + } + }); + + print('Conditional - Short property: ' + ('abc' in proxy)); // true + print('Conditional - Long property: ' + ('abcdef' in proxy)); // false + } + + // Scenario 3: Prototype chain + function testPrototypeHandling() { + const parent = { inherited: true }; + const child = Object.create(parent); + const proxy = new Proxy(child, { + has(target, prop) { + return prop === 'inherited' ? false : Reflect.has(target, prop); + } + }); + + print('Prototype - Blocked inherited: ' + ('inherited' in proxy)); // false + print('Prototype - Native method: ' + ('toString' in proxy)); // true + } + + // Scenario 4: Non-configurable property + function testNonConfigurableProperty() { + const target = {}; + Object.defineProperty(target, 'critical', { + value: 42, + configurable: false + }); + const proxy = new Proxy(target, { + has() { + return false; + } + }); + + try { + print('Non-config - Critical property: ' + ('critical' in proxy)); + } catch (e) { + if (e instanceof TypeError) { + print('Non-config - Correctly caught TypeError: ' + e.message); + } else { + print('Non-config - Wrong error type: ' + e.constructor.name); + } + } + } + + // Scenario 5: Exception in has trap + function testTrapException() { + const proxy = new Proxy({}, { + has() { + throw new Error('has trap exploded'); + } + }); + + try { + print('Exception - Any property: ' + ('anyProp' in proxy)); + } catch (e) { + if (e instanceof Error) { + print('Exception - Correctly caught Error: ' + e.message); + } else { + print('Exception - Wrong error type: ' + e.constructor.name); + } + } + } + + // Scenario 6: Falsy return value + function testFalsyConversion() { + const proxy = new Proxy({}, { + has() { + return 0; + } + }); + + print('Falsy - Test property: ' + ('test' in proxy)); // false + } + + // Scenario 7: Symbol properties + function testSymbolProperties() { + const sym = Symbol('secret'); + const proxy = new Proxy({ [sym]: true }, { + has(t, p) { + return typeof p === 'symbol' ? false : Reflect.has(t, p); + } + }); + + print('Symbol - Symbol property: ' + (sym in proxy)); // false + print('Symbol - Regular property: ' + ('public' in proxy)); // false + } + + // Scenario 8: Nested proxies + function testNestedProxies() { + const inner = new Proxy({ id: 1 }, { + has(t, p) { + return p !== 'blocked'; + } + }); + const outer = new Proxy(inner, {}); + + print('Nested - Regular property: ' + ('id' in outer)); // true + print('Nested - Blocked property: ' + ('blocked' in outer)); // false + } + + // Execute all tests + print('===== Starting Proxy has() Trap Tests ====='); + testBasicInterception(); + testConditionalExistence(); + testPrototypeHandling(); + testNonConfigurableProperty(); + testTrapException(); + testFalsyConversion(); + testSymbolProperties(); + testNestedProxies(); + print('===== Tests Completed ====='); +} + +// Run the test suite +runHasPropertyTests(); + +test_end(); -- Gitee