michael@0: /* michael@0: * Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/licenses/publicdomain/ michael@0: * Contributor: michael@0: * Jeff Walden michael@0: */ michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: var BUGNUMBER = 562448; michael@0: var summary = 'Function.prototype.apply should accept any arraylike arguments'; michael@0: print(BUGNUMBER + ": " + summary); michael@0: michael@0: /************** michael@0: * BEGIN TEST * michael@0: **************/ michael@0: michael@0: function expectTypeError(fun, msg) michael@0: { michael@0: try michael@0: { michael@0: fun(); michael@0: assertEq(true, false, "should have thrown a TypeError"); michael@0: } michael@0: catch (e) michael@0: { michael@0: assertEq(e instanceof TypeError, true, msg + "; instead threw " + e); michael@0: } michael@0: } michael@0: michael@0: function fun() { } michael@0: michael@0: var global = this; michael@0: michael@0: michael@0: /* Step 1. */ michael@0: var nonfuns = [null, 1, -1, 2.5, "[[Call]]", undefined, true, false, {}]; michael@0: for (var i = 0, sz = nonfuns.length; i < sz; i++) michael@0: { michael@0: var f = function() michael@0: { michael@0: Function.prototype.apply.apply(nonfuns[i], [1, 2, 3]); michael@0: }; michael@0: var msg = michael@0: "expected TypeError calling Function.prototype.apply with uncallable this"; michael@0: expectTypeError(f, msg); michael@0: } michael@0: michael@0: michael@0: /* Step 2. */ michael@0: var thisObj = {}; michael@0: michael@0: var currentThis, currentThisBox; michael@0: function funLength() michael@0: { michael@0: assertEq(arguments.length, 0, "should have been called with no arguments"); michael@0: assertEq(this, currentThis, "wrong this"); michael@0: } michael@0: function strictFunLength() michael@0: { michael@0: "use strict"; michael@0: assertEq(arguments.length, 0, "should have been called with no arguments"); michael@0: assertEq(this, currentThis, "wrong this"); michael@0: } michael@0: michael@0: currentThis = global; michael@0: funLength.apply(); michael@0: funLength.apply(undefined); michael@0: funLength.apply(undefined, undefined); michael@0: funLength.apply(undefined, null); michael@0: michael@0: currentThis = undefined; michael@0: strictFunLength.apply(); michael@0: strictFunLength.apply(undefined); michael@0: strictFunLength.apply(undefined, undefined); michael@0: strictFunLength.apply(undefined, null); michael@0: michael@0: currentThis = null; michael@0: strictFunLength.apply(null); michael@0: strictFunLength.apply(null, undefined); michael@0: strictFunLength.apply(null, null); michael@0: michael@0: currentThis = thisObj; michael@0: funLength.apply(thisObj); michael@0: funLength.apply(thisObj, null); michael@0: funLength.apply(thisObj, undefined); michael@0: strictFunLength.apply(thisObj); michael@0: strictFunLength.apply(thisObj, null); michael@0: strictFunLength.apply(thisObj, undefined); michael@0: michael@0: currentThis = 17; michael@0: strictFunLength.apply(17); michael@0: strictFunLength.apply(17, null); michael@0: strictFunLength.apply(17, undefined); michael@0: michael@0: function funThisPrimitive() michael@0: { michael@0: assertEq(arguments.length, 0, "should have been called with no arguments"); michael@0: assertEq(this instanceof currentThisBox, true, michael@0: "this not instanceof " + currentThisBox); michael@0: assertEq(this.valueOf(), currentThis, michael@0: "wrong this valueOf()"); michael@0: } michael@0: michael@0: currentThis = 17; michael@0: currentThisBox = Number; michael@0: funThisPrimitive.apply(17); michael@0: funThisPrimitive.apply(17, undefined); michael@0: funThisPrimitive.apply(17, null); michael@0: michael@0: currentThis = "foopy"; michael@0: currentThisBox = String; michael@0: funThisPrimitive.apply("foopy"); michael@0: funThisPrimitive.apply("foopy", undefined); michael@0: funThisPrimitive.apply("foopy", null); michael@0: michael@0: currentThis = false; michael@0: currentThisBox = Boolean; michael@0: funThisPrimitive.apply(false); michael@0: funThisPrimitive.apply(false, undefined); michael@0: funThisPrimitive.apply(false, null); michael@0: michael@0: michael@0: /* Step 3. */ michael@0: var nonobjs = [1, -1, 2.5, "[[Call]]", true, false]; michael@0: for (var i = 0, sz = nonobjs.length; i < sz; i++) michael@0: { michael@0: var f = function() { fun.apply(thisObj, nonobjs[i]); }; michael@0: var msg = "should have thrown a TypeError with non-object arguments"; michael@0: expectTypeError(f, msg); michael@0: } michael@0: michael@0: michael@0: /* Step 4. */ michael@0: var args = { get length() { throw 42; } }; michael@0: try michael@0: { michael@0: fun.apply(thisObj, args); michael@0: } michael@0: catch (e) michael@0: { michael@0: assertEq(e, 42, "didn't throw result of [[Get]] on arguments object"); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * NB: There was an erratum removing the steps numbered 5 and 7 in the original michael@0: * version of ES5; see also the comments in js_fun_apply. michael@0: */ michael@0: michael@0: /* Step 5. */ michael@0: var called = false; michael@0: var argsObjectLength = michael@0: { length: { valueOf: function() { called = true; return 17; } } }; michael@0: michael@0: fun.apply({}, argsObjectLength); michael@0: assertEq(called, true, "should have been set in valueOf called via ToUint32"); michael@0: michael@0: var upvar = "unset"; michael@0: var argsObjectPrimitiveLength = michael@0: { michael@0: length: michael@0: { michael@0: valueOf: function() { upvar = "valueOf"; return {}; }, michael@0: toString: function() michael@0: { michael@0: upvar = upvar === "valueOf" ? "both" : "toString"; michael@0: return 17; michael@0: } michael@0: } michael@0: }; michael@0: fun.apply({}, argsObjectPrimitiveLength); michael@0: assertEq(upvar, "both", "didn't call all hooks properly"); michael@0: michael@0: michael@0: /* Step 6-9. */ michael@0: var seenThis, res, steps; michael@0: var argsAccessors = michael@0: { michael@0: length: 4, michael@0: get 0() { steps.push("0"); return 1; }, michael@0: get 1() { steps.push("1"); return 2; }, michael@0: // make sure values shine through holes michael@0: get 3() { steps.push("3"); return 8; }, michael@0: }; michael@0: michael@0: Object.prototype[2] = 729; michael@0: michael@0: seenThis = "not seen"; michael@0: function argsAsArray() michael@0: { michael@0: seenThis = this; michael@0: steps.push(Math.PI); michael@0: return Array.prototype.map.call(arguments, function(v) { return v; }); michael@0: } michael@0: michael@0: steps = []; michael@0: res = argsAsArray.apply(thisObj, argsAccessors); michael@0: assertEq(seenThis, thisObj, "saw wrong this"); michael@0: michael@0: assertEq(steps.length, 4, "wrong steps: " + steps); michael@0: assertEq(steps[0], "0", "bad step 0"); michael@0: assertEq(steps[1], "1", "bad step 1"); michael@0: assertEq(steps[2], "3", "bad step 3"); michael@0: assertEq(steps[3], Math.PI, "bad last step"); michael@0: michael@0: assertEq(res.length, 4, "wrong return: " + res); michael@0: assertEq(res[0], 1, "wrong ret[0]"); michael@0: assertEq(res[1], 2, "wrong ret[0]"); michael@0: assertEq(res[2], 729, "wrong ret[0]"); michael@0: assertEq(res[3], 8, "wrong ret[0]"); michael@0: michael@0: seenThis = "not seen"; michael@0: function strictArgsAsArray() michael@0: { michael@0: "use strict"; michael@0: seenThis = this; michael@0: steps.push(NaN); michael@0: return Array.prototype.map.call(arguments, function(v) { return v; }); michael@0: } michael@0: michael@0: steps = []; michael@0: res = strictArgsAsArray.apply(null, argsAccessors); michael@0: assertEq(seenThis, null, "saw wrong this"); michael@0: michael@0: assertEq(steps.length, 4, "wrong steps: " + steps); michael@0: assertEq(steps[0], "0", "bad step 0"); michael@0: assertEq(steps[1], "1", "bad step 1"); michael@0: assertEq(steps[2], "3", "bad step 3"); michael@0: assertEq(steps[3], 0 / 0, "bad last step"); michael@0: michael@0: assertEq(res.length, 4, "wrong return: " + res); michael@0: assertEq(res[0], 1, "wrong ret[0]"); michael@0: assertEq(res[1], 2, "wrong ret[0]"); michael@0: assertEq(res[2], 729, "wrong ret[0]"); michael@0: assertEq(res[3], 8, "wrong ret[0]"); michael@0: michael@0: strictArgsAsArray.apply(17, argsAccessors); michael@0: assertEq(seenThis, 17, "saw wrong this"); michael@0: michael@0: /******************************************************************************/ michael@0: michael@0: if (typeof reportCompare === "function") michael@0: reportCompare(true, true); michael@0: michael@0: print("All tests passed!");