From 56832d772ea6fc6b82ca69abf32e034429331338 Mon Sep 17 00:00:00 2001 From: Szilagyi Adam Date: Thu, 26 Mar 2020 14:22:12 +0100 Subject: [PATCH] Implement Proxy object [[getPrototypeOf]] internal method (#3623) The algorithm is based on ECMA-262 v6, 9.5.1 JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu --- .../ecma/operations/ecma-proxy-object.c | 88 +++++++++++++- tests/jerry/es2015/proxy_get_prototoype_of.js | 108 +++++++++++++++++- tests/unit-core/test-api.c | 10 +- 3 files changed, 194 insertions(+), 12 deletions(-) diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index 459da0c29..c3bf3b458 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -292,8 +292,92 @@ ecma_value_t ecma_proxy_object_get_prototype_of (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.[[GetPrototypeOf]]")); + + ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p; + + /* 1. */ + ecma_value_t handler = proxy_obj_p->handler; + + /* 2-5. */ + ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_GET_PROTOTYPE_OF_UL); + + /* 6. */ + 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); + + /* 7. */ + if (ecma_is_value_undefined (trap)) + { + return ecma_builtin_object_object_get_prototype_of (target_obj_p); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); + + /* 8. */ + ecma_value_t handler_proto = ecma_op_function_call (func_obj_p, handler, &target, 1); + + ecma_deref_object (func_obj_p); + + /* 9. */ + if (ECMA_IS_VALUE_ERROR (handler_proto)) + { + return handler_proto; + } + + /* 10. */ + if (!ecma_is_value_object (handler_proto) && !ecma_is_value_null (handler_proto)) + { + ecma_free_value (handler_proto); + + return ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned neither object nor null.")); + } + + /* 11. */ + ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); + + /* 12. */ + if (ECMA_IS_VALUE_ERROR (extensible_target)) + { + ecma_free_value (handler_proto); + + return extensible_target; + } + + /* 13. */ + if (ecma_is_value_true (extensible_target)) + { + return handler_proto; + } + + /* 14. */ + ecma_value_t target_proto = ecma_builtin_object_object_get_prototype_of (target_obj_p); + + /* 15. */ + if (ECMA_IS_VALUE_ERROR (target_proto)) + { + return target_proto; + } + + ecma_value_t ret_value = handler_proto; + + /* 16. */ + if (handler_proto != target_proto) + { + ecma_free_value (handler_proto); + + ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Proxy target is non-extensible, but the trap did not " + "return its actual prototype.")); + } + + ecma_free_value (target_proto); + + /* 17. */ + return ret_value; } /* ecma_proxy_object_get_prototype_of */ /** diff --git a/tests/jerry/es2015/proxy_get_prototoype_of.js b/tests/jerry/es2015/proxy_get_prototoype_of.js index f01339fbc..e86f3014c 100644 --- a/tests/jerry/es2015/proxy_get_prototoype_of.js +++ b/tests/jerry/es2015/proxy_get_prototoype_of.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 = { getPrototypeOf (target) { @@ -26,7 +28,7 @@ try { Object.getPrototypeOf(proxy); assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } try { @@ -34,7 +36,7 @@ try { Object.prototype.isPrototypeOf(proxy); assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } (function () { @@ -49,7 +51,7 @@ try { g instanceof f; assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } // ecma_op_implicit_class_constructor_has_instance [[GetPrototypeOf]] @@ -57,7 +59,7 @@ try { g instanceof e; assert(false); } catch (e) { - assert(e instanceof TypeError); + assert(e === 42); } })(); @@ -68,3 +70,99 @@ try { } catch (e) { assert(e instanceof TypeError); } + +// test basic functionality +var target = {}; +var handler = { + getPrototypeOf(target) { + return Array.prototype; + } +} +var proxy = new Proxy(target, handler); + +assert(Object.getPrototypeOf(proxy) === Array.prototype); +assert(Reflect.getPrototypeOf(proxy) === Array.prototype); +assert(Array.prototype.isPrototypeOf(proxy)); +assert(proxy instanceof Array); + +var obj = Object.preventExtensions({}); +assert(Object.getPrototypeOf(obj) === Object.prototype); + +var handler = { + getPrototypeOf(target) { + return Object.prototype; + } +} +var proxy = new Proxy(target, handler); +assert(Object.getPrototypeOf(proxy) === Object.prototype); + +// test with no trap +var target = {}; +var handler = {}; +var proxy = new Proxy(target, handler); + +assert(Object.getPrototypeOf(proxy) === Object.prototype); + +// test with "undefined" trap +var target = {}; +var handler = { getPrototypeOf: null }; +var proxy = new Proxy(target, handler); + +assert(Object.getPrototypeOf(proxy) === Object.prototype); + +// test with invalid trap +var target = {}; +var handler = { getPrototypeOf: 42 }; +var proxy = new Proxy(target, handler); + +try { + Object.getPrototypeOf(proxy) + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when target is proxy +var target = {}; +var handler = {}; +var proxy = new Proxy(target, handler); + +var target_prototype = {}; +handler.getPrototypeOf = function() { + return target_prototype ; +} + +var proxy2 = new Proxy(proxy, handler); +assert(Object.getPrototypeOf(proxy2) === target_prototype); + +// test when invariants gets violated +var target = {}; +var handler = { + getPrototypeOf(target) { + return 'foo'; + } +} +var proxy = new Proxy(target, handler); + +try { + Object.getPrototypeOf(proxy); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +var target = Object.preventExtensions({}); +var handler = { + getPrototypeOf(target) { + return {}; + } +} + +var proxy = new Proxy(target, handler); + +try { + Object.getPrototypeOf(proxy); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} diff --git a/tests/unit-core/test-api.c b/tests/unit-core/test-api.c index 4414381df..5bf31fded 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -697,18 +697,18 @@ main (void) if (jerry_is_feature_enabled (JERRY_FEATURE_PROXY)) { - /* Note: update this test when the internal method is implemented */ jerry_value_t target = jerry_create_object (); jerry_value_t handler = jerry_create_object (); jerry_value_t proxy = jerry_create_proxy (target, handler); + jerry_value_t obj_proto = jerry_eval ((jerry_char_t *) "Object.prototype", 16, JERRY_PARSE_NO_OPTS); jerry_release_value (target); jerry_release_value (handler); proto_val = jerry_get_prototype (proxy); - TEST_ASSERT (jerry_value_is_error (proto_val)); - error = jerry_get_value_from_error (proto_val, true); - TEST_ASSERT (jerry_get_error_type (error) == JERRY_ERROR_TYPE); - jerry_release_value (error); + TEST_ASSERT (!jerry_value_is_error (proto_val)); + TEST_ASSERT (proto_val == obj_proto); + jerry_release_value (proto_val); + jerry_release_value (obj_proto); jerry_release_value (proxy); }