/* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ //----------------------------------------------------------------------------- var BUGNUMBER = 619283; var summary = "ECMAScript built-in methods that immediately throw when |this| is " + "|undefined| or |null| (due to CheckObjectCoercible, ToObject, or ToString)"; print(BUGNUMBER + ": " + summary); /************** * BEGIN TEST * **************/ // We can't just exhaustively loop over everything because 1) method properties // might be extensions with special |this| handling, and 2) some methods don't // *quite* immediately throw a TypeError, first thing, if |this| is |undefined| // or |null|, or their algorithms are very slightly ambiguous about whether they // do. Why? Ipse-dixitism. *shrug* var ClassToMethodMap = { Object: [/* "toString" has special |this| handling */ "toLocaleString", "valueOf", "hasOwnProperty", /* * "isPrototypeOf" has special |this| handling already tested in * ecma_5/Object/isPrototypeOf.js. */ /* * "isPrototypeOf" has special |this| handling already tested in * ecma_5/Object/propertyIsEnumerable.js. */], // Function methods often don't ToObject(this) as their very first step, // and they're already stepwise well-tested such that manual tests here // would be redundant. Array: ["toString", "toLocaleString", "concat", "join", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "unshift", "indexOf", "lastIndexOf", "every", "some", "forEach", "map", "filter", "reduce", "reduceRight"], String: ["toString", "valueOf", "charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "replace", "search", "slice", "split", "substring", "toLowerCase", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "trim", /* * "trimLeft" and "trimRight" are non-standard and thus are tested * in ecma_5/extensions/trim-extensions.js. */ ], Boolean: ["toString", "valueOf"], Number: ["toString", "toLocaleString", "valueOf", /* * toFixed doesn't *immediately* test |this| for number or * Number-ness, but because the ToInteger(void 0) which arguably * precedes it in the toFixed algorithm won't throw in this test, * we don't need to specially test it. */ "toFixed", "toExponential", "toPrecision"], Date: ["toDateString", "toTimeString", "toLocaleString", "toLocaleDateString", "toLocaleTimeString", "valueOf", "getTime", "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay", "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds", "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", /* * toFixed doesn't *immediately* test |this| for number or * Number-ness, but because the TimeClip(ToNumber(void 0)) which * arguably precedes it in the setTime algorithm won't throw in * this test, we don't need to specially test it. */ "setTime", "getTimezoneOffset", "setMilliseconds", "setUTCMilliseconds", "setSeconds", "setUTCSeconds", "setMinutes", "setUTCMinutes", "setHours", "setUTCHours", "setDate", "setUTCDate", "setMonth", "setUTCMonth", "setFullYear", "setUTCFullYear", "toUTCString", "toISOString", "toJSON"], RegExp: ["exec", "test", "toString"], Error: ["toString"], }; var badThisValues = [null, undefined]; function testMethod(Class, className, method) { var expr; // Try out explicit this values for (var i = 0, sz = badThisValues.length; i < sz; i++) { var badThis = badThisValues[i]; expr = className + ".prototype." + method + ".call(" + badThis + ")"; try { Class.prototype[method].call(badThis); throw new Error(expr + " didn't throw a TypeError"); } catch (e) { assertEq(e instanceof TypeError, true, "wrong error for " + expr + ", instead threw " + e); } expr = className + ".prototype." + method + ".apply(" + badThis + ")"; try { Class.prototype[method].apply(badThis); throw new Error(expr + " didn't throw a TypeError"); } catch (e) { assertEq(e instanceof TypeError, true, "wrong error for " + expr + ", instead threw " + e); } } // ..and for good measure.. expr = "(0, " + className + ".prototype." + method + ")()" try { // comma operator to call GetValue() on the method and de-Reference it (0, Class.prototype[method])(); throw new Error(expr + " didn't throw a TypeError"); } catch (e) { assertEq(e instanceof TypeError, true, "wrong error for " + expr + ", instead threw " + e); } } for (var className in ClassToMethodMap) { var Class = this[className]; var methodNames = ClassToMethodMap[className]; for (var i = 0, sz = methodNames.length; i < sz; i++) { var method = methodNames[i]; testMethod(Class, className, method); } } /******************************************************************************/ if (typeof reportCompare === "function") reportCompare(true, true); print("All tests passed!");