/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ // Reflect.apply calls functions. assertEq(Reflect.apply(Math.floor, undefined, [1.75]), 1); // Reflect.apply requires a target object that's callable. var nonCallable = [{}, [], (class clsX { constructor() {} })]; for (var value of nonCallable) { assertThrowsInstanceOf(() => Reflect.apply(nonCallable), TypeError); } // When target is not callable, Reflect.apply does not try to get argumentList.length before throwing. var hits = 0; var bogusArgumentList = {get length() { hit++; throw "FAIL";}}; assertThrowsInstanceOf(() => Reflect.apply({callable: false}, null, bogusArgumentList), TypeError); assertEq(hits, 0); // Reflect.apply works on a range of different callable objects. // Builtin functions (we also tested Math.floor above): assertEq(Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]), "hello"); // Builtin methods: assertEq(Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index, 4); // Builtin methods of primitive objects: assertEq(Reflect.apply("".charAt, "ponies", [3]), "i"); // Bound functions: assertEq(Reflect.apply(function () { return this; }.bind(Math), Function, []), Math); assertEq(Reflect.apply(Array.prototype.concat.bind([1, 2], [3]), [4, 5], [[6, 7, 8]]).join(), "1,2,3,6,7,8"); // Generator functions: function* g(arg) { yield "pass" + arg; } assertEq(Reflect.apply(g, undefined, ["word"]).next().value, "password"); // Proxies: function f() { return 13; } assertEq(Reflect.apply(new Proxy(f, {}), undefined, []), 13); // Cross-compartment wrappers: var gw = newGlobal(); assertEq(Reflect.apply(gw.parseInt, undefined, ["45"]), 45); assertEq(Reflect.apply(gw.Symbol.for, undefined, ["moon"]), Symbol.for("moon")); gw.eval("function q() { return q; }"); assertEq(Reflect.apply(gw.q, undefined, []), gw.q); // Exceptions are propagated. var nope = new Error("nope"); function fail() { throw nope; } assertThrowsValue(() => Reflect.apply(fail, undefined, []), nope); // Exceptions thrown by cross-compartment wrappers are re-wrapped for the // calling compartment. var gxw = gw.eval("var x = new Error('x'); x"); gw.eval("function fail() { throw x; }"); assertThrowsValue(() => Reflect.apply(gw.fail, undefined, []), gxw); // The thisArgument is passed to the target function as the 'this' value. var obj = {}; hits = 0; assertEq(Reflect.apply(function () { hits++; assertEq(this, obj); }, obj, []), undefined); assertEq(hits, 1); // Primitive values can be thisArgument. function strictThis() { "use strict"; return this; } for (var value of [null, undefined, 0, -0, NaN, Symbol("moon")]) { assertEq(Reflect.apply(strictThis, value, []), value); } // If the target is a non-strict function and thisArgument is a primitive value // other than null or undefined, then thisArgument is converted to a wrapper // object. var testValues = [true, 1e9, "ok", Symbol("ok")]; function nonStrictThis(expected) { assertEq(typeof this, "object"); assertEq(Reflect.apply(Object.prototype.toString, this, []).toLowerCase(), expected); return "ok"; } for (var value of testValues) { assertEq(Reflect.apply(nonStrictThis, value, ["[object " + typeof value + "]"]), "ok"); } // For more Reflect.apply tests, see target.js and argumentsList.js. reportCompare(0, 0);