/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ // Tests for the argumentList argument to Reflect.apply and Reflect.construct. // Reflect.apply and Reflect.construct require an argumentList argument that must be an object. assertThrowsInstanceOf(() => Reflect.apply(Math.min, undefined), // missing TypeError); assertThrowsInstanceOf(() => Reflect.construct(Object), // missing TypeError); for (var primitive of SOME_PRIMITIVE_VALUES) { assertThrowsInstanceOf(() => Reflect.apply(Math.min, undefined, primitive), TypeError); assertThrowsInstanceOf(() => Reflect.construct(Object, primitive), TypeError); } // Array used by several tests below. var BOTH = [ Reflect.apply, // Adapt Reflect.construct to accept the same arguments as Reflect.apply. (target, thisArgument, argumentList) => Reflect.construct(target, argumentList) ]; // The argumentList is copied and becomes the list of arguments passed to the function. function getRest(...x) { return x; } var args = [1, 2, 3]; for (var method of BOTH) { var result = method(getRest, undefined, args); assertEq(result.join(), args.join()); assertEq(result !== args, true); } // argumentList.length can be less than func.length. function testLess(a, b, c, d, e) { assertEq(a, 1); assertEq(b, true); assertEq(c, "three"); assertEq(d, Symbol.for); assertEq(e, undefined); assertEq(arguments.length, 4); assertEq(arguments !== args, true); return "ok"; } args = [1, true, "three", Symbol.for]; assertEq(Reflect.apply(testLess, undefined, args), "ok"); assertEq(Reflect.construct(testLess, args) instanceof testLess, true); // argumentList.length can be more than func.length. function testMoar(a) { assertEq(a, args[0]); return "good"; } assertEq(Reflect.apply(testMoar, undefined, args), "good"); assertEq(Reflect.construct(testMoar, args) instanceof testMoar, true); // argumentList can be any object with a .length property. function getArgs(...args) { return args; } for (var method of BOTH) { assertDeepEq(method(getArgs, undefined, {length: 0}), []); assertDeepEq(method(getArgs, undefined, {length: 1, "0": "zero"}), ["zero"]); assertDeepEq(method(getArgs, undefined, {length: 2}), [undefined, undefined]); assertDeepEq(method(getArgs, undefined, function (a, b, c) {}), [undefined, undefined, undefined]); } // The Iterable/Iterator interfaces are not used. var funnyArgs = { 0: "zero", 1: "one", length: 2, [Symbol.iterator]() { throw "FAIL 1"; }, next() { throw "FAIL 2"; } }; for (var method of BOTH) { assertDeepEq(method(getArgs, undefined, funnyArgs), ["zero", "one"]); } // If argumentList has no .length property, no arguments are passed. function count() { return {numArgsReceived: arguments.length}; } for (var method of BOTH) { assertEq(method(count, undefined, {"0": 0, "1": 1}).numArgsReceived, 0); function* g() { yield 1; yield 2; } assertEq(method(count, undefined, g()).numArgsReceived, 0); } // If argumentsList.length has a getter, it is called. var log; args = { get length() { log += "L"; return 1; }, get "0"() { log += "0"; return "zero"; }, get "1"() { log += "1"; return "one"; } }; for (var method of BOTH) { log = ""; assertDeepEq(method(getArgs, undefined, args), ["zero"]); assertEq(log, "L0"); } // The argumentsList.length getter can throw; the exception is propagated. var exc = {status: "bad"}; args = { get length() { throw exc; } }; for (var method of BOTH) { assertThrowsValue(() => method(count, undefined, args), exc); } // If argumentsList.length is unreasonably huge, we get an error. // (This is an implementation limit.) for (var method of BOTH) { for (var value of [1e12, 1e240, Infinity]) { assertThrowsInstanceOf(() => method(count, undefined, {length: value}), Error); } } // argumentsList.length is converted to an integer. for (var value of [1.7, "1", {valueOf() { return "1"; }}]) { args = { length: value, "0": "ponies" }; for (var method of BOTH) { var result = method(getArgs, undefined, args); assertEq(result.length, 1); assertEq(result[0], "ponies"); } } // If argumentsList.length is negative or NaN, no arguments are passed. for (var method of BOTH) { for (var num of [-1, -0.1, -0, -1e99, -Infinity, NaN]) { assertEq(method(count, undefined, {length: num}).numArgsReceived, 0); } } // Many arguments can be passed. var many = 65537; var args = {length: many, 0: "zero", [many - 1]: "last"}; function testMany(...args) { for (var i = 0; i < many; i++) { assertEq(i in args, true); assertEq(args[i], i === 0 ? "zero" : i === many - 1 ? "last" : undefined); } return this; } assertEq(Reflect.apply(testMany, "pass", args).toString(), "pass"); assertEq(Reflect.construct(testMany, args) instanceof testMany, true); assertEq(Reflect.apply(new Proxy(testMany, {}), "pass", args).toString(), "pass"); assertEq(Reflect.construct(new Proxy(testMany, {}), args) instanceof testMany, true); reportCompare(0, 0);