From b2e54e915b53c98e7f29905e5ca0d37107660650 Mon Sep 17 00:00:00 2001 From: Szilagyi Adam Date: Wed, 18 Mar 2020 16:52:24 +0100 Subject: [PATCH] Implement Proxy object [[Delete]] internal method (#3616) The algorithm is based on ECMA-262 v6, 9.5.10 JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu --- .../ecma/operations/ecma-proxy-object.c | 81 ++++++++++- jerry-core/lit/lit-magic-strings.inc.h | 3 +- tests/jerry/es2015/proxy_delete.js | 127 +++++++++++++++++- 3 files changed, 206 insertions(+), 5 deletions(-) diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index ac7c15a4a..459da0c29 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -781,8 +781,85 @@ ecma_proxy_object_delete_property (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.[[Delete]]")); + + 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_DELETE_PROPERTY_UL); + + /* 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_delete (target_obj_p, prop_name_p, false); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); + ecma_value_t prop_name_value = ecma_make_prop_name_value (prop_name_p); + ecma_value_t args[] = { target, prop_name_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) + { + return ECMA_VALUE_FALSE; + } + + /* 12. */ + 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); + + /* 13. */ + if (ECMA_IS_VALUE_ERROR (status)) + { + return status; + } + + /* 14. */ + if (ecma_is_value_false (status)) + { + return ECMA_VALUE_TRUE; + } + + ecma_value_t ret_value = ECMA_VALUE_TRUE; + + /* 15. */ + if (!(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE)) + { + ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned truish for property which is " + "non-configurable in the proxy target.")); + } + + ecma_free_property_descriptor (&target_desc); + + /* 16. */ + return ret_value; } /* ecma_proxy_object_delete_property */ /** diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 0fc70a444..777207b9d 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -798,7 +798,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARRAY_ITERATOR_UL, "Array Iterator") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REFERENCE_ERROR_UL, "ReferenceError") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_PROPERTY_UL, "defineProperty") -#if ENABLED (JERRY_ES2015_BUILTIN_REFLECT) +#if ENABLED (JERRY_ES2015_BUILTIN_PROXY) \ +|| ENABLED (JERRY_ES2015_BUILTIN_REFLECT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DELETE_PROPERTY_UL, "deleteProperty") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL, "getPrototypeOf") diff --git a/tests/jerry/es2015/proxy_delete.js b/tests/jerry/es2015/proxy_delete.js index 04b765da1..1e35066bd 100644 --- a/tests/jerry/es2015/proxy_delete.js +++ b/tests/jerry/es2015/proxy_delete.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 = { deleteProperty (target) { @@ -39,7 +41,128 @@ try { try { // 22.1.3.16.6.e - Array.prototype.pop.call(proxy) + Array.prototype.pop.call(proxy); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test basic functionality +var target = {foo: "bar"}; +var handler = { + deleteProperty(obj, prop) { + delete obj[prop]; + } +} + +var proxy = new Proxy(target, handler); + +assert(target.foo === "bar") +assert(proxy.foo === "bar"); + +assert(delete proxy.foo === false); + +assert(target.foo === undefined); +assert(proxy.foo === undefined); + +assert(target.bar === undefined); +assert(delete proxy.bar == false); +assert(target.bar === undefined); + +var handler2 = { + deleteProperty(obj, prop) { + delete obj[prop]; + return true; + } +} + +var proxy = new Proxy(target, handler2); + +assert(target.bar === undefined); +assert(delete proxy.bar == true); +assert(target.bar === undefined); + +// test with no trap +var target = {1: 42}; +var handler = {}; +var proxy = new Proxy(target, handler); + +assert(target[1] === 42) +assert(delete proxy[1] === true) +assert(target[1] === undefined); + +// test with undefined trap +var target = {2: 52}; +var handler = { deleteProperty: null}; +var proxy = new Proxy(target, handler); + +assert(target[2] === 52) +assert(delete proxy[2] === true) +assert(target[2] === undefined); + +// test when trap is invalid +var target = {}; +var handler = { deleteProperty: true }; +var proxy = new Proxy(target, handler); + +try { + delete proxy[0]; + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when handler is null +var revocable = Proxy.revocable ({}, {}); +var proxy = revocable.proxy; +revocable.revoke(); + +try { + delete proxy.foo; + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when target is proxy +var target = {prop: "foo"}; +var handler = { + deleteProperty(obj, prop) { + delete obj[prop]; + } +}; + +var proxy1 = new Proxy(target, handler); +var proxy2 = new Proxy(proxy1, handler); + +assert(target.prop === "foo"); +assert(proxy1.prop === "foo"); +assert(proxy2.prop === "foo"); + +delete proxy2.prop; + +assert(target.prop === undefined); +assert(proxy1.prop === undefined); +assert(proxy2.prop === undefined); + +// tests when invariants gets violated +var target = {}; +var handler = { + deleteProperty(obj, prop) { + delete obj[prop]; + return true; + } +}; + +Object.defineProperty(target, "foo", { + configurable: false, + value: "foo" +}); + +var proxy = new Proxy (target, handler); + +try { + delete proxy.foo; assert(false); } catch (e) { assert(e instanceof TypeError);