* Copyright (c) 2021-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/js_proxy.h"
#include "ecmascript/global_env.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_object-inl.h"
namespace panda::ecmascript {
JSHandle<JSProxy> JSProxy::ProxyCreate(JSThread *thread, const JSHandle<JSTaggedValue> &target,
const JSHandle<JSTaggedValue> &handler)
{
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: target is not Object",
JSHandle<JSProxy>(thread, JSTaggedValue::Exception()));
}
if (!handler->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: handler is not Object",
JSHandle<JSProxy>(thread, JSTaggedValue::Exception()));
}
return thread->GetEcmaVM()->GetFactory()->NewJSProxy(target, handler);
}
JSTaggedValue JSProxy::GetPrototype(JSThread *thread, const JSHandle<JSProxy> &proxy)
{
STACK_LIMIT_CHECK(thread, JSTaggedValue::Exception());
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: handler is null", JSTaggedValue::Exception());
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name(globalConst->GetHandledGetPrototypeOfString());
JSHandle<JSTaggedValue> trap = JSObject::GetMethod(thread, handler, name);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (trap->IsUndefined()) {
return JSTaggedValue::GetPrototype(thread, targetHandle);
}
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(targetHandle.GetTaggedValue());
JSHandle<JSTaggedValue> handlerProto(thread, JSFunction::Call(info));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!handlerProto->IsECMAObject() && !handlerProto->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: Type(handlerProto) is neither Object nor Null",
JSTaggedValue::Exception());
}
if (targetHandle->IsExtensible(thread)) {
return handlerProto.GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue targetProto = JSTaggedValue::GetPrototype(thread, targetHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!JSTaggedValue::SameValue(thread, handlerProto.GetTaggedValue(), targetProto)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: SameValue(handlerProto, targetProto) is false",
JSTaggedValue::Exception());
}
return handlerProto.GetTaggedValue();
}
bool JSProxy::SetPrototype(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &proto)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(proto->IsECMAObject() || proto->IsNull());
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: handler is null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledSetPrototypeOfString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::SetPrototype(thread, targetHandle, proto);
}
const uint32_t argsLength = 2;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue(), proto.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
if (!booleanTrapResult) {
return false;
}
if (targetHandle->IsExtensible(thread)) {
return booleanTrapResult;
}
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
JSTaggedValue targetProto = JSTaggedValue::GetPrototype(thread, targetHandle);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (booleanTrapResult && !JSTaggedValue::SameValue(thread, proto.GetTaggedValue(), targetProto)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: TypeError of targetProto and Result", false);
}
return booleanTrapResult;
}
bool JSProxy::IsExtensible(JSThread *thread, const JSHandle<JSProxy> &proxy)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: handler is null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledIsExtensibleString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return targetHandle->IsExtensible(thread);
}
JSHandle<JSTaggedValue> newTgt(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (targetHandle->IsExtensible(thread) != booleanTrapResult) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: TypeError of targetResult", false);
}
return booleanTrapResult;
}
bool JSProxy::PreventExtensions(JSThread *thread, const JSHandle<JSProxy> &proxy)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: handler is null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledPreventExtensionsString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::PreventExtensions(thread, targetHandle);
}
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (booleanTrapResult && targetHandle->IsExtensible(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: targetIsExtensible is true", false);
}
return booleanTrapResult;
}
bool JSProxy::GetOwnProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
PropertyDescriptor &desc)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(JSTaggedValue::IsPropertyKey(key));
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: handler is null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledGetOwnPropertyDescriptorString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::GetOwnProperty(thread, targetHandle, key, desc);
}
const uint32_t argsLength = 2;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue());
JSTaggedValue trapResultObj = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
JSHandle<JSTaggedValue> resultHandle(thread, trapResultObj);
if (!trapResultObj.IsECMAObject() && !trapResultObj.IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of trapResultObj", false);
}
PropertyDescriptor targetDesc(thread);
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (resultHandle->IsUndefined()) {
if (!found) {
return false;
}
if (!targetDesc.IsConfigurable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: targetDesc.[[Configurable]] is false", false);
}
if (!targetHandle->IsExtensible(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: extensibleTarget is false", false);
}
return false;
}
PropertyDescriptor &resultDesc = desc;
JSObject::ToPropertyDescriptor(thread, resultHandle, resultDesc);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
PropertyDescriptor::CompletePropertyDescriptor(thread, resultDesc);
bool valid =
JSObject::IsCompatiblePropertyDescriptor(thread, targetHandle->IsExtensible(thread), resultDesc, targetDesc);
if (!valid) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of valid", false);
}
if (!resultDesc.IsConfigurable()) {
if (!found || targetDesc.IsConfigurable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc configurable", false);
}
if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc writable", false);
}
}
return true;
}
bool JSProxy::DefineOwnProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
const PropertyDescriptor &desc, bool mayThrow)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(JSTaggedValue::IsPropertyKey(key));
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: handler is Null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledDefinePropertyString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::DefineOwnProperty(thread, targetHandle, key, desc, SCheckMode::CHECK, mayThrow);
}
JSHandle<JSTaggedValue> descObj = JSObject::FromPropertyDescriptor(thread, desc);
const uint32_t argsLength = 3;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue(), descObj.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (!booleanTrapResult) {
return false;
}
PropertyDescriptor targetDesc(thread);
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool settingConfigFalse = false;
if (desc.HasConfigurable() && !desc.IsConfigurable()) {
settingConfigFalse = true;
}
if (!found) {
if (!targetHandle->IsExtensible(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: extensibleTarget is false", false);
}
if (settingConfigFalse) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: settingConfigFalse is true", false);
}
} else {
if (!JSObject::IsCompatiblePropertyDescriptor(thread, targetHandle->IsExtensible(thread), desc, targetDesc)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: CompatiblePropertyDescriptor err", false);
}
if (settingConfigFalse && targetDesc.IsConfigurable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of settingConfigFalse", false);
}
if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() &&
desc.HasWritable() && !desc.IsWritable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of DataDescriptor", false);
}
}
return true;
}
bool JSProxy::HasProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(JSTaggedValue::IsPropertyKey(key));
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: handler is Null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledHasString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::HasProperty(thread, targetHandle, key);
}
const uint32_t argsLength = 2;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (!booleanTrapResult) {
PropertyDescriptor targetDesc(thread);
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (found) {
if (!targetDesc.IsConfigurable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: TypeError of targetDesc", false);
}
if (!targetHandle->IsExtensible(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: extensibleTarget is false", false);
}
}
}
return booleanTrapResult;
}
OperationResult JSProxy::CheckGetTrapResult(JSThread *thread, const JSHandle<JSTaggedValue> &targetHandle,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &resultHandle)
{
JSHandle<JSTaggedValue> exceptionHandle(thread, JSTaggedValue::Exception());
PropertyDescriptor targetDesc(thread);
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
RETURN_VALUE_IF_ABRUPT_COMPLETION(
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
if (found) {
if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) {
if (!JSTaggedValue::SameValue(thread, resultHandle.GetTaggedValue(),
targetDesc.GetValue().GetTaggedValue())) {
THROW_TYPE_ERROR_AND_RETURN(
thread, "JSProxy::GetProperty: TypeError of trapResult",
OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
}
}
if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() &&
targetDesc.GetGetter()->IsUndefined()) {
if (!resultHandle.GetTaggedValue().IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(
thread, "JSProxy::GetProperty: trapResult is not undefined",
OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
}
}
}
return OperationResult(thread, resultHandle.GetTaggedValue(), PropertyMetaData(true));
}
OperationResult JSProxy::GetProperty(JSThread *thread, const JSHandle<JSProxy> &proxy,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &receiver)
{
STACK_LIMIT_CHECK(thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(JSTaggedValue::IsPropertyKey(key));
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
JSHandle<JSTaggedValue> exceptionHandle(thread, JSTaggedValue::Exception());
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetProperty: handler is Null",
OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledGetString();
JSHandle<JSTaggedValue> trap(JSObject::FastGetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
if (trap->IsUndefined()) {
return JSTaggedValue::GetProperty(thread, targetHandle, key, receiver);
}
const uint32_t argsLength = 3;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue(), receiver.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
JSHandle<JSTaggedValue> resultHandle(thread, trapResult);
RETURN_VALUE_IF_ABRUPT_COMPLETION(
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
return CheckGetTrapResult(thread, targetHandle, key, resultHandle);
}
bool JSProxy::CheckSetTrapResult(JSThread *thread, const JSHandle<JSTaggedValue> &targetHandle,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value)
{
PropertyDescriptor targetDesc(thread);
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
if (found) {
if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) {
if (!JSTaggedValue::SameValue(thread, value, targetDesc.GetValue())) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of trapResult", false);
}
}
if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() &&
targetDesc.GetSetter()->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of AccessorDescriptor", false);
}
}
return true;
}
bool JSProxy::SetProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &receiver, bool mayThrow)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(JSTaggedValue::IsPropertyKey(key));
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: handler is Null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledSetString();
JSHandle<JSTaggedValue> trap(JSObject::FastGetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::SetProperty(thread, targetHandle, key, value, receiver, mayThrow);
}
const uint32_t argsLength = 4;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(
targetHandle.GetTaggedValue(), key.GetTaggedValue(), value.GetTaggedValue(), receiver.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (!booleanTrapResult) {
if (mayThrow) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: 'set' return false", false);
}
return false;
}
return CheckSetTrapResult(thread, targetHandle, key, value);
}
bool JSProxy::DeleteProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key)
{
STACK_LIMIT_CHECK(thread, false);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(JSTaggedValue::IsPropertyKey(key));
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: handler is Null", false);
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> name = globalConst->GetHandledDeletePropertyString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, name));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (trap->IsUndefined()) {
return JSTaggedValue::DeleteProperty(thread, targetHandle, key);
}
const uint32_t argsLength = 2;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, argsLength);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue());
JSTaggedValue trapResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
bool booleanTrapResult = trapResult.ToBoolean();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
if (!booleanTrapResult) {
return false;
}
PropertyDescriptor targetDesc(thread);
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
if (!found) {
return true;
}
if (!targetDesc.IsConfigurable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetDesc is not Configurable", false);
}
if (!targetHandle->IsExtensible(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetHandle is not Extensible", false);
}
return true;
}
JSHandle<TaggedArray> JSProxy::OwnPropertyKeys(JSThread *thread, const JSHandle<JSProxy> &proxy)
{
STACK_LIMIT_CHECK(thread, JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null",
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> key = globalConst->GetHandledOwnKeysString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, key));
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
if (trap->IsUndefined()) {
return JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
}
JSHandle<JSFunction> tagFunc(targetHandle);
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
info->SetCallArg(targetHandle.GetTaggedValue());
JSTaggedValue res = JSFunction::Call(info);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
JSHandle<JSTaggedValue> trapResArr(thread, res);
JSHandle<TaggedArray> trapRes(
JSObject::CreateListFromArrayLike<ElementTypes::STRING_AND_SYMBOL>(thread, trapResArr));
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
if (trapRes->HasDuplicateEntry(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: contains duplicate entries",
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
}
bool extensibleTarget = targetHandle->IsExtensible(thread);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
JSHandle<TaggedArray> targetKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
uint32_t length = targetKeys->GetLength();
JSHandle<TaggedArray> tgtCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length);
JSHandle<TaggedArray> tgtNoCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length);
uint32_t cfigLength = 0;
uint32_t noCfigLength = 0;
for (uint32_t i = 0; i < length; i++) {
JSHandle<JSTaggedValue> targetKey(thread, targetKeys->Get(thread, i));
ASSERT(targetKey->IsStringOrSymbol());
PropertyDescriptor desc(thread);
JSTaggedValue::GetOwnProperty(thread, targetHandle, targetKey, desc);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
if (!desc.IsEmpty() && !desc.IsConfigurable()) {
tgtNoCfigKeys->Set(thread, noCfigLength, targetKey);
noCfigLength++;
} else {
tgtCfigKeys->Set(thread, cfigLength, targetKey);
cfigLength++;
}
}
if (extensibleTarget && (cfigLength == 0)) {
return trapRes;
}
JSHandle<TaggedArray> uncheckFesKeys =
thread->GetEcmaVM()->GetFactory()->CopyArray(trapRes, trapRes->GetLength(), trapRes->GetLength());
uint32_t uncheckLength = uncheckFesKeys->GetLength();
for (uint32_t i = 0; i < noCfigLength; i++) {
uint32_t idx = uncheckFesKeys->GetIdx(thread, tgtNoCfigKeys->Get(thread, i));
if (idx == TaggedArray::MAX_ARRAY_INDEX) {
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys",
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
}
uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole());
uncheckLength--;
}
if (extensibleTarget) {
return trapRes;
}
for (uint32_t i = 0; i < cfigLength; i++) {
uint32_t idx = uncheckFesKeys->GetIdx(thread, tgtCfigKeys->Get(thread, i));
if (idx == TaggedArray::MAX_ARRAY_INDEX) {
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys",
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
}
uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole());
uncheckLength--;
}
if (uncheckLength != 0) {
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: uncheckedResultKeys is not empty",
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
}
return trapRes;
}
JSHandle<TaggedArray> JSProxy::GetAllPropertyKeys(JSThread *thread, const JSHandle<JSProxy> &proxy, uint32_t filter)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null",
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> key = globalConst->GetHandledOwnKeysString();
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handler, key));
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
if (trap->IsUndefined()) {
return JSTaggedValue::GetAllPropertyKeys(thread, targetHandle, filter);
}
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
info->SetCallArg(targetHandle.GetTaggedValue());
JSTaggedValue res = JSFunction::Call(info);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
JSHandle<JSTaggedValue> trapResArr(thread, res);
JSHandle<TaggedArray> trapRes(
JSObject::CreateListFromArrayLike<ElementTypes::STRING_AND_SYMBOL>(thread, trapResArr));
JSHandle<TaggedArray> ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
JSHandle<TaggedArray> reciveArray = JSTaggedValue::GetAllPropertyKeys(thread, targetHandle, filter);
uint32_t trapResLength = trapRes->GetLength();
uint32_t ownKeysLength = ownKeys->GetLength();
uint32_t reciveArrayLength = reciveArray->GetLength();
uint32_t newArrayLength = reciveArrayLength + trapResLength - ownKeysLength;
JSHandle<TaggedArray> resArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(newArrayLength);
uint32_t elementIndex = 0;
if (filter & NATIVE_KEY_SKIP_SYMBOLS) {
for (uint32_t index = 0; index < reciveArrayLength; index++) {
if (!ownKeys->Get(thread, index).IsSymbol()) {
resArray->Set(thread, elementIndex, reciveArray->Get(thread, index));
elementIndex++;
}
}
return resArray;
}
for (uint32_t i = 0; i < trapResLength; i++) {
resArray->Set(thread, i, trapRes->Get(thread, i));
}
uint32_t index = ownKeysLength;
for (uint32_t j = 0; j < reciveArrayLength - ownKeysLength; j++) {
resArray->Set(thread, trapResLength + j, reciveArray->Get(thread, index));
index++;
}
return resArray;
}
JSTaggedValue JSProxy::CallInternal(EcmaRuntimeCallInfo *info)
{
if (info == nullptr) {
return JSTaggedValue::Exception();
}
JSThread *thread = info->GetThread();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSProxy> proxy(info->GetFunction());
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Call: handler is null", JSTaggedValue::Exception());
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> target(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> key(globalConst->GetHandledApplyString());
JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint32_t argc = info->GetArgsNumber();
JSHandle<JSTaggedValue> thisArg = info->GetThis();
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
if (method->IsUndefined()) {
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, target, thisArg, undefined, argc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
runtimeInfo->SetCallArg(argc, 0, info, 0);
return JSFunction::Call(runtimeInfo);
}
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(argc);
for (uint32_t index = 0; index < argc; ++index) {
taggedArray->Set(thread, index, info->GetCallArg(index));
}
JSHandle<JSArray> arrHandle = JSArray::CreateArrayFromList(thread, taggedArray);
const uint32_t argsLength = 3;
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
runtimeInfo->SetCallArg(target.GetTaggedValue(), thisArg.GetTaggedValue(), arrHandle.GetTaggedValue());
return JSFunction::Call(runtimeInfo);
}
JSTaggedValue JSProxy::ConstructInternal(EcmaRuntimeCallInfo *info)
{
if (info == nullptr) {
return JSTaggedValue::Exception();
}
JSThread *thread = info->GetThread();
if (thread->DoStackLimitCheck()) {
return JSTaggedValue::Exception();
}
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSProxy> proxy(info->GetFunction());
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler(thread));
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception());
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> target(thread, proxy->GetTarget(thread));
JSHandle<JSTaggedValue> key(globalConst->GetHandledProxyConstructString());
JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (method->IsUndefined()) {
if (!target->IsConstructor()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception());
}
ASSERT(target->IsConstructor());
info->SetFunction(target.GetTaggedValue());
return JSFunction::Construct(info);
}
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
uint32_t argc = info->GetArgsNumber();
JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(argc);
for (uint32_t index = 0; index < argc; ++index) {
taggedArray->Set(thread, index, info->GetCallArg(index));
}
JSHandle<JSArray> arrHandle = JSArray::CreateArrayFromList(thread, taggedArray);
JSHandle<JSTaggedValue> newTarget(thread, info->GetNewTargetValue());
const uint32_t argsLength = 3;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
JSTaggedType *currentSp = reinterpret_cast<JSTaggedType *>(info);
InterpretedEntryFrame *currentEntryState = InterpretedEntryFrame::GetFrameFromSp(currentSp);
JSTaggedType *prevSp = currentEntryState->base.prev;
thread->SetCurrentSPFrame(prevSp);
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
runtimeInfo->SetCallArg(target.GetTaggedValue(), arrHandle.GetTaggedValue(), newTarget.GetTaggedValue());
JSTaggedValue newObj = JSFunction::Call(runtimeInfo);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!newObj.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception());
}
return newObj;
}
bool JSProxy::IsArray(JSThread *thread) const
{
if (thread->DoStackLimitCheck()) {
return false;
}
if (GetHandler(thread).IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsArray: handler is null", false);
}
return GetTarget(thread).IsArray(thread);
}
JSHandle<JSTaggedValue> JSProxy::GetSourceTarget(JSThread *thread) const
{
JSMutableHandle<JSProxy> proxy(thread, JSTaggedValue(this));
JSMutableHandle<JSTaggedValue> target(thread, proxy->GetTarget(thread));
while (target->IsJSProxy()) {
proxy.Update(target.GetTaggedValue());
target.Update(proxy->GetTarget(thread));
}
return target;
}
}