diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index c3036e175..bbc178413 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -714,8 +714,90 @@ ecma_proxy_object_has (ecma_object_t *obj_p, /**< proxy object */ ecma_string_t *prop_name_p) /**< property name */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); - JERRY_UNUSED_2 (obj_p, prop_name_p); - return ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[HasProperty]]")); + + ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; + + /* 2. */ + ecma_value_t handler = proxy_obj_p->handler; + + /* 3-6. */ + ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_HAS); + + /* 7. */ + if (ECMA_IS_VALUE_ERROR (trap)) + { + return trap; + } + + ecma_value_t target = proxy_obj_p->target; + ecma_object_t *target_obj_p = ecma_get_object_from_value (target); + + /* 8. */ + if (ecma_is_value_undefined (trap)) + { + return ecma_op_object_has_property (target_obj_p, prop_name_p); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); + ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p); + ecma_value_t args[] = {target, prop_value}; + + /* 9. */ + ecma_value_t trap_result = ecma_op_function_call (func_obj_p, handler, args, 2); + + ecma_deref_object (func_obj_p); + + /* 10. */ + 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); + + /* 11. */ + if (!boolean_trap_result) + { + ecma_property_descriptor_t target_desc; + + ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p, prop_name_p, &target_desc); + + if (ECMA_IS_VALUE_ERROR (status)) + { + return status; + } + + if (ecma_is_value_true (status)) + { + bool prop_is_configurable = target_desc.flags & ECMA_PROP_IS_CONFIGURABLE; + + ecma_free_property_descriptor (&target_desc); + + if (!prop_is_configurable) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned falsish for property which exists " + "in the proxy target as non-configurable")); + } + + ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); + + if (ECMA_IS_VALUE_ERROR (extensible_target)) + { + return extensible_target; + } + + if (ecma_is_value_false (extensible_target)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned falsish for property but " + "the proxy target is not extensible")); + } + } + } + + /* 12. */ + return ecma_make_boolean_value (boolean_trap_result); } /* ecma_proxy_object_has */ /** diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index f40146193..ca335cabc 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -75,6 +75,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FOR, "for") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET, "get") #if ENABLED (JERRY_ES2015_BUILTIN_MAP) \ +|| ENABLED (JERRY_ES2015_BUILTIN_PROXY) \ || ENABLED (JERRY_ES2015_BUILTIN_REFLECT) \ || ENABLED (JERRY_ES2015_BUILTIN_SET) \ || ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) \ diff --git a/tests/jerry/es2015/proxy_delete.js b/tests/jerry/es2015/proxy_delete.js index 1e35066bd..1cd21ce74 100644 --- a/tests/jerry/es2015/proxy_delete.js +++ b/tests/jerry/es2015/proxy_delete.js @@ -29,14 +29,9 @@ var proxy = new Proxy(target, handler); var a = 5; -try { - // ecma_op_delete_binding - with (proxy) { - delete a - } - assert(false); -} catch (e) { - assert(e instanceof TypeError); +// ecma_op_delete_binding +with (proxy) { + delete a } try { diff --git a/tests/jerry/es2015/proxy_has.js b/tests/jerry/es2015/proxy_has.js index af23c8e43..fd7657553 100644 --- a/tests/jerry/es2015/proxy_has.js +++ b/tests/jerry/es2015/proxy_has.js @@ -12,7 +12,9 @@ // 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. var target = {}; var handler = { has (target) { @@ -26,7 +28,7 @@ try { "foo" in proxy; assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } try { @@ -37,7 +39,7 @@ try { } assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } try { @@ -47,6 +49,124 @@ try { assert(false); } assert(false); +} catch (e) { + assert(e === 42); +} + +// test basic functionality +var target = { + "target_one": 1 +}; + +var handler = { + has: function(target, prop) { + return prop == "present"; + } +} + +var proxy = new Proxy(target, handler); + +assert("present" in proxy === true); +assert("non_present" in proxy === false); + +var target = { + foo: 5, + bar: 10 +}; + +var handler = { + has: function(target, prop) { + if (prop[0] === 'f') { + return false; + } + return prop in target; + } +}; + +var proxy = new Proxy(target, handler); + +assert("foo" in proxy === false); +assert("bar" in proxy === true); + +// test with no trap +var target = { + foo: "foo" +}; +var handler = {}; +var proxy = new Proxy(target, handler); + +assert("foo" in proxy === true); + +// test with "undefined" trap +var target = { + foo: "foo" +}; +var handler = {has: null}; +var proxy = new Proxy(target, handler); + +assert("foo" in proxy === true); + +// test with invalid trap +var target = { + foo: "foo" +}; +var handler = {has: 42}; +var proxy = new Proxy(target, handler); + +try { + "foo" in proxy; + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when target is proxy +var target = { + foo: "foo" +}; + +var handler = { + has: function(target, prop) { + return prop in target; + } +} + +var proxy1 = new Proxy(target, handler); +var proxy2 = new Proxy(proxy1, handler); + +assert("foo" in proxy2 === true); + +// test when invariants gets violated +var target = {}; + +Object.defineProperty(target, "prop", { + configurable: false, + value: 10 +}) + +var handler = { + has: function(target, prop) { + return false; + } +} + +var proxy = new Proxy(target, handler); + +try { + 'prop' in proxy; + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +var target = { a: 10 }; +Object.preventExtensions(target); + +var proxy = new Proxy(target, handler); + +try { + 'a' in proxy; + assert(false); } catch (e) { assert(e instanceof TypeError); }