mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
1082 lines
46 KiB
C++
1082 lines
46 KiB
C++
/*
|
||
* Copyright (c) 2018-present Samsung Electronics Co., Ltd
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2.1 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with this library; if not, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||
* USA
|
||
*/
|
||
|
||
#include "Escargot.h"
|
||
#include "ProxyObject.h"
|
||
#include "Context.h"
|
||
#include "runtime/ArrayObject.h"
|
||
#include "interpreter/ByteCodeInterpreter.h"
|
||
|
||
namespace Escargot {
|
||
|
||
ProxyObject::ProxyObject(ExecutionState& state)
|
||
: DerivedObject(state)
|
||
, m_isCallable(false)
|
||
, m_isConstructible(false)
|
||
, m_target(nullptr)
|
||
, m_handler(nullptr)
|
||
{
|
||
}
|
||
|
||
void* ProxyObject::operator new(size_t size)
|
||
{
|
||
static MAY_THREAD_LOCAL bool typeInited = false;
|
||
static MAY_THREAD_LOCAL GC_descr descr;
|
||
if (!typeInited) {
|
||
GC_word obj_bitmap[GC_BITMAP_SIZE(ProxyObject)] = { 0 };
|
||
Object::fillGCDescriptor(obj_bitmap);
|
||
GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ProxyObject, m_target));
|
||
GC_set_bit(obj_bitmap, GC_WORD_OFFSET(ProxyObject, m_handler));
|
||
descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(ProxyObject));
|
||
typeInited = true;
|
||
}
|
||
return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
|
||
}
|
||
|
||
Context* ProxyObject::getFunctionRealm(ExecutionState& state)
|
||
{
|
||
if (m_handler == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_NULL(state, ErrorCode::TypeError, state.context()->staticStrings().Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
return m_target->getFunctionRealm(state);
|
||
}
|
||
|
||
|
||
// https://tc39.es/ecma262/#sec-proxycreate
|
||
ProxyObject* ProxyObject::createProxy(ExecutionState& state, const Value& target, const Value& handler)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
|
||
// If Type(target) is not Object, throw a TypeError exception.
|
||
if (!target.isObject()) {
|
||
THROW_BUILTIN_ERROR_RETURN_NULL(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: \'target\' argument of Proxy must be an object");
|
||
}
|
||
|
||
// If Type(handler) is not Object, throw a TypeError exception.
|
||
if (!handler.isObject()) {
|
||
THROW_BUILTIN_ERROR_RETURN_NULL(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: \'handler\' argument of Proxy must be an object");
|
||
}
|
||
|
||
// Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
|
||
// Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 9.5.
|
||
ProxyObject* P = new ProxyObject(state);
|
||
|
||
// If IsCallable(target) is true, then
|
||
if (target.isCallable()) {
|
||
// Set P.[[Call]] as specified in 9.5.12.
|
||
P->m_isCallable = true;
|
||
// If IsConstructor(target) is true, then
|
||
if (target.isConstructor()) {
|
||
// Set P.[[Construct]] as specified in 9.5.13.
|
||
P->m_isConstructible = true;
|
||
}
|
||
}
|
||
|
||
// Set P.[[ProxyTarget]] to target.
|
||
P->setTarget(target.asObject());
|
||
|
||
// Set P.[[ProxyHandler]] to handler.
|
||
P->setHandler(handler.asObject());
|
||
|
||
// Return P.
|
||
return P;
|
||
}
|
||
|
||
bool ProxyObject::defineOwnProperty(ExecutionState& state, const ObjectPropertyName& P, const ObjectPropertyDescriptor& desc)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "defineProperty").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->defineProperty.string()));
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[DefineOwnProperty]](P, Desc).
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->defineOwnProperty(state, P, desc);
|
||
}
|
||
|
||
// 9. Let descObj be FromPropertyDescriptor(Desc).
|
||
// 10. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, descObj»)).
|
||
// 11. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target, P.toPropertyKeyValue(), Value(ObjectPropertyDescriptor::fromObjectPropertyDescriptor(state, desc)) };
|
||
Value trapResult = Object::call(state, trap, handler, 3, arguments);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// 12. If booleanTrapResult is false, return false.
|
||
if (!booleanTrapResult) {
|
||
return false;
|
||
}
|
||
|
||
// 13. Let targetDesc be target.[[GetOwnProperty]](P).
|
||
ObjectGetResult targetDesc = target.asObject()->getOwnProperty(state, P);
|
||
|
||
// 15. Let extensibleTarget be IsExtensible(target).
|
||
bool extensibleTarget = target.asObject()->isExtensible(state);
|
||
|
||
// 17. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
|
||
// a. Let settingConfigFalse be true.
|
||
// 18. Else let settingConfigFalse be false.
|
||
bool settingConfigFalse = false;
|
||
if (desc.isConfigurablePresent() && !desc.isConfigurable()) {
|
||
settingConfigFalse = true;
|
||
}
|
||
|
||
// 19. If targetDesc is undefined, then
|
||
// a. If extensibleTarget is false, throw a TypeError exception.
|
||
// b. If settingConfigFalse is true, throw a TypeError exception.
|
||
if (!targetDesc.hasValue()) {
|
||
if (!extensibleTarget || settingConfigFalse) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
} else {
|
||
// 20. Else targetDesc is not undefined,
|
||
// a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc , targetDesc) is false, throw a TypeError exception.
|
||
// b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
|
||
if (!Object::isCompatiblePropertyDescriptor(state, extensibleTarget, desc, targetDesc) || (settingConfigFalse && targetDesc.isConfigurable())) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
if (targetDesc.isDataProperty() && !targetDesc.isConfigurable() && targetDesc.isWritable()) {
|
||
if (desc.isWritablePresent() && !desc.isWritable()) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool ProxyObject::deleteOwnProperty(ExecutionState& state, const ObjectPropertyName& P)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "deleteProperty").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->deleteProperty.string()));
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[Delete]](P).
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->deleteOwnProperty(state, P);
|
||
}
|
||
|
||
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)).
|
||
// 10. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target, P.toPropertyKeyValue() };
|
||
Value trapResult = Object::call(state, trap, handler, 2, arguments);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// 11. If booleanTrapResult is false, return false.
|
||
if (!booleanTrapResult) {
|
||
return false;
|
||
}
|
||
|
||
// 12. Let targetDesc be target.[[GetOwnProperty]](P).
|
||
ObjectGetResult targetDesc = target.asObject()->getOwnProperty(state, P);
|
||
|
||
// 14. If targetDesc is undefined, return true.
|
||
if (targetDesc.value(state, target).isUndefined()) {
|
||
return true;
|
||
}
|
||
|
||
// 15. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
||
if (!targetDesc.isConfigurable()) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
if (!target.asObject()->isExtensible(state)) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
return true;
|
||
}
|
||
|
||
ObjectGetResult ProxyObject::getOwnProperty(ExecutionState& state, const ObjectPropertyName& P)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "getOwnPropertyDescriptor").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->getOwnPropertyDescriptor.string()));
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[GetOwnProperty]](P).
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->getOwnProperty(state, P);
|
||
}
|
||
|
||
// 9. Let trapResultObj be Call(trap, handler, «target, P»).
|
||
// 10. ReturnIfAbrupt(trapResultObj).
|
||
Value trapResultObj;
|
||
Value arguments[] = { target, P.toPropertyKeyValue() };
|
||
trapResultObj = Object::call(state, trap, handler, 2, arguments);
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
|
||
if (!trapResultObj.isObject() && !trapResultObj.isUndefined()) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 12. Let targetDesc be target.[[GetOwnProperty]](P).
|
||
ObjectGetResult targetDesc = target.asObject()->getOwnProperty(state, P);
|
||
|
||
// 14. If trapResultObj is undefined, then
|
||
if (trapResultObj.isUndefined()) {
|
||
// a. If targetDesc is undefined, return undefined.
|
||
if (targetDesc.value(state, target).isUndefined()) {
|
||
return ObjectGetResult();
|
||
}
|
||
// b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
||
if (!targetDesc.isConfigurable()) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
return ObjectGetResult();
|
||
}
|
||
// c. Let extensibleTarget be IsExtensible(target).
|
||
// d. ReturnIfAbrupt(extensibleTarget).
|
||
// e. Assert: Type(extensibleTarget) is Boolean.
|
||
bool extensibleTarget = target.asObject()->isExtensible(state);
|
||
// f. If extensibleTarget is false, throw a TypeError exception.
|
||
if (!extensibleTarget) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
return ObjectGetResult();
|
||
}
|
||
// g. Return undefined.
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 15. Let extensibleTarget be IsExtensible(target).
|
||
// 17. Let resultDesc be ToPropertyDescriptor(trapResultObj).
|
||
// 18. ReturnIfAbrupt(resultDesc).
|
||
ObjectPropertyDescriptor resultDesc(state, trapResultObj.asObject());
|
||
// 19. Call CompletePropertyDescriptor(resultDesc).
|
||
ObjectPropertyDescriptor::completePropertyDescriptor(resultDesc);
|
||
// 20. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, resultDesc, targetDesc).
|
||
// 21. If valid is false, throw a TypeError exception.
|
||
if (!Object::isCompatiblePropertyDescriptor(state, target.asObject()->isExtensible(state), resultDesc, targetDesc)) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Proxy::getOwnPropertyDescriptor error");
|
||
return ObjectGetResult();
|
||
}
|
||
// 22. If resultDesc.[[Configurable]] is false, then
|
||
if (!resultDesc.isConfigurable()) {
|
||
// a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
|
||
if (!targetDesc.hasValue() || targetDesc.isConfigurable()) {
|
||
// i. Throw a TypeError exception.
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Proxy::getOwnPropertyDescriptor error");
|
||
return ObjectGetResult();
|
||
}
|
||
if (resultDesc.isWritablePresent() && !resultDesc.isWritable() && targetDesc.isWritable()) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Proxy::getOwnPropertyDescriptor error");
|
||
return ObjectGetResult();
|
||
}
|
||
}
|
||
|
||
// 23. Return resultDesc.
|
||
if (resultDesc.isDataDescriptor()) {
|
||
return ObjectGetResult(resultDesc.value(), resultDesc.isWritable(), resultDesc.isEnumerable(), resultDesc.isConfigurable());
|
||
}
|
||
return ObjectGetResult(new JSGetterSetter(resultDesc.getterSetter()), resultDesc.isEnumerable(), resultDesc.isConfigurable());
|
||
}
|
||
|
||
bool ProxyObject::preventExtensions(ExecutionState& state)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
// 2. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 3. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 5. Let trap be GetMethod(handler, "preventExtensions").
|
||
// 6. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->preventExtensions.string()));
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 7. If trap is undefined, then
|
||
// a. Return target.[[PreventExtensions]]().
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->preventExtensions(state);
|
||
}
|
||
|
||
// 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)).
|
||
// 9. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target };
|
||
Value trapResult = Object::call(state, trap, handler, 1, arguments);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// 10. If booleanTrapResult is true, then
|
||
if (booleanTrapResult) {
|
||
// a. Let targetIsExtensible be target.[[IsExtensible]]().
|
||
bool targetIsExtensible = target.asObject()->isExtensible(state);
|
||
// c. If targetIsExtensible is true, throw a TypeError exception.
|
||
if (targetIsExtensible) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
}
|
||
|
||
// 11. Return booleanTrapResult.
|
||
return booleanTrapResult;
|
||
}
|
||
|
||
ObjectHasPropertyResult ProxyObject::hasProperty(ExecutionState& state, const ObjectPropertyName& propertyName)
|
||
{
|
||
//CHECK_STACK_OVERFLOW(state);
|
||
|
||
auto strings = &state.context()->staticStrings();
|
||
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
return ObjectHasPropertyResult();
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "has").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->has.string()));
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return ObjectHasPropertyResult();
|
||
}
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[HasProperty]](P).
|
||
if (trap.isUndefined()) {
|
||
auto exist = target.asObject()->hasProperty(state, propertyName);
|
||
if (exist) {
|
||
return ObjectHasPropertyResult([](ExecutionState& state, const ObjectPropertyName& P, void* handlerData) -> Value {
|
||
ProxyObject* p = (ProxyObject*)handlerData;
|
||
return p->get(state, P, p).value(state, p);
|
||
},
|
||
this);
|
||
} else {
|
||
return ObjectHasPropertyResult();
|
||
}
|
||
}
|
||
|
||
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)).
|
||
// 10. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target, propertyName.toPropertyKeyValue() };
|
||
Value trapResult = Object::call(state, trap, handler, 2, arguments);
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return ObjectGetResult();
|
||
}
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// 11. If booleanTrapResult is false, then
|
||
if (!booleanTrapResult) {
|
||
// a. Let targetDesc be target.[[GetOwnProperty]](P).
|
||
ObjectGetResult targetDesc = target.asObject()->getOwnProperty(state, propertyName);
|
||
// c. If targetDesc is not undefined, then
|
||
if (targetDesc.hasValue()) {
|
||
// i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
|
||
if (!targetDesc.isConfigurable()) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
return ObjectGetResult();
|
||
}
|
||
// ii. Let extensibleTarget be IsExtensible(target).
|
||
bool extensibleTarget = target.asObject()->isExtensible(state);
|
||
// iv. If extensibleTarget is false, throw a TypeError exception.
|
||
if (!extensibleTarget) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
return ObjectGetResult();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 12. Return booleanTrapResult.
|
||
if (booleanTrapResult) {
|
||
return ObjectHasPropertyResult([](ExecutionState& state, const ObjectPropertyName& P, void* handlerData) -> Value {
|
||
ProxyObject* p = (ProxyObject*)handlerData;
|
||
return p->get(state, P, p).value(state, p);
|
||
},
|
||
this);
|
||
}
|
||
return ObjectHasPropertyResult();
|
||
}
|
||
|
||
Object::OwnPropertyKeyVector ProxyObject::ownPropertyKeys(ExecutionState& state)
|
||
{
|
||
// https://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
|
||
auto strings = &state.context()->staticStrings();
|
||
|
||
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
// 2. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
Value handler(this->handler());
|
||
|
||
// 3. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 5. Let trap be GetMethod(handler, "ownKeys").
|
||
// 6. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->ownKeys));
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
|
||
// 7. If trap is undefined, then
|
||
// a. Return target.[[OwnPropertyKeys]]().
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->ownPropertyKeys(state);
|
||
}
|
||
|
||
// 8. Let trapResultArray be Call(trap, handler, «target»).
|
||
// 9. Let trapResult be CreateListFromArrayLike(trapResultArray, «String, Symbol»).
|
||
// 10. ReturnIfAbrupt(trapResult).
|
||
Value trapResultArray;
|
||
Value arguments[] = { target };
|
||
trapResultArray = Object::call(state, trap, handler, 1, arguments);
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
|
||
auto trapResult = Object::createListFromArrayLike(state, trapResultArray, (static_cast<uint8_t>(ElementTypes::String) | static_cast<uint8_t>(ElementTypes::Symbol)));
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
|
||
// If trapResult contains any duplicate entries, throw a TypeError exception
|
||
for (size_t i = 0; i < trapResult.size(); i++) {
|
||
for (size_t j = i + 1; j < trapResult.size(); j++) {
|
||
if (trapResult[i].equalsTo(state, trapResult[j])) {
|
||
// return exception done!
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s Contains duplacted entries.");
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
}
|
||
}
|
||
// 11. Let extensibleTarget be IsExtensible(target).
|
||
// 12. ReturnIfAbrupt(extensibleTarget).
|
||
bool extensibleTarget = target.asObject()->isExtensible(state);
|
||
|
||
// 13. Let targetKeys be target.[[OwnPropertyKeys]]().
|
||
// 14. ReturnIfAbrupt(targetKeys).
|
||
auto targetKeys = target.asObject()->ownPropertyKeys(state);
|
||
// 15. targetKeys is a List containing only String and Symbol values.
|
||
for (size_t i = 0; i < targetKeys.size(); ++i) {
|
||
ASSERT((targetKeys[i].isString() || targetKeys[i].isSymbol()));
|
||
}
|
||
|
||
// 16. Let targetConfigurableKeys be an empty List.
|
||
// 17. Let targetNonconfigurableKeys be an empty List.
|
||
ValueVector targetConfigurableKeys;
|
||
ValueVector targetNonconfigurableKeys;
|
||
|
||
// 18 .Repeat, for each element key of targetKeys,
|
||
for (size_t i = 0; i < targetKeys.size(); ++i) {
|
||
auto& key = targetKeys[i];
|
||
// a. Let desc be target.[[GetOwnProperty]](key).
|
||
// b. ReturnIfAbrupt(desc).
|
||
ObjectPropertyName propertyname = ObjectPropertyName(state, key);
|
||
auto desc = target.asObject()->getOwnProperty(state, propertyname);
|
||
|
||
// c. If desc is not undefined and desc.[[Configurable]] is false, then
|
||
if (desc.hasValue() && !desc.isConfigurable()) {
|
||
// i. Append key as an element of targetNonconfigurableKeys.
|
||
targetNonconfigurableKeys.pushBack(key);
|
||
} else { // d.Else,
|
||
// i. Append key as an element of targetConfigurableKeys.
|
||
targetConfigurableKeys.pushBack(key);
|
||
}
|
||
}
|
||
// 19. If extensibleTarget is true and targetNonconfigurableKeys is empty, then
|
||
if (extensibleTarget && targetNonconfigurableKeys.size() == 0) {
|
||
// a. Return trapResult.
|
||
return OwnPropertyKeyVector(trapResult.data(), trapResult.size());
|
||
}
|
||
|
||
// 20. Let uncheckedResultKeys be a new List which is a copy of trapResult.
|
||
ValueVector uncheckedResultKeys;
|
||
for (size_t i = 0; i < trapResult.size(); ++i) {
|
||
uncheckedResultKeys.push_back(trapResult[i]);
|
||
}
|
||
|
||
// 21. Repeat, for each key that is an element of targetNonconfigurableKeys,
|
||
for (size_t i = 0; i < targetNonconfigurableKeys.size(); ++i) {
|
||
auto& key = targetNonconfigurableKeys[i];
|
||
// a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
|
||
// b. Remove key from uncheckedResultKeys
|
||
bool found = false;
|
||
for (size_t i = 0; i < uncheckedResultKeys.size(); i++) {
|
||
if (uncheckedResultKeys[i].equalsTo(state, key)) {
|
||
uncheckedResultKeys.erase(i);
|
||
found = true;
|
||
i--;
|
||
}
|
||
}
|
||
if (!found) {
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: the key of targetNonconfigurableKeys is not an element of uncheckedResultKeys.");
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
}
|
||
|
||
// 22. If extensibleTarget is true, return trapResult.
|
||
if (extensibleTarget) {
|
||
return OwnPropertyKeyVector(trapResult.data(), trapResult.size());
|
||
}
|
||
|
||
// 23. Repeat, for each key that is an element of targetConfigurableKeys,
|
||
for (size_t i = 0; i < targetConfigurableKeys.size(); ++i) {
|
||
auto& key = targetConfigurableKeys[i];
|
||
// a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
|
||
// b. Remove key from uncheckedResultKeys
|
||
bool found = false;
|
||
for (size_t i = 0; i < uncheckedResultKeys.size(); i++) {
|
||
if (uncheckedResultKeys[i].equalsTo(state, key)) {
|
||
uncheckedResultKeys.erase(i);
|
||
found = true;
|
||
i--;
|
||
}
|
||
}
|
||
if (!found) {
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: the key of targetConfigurableKeys is not an element of uncheckedResultKeys.");
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
}
|
||
// 24. If uncheckedResultKeys is not empty, throw a TypeError exception.
|
||
if (uncheckedResultKeys.size()) {
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: uncheckedResultKeys is not empty");
|
||
return OwnPropertyKeyVector();
|
||
}
|
||
|
||
// 25. Return trapResult.
|
||
return OwnPropertyKeyVector(trapResult.data(), trapResult.size());
|
||
}
|
||
|
||
bool ProxyObject::isExtensible(ExecutionState& state)
|
||
{
|
||
//CHECK_STACK_OVERFLOW(state);
|
||
|
||
auto strings = &state.context()->staticStrings();
|
||
// 2. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 3. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 5. Let trap be GetMethod(handler, "isExtensible").
|
||
// 6. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->isExtensible.string()));
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 7. If trap is undefined, then
|
||
// a. Return target.[[IsExtensible]]().
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->isExtensible(state);
|
||
}
|
||
|
||
// 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)).
|
||
// 9. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target };
|
||
Value trapResult = Object::call(state, trap, handler, 1, arguments);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// 10. Let targetResult be target.[[IsExtensible]]().
|
||
bool targetResult = target.asObject()->isExtensible(state);
|
||
|
||
// 12. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
|
||
if (targetResult != booleanTrapResult) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
// 13. Return booleanTrapResult.
|
||
return booleanTrapResult;
|
||
}
|
||
|
||
void ProxyObject::enumeration(ExecutionState& state, bool (*callback)(ExecutionState& state, Object* self, const ObjectPropertyName&, const ObjectStructurePropertyDescriptor& desc, void* data), void* data, bool shouldSkipSymbolKey)
|
||
{
|
||
// Note : the enumeration method is not [[Enumerate]] () in spec
|
||
// In our implementation, Object::enumeration method should be overridden properly, therefore when calling this functionm, must run enumeration of target
|
||
this->target()->enumeration(state, callback, data, shouldSkipSymbolKey);
|
||
}
|
||
|
||
// https://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
|
||
bool ProxyObject::setPrototype(ExecutionState& state, const Value& value)
|
||
{
|
||
//CHECK_STACK_OVERFLOW(state);
|
||
|
||
auto strings = &state.context()->staticStrings();
|
||
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
|
||
ASSERT(value.isObject() || value.isNull());
|
||
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "setPrototypeOf").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->setPrototypeOf.string()));
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[SetPrototypeOf]](V).
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->setPrototype(state, value);
|
||
}
|
||
|
||
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, V»)).
|
||
// 10. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target, value };
|
||
Value trapResult = Object::call(state, trap, handler, 2, arguments);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// For ES2018 compatibility 9.5.2.9
|
||
if (!booleanTrapResult) {
|
||
return booleanTrapResult;
|
||
}
|
||
|
||
// 11. Let extensibleTarget be IsExtensible(target).
|
||
bool extensibleTarget = target.asObject()->isExtensible(state);
|
||
|
||
// 13. If extensibleTarget is true, return booleanTrapResult.
|
||
if (extensibleTarget) {
|
||
return booleanTrapResult;
|
||
}
|
||
|
||
// 14. Let targetProto be target.[[GetPrototypeOf]]().
|
||
Value targetProto = target.asObject()->getPrototype(state);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 16. If booleanTrapResult is true and SameValue(V, targetProto) is false, throw a TypeError exception.
|
||
if (booleanTrapResult && value != targetProto) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
|
||
// 17. Return booleanTrapResult.
|
||
return booleanTrapResult;
|
||
}
|
||
|
||
Object* ProxyObject::getPrototypeObject(ExecutionState& state)
|
||
{
|
||
if (!this->target()) {
|
||
return nullptr;
|
||
}
|
||
|
||
Value result = getPrototype(state);
|
||
RETURN_NULL_IF_PENDING_EXCEPTION
|
||
if (result.isObject()) {
|
||
return result.asObject();
|
||
}
|
||
|
||
return nullptr;
|
||
}
|
||
|
||
// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof
|
||
Value ProxyObject::getPrototype(ExecutionState& state)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
// 2. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 3. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 5. Let trap be GetMethod(handler, "getPrototypeOf").
|
||
// 6. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->getPrototypeOf.string()));
|
||
RETURN_VALUE_IF_PENDING_EXCEPTION
|
||
|
||
// 7. If trap is undefined, then
|
||
// a. Return target.[[GetPrototypeOf]]().
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->getPrototype(state);
|
||
}
|
||
|
||
// 8. Let handlerProto be Call(trap, handler, «target»).
|
||
// 9. ReturnIfAbrupt(handlerProto).
|
||
Value handlerProto;
|
||
Value arguments[] = { target };
|
||
handlerProto = Object::call(state, trap, handler, 1, arguments);
|
||
RETURN_VALUE_IF_PENDING_EXCEPTION
|
||
|
||
// 10. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.
|
||
if (!handlerProto.isObject() && !handlerProto.isNull()) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
|
||
// 11. Let extensibleTarget be IsExtensible(target).
|
||
bool extensibleTarget = target.asObject()->isExtensible(state);
|
||
|
||
// 13. If extensibleTarget is true, return handlerProto.
|
||
if (extensibleTarget) {
|
||
return handlerProto;
|
||
}
|
||
|
||
// 14. Let targetProto be target.[[GetPrototypeOf]]().
|
||
Value targetProto = target.asObject()->getPrototype(state);
|
||
RETURN_VALUE_IF_PENDING_EXCEPTION
|
||
|
||
// 16. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
|
||
if (handlerProto != targetProto) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error");
|
||
}
|
||
|
||
// 17. Return handlerProto.
|
||
return handlerProto;
|
||
}
|
||
|
||
// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
|
||
ObjectGetResult ProxyObject::get(ExecutionState& state, const ObjectPropertyName& propertyName, const Value& receiver)
|
||
{
|
||
//CHECK_STACK_OVERFLOW(state);
|
||
|
||
auto strings = &state.context()->staticStrings();
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "get").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->get.string()));
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[Get]](P, Receiver).
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->get(state, propertyName, receiver);
|
||
}
|
||
|
||
// 9. Let trapResult be Call(trap, handler, «target, P, Receiver»).
|
||
// 10. ReturnIfAbrupt(trapResult).
|
||
Value trapResult;
|
||
Value arguments[] = { target, propertyName.toPropertyKeyValue(), Value(this) };
|
||
trapResult = Object::call(state, trap, handler, 3, arguments);
|
||
if (UNLIKELY(state.hasPendingException())) {
|
||
return ObjectGetResult();
|
||
}
|
||
|
||
// 11. Let targetDesc be target.[[GetOwnProperty]](P).
|
||
ObjectGetResult targetDesc = target.asObject()->getOwnProperty(state, propertyName);
|
||
|
||
// 13. If targetDesc is not undefined, then
|
||
if (targetDesc.hasValue()) {
|
||
// a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
|
||
if (targetDesc.isDataProperty() && !targetDesc.isConfigurable() && !targetDesc.isWritable()) {
|
||
// i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
|
||
if (trapResult != targetDesc.value(state, target)) {
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error.");
|
||
return ObjectGetResult();
|
||
}
|
||
}
|
||
// b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is undefined, then
|
||
if (!targetDesc.isDataProperty() && !targetDesc.isConfigurable() && (!targetDesc.jsGetterSetter()->hasGetter() || targetDesc.jsGetterSetter()->getter().isUndefined())) {
|
||
// i. If trapResult is not undefined, throw a TypeError exception.
|
||
if (!trapResult.isUndefined()) {
|
||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error.");
|
||
return ObjectGetResult();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 14. Return trapResult.
|
||
return ObjectGetResult(trapResult, true, true, true);
|
||
}
|
||
|
||
// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
|
||
bool ProxyObject::set(ExecutionState& state, const ObjectPropertyName& propertyName, const Value& v, const Value& receiver)
|
||
{
|
||
//CHECK_STACK_OVERFLOW(state);
|
||
|
||
auto strings = &state.context()->staticStrings();
|
||
// 3. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error.");
|
||
}
|
||
|
||
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 4. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 6. Let trap be GetMethod(handler, "set").
|
||
// 7. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->set.string()));
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
|
||
// 8. If trap is undefined, then
|
||
// a. Return target.[[Set]](P, V, Receiver).
|
||
if (trap.isUndefined()) {
|
||
return target.asObject()->set(state, propertyName, v, receiver);
|
||
}
|
||
|
||
// 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»)).
|
||
// 10. ReturnIfAbrupt(booleanTrapResult).
|
||
Value arguments[] = { target, propertyName.toPropertyKeyValue(), v, receiver };
|
||
Value trapResult = Object::call(state, trap, handler, 4, arguments);
|
||
RETURN_ZERO_IF_PENDING_EXCEPTION
|
||
bool booleanTrapResult = trapResult.toBoolean(state);
|
||
|
||
// 11. If booleanTrapResult is false, return false.
|
||
if (!booleanTrapResult) {
|
||
return false;
|
||
}
|
||
|
||
// 12. Let targetDesc be target.[[GetOwnProperty]](P).
|
||
ObjectGetResult targetDesc = target.asObject()->getOwnProperty(state, propertyName);
|
||
|
||
// 14. If targetDesc is not undefined, then
|
||
if (targetDesc.hasValue()) {
|
||
// a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
|
||
if (targetDesc.isDataProperty() && !targetDesc.isConfigurable() && !targetDesc.isWritable()) {
|
||
// i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
|
||
if (v != targetDesc.value(state, target)) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error.");
|
||
}
|
||
}
|
||
// b. TODO If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then
|
||
if (!targetDesc.isDataProperty() && !targetDesc.isConfigurable()) {
|
||
// i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
|
||
if ((!targetDesc.jsGetterSetter()->hasSetter() || targetDesc.jsGetterSetter()->setter().isUndefined())) {
|
||
THROW_BUILTIN_ERROR_RETURN_ZERO(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy Type Error.");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 15. Return true.
|
||
return true;
|
||
}
|
||
|
||
// https://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
|
||
Value ProxyObject::call(ExecutionState& state, const Value& receiver, const size_t argc, Value* argv)
|
||
{
|
||
if (UNLIKELY(!m_isCallable)) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, ErrorObject::Messages::NOT_Callable);
|
||
}
|
||
|
||
auto strings = &state.context()->staticStrings();
|
||
// 2. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 3. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 5. Let trap be GetMethod(handler, "apply").
|
||
// 6. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->apply.string()));
|
||
RETURN_VALUE_IF_PENDING_EXCEPTION
|
||
|
||
// 7. If trap is undefined, then
|
||
// a. Return Call(target, thisArgument, argumentsList).
|
||
if (trap.isUndefined()) {
|
||
return Object::call(state, target, receiver, argc, argv);
|
||
}
|
||
|
||
// 8. Let argArray be CreateArrayFromList(argumentsList).
|
||
ArrayObject* argArray = Object::createArrayFromList(state, argc, argv);
|
||
|
||
// 9. Return Call(trap, handler, «target, thisArgument, argArray»).
|
||
Value arguments[] = { target, receiver, Value(argArray) };
|
||
return Object::call(state, trap, handler, 3, arguments);
|
||
}
|
||
|
||
// https://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget
|
||
Value ProxyObject::construct(ExecutionState& state, const size_t argc, Value* argv, Object* newTarget)
|
||
{
|
||
auto strings = &state.context()->staticStrings();
|
||
// 2. If handler is null, throw a TypeError exception.
|
||
if (this->handler() == nullptr) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: Proxy handler should not be null.");
|
||
}
|
||
|
||
// 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
|
||
Value handler(this->handler());
|
||
|
||
// 3. Assert: Type(handler) is Object.
|
||
ASSERT(handler.isObject());
|
||
|
||
// 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
|
||
Value target(this->target());
|
||
|
||
// 5. Let trap be GetMethod(handler, "construct").
|
||
// 6. ReturnIfAbrupt(trap).
|
||
Value trap;
|
||
trap = Object::getMethod(state, handler, ObjectPropertyName(state, strings->construct.string()));
|
||
RETURN_VALUE_IF_PENDING_EXCEPTION
|
||
|
||
// 7. If trap is undefined, then
|
||
// a. Assert: target has a [[Construct]] internal method.
|
||
// b. Return Construct(target, argumentsList, newTarget).
|
||
if (trap.isUndefined()) {
|
||
ASSERT(target.isConstructor());
|
||
return Object::construct(state, target, argc, argv, newTarget);
|
||
}
|
||
|
||
// 8. Let argArray be CreateArrayFromList(argumentsList).
|
||
ArrayObject* argArray = Object::createArrayFromList(state, argc, argv);
|
||
|
||
// 9. Let newObj be Call(trap, handler, «target, argArray, newTarget »).
|
||
Value arguments[] = { target, Value(argArray), Value(newTarget) };
|
||
Value newObj = Object::call(state, trap, handler, 3, arguments);
|
||
RETURN_VALUE_IF_PENDING_EXCEPTION
|
||
|
||
// 11. If Type(newObj) is not Object, throw a TypeError exception.
|
||
if (!newObj.isObject()) {
|
||
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: The result of [[Construct]] must be an Object.");
|
||
}
|
||
|
||
// 12. Return newObj.
|
||
return newObj;
|
||
}
|
||
} // namespace Escargot
|