/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/compiler/builtins/builtins_proxy_stub_builder.h" #include "ecmascript/compiler/call_stub_builder.h" #include "ecmascript/compiler/new_object_stub_builder.h" namespace panda::ecmascript::kungfu { void BuiltinsProxyStubBuilder::GenProxyConstructor(GateRef nativeCode, GateRef func, GateRef newTarget) { auto env = GetEnvironment(); DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); Label thisCollectionObj(env); Label slowPath(env); Label slowPath1(env); Label exit(env); Label handleIsEcma(env); Label targetIsEcma(env); Label newTargetNotUndefined(env); BRANCH(TaggedIsUndefined(newTarget), &slowPath, &newTargetNotUndefined); Bind(&newTargetNotUndefined); GateRef target = GetArgFromArgv(glue_, IntPtr(0), numArgs_, true); GateRef handler = GetArgFromArgv(glue_, IntPtr(1), numArgs_, true); BRANCH(IsEcmaObject(glue_, target), &targetIsEcma, &slowPath); Bind(&targetIsEcma); BRANCH(IsEcmaObject(glue_, handler), &handleIsEcma, &slowPath); Bind(&handleIsEcma); { NewObjectStubBuilder newBuilder(this, GetCurrentGlobalEnv()); newBuilder.SetParameters(glue_, IntPtr(0)); res = newBuilder.NewJSProxy(glue_, target, handler); Jump(&exit); } Bind(&slowPath); { GateRef argv = GetArgv(); res = CallBuiltinRuntime(glue_, { glue_, nativeCode, func, thisValue_, numArgs_, argv }, true); Jump(&exit); } Bind(&exit); Return(*res); } void BuiltinsProxyStubBuilder::CheckGetTrapResult(GateRef target, GateRef key, Variable *result, Label *exit) { auto env = GetEnvironment(); Label callRuntime(env); Label isFoundData(env); Label isFoundAccessor(env); DEFVARIABLE(value, VariableType::JS_ANY(), Hole()); DEFVARIABLE(attr, VariableType::INT64(), Int64(0)); TryGetOwnProperty(glue_, target, key, Circuit::NullGate(), &value, &attr, &isFoundData, &isFoundAccessor, exit, &callRuntime); Bind(&isFoundData); { Label trapResultTypeError(env); GateRef rAttr = attr.ReadVariable(); GateRef rValue = value.ReadVariable(); GateRef rResult = result->ReadVariable(); GateRef trapResultCheck = LogicOrBuilder(env).Or(IsConfigable(rAttr)) .Or(IsWritable(rAttr)) .Or(SameValue(glue_, rResult, rValue)) .Done(); BRANCH(BoolNot(trapResultCheck), &trapResultTypeError, exit); Bind(&trapResultTypeError); { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPropertyResultTypeError)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); result->WriteVariable(Exception()); Jump(exit); } } Bind(&isFoundAccessor); { Label trapResultIsUndefined(env); Label trapResultIsNotUndefined(env); GateRef rAttr = attr.ReadVariable(); GateRef rValue = value.ReadVariable(); GateRef rResult = result->ReadVariable(); GateRef getter = Load(VariableType::JS_ANY(), glue_, rValue, IntPtr(AccessorData::GETTER_OFFSET)); GateRef trapResultCheck = LogicAndBuilder(env).And(BoolNot(IsConfigable(rAttr))) .And(TaggedIsUndefined(getter)) .And(BoolNot(TaggedIsUndefined(rResult))) .Done(); BRANCH(trapResultCheck, &trapResultIsNotUndefined, exit); Bind(&trapResultIsNotUndefined); { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPropertyResultNotUndefined)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); result->WriteVariable(Exception()); Jump(exit); } } Bind(&callRuntime); { result->WriteVariable(CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(CheckGetTrapResult), { target, key, result->ReadVariable() })); Jump(exit); } } void BuiltinsProxyStubBuilder::CheckSetTrapResult(GateRef target, GateRef key, GateRef value, Variable *result, Label *exit) { auto env = GetEnvironment(); Label callRuntime(env); Label isFoundData(env); Label isFoundAccessor(env); DEFVARIABLE(tValue, VariableType::JS_ANY(), Hole()); DEFVARIABLE(attr, VariableType::INT64(), Int64(0)); TryGetOwnProperty(glue_, target, key, Circuit::NullGate(), &tValue, &attr, &isFoundData, &isFoundAccessor, exit, &callRuntime); Bind(&isFoundData); { Label trapResultTypeError(env); GateRef rAttr = attr.ReadVariable(); GateRef rValue = tValue.ReadVariable(); GateRef trapResultCheck = LogicOrBuilder(env).Or(IsConfigable(rAttr)) .Or(IsWritable(rAttr)) .Or(SameValue(glue_, value, rValue)) .Done(); BRANCH(BoolNot(trapResultCheck), &trapResultTypeError, exit); Bind(&trapResultTypeError); { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxySetPropertyResultTypeError)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); result->WriteVariable(TaggedFalse()); Jump(exit); } } Bind(&isFoundAccessor); { Label trapResultIsUndefined(env); Label trapResultIsNotUndefined(env); GateRef rAttr = attr.ReadVariable(); GateRef rValue = tValue.ReadVariable(); GateRef setter = Load(VariableType::JS_ANY(), glue_, rValue, IntPtr(AccessorData::SETTER_OFFSET)); GateRef trapResultCheck = LogicAndBuilder(env).And(BoolNot(IsConfigable(rAttr))) .And(TaggedIsUndefined(setter)) .Done(); BRANCH(trapResultCheck, &trapResultIsNotUndefined, exit); Bind(&trapResultIsNotUndefined); { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxySetPropertyResultNotAccessor)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); result->WriteVariable(TaggedFalse()); Jump(exit); } } Bind(&callRuntime); { result->WriteVariable(CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(CheckSetTrapResult), { target, key, value })); Jump(exit); } } GateRef BuiltinsProxyStubBuilder::GetProperty(GateRef proxy, GateRef key, GateRef receiver) { auto env = GetEnvironment(); DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); Label stackOverflow(env); Label dispatch(env); Label checkGetTrapResult(env); Label exit(env); Label handlerIsNull(env); Label handlerIsNotNull(env); Label slowPath(env); Label trapIsCallable(env); Label trapFastPath(env); BRANCH_UNLIKELY(CheckStackOverflow(glue_), &stackOverflow, &dispatch); Bind(&stackOverflow); { CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowStackOverflowException), {}); result = Exception(); Jump(&exit); } Bind(&dispatch); GateRef handler = GetHandler(glue_, proxy); BRANCH(TaggedIsNull(handler), &handlerIsNull, &handlerIsNotNull); Bind(&handlerIsNull); { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxyGetPropertyHandlerIsNull)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); result = Exception(); Jump(&exit); } Bind(&handlerIsNotNull); { GateRef target = GetTarget(glue_, proxy); GateRef name = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::GET_STRING_INDEX); GateRef trap = GetPropertyByName(glue_, handler, name); Label isPendingException(env); Label noPendingException(env); BRANCH_UNLIKELY(HasPendingException(glue_), &isPendingException, &noPendingException); Bind(&isPendingException); { result = Exception(); Jump(&exit); } Bind(&noPendingException); BRANCH(TaggedIsHeapObject(trap), &trapFastPath, &slowPath); Bind(&trapFastPath); { BRANCH(IsCallable(glue_, trap), &trapIsCallable, &slowPath); Bind(&trapIsCallable); { JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN); callArgs.callThisArg3WithReturnArgs = { handler, target, key, receiver }; CallStubBuilder callBuilder(this, glue_, trap, Int32(3), 0, nullptr, Circuit::NullGate(), callArgs); // 3 : three arg result = callBuilder.JSCallDispatch(); Jump(&checkGetTrapResult); } } Bind(&checkGetTrapResult); { CheckGetTrapResult(target, key, &result, &exit); } Bind(&slowPath); { result = CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(JSProxyGetProperty), { proxy, key, receiver }); Jump(&exit); } } Bind(&exit); auto ret = *result; return ret; } GateRef BuiltinsProxyStubBuilder::SetProperty(GateRef proxy, GateRef key, GateRef value, GateRef receiver, bool mayThrow) { auto env = GetEnvironment(); DEFVARIABLE(result, VariableType::JS_ANY(), TaggedTrue()); Label stackOverflow(env); Label dispatch(env); Label trapResultIsFalse(env); Label checkSetTrapResult(env); Label exit(env); Label handlerIsNull(env); Label handlerIsNotNull(env); Label slowPath(env); Label trapIsCallable(env); Label trapFastPath(env); BRANCH_UNLIKELY(CheckStackOverflow(glue_), &stackOverflow, &dispatch); Bind(&stackOverflow); { CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowStackOverflowException), {}); result = TaggedFalse(); Jump(&exit); } Bind(&dispatch); GateRef handler = GetHandler(glue_, proxy); BRANCH(TaggedIsNull(handler), &handlerIsNull, &handlerIsNotNull); Bind(&handlerIsNull); { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxySetPropertyHandlerIsNull)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); result = TaggedFalse(); Jump(&exit); } Bind(&handlerIsNotNull); { GateRef target = GetTarget(glue_, proxy); GateRef name = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::SET_STRING_INDEX); GateRef trap = GetPropertyByName(glue_, handler, name); Label isPendingException(env); Label noPendingException(env); BRANCH_UNLIKELY(HasPendingException(glue_), &isPendingException, &noPendingException); Bind(&isPendingException); { result = TaggedFalse(); Jump(&exit); } Bind(&noPendingException); BRANCH(TaggedIsHeapObject(trap), &trapFastPath, &slowPath); Bind(&trapFastPath); { BRANCH(IsCallable(glue_, trap), &trapIsCallable, &slowPath); Bind(&trapIsCallable); { GateRef argsLength = Int32(4); NewObjectStubBuilder newBuilder(this); GateRef argList = newBuilder.NewTaggedArray(glue_, argsLength); // param 0: target SetValueToTaggedArray(VariableType::JS_ANY(), glue_, argList, Int32(0), target); // param 1: key SetValueToTaggedArray(VariableType::JS_ANY(), glue_, argList, Int32(1), key); // param 2: value SetValueToTaggedArray(VariableType::JS_ANY(), glue_, argList, Int32(2), value); // param 3: receiver SetValueToTaggedArray(VariableType::JS_ANY(), glue_, argList, Int32(3), receiver); GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET)); JSCallArgs callArgs(JSCallMode::CALL_THIS_ARGV_WITH_RETURN); callArgs.callThisArgvWithReturnArgs = { argsLength, argv, handler }; CallStubBuilder callBuilder(this, glue_, trap, argsLength, 0, nullptr, Circuit::NullGate(), callArgs); GateRef trapResult = callBuilder.JSCallDispatch(); BRANCH(TaggedIsFalse(FastToBoolean(glue_, trapResult)), &trapResultIsFalse, &checkSetTrapResult); Bind(&trapResultIsFalse); { if (mayThrow) { GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(ProxySetPropertyReturnFalse)); CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(ThrowTypeError), {IntToTaggedInt(taggedId)}); } result = TaggedFalse(); Jump(&exit); } } } Bind(&checkSetTrapResult); { CheckSetTrapResult(target, key, value, &result, &exit); } Bind(&slowPath); { result = CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(), RTSTUB_ID(JSProxySetProperty), {proxy, key, value, receiver, mayThrow ? TaggedTrue() : TaggedFalse()}); Jump(&exit); } } Bind(&exit); auto ret = *result; return ret; } } // namespace panda::ecmascript::kungfu