mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-27 04:00:37 +00:00
cdbfa4340e
Issue:#IALVU8 Signed-off-by: fangting <fangting12@huawei.com>
1075 lines
52 KiB
C++
1075 lines
52 KiB
C++
/*
|
|
* 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 {
|
|
// ES6 9.5.15 ProxyCreate(target, handler)
|
|
JSHandle<JSProxy> JSProxy::ProxyCreate(JSThread *thread, const JSHandle<JSTaggedValue> &target,
|
|
const JSHandle<JSTaggedValue> &handler)
|
|
{
|
|
// 1. If Type(target) is not Object, throw a TypeError exception.
|
|
if (!target->IsECMAObject()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: target is not Object",
|
|
JSHandle<JSProxy>(thread, JSTaggedValue::Exception()));
|
|
}
|
|
|
|
// 2. If Type(handler) is not Object, throw a TypeError exception.
|
|
if (!handler->IsECMAObject()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: handler is not Object",
|
|
JSHandle<JSProxy>(thread, JSTaggedValue::Exception()));
|
|
}
|
|
// 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
|
|
// 6. If IsCallable(target) is true, then P.[[Call]] as specified in 9.5.12.
|
|
|
|
// 8. Set the [[ProxyTarget]] internal slot of P to target.
|
|
// 9. Set the [[ProxyHandler]] internal slot of P to handler.
|
|
return thread->GetEcmaVM()->GetFactory()->NewJSProxy(target, handler);
|
|
}
|
|
|
|
// ES6 9.5.1 [[GetPrototypeOf]] ( )
|
|
JSTaggedValue JSProxy::GetPrototype(JSThread *thread, const JSHandle<JSProxy> &proxy)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, JSTaggedValue::Exception());
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
|
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler());
|
|
// 2. If handler is null, throw a TypeError exception.
|
|
if (handler->IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: handler is null", JSTaggedValue::Exception());
|
|
}
|
|
// 3. Assert: Type(handler) is Object.
|
|
ASSERT(handler->IsECMAObject());
|
|
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
// 5. Let trap be GetMethod(handler, "getPrototypeOf").
|
|
JSHandle<JSTaggedValue> name(globalConst->GetHandledGetPrototypeOfString());
|
|
JSHandle<JSTaggedValue> trap = JSObject::GetMethod(thread, handler, name);
|
|
// 6. ReturnIfAbrupt(trap).
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
|
|
// 7. If trap is undefined, then Return target.[[GetPrototypeOf]]().
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::GetPrototype(thread, targetHandle);
|
|
}
|
|
// 8. Let handlerProto be Call(trap, handler, «target»).
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handler, undefined, 1);
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
info->SetCallArg(targetHandle.GetTaggedValue());
|
|
JSTaggedValue handlerProto = JSFunction::Call(info);
|
|
|
|
// 9. ReturnIfAbrupt(handlerProto).
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
// 10. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.
|
|
if (!handlerProto.IsECMAObject() && !handlerProto.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: Type(handlerProto) is neither Object nor Null",
|
|
JSTaggedValue::Exception());
|
|
}
|
|
// 11. Let extensibleTarget be IsExtensible(target).
|
|
// 12. ReturnIfAbrupt(extensibleTarget).
|
|
// 13. If extensibleTarget is true, return handlerProto.
|
|
if (targetHandle->IsExtensible(thread)) {
|
|
return handlerProto;
|
|
}
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
|
|
// 14. Let targetProto be target.[[GetPrototypeOf]]().
|
|
JSTaggedValue targetProto = JSTaggedValue::GetPrototype(thread, targetHandle);
|
|
// 15. ReturnIfAbrupt(targetProto).
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
// 16. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
|
|
if (!JSTaggedValue::SameValue(handlerProto, targetProto)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: SameValue(handlerProto, targetProto) is false",
|
|
JSTaggedValue::Exception());
|
|
}
|
|
// 17. Return handlerProto.
|
|
return handlerProto;
|
|
}
|
|
|
|
// ES6 9.5.2 [[SetPrototypeOf]] (V)
|
|
bool JSProxy::SetPrototype(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &proto)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, false);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
|
|
ASSERT(proto->IsECMAObject() || proto->IsNull());
|
|
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
// 3. If handler is null, throw a TypeError exception.
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: handler is null", false);
|
|
}
|
|
// 4. Assert: Type(handler) is Object.
|
|
ASSERT(handler.IsECMAObject());
|
|
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
// 6. Let trap be GetMethod(handler, "setPrototypeOf").
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledSetPrototypeOfString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 7. If trap is undefined, then Return target.[[SetPrototypeOf]](V).
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::SetPrototype(thread, targetHandle, proto);
|
|
}
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
const uint32_t argsLength = 2; // 2: target and proto
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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);
|
|
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, V»)).
|
|
// If booleanTrapResult is false, return false
|
|
bool booleanTrapResult = trapResult.ToBoolean();
|
|
if (!booleanTrapResult) {
|
|
return false;
|
|
}
|
|
// 10. ReturnIfAbrupt(booleanTrapResult).
|
|
// 11. Let extensibleTarget be IsExtensible(target).
|
|
// 12. ReturnIfAbrupt(extensibleTarget).
|
|
// 13. If extensibleTarget is true, return booleanTrapResult
|
|
if (targetHandle->IsExtensible(thread)) {
|
|
return booleanTrapResult;
|
|
}
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 14. Let targetProto be target.[[GetPrototypeOf]]().
|
|
JSTaggedValue targetProto = JSTaggedValue::GetPrototype(thread, targetHandle);
|
|
// 15. ReturnIfAbrupt(targetProto).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 16. If booleanTrapResult is true and SameValue(V, targetProto) is false, throw a TypeError exception.
|
|
if (booleanTrapResult && !JSTaggedValue::SameValue(proto.GetTaggedValue(), targetProto)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: TypeError of targetProto and Result", false);
|
|
}
|
|
// 17. Return handlerProto.
|
|
return booleanTrapResult;
|
|
}
|
|
|
|
// ES6 9.5.3 [[IsExtensible]] ( )
|
|
bool JSProxy::IsExtensible(JSThread *thread, const JSHandle<JSProxy> &proxy)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, false);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
// 2. If handler is null, throw a TypeError exception.
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: handler is null", false);
|
|
}
|
|
// 3. Assert: Type(handler) is Object.
|
|
ASSERT(handler.IsECMAObject());
|
|
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
// 5. Let trap be GetMethod(handler, "isExtensible").
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledIsExtensibleString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 6. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 7. If trap is undefined, then Return target.[[IsExtensible]]().
|
|
if (trap->IsUndefined()) {
|
|
return targetHandle->IsExtensible(thread);
|
|
}
|
|
// 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)).
|
|
JSHandle<JSTaggedValue> newTgt(thread, JSTaggedValue::Undefined());
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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();
|
|
// 9. ReturnIfAbrupt(booleanTrapResult).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 10. Let targetResult be target.[[IsExtensible]]().
|
|
// 11. ReturnIfAbrupt(targetResult).
|
|
// 12. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
|
|
// 13. Return booleanTrapResult.
|
|
if (targetHandle->IsExtensible(thread) != booleanTrapResult) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: TypeError of targetResult", false);
|
|
}
|
|
|
|
return booleanTrapResult;
|
|
}
|
|
|
|
// ES6 9.5.4 [[PreventExtensions]] ( )
|
|
bool JSProxy::PreventExtensions(JSThread *thread, const JSHandle<JSProxy> &proxy)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, false);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
|
// 2. If handler is null, throw a TypeError exception.
|
|
// 3. Assert: Type(handler) is Object.
|
|
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
|
// 5. Let trap be GetMethod(handler, "preventExtensions").
|
|
// 6. ReturnIfAbrupt(trap).
|
|
// 7. If trap is undefined, then
|
|
// a. Return target.[[PreventExtensions]]().
|
|
// 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)).
|
|
// 9. ReturnIfAbrupt(booleanTrapResult).
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: handler is null", false);
|
|
}
|
|
ASSERT(handler.IsECMAObject());
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledPreventExtensionsString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 6. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::PreventExtensions(thread, targetHandle);
|
|
}
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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();
|
|
// 9. ReturnIfAbrupt(booleanTrapResult).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
// 10. If booleanTrapResult is true, then
|
|
// a. Let targetIsExtensible be target.[[IsExtensible]]().
|
|
// b. ReturnIfAbrupt(targetIsExtensible).
|
|
// c. If targetIsExtensible is true, throw a TypeError exception.
|
|
if (booleanTrapResult && targetHandle->IsExtensible(thread)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: targetIsExtensible is true", false);
|
|
}
|
|
// 11. Return booleanTrapResult.
|
|
return booleanTrapResult;
|
|
}
|
|
|
|
// ES6 9.5.5 [[GetOwnProperty]] (P)
|
|
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();
|
|
// 1. Assert: IsPropertyKey(P) is true.
|
|
ASSERT(JSTaggedValue::IsPropertyKey(key));
|
|
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
|
// 3. If handler is null, throw a TypeError exception.
|
|
// 4. Assert: Type(handler) is Object.
|
|
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
|
// 6. Let trap be GetMethod(handler, "getOwnPropertyDescriptor").
|
|
// 7. ReturnIfAbrupt(trap).
|
|
// 8. If trap is undefined, then
|
|
// a. Return target.[[GetOwnProperty]](P).
|
|
// 9. Let trapResultObj be Call(trap, handler, «target, P»).
|
|
// 10. ReturnIfAbrupt(trapResultObj).
|
|
// 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: handler is null", false);
|
|
}
|
|
ASSERT(handler.IsECMAObject());
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledGetOwnPropertyDescriptorString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::GetOwnProperty(thread, targetHandle, key, desc);
|
|
}
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
const uint32_t argsLength = 2; // 2: target and key
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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);
|
|
|
|
// 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
|
|
if (!trapResultObj.IsECMAObject() && !trapResultObj.IsUndefined()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of trapResultObj", false);
|
|
}
|
|
// 12. Let targetDesc be target.[[GetOwnProperty]](P).
|
|
PropertyDescriptor targetDesc(thread);
|
|
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
|
|
// 13. ReturnIfAbrupt(targetDesc).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 14. If trapResultObj is undefined, then
|
|
if (resultHandle->IsUndefined()) {
|
|
// a. If targetDesc is undefined, return undefined.
|
|
if (!found) {
|
|
return false;
|
|
}
|
|
// b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
|
if (!targetDesc.IsConfigurable()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: targetDesc.[[Configurable]] is false", false);
|
|
}
|
|
// c. Let extensibleTarget be IsExtensible(target).
|
|
// d. ReturnIfAbrupt(extensibleTarget).
|
|
// e. Assert: Type(extensibleTarget) is Boolean.
|
|
// f. If extensibleTarget is false, throw a TypeError exception.
|
|
if (!targetHandle->IsExtensible(thread)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: extensibleTarget is false", false);
|
|
}
|
|
// g. Return undefined.
|
|
return false;
|
|
}
|
|
// 15. Let extensibleTarget be IsExtensible(target).
|
|
// 16. ReturnIfAbrupt(extensibleTarget).
|
|
// 17. Let resultDesc be ToPropertyDescriptor(trapResultObj).
|
|
PropertyDescriptor &resultDesc = desc;
|
|
JSObject::ToPropertyDescriptor(thread, resultHandle, resultDesc);
|
|
// 18. ReturnIfAbrupt(resultDesc)
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
// 19. Call CompletePropertyDescriptor(resultDesc).
|
|
PropertyDescriptor::CompletePropertyDescriptor(thread, resultDesc);
|
|
// 20. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, resultDesc, targetDesc).
|
|
bool valid = JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), resultDesc, targetDesc);
|
|
if (!valid) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of valid", false);
|
|
}
|
|
// 22. If resultDesc.[[Configurable]] is false, then
|
|
if (!resultDesc.IsConfigurable()) {
|
|
// a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
|
|
if (!found || targetDesc.IsConfigurable()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc configurable", false);
|
|
}
|
|
// b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
|
|
// If targetDesc.[[Writable]] is true, throw a TypeError exception.
|
|
if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc writable", false);
|
|
}
|
|
}
|
|
// 23. Return resultDesc.
|
|
return true;
|
|
}
|
|
|
|
// ES6 9.5.6 [[DefineOwnProperty]] (P, Desc)
|
|
bool JSProxy::DefineOwnProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key,
|
|
const PropertyDescriptor &desc)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, false);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// step 1 ~ 10 are almost same as GetOwnProperty
|
|
ASSERT(JSTaggedValue::IsPropertyKey(key));
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: handler is Null", false);
|
|
}
|
|
ASSERT(handler.IsECMAObject());
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledDefinePropertyString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::DefineOwnProperty(thread, targetHandle, key, desc);
|
|
}
|
|
|
|
// 9. Let descObj be FromPropertyDescriptor(Desc).
|
|
JSHandle<JSTaggedValue> descObj = JSObject::FromPropertyDescriptor(thread, desc);
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
const uint32_t argsLength = 3; // 3: target, key and desc
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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();
|
|
// 11. ReturnIfAbrupt(booleanTrapResult).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
if (!booleanTrapResult) {
|
|
return false;
|
|
}
|
|
// 13. Let targetDesc be target.[[GetOwnProperty]](P).
|
|
PropertyDescriptor targetDesc(thread);
|
|
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
|
|
// 14. ReturnIfAbrupt(targetDesc).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 15. Let extensibleTarget be IsExtensible(target).
|
|
// 16. ReturnIfAbrupt(extensibleTarget).
|
|
// 17. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then Let settingConfigFalse be
|
|
// true.
|
|
// 18. Else let settingConfigFalse be false.
|
|
bool settingConfigFalse = false;
|
|
if (desc.HasConfigurable() && !desc.IsConfigurable()) {
|
|
settingConfigFalse = true;
|
|
}
|
|
// 19. If targetDesc is undefined, then
|
|
if (!found) {
|
|
// a. If extensibleTarget is false, throw a TypeError exception.
|
|
if (!targetHandle->IsExtensible(thread)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: extensibleTarget is false", false);
|
|
}
|
|
// b. If settingConfigFalse is true, throw a TypeError exception.
|
|
if (settingConfigFalse) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: settingConfigFalse is true", false);
|
|
}
|
|
} else {
|
|
// a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc , targetDesc) is false, throw a TypeError
|
|
// exception.
|
|
if (!JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), desc, targetDesc)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: CompatiblePropertyDescriptor err", false);
|
|
}
|
|
// b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
|
|
if (settingConfigFalse && targetDesc.IsConfigurable()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of settingConfigFalse", false);
|
|
}
|
|
// c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]]
|
|
// is true, then If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
|
|
if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() &&
|
|
desc.HasWritable() && !desc.IsWritable()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of DataDescriptor", false);
|
|
}
|
|
}
|
|
// 21. Return true.
|
|
return true;
|
|
}
|
|
|
|
// ES6 9.5.7 [[HasProperty]] (P)
|
|
bool JSProxy::HasProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, false);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// step 1 ~ 10 are almost same as GetOwnProperty
|
|
ASSERT(JSTaggedValue::IsPropertyKey(key));
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: handler is Null", false);
|
|
}
|
|
ASSERT(handler.IsECMAObject());
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledHasString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::HasProperty(thread, targetHandle, key);
|
|
}
|
|
|
|
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)).
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
|
|
const uint32_t argsLength = 2; // 2: target and key
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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();
|
|
// 10. ReturnIfAbrupt(booleanTrapResult).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
|
|
// 11. If booleanTrapResult is false, then
|
|
if (!booleanTrapResult) {
|
|
// a. Let targetDesc be target.[[GetOwnProperty]](P).
|
|
PropertyDescriptor targetDesc(thread);
|
|
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
|
|
// b. ReturnIfAbrupt(targetDesc).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
// c. If targetDesc is not undefined, then
|
|
if (found) {
|
|
// i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
|
if (!targetDesc.IsConfigurable()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: TypeError of targetDesc", false);
|
|
}
|
|
// ii. Let extensibleTarget be IsExtensible(target).
|
|
// iii. ReturnIfAbrupt(extensibleTarget).
|
|
// iv. If extensibleTarget is false, throw a TypeError exception.
|
|
if (!targetHandle->IsExtensible(thread)) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: extensibleTarget is false", false);
|
|
}
|
|
}
|
|
}
|
|
return booleanTrapResult;
|
|
}
|
|
|
|
// ES6 9.5.8 [[Get]] (P, Receiver)
|
|
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();
|
|
// step 1 ~ 10 are almost same as GetOwnProperty
|
|
ASSERT(JSTaggedValue::IsPropertyKey(key));
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
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());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledGetString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(
|
|
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
|
|
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::GetProperty(thread, targetHandle, key, receiver);
|
|
}
|
|
// 9. Let trapResult be Call(trap, handler, «target, P, Receiver»).
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
const uint32_t argsLength = 3; // 3: «target, P, Receiver»
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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);
|
|
|
|
// 10. ReturnIfAbrupt(trapResult).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(
|
|
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
|
|
|
|
// 11. Let targetDesc be target.[[GetOwnProperty]](P).
|
|
PropertyDescriptor targetDesc(thread);
|
|
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
|
|
// 12. ReturnIfAbrupt(targetDesc).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(
|
|
thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
|
|
|
|
// 13. If targetDesc is not undefined, then
|
|
if (found) {
|
|
// a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is
|
|
// false, then
|
|
if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) {
|
|
// i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
|
|
if (!JSTaggedValue::SameValue(resultHandle.GetTaggedValue(), targetDesc.GetValue().GetTaggedValue())) {
|
|
THROW_TYPE_ERROR_AND_RETURN(
|
|
thread, "JSProxy::GetProperty: TypeError of trapResult",
|
|
OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
|
|
}
|
|
}
|
|
// b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is
|
|
// undefined, then
|
|
if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() &&
|
|
targetDesc.GetGetter()->IsUndefined()) {
|
|
// i. If trapResult is not undefined, throw a TypeError exception.
|
|
if (!resultHandle.GetTaggedValue().IsUndefined()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(
|
|
thread, "JSProxy::GetProperty: trapResult is not undefined",
|
|
OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false)));
|
|
}
|
|
}
|
|
}
|
|
// 14. Return trapResult.
|
|
return OperationResult(thread, resultHandle.GetTaggedValue(), PropertyMetaData(true));
|
|
}
|
|
|
|
// ES6 9.5.9 [[Set]] ( P, V, Receiver)
|
|
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();
|
|
// step 1 ~ 10 are almost same as GetOwnProperty
|
|
ASSERT(JSTaggedValue::IsPropertyKey(key));
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: handler is Null", false);
|
|
}
|
|
ASSERT(handler.IsECMAObject());
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledSetString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::SetProperty(thread, targetHandle, key, value, receiver, mayThrow);
|
|
}
|
|
|
|
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»))
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
const uint32_t argsLength = 4; // 4: «target, P, V, Receiver»
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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();
|
|
// 11. ReturnIfAbrupt(booleanTrapResult).
|
|
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;
|
|
}
|
|
// 13. Let targetDesc be target.[[GetOwnProperty]](P).
|
|
PropertyDescriptor targetDesc(thread);
|
|
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
|
|
// 14. If targetDesc is not undefined, then
|
|
if (found) {
|
|
// a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is
|
|
// false, then
|
|
if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) {
|
|
// i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
|
|
if (!JSTaggedValue::SameValue(value, targetDesc.GetValue())) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of trapResult", false);
|
|
}
|
|
}
|
|
// b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then
|
|
// i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
|
|
if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() &&
|
|
targetDesc.GetSetter()->IsUndefined()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of AccessorDescriptor", false);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ES6 9.5.10 [[Delete]] (P)
|
|
bool JSProxy::DeleteProperty(JSThread *thread, const JSHandle<JSProxy> &proxy, const JSHandle<JSTaggedValue> &key)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, false);
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// step 1 ~ 13 are almost same as GetOwnProperty
|
|
ASSERT(JSTaggedValue::IsPropertyKey(key));
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
if (handler.IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: handler is Null", false);
|
|
}
|
|
ASSERT(handler.IsECMAObject());
|
|
JSHandle<JSTaggedValue> targetHandle(thread, proxy->GetTarget());
|
|
JSHandle<JSTaggedValue> name = globalConst->GetHandledDeletePropertyString();
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, JSHandle<JSTaggedValue>(thread, handler), name));
|
|
// 7. ReturnIfAbrupt(trap).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::DeleteProperty(thread, targetHandle, key);
|
|
}
|
|
|
|
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)).
|
|
JSHandle<JSTaggedValue> newTgt(thread, JSTaggedValue::Undefined());
|
|
JSHandle<JSTaggedValue> handlerTag(thread, proxy->GetHandler());
|
|
const uint32_t argsLength = 2; // 2: target and key
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, 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();
|
|
// 11. ReturnIfAbrupt(booleanTrapResult).
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
|
|
if (!booleanTrapResult) {
|
|
return false;
|
|
}
|
|
// 13. Let targetDesc be target.[[GetOwnProperty]](P).
|
|
PropertyDescriptor targetDesc(thread);
|
|
bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc);
|
|
// 14. If targetDesc is undefined, return true.
|
|
if (!found) {
|
|
return true;
|
|
}
|
|
// 15. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
|
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);
|
|
}
|
|
// 16. Return true.
|
|
return true;
|
|
}
|
|
|
|
// ES6 9.5.12 [[OwnPropertyKeys]] ()
|
|
JSHandle<TaggedArray> JSProxy::OwnPropertyKeys(JSThread *thread, const JSHandle<JSProxy> &proxy)
|
|
{
|
|
STACK_LIMIT_CHECK(thread, JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// step 1 ~ 4 get ProxyHandler and ProxyTarget
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
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());
|
|
|
|
// 5.Let trap be GetMethod(handler, "ownKeys").
|
|
JSHandle<JSTaggedValue> key = globalConst->GetHandledOwnKeysString();
|
|
JSHandle<JSTaggedValue> handlerHandle(thread, handler);
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handlerHandle, key));
|
|
|
|
// 6.ReturnIfAbrupt(trap).
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
|
|
|
|
// 7.If trap is undefined, then
|
|
// a.Return target.[[OwnPropertyKeys]]().
|
|
if (trap->IsUndefined()) {
|
|
return JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
|
|
}
|
|
|
|
// 8.Let trapResultArray be Call(trap, handler, «target»).
|
|
JSHandle<JSFunction> tagFunc(targetHandle);
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerHandle, 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);
|
|
|
|
// 9.Let trapResult be CreateListFromArrayLike(trapResultArray, «String, Symbol»).
|
|
// 10.ReturnIfAbrupt(trapResult)
|
|
// If trapResult contains any duplicate entries, throw a TypeError exception.
|
|
JSHandle<TaggedArray> trapRes(
|
|
JSObject::CreateListFromArrayLike<ElementTypes::STRING_AND_SYMBOL>(thread, trapResArr));
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
|
|
|
|
if (trapRes->HasDuplicateEntry()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: contains duplicate entries",
|
|
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
|
|
}
|
|
|
|
// 11.Let extensibleTarget be IsExtensible(target).
|
|
bool extensibleTarget = targetHandle->IsExtensible(thread);
|
|
|
|
// 12.ReturnIfAbrupt(extensibleTarget).
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
|
|
|
|
// 13.Let targetKeys be target.[[OwnPropertyKeys]]().
|
|
JSHandle<TaggedArray> targetKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle);
|
|
|
|
// 14.ReturnIfAbrupt(targetKeys).
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
|
|
|
|
// 15.Assert: targetKeys is a List containing only String and Symbol values.
|
|
// 16.Let targetConfigurableKeys be an empty List.
|
|
// 17.Let targetNonconfigurableKeys be an empty List.
|
|
// 18.Repeat, for each element key of targetKeys,
|
|
// a.Let desc be target.[[GetOwnProperty]](key).
|
|
// b.ReturnIfAbrupt(desc).
|
|
// c.If desc is not undefined and desc.[[Configurable]] is false, then
|
|
// i.Append key as an element of targetNonconfigurableKeys.
|
|
// d.Else,
|
|
// i.Append key as an element of targetConfigurableKeys.
|
|
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(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++;
|
|
}
|
|
}
|
|
|
|
// 19.If extensibleTarget is true and targetNonconfigurableKeys is empty, then
|
|
// a.Return trapResult.
|
|
if (extensibleTarget && (cfigLength == 0)) {
|
|
return trapRes;
|
|
}
|
|
|
|
// 20.Let uncheckedResultKeys be a new List which is a copy of trapResult.
|
|
JSHandle<TaggedArray> uncheckFesKeys =
|
|
thread->GetEcmaVM()->GetFactory()->CopyArray(trapRes, trapRes->GetLength(), trapRes->GetLength());
|
|
uint32_t uncheckLength = uncheckFesKeys->GetLength();
|
|
|
|
// 21.Repeat, for each key that is an element of targetNonconfigurableKeys,
|
|
// a.If key is not an element of uncheckedResultKeys, throw a TypeError exception.
|
|
// b.Remove key from uncheckedResultKeys
|
|
for (uint32_t i = 0; i < noCfigLength; i++) {
|
|
uint32_t idx = uncheckFesKeys->GetIdx(tgtNoCfigKeys->Get(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--;
|
|
}
|
|
|
|
// 22.If extensibleTarget is true, return trapResult.
|
|
if (extensibleTarget) {
|
|
return trapRes;
|
|
}
|
|
|
|
// 23.Repeat, for each key that is an element of targetConfigurableKeys,
|
|
// a.If key is not an element of uncheckedResultKeys, throw a TypeError exception.
|
|
// b.Remove key from uncheckedResultKeys
|
|
for (uint32_t i = 0; i < cfigLength; i++) {
|
|
uint32_t idx = uncheckFesKeys->GetIdx(tgtCfigKeys->Get(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--;
|
|
}
|
|
|
|
// 24.If uncheckedResultKeys is not empty, throw a TypeError exception.
|
|
if (uncheckLength != 0) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: uncheckedResultKeys is not empty",
|
|
JSHandle<TaggedArray>(thread, JSTaggedValue::Exception()));
|
|
}
|
|
|
|
// 25.Return trapResult.
|
|
return trapRes;
|
|
}
|
|
|
|
JSHandle<TaggedArray> JSProxy::GetAllPropertyKeys(JSThread *thread, const JSHandle<JSProxy> &proxy, uint32_t filter)
|
|
{
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
|
|
JSTaggedValue handler = proxy->GetHandler();
|
|
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());
|
|
|
|
JSHandle<JSTaggedValue> key = globalConst->GetHandledOwnKeysString();
|
|
JSHandle<JSTaggedValue> handlerHandle(thread, handler);
|
|
JSHandle<JSTaggedValue> trap(JSObject::GetMethod(thread, handlerHandle, 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, handlerHandle, 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(index).IsSymbol()) {
|
|
resArray->Set(thread, elementIndex, reciveArray->Get(index));
|
|
elementIndex++;
|
|
}
|
|
}
|
|
return resArray;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < trapResLength; i++) {
|
|
resArray->Set(thread, i, trapRes->Get(i));
|
|
}
|
|
|
|
uint32_t index = ownKeysLength;
|
|
for (uint32_t j = 0; j < reciveArrayLength - ownKeysLength; j++) {
|
|
resArray->Set(thread, trapResLength + j, reciveArray->Get(index));
|
|
index++;
|
|
}
|
|
return resArray;
|
|
}
|
|
|
|
// ES6 9.5.13 [[Call]] (thisArgument, argumentsList)
|
|
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());
|
|
// step 1 ~ 4 get ProxyHandler and ProxyTarget
|
|
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler());
|
|
if (handler->IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "Call: handler is null", JSTaggedValue::Exception());
|
|
}
|
|
ASSERT(handler->IsECMAObject());
|
|
JSHandle<JSTaggedValue> target(thread, proxy->GetTarget());
|
|
|
|
// 5.Let trap be GetMethod(handler, "apply").
|
|
JSHandle<JSTaggedValue> key(globalConst->GetHandledApplyString());
|
|
JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
|
|
|
|
// 6.ReturnIfAbrupt(trap).
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
uint32_t argc = info->GetArgsNumber();
|
|
JSHandle<JSTaggedValue> thisArg = info->GetThis();
|
|
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
|
|
// 7.If trap is undefined, then
|
|
// a.Return Call(target, thisArgument, argumentsList).
|
|
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);
|
|
}
|
|
// 8.Let argArray be CreateArrayFromList(argumentsList).
|
|
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);
|
|
|
|
// 9.Return Call(trap, handler, «target, thisArgument, argArray»).
|
|
const uint32_t argsLength = 3; // 3: «target, thisArgument, argArray»
|
|
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);
|
|
}
|
|
|
|
// ES6 9.5.14 [[Construct]] ( argumentsList, newTarget)
|
|
JSTaggedValue JSProxy::ConstructInternal(EcmaRuntimeCallInfo *info)
|
|
{
|
|
if (info == nullptr) {
|
|
return JSTaggedValue::Exception();
|
|
}
|
|
JSThread *thread = info->GetThread();
|
|
// check stack overflow because infinite recursion may occur
|
|
if (thread->DoStackLimitCheck()) {
|
|
return JSTaggedValue::Exception();
|
|
}
|
|
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
// step 1 ~ 4 get ProxyHandler and ProxyTarget
|
|
JSHandle<JSProxy> proxy(info->GetFunction());
|
|
JSHandle<JSTaggedValue> handler(thread, proxy->GetHandler());
|
|
if (handler->IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception());
|
|
}
|
|
ASSERT(handler->IsECMAObject());
|
|
JSHandle<JSTaggedValue> target(thread, proxy->GetTarget());
|
|
|
|
// 5.Let trap be GetMethod(handler, "construct").
|
|
JSHandle<JSTaggedValue> key(globalConst->GetHandledProxyConstructString());
|
|
JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
|
|
|
|
// 6.ReturnIfAbrupt(trap).
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
// 7.If trap is undefined, then
|
|
// a.Assert: target has a [[Construct]] internal method.
|
|
// b.Return Construct(target, argumentsList, newTarget).
|
|
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);
|
|
}
|
|
|
|
// 8.Let argArray be CreateArrayFromList(argumentsList).
|
|
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);
|
|
|
|
// step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »).
|
|
JSHandle<JSTaggedValue> newTarget(thread, info->GetNewTargetValue());
|
|
const uint32_t argsLength = 3; // 3: «target, argArray, newTarget »
|
|
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);
|
|
|
|
// 10.ReturnIfAbrupt(newObj).
|
|
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
|
|
// 11.If Type(newObj) is not Object, throw a TypeError exception.
|
|
if (!newObj.IsECMAObject()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception());
|
|
}
|
|
// 12.Return newObj.
|
|
return newObj;
|
|
}
|
|
|
|
bool JSProxy::IsArray(JSThread *thread) const
|
|
{
|
|
// check stack overflow because infinite recursion may occur
|
|
if (thread->DoStackLimitCheck()) {
|
|
return false;
|
|
}
|
|
if (GetHandler().IsNull()) {
|
|
THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsArray: handler is null", false);
|
|
}
|
|
return GetTarget().IsArray(thread);
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSProxy::GetSourceTarget(JSThread *thread) const
|
|
{
|
|
JSMutableHandle<JSProxy> proxy(thread, JSTaggedValue(this));
|
|
JSMutableHandle<JSTaggedValue> target(thread, proxy->GetTarget());
|
|
while (target->IsJSProxy()) {
|
|
proxy.Update(target.GetTaggedValue());
|
|
target.Update(proxy->GetTarget());
|
|
}
|
|
return target;
|
|
}
|
|
} // namespace panda::ecmascript
|