|
1 /* |
|
2 * Any copyright is dedicated to the Public Domain. |
|
3 * http://creativecommons.org/licenses/publicdomain/ |
|
4 * Contributor: |
|
5 * Jeff Walden <jwalden+code@mit.edu> |
|
6 */ |
|
7 |
|
8 //----------------------------------------------------------------------------- |
|
9 var BUGNUMBER = 562448; |
|
10 var summary = 'Function.prototype.apply should accept any arraylike arguments'; |
|
11 print(BUGNUMBER + ": " + summary); |
|
12 |
|
13 /************** |
|
14 * BEGIN TEST * |
|
15 **************/ |
|
16 |
|
17 function expectTypeError(fun, msg) |
|
18 { |
|
19 try |
|
20 { |
|
21 fun(); |
|
22 assertEq(true, false, "should have thrown a TypeError"); |
|
23 } |
|
24 catch (e) |
|
25 { |
|
26 assertEq(e instanceof TypeError, true, msg + "; instead threw " + e); |
|
27 } |
|
28 } |
|
29 |
|
30 function fun() { } |
|
31 |
|
32 var global = this; |
|
33 |
|
34 |
|
35 /* Step 1. */ |
|
36 var nonfuns = [null, 1, -1, 2.5, "[[Call]]", undefined, true, false, {}]; |
|
37 for (var i = 0, sz = nonfuns.length; i < sz; i++) |
|
38 { |
|
39 var f = function() |
|
40 { |
|
41 Function.prototype.apply.apply(nonfuns[i], [1, 2, 3]); |
|
42 }; |
|
43 var msg = |
|
44 "expected TypeError calling Function.prototype.apply with uncallable this"; |
|
45 expectTypeError(f, msg); |
|
46 } |
|
47 |
|
48 |
|
49 /* Step 2. */ |
|
50 var thisObj = {}; |
|
51 |
|
52 var currentThis, currentThisBox; |
|
53 function funLength() |
|
54 { |
|
55 assertEq(arguments.length, 0, "should have been called with no arguments"); |
|
56 assertEq(this, currentThis, "wrong this"); |
|
57 } |
|
58 function strictFunLength() |
|
59 { |
|
60 "use strict"; |
|
61 assertEq(arguments.length, 0, "should have been called with no arguments"); |
|
62 assertEq(this, currentThis, "wrong this"); |
|
63 } |
|
64 |
|
65 currentThis = global; |
|
66 funLength.apply(); |
|
67 funLength.apply(undefined); |
|
68 funLength.apply(undefined, undefined); |
|
69 funLength.apply(undefined, null); |
|
70 |
|
71 currentThis = undefined; |
|
72 strictFunLength.apply(); |
|
73 strictFunLength.apply(undefined); |
|
74 strictFunLength.apply(undefined, undefined); |
|
75 strictFunLength.apply(undefined, null); |
|
76 |
|
77 currentThis = null; |
|
78 strictFunLength.apply(null); |
|
79 strictFunLength.apply(null, undefined); |
|
80 strictFunLength.apply(null, null); |
|
81 |
|
82 currentThis = thisObj; |
|
83 funLength.apply(thisObj); |
|
84 funLength.apply(thisObj, null); |
|
85 funLength.apply(thisObj, undefined); |
|
86 strictFunLength.apply(thisObj); |
|
87 strictFunLength.apply(thisObj, null); |
|
88 strictFunLength.apply(thisObj, undefined); |
|
89 |
|
90 currentThis = 17; |
|
91 strictFunLength.apply(17); |
|
92 strictFunLength.apply(17, null); |
|
93 strictFunLength.apply(17, undefined); |
|
94 |
|
95 function funThisPrimitive() |
|
96 { |
|
97 assertEq(arguments.length, 0, "should have been called with no arguments"); |
|
98 assertEq(this instanceof currentThisBox, true, |
|
99 "this not instanceof " + currentThisBox); |
|
100 assertEq(this.valueOf(), currentThis, |
|
101 "wrong this valueOf()"); |
|
102 } |
|
103 |
|
104 currentThis = 17; |
|
105 currentThisBox = Number; |
|
106 funThisPrimitive.apply(17); |
|
107 funThisPrimitive.apply(17, undefined); |
|
108 funThisPrimitive.apply(17, null); |
|
109 |
|
110 currentThis = "foopy"; |
|
111 currentThisBox = String; |
|
112 funThisPrimitive.apply("foopy"); |
|
113 funThisPrimitive.apply("foopy", undefined); |
|
114 funThisPrimitive.apply("foopy", null); |
|
115 |
|
116 currentThis = false; |
|
117 currentThisBox = Boolean; |
|
118 funThisPrimitive.apply(false); |
|
119 funThisPrimitive.apply(false, undefined); |
|
120 funThisPrimitive.apply(false, null); |
|
121 |
|
122 |
|
123 /* Step 3. */ |
|
124 var nonobjs = [1, -1, 2.5, "[[Call]]", true, false]; |
|
125 for (var i = 0, sz = nonobjs.length; i < sz; i++) |
|
126 { |
|
127 var f = function() { fun.apply(thisObj, nonobjs[i]); }; |
|
128 var msg = "should have thrown a TypeError with non-object arguments"; |
|
129 expectTypeError(f, msg); |
|
130 } |
|
131 |
|
132 |
|
133 /* Step 4. */ |
|
134 var args = { get length() { throw 42; } }; |
|
135 try |
|
136 { |
|
137 fun.apply(thisObj, args); |
|
138 } |
|
139 catch (e) |
|
140 { |
|
141 assertEq(e, 42, "didn't throw result of [[Get]] on arguments object"); |
|
142 } |
|
143 |
|
144 |
|
145 /* |
|
146 * NB: There was an erratum removing the steps numbered 5 and 7 in the original |
|
147 * version of ES5; see also the comments in js_fun_apply. |
|
148 */ |
|
149 |
|
150 /* Step 5. */ |
|
151 var called = false; |
|
152 var argsObjectLength = |
|
153 { length: { valueOf: function() { called = true; return 17; } } }; |
|
154 |
|
155 fun.apply({}, argsObjectLength); |
|
156 assertEq(called, true, "should have been set in valueOf called via ToUint32"); |
|
157 |
|
158 var upvar = "unset"; |
|
159 var argsObjectPrimitiveLength = |
|
160 { |
|
161 length: |
|
162 { |
|
163 valueOf: function() { upvar = "valueOf"; return {}; }, |
|
164 toString: function() |
|
165 { |
|
166 upvar = upvar === "valueOf" ? "both" : "toString"; |
|
167 return 17; |
|
168 } |
|
169 } |
|
170 }; |
|
171 fun.apply({}, argsObjectPrimitiveLength); |
|
172 assertEq(upvar, "both", "didn't call all hooks properly"); |
|
173 |
|
174 |
|
175 /* Step 6-9. */ |
|
176 var seenThis, res, steps; |
|
177 var argsAccessors = |
|
178 { |
|
179 length: 4, |
|
180 get 0() { steps.push("0"); return 1; }, |
|
181 get 1() { steps.push("1"); return 2; }, |
|
182 // make sure values shine through holes |
|
183 get 3() { steps.push("3"); return 8; }, |
|
184 }; |
|
185 |
|
186 Object.prototype[2] = 729; |
|
187 |
|
188 seenThis = "not seen"; |
|
189 function argsAsArray() |
|
190 { |
|
191 seenThis = this; |
|
192 steps.push(Math.PI); |
|
193 return Array.prototype.map.call(arguments, function(v) { return v; }); |
|
194 } |
|
195 |
|
196 steps = []; |
|
197 res = argsAsArray.apply(thisObj, argsAccessors); |
|
198 assertEq(seenThis, thisObj, "saw wrong this"); |
|
199 |
|
200 assertEq(steps.length, 4, "wrong steps: " + steps); |
|
201 assertEq(steps[0], "0", "bad step 0"); |
|
202 assertEq(steps[1], "1", "bad step 1"); |
|
203 assertEq(steps[2], "3", "bad step 3"); |
|
204 assertEq(steps[3], Math.PI, "bad last step"); |
|
205 |
|
206 assertEq(res.length, 4, "wrong return: " + res); |
|
207 assertEq(res[0], 1, "wrong ret[0]"); |
|
208 assertEq(res[1], 2, "wrong ret[0]"); |
|
209 assertEq(res[2], 729, "wrong ret[0]"); |
|
210 assertEq(res[3], 8, "wrong ret[0]"); |
|
211 |
|
212 seenThis = "not seen"; |
|
213 function strictArgsAsArray() |
|
214 { |
|
215 "use strict"; |
|
216 seenThis = this; |
|
217 steps.push(NaN); |
|
218 return Array.prototype.map.call(arguments, function(v) { return v; }); |
|
219 } |
|
220 |
|
221 steps = []; |
|
222 res = strictArgsAsArray.apply(null, argsAccessors); |
|
223 assertEq(seenThis, null, "saw wrong this"); |
|
224 |
|
225 assertEq(steps.length, 4, "wrong steps: " + steps); |
|
226 assertEq(steps[0], "0", "bad step 0"); |
|
227 assertEq(steps[1], "1", "bad step 1"); |
|
228 assertEq(steps[2], "3", "bad step 3"); |
|
229 assertEq(steps[3], 0 / 0, "bad last step"); |
|
230 |
|
231 assertEq(res.length, 4, "wrong return: " + res); |
|
232 assertEq(res[0], 1, "wrong ret[0]"); |
|
233 assertEq(res[1], 2, "wrong ret[0]"); |
|
234 assertEq(res[2], 729, "wrong ret[0]"); |
|
235 assertEq(res[3], 8, "wrong ret[0]"); |
|
236 |
|
237 strictArgsAsArray.apply(17, argsAccessors); |
|
238 assertEq(seenThis, 17, "saw wrong this"); |
|
239 |
|
240 /******************************************************************************/ |
|
241 |
|
242 if (typeof reportCompare === "function") |
|
243 reportCompare(true, true); |
|
244 |
|
245 print("All tests passed!"); |