diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index c3bf3b458..c3036e175 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -396,9 +396,93 @@ ecma_proxy_object_set_prototype_of (ecma_object_t *obj_p, /**< proxy object */ ecma_value_t proto) /**< new prototype object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); + + /* 1. */ JERRY_ASSERT (ecma_is_value_object (proto) || ecma_is_value_null (proto)); - JERRY_UNUSED_2 (obj_p, proto); - return ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[SetPrototypeOf]]")); + + 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_SET_PROTOTYPE_OF_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)) + { + if (ECMA_OBJECT_IS_PROXY (target_obj_p)) + { + return ecma_proxy_object_set_prototype_of (target_obj_p, proto); + } + + return ecma_op_ordinary_object_set_prototype_of (target_obj_p, proto); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); + ecma_value_t args[] = { target, proto }; + + /* 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. */ + ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p); + + /* 12. */ + if (ECMA_IS_VALUE_ERROR (extensible_target)) + { + return extensible_target; + } + + /* 13. */ + if (ecma_is_value_true (extensible_target)) + { + return ecma_make_boolean_value (boolean_trap_result); + } + + /* 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 = ecma_make_boolean_value (boolean_trap_result); + + /* 16. */ + if (boolean_trap_result && (target_proto != proto)) + { + ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Target object is non-extensible and trap " + "returned different prototype.")); + } + + ecma_free_value (target_proto); + + /* 17. */ + return ret_value; } /* ecma_proxy_object_set_prototype_of */ /** diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index fb5a527d8..f40146193 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -814,6 +814,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_FULL_YEAR_UL, "getUTCFullYear") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_HAS_OWN_PROPERTY_UL, "hasOwnProperty") #if ENABLED (JERRY_ES2015) \ +|| ENABLED (JERRY_ES2015_BUILTIN_PROXY) \ || ENABLED (JERRY_ES2015_BUILTIN_REFLECT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_PROTOTYPE_OF_UL, "setPrototypeOf") #endif diff --git a/tests/jerry/es2015/proxy_set_prototoype_of.js b/tests/jerry/es2015/proxy_set_prototoype_of.js index c39ef79cf..f215bc499 100644 --- a/tests/jerry/es2015/proxy_set_prototoype_of.js +++ b/tests/jerry/es2015/proxy_set_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 = { setPrototypeOf (target) { @@ -25,6 +27,132 @@ try { // 19.1.2.18 Object.setPrototypeOf(proxy, {}); assert(false); +} catch (e) { + assert(e === 42); +} + +// test basic funcionality +var targetProto = {}; +var target = { + foo: false +}; + +var handler = { + setPrototypeOf(target, targetrProto) { + target.foo = true; + return false; + } +}; + +var proxy = new Proxy(target, handler); + +assert(Reflect.setPrototypeOf(proxy, targetProto) === false); +assert(target.foo === true); + +try { + Object.setPrototypeOf(proxy, targetProto); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + Object.setPrototypeOf(proxy, undefined); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + Object.setPrototypeOf(proxy, 1); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +var target = {}; +var handler = {}; +var prototype = []; + +var proxy = new Proxy(target, handler); + +Object.setPrototypeOf(proxy, prototype); +assert(Object.getPrototypeOf(target) === prototype); + +handler.setPrototypeOf = function(target, proto) { + return false; +}; + +try { + Object.setPrototypeOf(proxy, {}); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +handler.setPrototypeOf = function(target, proto) { + return undefined; +}; + +try { + Object.setPrototypeOf(proxy, {}); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +handler.setPrototypeOf = function(proto) {}; + +try { + Object.setPrototypeOf(proxy, {}); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +var proto = {}; +Object.setPrototypeOf(proto, Function.prototype); + +var seen_prototype; +var seen_target; + +handler.setPrototypeOf = function(target, proto) { + seen_target = target; + seen_prototype = proto; + return true; +} + +Object.setPrototypeOf(proxy, proto); +assert(target === seen_target); +assert(proto === seen_prototype); + +// test when target is proxy +var target = {}; +var handler = {}; +var handler2 = {}; +var target2 = new Proxy(target, handler2); +var proxy2 = new Proxy(target2, handler); +var prototype = [2,3]; + +Object.setPrototypeOf(proxy2, prototype); + +assert(prototype === Object.getPrototypeOf(target)); + +// test when invariants gets violated +var target = {}; +var handler = { + setPrototypeOf(target, proto) { + Object.setPrototypeOf(target, Function.prototype); + Object.preventExtensions(target); + return true; + } +} + +var proxy = new Proxy(target, handler); + +try { + Object.setPrototypeOf(proxy, Array.prototype); + 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 5bf31fded..a0b55244c 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -733,21 +733,21 @@ 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); + new_proto = jerry_eval ((jerry_char_t *) "Function.prototype", 18, JERRY_PARSE_NO_OPTS); + + res = jerry_set_prototype (proxy, new_proto); + TEST_ASSERT (!jerry_value_is_error (res)); + jerry_value_t target_proto = jerry_get_prototype (target); + TEST_ASSERT (target_proto == new_proto); jerry_release_value (target); jerry_release_value (handler); - new_proto = jerry_create_object (); - res = jerry_set_prototype (proxy, new_proto); - jerry_release_value (new_proto); - TEST_ASSERT (jerry_value_is_error (res)); - error = jerry_get_value_from_error (res, true); - TEST_ASSERT (jerry_get_error_type (error) == JERRY_ERROR_TYPE); - jerry_release_value (error); jerry_release_value (proxy); + jerry_release_value (new_proto); + jerry_release_value (target_proto); } /* Test: eval */