diff --git a/jerry-core/ecma/operations/ecma-proxy-object.c b/jerry-core/ecma/operations/ecma-proxy-object.c index c805e702a..e456b12e4 100644 --- a/jerry-core/ecma/operations/ecma-proxy-object.c +++ b/jerry-core/ecma/operations/ecma-proxy-object.c @@ -14,6 +14,7 @@ */ #include "ecma-alloc.h" +#include "ecma-array-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" @@ -611,8 +612,50 @@ ecma_proxy_object_call (ecma_object_t *obj_p, /**< proxy object */ ecma_length_t argc) /**< number of arguments */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p)); - JERRY_UNUSED_4 (obj_p, this_argument, args_p, argc); - return ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[Call]]")); + + 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_APPLY); + + /* 6. */ + if (ECMA_IS_VALUE_ERROR (trap)) + { + return trap; + } + + /* 7. */ + if (ecma_is_value_undefined (trap)) + { + ecma_object_t *target_obj_p = ecma_get_object_from_value (target); + return ecma_op_function_call (target_obj_p, this_argument, args_p, argc); + } + + /* 8. */ + ecma_value_t args_array = ecma_op_create_array_object (args_p, argc, false); + ecma_value_t value_array[] = {target, this_argument, args_array}; + ecma_object_t *func_obj_p = ecma_get_object_from_value (trap); + /* 9. */ + ecma_value_t ret_value = ecma_op_function_call (func_obj_p, handler, value_array, 3); + ecma_deref_object (func_obj_p); + ecma_deref_object (ecma_get_object_from_value (args_array)); + + return ret_value; } /* ecma_proxy_object_call */ /** diff --git a/tests/jerry/es2015/proxy_call.js b/tests/jerry/es2015/proxy_call.js index 74e2ab2c5..d57505713 100644 --- a/tests/jerry/es2015/proxy_call.js +++ b/tests/jerry/es2015/proxy_call.js @@ -12,29 +12,344 @@ // 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 = function () {}; var handler = { apply (target) { throw 42; }}; -var proxy = new Proxy(target, handler); +var proxy = new Proxy (target, handler); try { // opfunc_call - proxy(5) - assert(false); + proxy (5) + assert (false); } catch (e) { - assert(e instanceof TypeError); + assert (e == 42); } try { - var revocable = Proxy.revokable(function() {}, {}); + var revocable = Proxy.revokable (function () {}, {}); proxy = new Proxy(revocable.proxy, {}) revocable.revoke(); - proxy(5) - assert(false); + proxy (5) + assert (false); } catch (e) { - assert(e instanceof TypeError); + assert (e instanceof TypeError); } + +function sum (a, b) { + return a + b; +} + +var handler = { + apply: function (target, thisArg, argumentsList) { + return target (argumentsList[0], argumentsList[1]) * 10; + } +}; + +var proxy1 = new Proxy(sum, handler); + +assert (sum (1, 2) === 3); +assert (proxy1 (1, 2) === 30); + +// Non Callable tests +var proxy = new Proxy ({},{}); +try { + proxy() + assert (false) +} catch (e) { + assert (e instanceof TypeError) +} + +var proxy2 = new Proxy(proxy, {}); +try { + proxy2() + assert (false) +} catch (e) { + assert (e instanceof TypeError) +} + +// No arguments +var called = false; +var target = function () { + called = true; +} +var proxy = new Proxy (target, {}); +assert (!called); +proxy(); +assert (called); + +called = false; +var proxy2 = new Proxy (proxy, {}); +assert (!called); +proxy2(); +assert (called); + +//1 Argument +var called = false; +var target = function (a) { + called = true; + assert ('1' === a); +} +var proxy = new Proxy (target, {}); +assert (!called); +proxy ('1'); +assert (called); + +// 2 Arguments +var called = false; +var target = function (a, b) { + called = true; + assert ('1' === a); + assert ('2' === b); +} +var proxy = new Proxy (target, {}); +assert (!called); +proxy ('1', '2'); +assert (called); + +// Changed receiver +var apply_receiver = {receiver:true}; +var seen_receiver = undefined; +var target = function () { + seen_receiver = this; +} +var proxy = new Proxy (target, {}); +assert (undefined === seen_receiver); +Reflect.apply (proxy, apply_receiver, [1,2,3,4]); +assert (apply_receiver === seen_receiver); + +// Trap +var called_target = false; +var called_handler = false; +var target = function (a, b) { + called_target = true; + assert (1 === a); + assert (2 === b); +} +var handler = { + apply: function (target, this_arg, args) { + target.apply (this_arg, args); + called_handler = true; + } +} +var proxy = new Proxy (target, handler); +assert (!called_target); +assert (!called_handler); +Reflect.apply (proxy, {rec:1}, [1,2]); +assert (called_target); +assert (called_handler); + +// Trap array arg +var called_target = false; +var called_handler = false; +var target = function (a, b) { + called_target = true; + var arg = [1, 2]; + assert (arg[0] === a[0]); + assert (arg[1] === a[1]); + assert (3 === b); +} +var handler = { + apply: function (target, this_arg, args) { + target.apply (this_arg, args); + called_handler = true; + } +} +var proxy = new Proxy (target, handler); +assert (!called_target); +assert (!called_handler); +proxy ([1,2], 3); +assert (called_target); +assert (called_handler); + +// Trap object arg +var called_target = false; +var called_handler = false; +var target = function (o) { + called_target = true; + var obj = {a: 1, b: 2} + assert (obj.a === o.a); + assert (obj.b === o.b) +} +var handler = { + apply: function (target, this_arg, args) { + target.apply (this_arg, args); + called_handler = true; + } +} +var proxy = new Proxy (target, handler); +assert (!called_target); +assert (!called_handler); +proxy ({a: 1, b: 2}); +assert (called_target); +assert (called_handler); + +// Trap generator arg +function* gen () { + yield 1; + yield 2; + yield 3; +} +var called_target = false; +var called_handler = false; +var target = function (g) { + called_target = true; + var arr = [1, 2, 3]; + var arr2 = [...g]; + assert (arr[0] === arr2[0]); + assert (arr[1] === arr2[1]); + assert (arr[2] === arr2[2]); +} +var handler = { + apply: function (target, this_arg, args) { + target.apply (this_arg, args); + called_handler = true; + } +} +var proxy = new Proxy (target, handler); +assert (!called_target); +assert (!called_handler); +proxy (gen()); +assert (called_target); +assert (called_handler); + +// Noncallable Trap +var called_target = false; +var target = function () { + called_target = true; +}; +var handler = { + apply: 'non callable trap' +}; + +var proxy = new Proxy(target, handler); +try { + proxy (); + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +assert (!called_target); + +// Null trap +var _args; +var target = function (a, b) { + _args = [a, b]; + return a + b; +}; +var handler = { + apply: null +}; + +var proxy = new Proxy (target, handler); +var result = proxy (1, 2); + +assert (result === 3); +assert (_args.length === 2); +assert (_args[0] === 1); +assert (_args[1] === 2); + +var values = [NaN, 1.5, 100, /RegExp/, "string", {}, [], Symbol(), + new Map(), new Set(), new WeakMap(), new WeakSet()]; +values.forEach(target => { + target = Object (target); + var proxy = new Proxy(target, { apply() { assert (false) } }); + try { + proxy(); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + ({ proxy }).proxy(); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Reflect.apply(proxy, null, []); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Reflect.apply(proxy, { proxy }, []); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Reflect.apply(proxy, { proxy }, []); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Function.prototype.call.apply (proxy, [null]); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Function.prototype.apply.apply (proxy, [null, []]); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + var proxy_to_proxy = new Proxy (proxy, { apply() { assert (false); } }); + + try { + proxy_to_proxy (); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + ({ proxy_to_proxy }).proxy_to_proxy(); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Reflect.apply (proxy_to_proxy, null, []); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Reflect.apply (proxy_to_proxy, { proxy }, []); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Function.prototype.call.apply (proxy_to_proxy, [null]); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + Function.prototype.apply.apply (proxy_to_proxy, [null, []]); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } +});