| /* |
| * 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!"); |