diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index ad177ba40..5c5e167c9 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -16,6 +16,7 @@ #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtins.h" +#include "ecma-builtin-object.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" #include "ecma-gc.h" @@ -311,8 +312,84 @@ ecma_value_t ecma_proxy_object_is_extensible (ecma_object_t *obj_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); - JERRY_UNUSED (obj_p); - return ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[IsExtensible]]")); + + ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; + + /* 1. */ + ecma_value_t handler = proxy_obj_p->handler; + + /* 2. */ + if (ecma_is_value_null (handler)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Handler can not be null.")); + } + + /* 3. */ + JERRY_ASSERT (ecma_is_value_object (handler)); + + /* 4. */ + ecma_value_t target = proxy_obj_p->target; + + /* 5. */ + ecma_value_t trap = ecma_op_get_method_by_magic_id (handler, LIT_MAGIC_STRING_IS_EXTENSIBLE); + + /* 6. */ + if (ECMA_IS_VALUE_ERROR (trap)) + { + return trap; + } + + ecma_object_t *target_obj_p = ecma_get_object_from_value (target); + + /* 7. */ + if (ecma_is_value_undefined (trap)) + { + return ecma_builtin_object_object_is_extensible (target_obj_p); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); + + /* 8. */ + ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, &target, 1); + + ecma_deref_object (func_obj_p); + + /* 9. */ + if (ECMA_IS_VALUE_ERROR (trap_result)) + { + return trap_result; + } + + bool boolean_trap_result = ecma_op_to_boolean (trap_result); + + ecma_free_value (trap_result); + + bool target_result; + + /* 10. */ + if (ECMA_OBJECT_IS_PROXY (target_obj_p)) + { + ecma_value_t proxy_is_ext = ecma_proxy_object_is_extensible (target_obj_p); + + if (ECMA_IS_VALUE_ERROR (proxy_is_ext)) + { + return proxy_is_ext; + } + + target_result = ecma_is_value_true (proxy_is_ext); + } + else + { + target_result = ecma_op_ordinary_object_is_extensible (target_obj_p); + } + + /* 12. */ + if (boolean_trap_result != target_result) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Trap result does not reflect extensibility of proxy target")); + } + + return ecma_make_boolean_value (boolean_trap_result); } /* ecma_proxy_object_is_extensible */ /** diff --git a/tests/jerry/es2015/proxy_is_extensible.js b/tests/jerry/es2015/proxy_is_extensible.js index cf83cf4ee..0f01c90f4 100644 --- a/tests/jerry/es2015/proxy_is_extensible.js +++ b/tests/jerry/es2015/proxy_is_extensible.js @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -// TODO: Update these tests when the internal routine has been implemented +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// error checks var target = {}; var handler = { isExtensible (target) { throw 42; @@ -26,7 +29,7 @@ try { Object.isFrozen(proxy) assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } try { @@ -34,13 +37,128 @@ try { Object.isSealed(proxy) assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } try { // 7.2.5 Object.isExtensible(proxy) assert(false); +} catch (e) { + assert(e === 42); +} + +// no trap +var target = {}; +var handler = {}; +var proxy = new Proxy(target, handler); +assert(Object.isExtensible(target) === true) +assert(Object.isExtensible(proxy) === true); +Object.preventExtensions(target); +assert(Object.isExtensible(target) === false); +assert(Object.isExtensible(proxy) === false); + +// undefined trap +var target = {}; +var handler = { isExtensible: null }; +var proxy = new Proxy(target, handler); +assert(Object.isExtensible(target) === true) +assert(Object.isExtensible(proxy) === true); +Object.preventExtensions(target); +assert(Object.isExtensible(target) === false); +assert(Object.isExtensible(proxy) === false); + +// invalid trap +var target = {}; +var handler = { isExtensible: true }; +var proxy = new Proxy(target, handler); + +try { + Object.isExtensible(proxy); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// valid trap +var target = { prop1: true }; +var handler = { + isExtensible(target) { + target.prop1 = false; + return Object.isExtensible(target); + } +}; + +var proxy = new Proxy(target, handler); +assert(Object.isExtensible(proxy) === true); +assert(target.prop1 === false); +Object.preventExtensions(target); +assert(Object.isExtensible(target) === false); +assert(Object.isExtensible(proxy) === false); + +// trap result is invalid +var target = {}; +var handler = { + isExtensible(target) { + return false; + } +}; + +var proxy = new Proxy(target, handler); + +try { + Object.isExtensible(proxy); + assert(false); } catch (e) { assert(e instanceof TypeError); } + +// handler is null +var target = {}; +var handler = { + isExtensible (target) { + return Object.isExtensible(target); + } +}; + +var revocable = Proxy.revocable (target, {}); +var proxy = revocable.proxy; +revocable.revoke(); + +try { + Object.isExtensible(proxy); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// proxy within proxy +var target = {}; +var handler1 = { + isExtensible(target) { + return Object.isExtensible(target); + } +}; + +var handler2 = { + isExtensible(target) { + return false; + } +}; + +var proxy = new Proxy(target, handler1); +var proxy2 = new Proxy(proxy, handler1); +assert(Object.isExtensible(proxy2) === true); + +var proxy3 = new Proxy(proxy, handler2); + +try { + Object.isExtensible(proxy3); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +var proxy4 = new Proxy(proxy2, handler1); +Object.preventExtensions(target); +assert(Object.isExtensible(proxy4) === false);