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