|
1 /* |
|
2 * Any copyright is dedicated to the Public Domain. |
|
3 * http://creativecommons.org/licenses/publicdomain/ |
|
4 */ |
|
5 |
|
6 //----------------------------------------------------------------------------- |
|
7 var BUGNUMBER = 619283; |
|
8 var summary = |
|
9 "ECMAScript built-in methods that immediately throw when |this| is " + |
|
10 "|undefined| or |null| (due to CheckObjectCoercible, ToObject, or ToString)"; |
|
11 |
|
12 print(BUGNUMBER + ": " + summary); |
|
13 |
|
14 /************** |
|
15 * BEGIN TEST * |
|
16 **************/ |
|
17 |
|
18 // We can't just exhaustively loop over everything because 1) method properties |
|
19 // might be extensions with special |this| handling, and 2) some methods don't |
|
20 // *quite* immediately throw a TypeError, first thing, if |this| is |undefined| |
|
21 // or |null|, or their algorithms are very slightly ambiguous about whether they |
|
22 // do. Why? Ipse-dixitism. *shrug* |
|
23 |
|
24 var ClassToMethodMap = |
|
25 { |
|
26 Object: [/* "toString" has special |this| handling */ |
|
27 "toLocaleString", "valueOf", "hasOwnProperty", |
|
28 /* |
|
29 * "isPrototypeOf" has special |this| handling already tested in |
|
30 * ecma_5/Object/isPrototypeOf.js. |
|
31 */ |
|
32 /* |
|
33 * "isPrototypeOf" has special |this| handling already tested in |
|
34 * ecma_5/Object/propertyIsEnumerable.js. |
|
35 */], |
|
36 // Function methods often don't ToObject(this) as their very first step, |
|
37 // and they're already stepwise well-tested such that manual tests here |
|
38 // would be redundant. |
|
39 Array: ["toString", "toLocaleString", "concat", "join", "pop", "push", |
|
40 "reverse", "shift", "slice", "sort", "splice", "unshift", |
|
41 "indexOf", "lastIndexOf", "every", "some", "forEach", "map", |
|
42 "filter", "reduce", "reduceRight"], |
|
43 String: ["toString", "valueOf", "charAt", "charCodeAt", "concat", |
|
44 "indexOf", "lastIndexOf", "localeCompare", "match", "replace", |
|
45 "search", "slice", "split", "substring", "toLowerCase", |
|
46 "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "trim", |
|
47 /* |
|
48 * "trimLeft" and "trimRight" are non-standard and thus are tested |
|
49 * in ecma_5/extensions/trim-extensions.js. |
|
50 */ |
|
51 ], |
|
52 Boolean: ["toString", "valueOf"], |
|
53 Number: ["toString", "toLocaleString", "valueOf", |
|
54 /* |
|
55 * toFixed doesn't *immediately* test |this| for number or |
|
56 * Number-ness, but because the ToInteger(void 0) which arguably |
|
57 * precedes it in the toFixed algorithm won't throw in this test, |
|
58 * we don't need to specially test it. |
|
59 */ |
|
60 "toFixed", |
|
61 "toExponential", "toPrecision"], |
|
62 Date: ["toString", "toDateString", "toTimeString", "toLocaleString", |
|
63 "toLocaleDateString", "toLocaleTimeString", "valueOf", "getTime", |
|
64 "getFullYear", "getUTCFullYear", "getMonth", "getUTCMonth", |
|
65 "getDate", "getUTCDate", "getDay", "getUTCDay", "getHours", |
|
66 "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds", |
|
67 "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", |
|
68 /* |
|
69 * toFixed doesn't *immediately* test |this| for number or |
|
70 * Number-ness, but because the TimeClip(ToNumber(void 0)) which |
|
71 * arguably precedes it in the setTime algorithm won't throw in |
|
72 * this test, we don't need to specially test it. |
|
73 */ |
|
74 "setTime", |
|
75 "getTimezoneOffset", "setMilliseconds", "setUTCMilliseconds", |
|
76 "setSeconds", "setUTCSeconds", "setMinutes", "setUTCMinutes", |
|
77 "setHours", "setUTCHours", "setDate", "setUTCDate", "setMonth", |
|
78 "setUTCMonth", "setFullYear", "setUTCFullYear", "toUTCString", |
|
79 "toISOString", "toJSON"], |
|
80 RegExp: ["exec", "test", "toString"], |
|
81 Error: ["toString"], |
|
82 }; |
|
83 |
|
84 var badThisValues = [null, undefined]; |
|
85 |
|
86 function testMethod(Class, className, method) |
|
87 { |
|
88 var expr; |
|
89 |
|
90 // Try out explicit this values |
|
91 for (var i = 0, sz = badThisValues.length; i < sz; i++) |
|
92 { |
|
93 var badThis = badThisValues[i]; |
|
94 |
|
95 expr = className + ".prototype." + method + ".call(" + badThis + ")"; |
|
96 try |
|
97 { |
|
98 Class.prototype[method].call(badThis); |
|
99 throw new Error(expr + " didn't throw a TypeError"); |
|
100 } |
|
101 catch (e) |
|
102 { |
|
103 assertEq(e instanceof TypeError, true, |
|
104 "wrong error for " + expr + ", instead threw " + e); |
|
105 } |
|
106 |
|
107 expr = className + ".prototype." + method + ".apply(" + badThis + ")"; |
|
108 try |
|
109 { |
|
110 Class.prototype[method].apply(badThis); |
|
111 throw new Error(expr + " didn't throw a TypeError"); |
|
112 } |
|
113 catch (e) |
|
114 { |
|
115 assertEq(e instanceof TypeError, true, |
|
116 "wrong error for " + expr + ", instead threw " + e); |
|
117 } |
|
118 } |
|
119 |
|
120 // ..and for good measure.. |
|
121 |
|
122 expr = "(0, " + className + ".prototype." + method + ")()" |
|
123 try |
|
124 { |
|
125 // comma operator to call GetValue() on the method and de-Reference it |
|
126 (0, Class.prototype[method])(); |
|
127 throw new Error(expr + " didn't throw a TypeError"); |
|
128 } |
|
129 catch (e) |
|
130 { |
|
131 assertEq(e instanceof TypeError, true, |
|
132 "wrong error for " + expr + ", instead threw " + e); |
|
133 } |
|
134 } |
|
135 |
|
136 for (var className in ClassToMethodMap) |
|
137 { |
|
138 var Class = this[className]; |
|
139 |
|
140 var methodNames = ClassToMethodMap[className]; |
|
141 for (var i = 0, sz = methodNames.length; i < sz; i++) |
|
142 { |
|
143 var method = methodNames[i]; |
|
144 testMethod(Class, className, method); |
|
145 } |
|
146 } |
|
147 |
|
148 /******************************************************************************/ |
|
149 |
|
150 if (typeof reportCompare === "function") |
|
151 reportCompare(true, true); |
|
152 |
|
153 print("All tests passed!"); |