OLD | NEW |
| (Empty) |
1 // Copyright 2014 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Flags: --harmony-concat-spreadable --harmony-proxies --harmony-reflect | |
6 | |
7 (function testArrayConcatArity() { | |
8 "use strict"; | |
9 assertEquals(1, Array.prototype.concat.length); | |
10 })(); | |
11 | |
12 | |
13 (function testArrayConcatNoPrototype() { | |
14 "use strict"; | |
15 assertEquals(void 0, Array.prototype.concat.prototype); | |
16 })(); | |
17 | |
18 | |
19 (function testArrayConcatDescriptor() { | |
20 "use strict"; | |
21 var desc = Object.getOwnPropertyDescriptor(Array.prototype, 'concat'); | |
22 assertEquals(false, desc.enumerable); | |
23 })(); | |
24 | |
25 | |
26 (function testConcatArrayLike() { | |
27 "use strict"; | |
28 var obj = { | |
29 "length": 6, | |
30 "1": "A", | |
31 "3": "B", | |
32 "5": "C" | |
33 }; | |
34 obj[Symbol.isConcatSpreadable] = true; | |
35 var obj2 = { length: 3, "0": "0", "1": "1", "2": "2" }; | |
36 var arr = ["X", "Y", "Z"]; | |
37 assertEquals([void 0, "A", void 0, "B", void 0, "C", | |
38 { "length": 3, "0": "0", "1": "1", "2": "2" }, | |
39 "X", "Y", "Z"], Array.prototype.concat.call(obj, obj2, arr)); | |
40 })(); | |
41 | |
42 | |
43 (function testConcatArrayLikeStringLength() { | |
44 "use strict"; | |
45 var obj = { | |
46 "length": "6", | |
47 "1": "A", | |
48 "3": "B", | |
49 "5": "C" | |
50 }; | |
51 obj[Symbol.isConcatSpreadable] = true; | |
52 var obj2 = { length: 3, "0": "0", "1": "1", "2": "2" }; | |
53 var arr = ["X", "Y", "Z"]; | |
54 assertEquals([void 0, "A", void 0, "B", void 0, "C", | |
55 { "length": 3, "0": "0", "1": "1", "2": "2" }, | |
56 "X", "Y", "Z"], Array.prototype.concat.call(obj, obj2, arr)); | |
57 })(); | |
58 | |
59 | |
60 (function testConcatArrayLikeNegativeLength() { | |
61 "use strict"; | |
62 var obj = { | |
63 "length": -6, | |
64 "1": "A", | |
65 "3": "B", | |
66 "5": "C" | |
67 }; | |
68 obj[Symbol.isConcatSpreadable] = true; | |
69 assertEquals([], [].concat(obj)); | |
70 obj.length = -6.7; | |
71 assertEquals([], [].concat(obj)); | |
72 obj.length = "-6"; | |
73 assertEquals([], [].concat(obj)); | |
74 })(); | |
75 | |
76 | |
77 (function testConcatArrayLikeToLengthThrows() { | |
78 "use strict"; | |
79 var obj = { | |
80 "length": {valueOf: null, toString: null}, | |
81 "1": "A", | |
82 "3": "B", | |
83 "5": "C" | |
84 }; | |
85 obj[Symbol.isConcatSpreadable] = true; | |
86 var obj2 = { length: 3, "0": "0", "1": "1", "2": "2" }; | |
87 var arr = ["X", "Y", "Z"]; | |
88 assertThrows(function() { | |
89 Array.prototype.concat.call(obj, obj2, arr); | |
90 }, TypeError); | |
91 })(); | |
92 | |
93 | |
94 (function testConcatArrayLikePrimitiveNonNumberLength() { | |
95 "use strict"; | |
96 var obj = { | |
97 "1": "A", | |
98 "3": "B", | |
99 "5": "C" | |
100 }; | |
101 obj[Symbol.isConcatSpreadable] = true; | |
102 obj.length = {toString: function() { return "SIX"; }, valueOf: null }; | |
103 assertEquals([], [].concat(obj)); | |
104 obj.length = {toString: null, valueOf: function() { return "SIX"; } }; | |
105 assertEquals([], [].concat(obj)); | |
106 })(); | |
107 | |
108 | |
109 (function testConcatArrayLikeLengthToStringThrows() { | |
110 "use strict"; | |
111 function MyError() {} | |
112 var obj = { | |
113 "length": { toString: function() { | |
114 throw new MyError(); | |
115 }, valueOf: null | |
116 }, | |
117 "1": "A", | |
118 "3": "B", | |
119 "5": "C" | |
120 }; | |
121 obj[Symbol.isConcatSpreadable] = true; | |
122 assertThrows(function() { | |
123 [].concat(obj); | |
124 }, MyError); | |
125 })(); | |
126 | |
127 | |
128 (function testConcatArrayLikeLengthValueOfThrows() { | |
129 "use strict"; | |
130 function MyError() {} | |
131 var obj = { | |
132 "length": { valueOf: function() { | |
133 throw new MyError(); | |
134 }, toString: null | |
135 }, | |
136 "1": "A", | |
137 "3": "B", | |
138 "5": "C" | |
139 }; | |
140 obj[Symbol.isConcatSpreadable] = true; | |
141 assertThrows(function() { | |
142 [].concat(obj); | |
143 }, MyError); | |
144 })(); | |
145 | |
146 | |
147 (function testConcatHoleyArray() { | |
148 "use strict"; | |
149 var arr = []; | |
150 arr[4] = "Item 4"; | |
151 arr[8] = "Item 8"; | |
152 var arr2 = [".", "!", "?"]; | |
153 assertEquals([void 0, void 0, void 0, void 0, "Item 4", void 0, void 0, | |
154 void 0, "Item 8", ".", "!", "?"], arr.concat(arr2)); | |
155 })(); | |
156 | |
157 | |
158 (function testIsConcatSpreadableGetterThrows() { | |
159 "use strict"; | |
160 function MyError() {} | |
161 var obj = {}; | |
162 Object.defineProperty(obj, Symbol.isConcatSpreadable, { | |
163 get: function() { throw new MyError(); } | |
164 }); | |
165 | |
166 assertThrows(function() { | |
167 [].concat(obj); | |
168 }, MyError); | |
169 | |
170 assertThrows(function() { | |
171 Array.prototype.concat.call(obj, 1, 2, 3); | |
172 }, MyError); | |
173 })(); | |
174 | |
175 | |
176 (function testConcatLengthThrows() { | |
177 "use strict"; | |
178 function MyError() {} | |
179 var obj = {}; | |
180 obj[Symbol.isConcatSpreadable] = true; | |
181 Object.defineProperty(obj, "length", { | |
182 get: function() { throw new MyError(); } | |
183 }); | |
184 | |
185 assertThrows(function() { | |
186 [].concat(obj); | |
187 }, MyError); | |
188 | |
189 assertThrows(function() { | |
190 Array.prototype.concat.call(obj, 1, 2, 3); | |
191 }, MyError); | |
192 })(); | |
193 | |
194 | |
195 (function testConcatArraySubclass() { | |
196 "use strict"; | |
197 // If @@isConcatSpreadable is not used, the value of IsArray(O) | |
198 // is used to determine the spreadable property. | |
199 class A extends Array {} | |
200 var obj = [].concat(new A(1, 2, 3), new A(4, 5, 6), new A(7, 8, 9)); | |
201 assertEquals(9, obj.length); | |
202 for (var i = 0; i < obj.length; ++i) { | |
203 assertEquals(i + 1, obj[i]); | |
204 } | |
205 | |
206 // TODO(caitp): when concat is called on instances of classes which extend | |
207 // Array, they should: | |
208 // | |
209 // - return an instance of the class, rather than an Array instance (if from | |
210 // same Realm) | |
211 // - always treat such classes as concat-spreadable | |
212 })(); | |
213 | |
214 | |
215 (function testConcatArraySubclassOptOut() { | |
216 "use strict"; | |
217 class A extends Array { | |
218 get [Symbol.isConcatSpreadable]() { return false; } | |
219 } | |
220 var obj = [].concat(new A(1, 2, 3), new A(4, 5, 6), new A(7, 8, 9)); | |
221 assertEquals(3, obj.length); | |
222 assertEquals(3, obj[0].length); | |
223 assertEquals(3, obj[1].length); | |
224 assertEquals(3, obj[2].length); | |
225 })(); | |
226 | |
227 | |
228 (function testConcatNonArray() { | |
229 "use strict"; | |
230 class NonArray { | |
231 constructor() { Array.apply(this, arguments); } | |
232 }; | |
233 | |
234 var obj = new NonArray(1,2,3); | |
235 var result = Array.prototype.concat.call(obj, 4, 5, 6); | |
236 assertEquals(Array, result.constructor); | |
237 assertEquals([obj,4,5,6], result); | |
238 assertFalse(result instanceof NonArray); | |
239 })(); | |
240 | |
241 | |
242 function testConcatTypedArray(type, elems, modulo) { | |
243 "use strict"; | |
244 var items = new Array(elems); | |
245 var ta_by_len = new type(elems); | |
246 for (var i = 0; i < elems; ++i) { | |
247 ta_by_len[i] = items[i] = modulo === false ? i : elems % modulo; | |
248 } | |
249 var ta = new type(items); | |
250 assertEquals([ta, ta], [].concat(ta, ta)); | |
251 ta[Symbol.isConcatSpreadable] = true; | |
252 assertEquals(items, [].concat(ta)); | |
253 | |
254 assertEquals([ta_by_len, ta_by_len], [].concat(ta_by_len, ta_by_len)); | |
255 ta_by_len[Symbol.isConcatSpreadable] = true; | |
256 assertEquals(items, [].concat(ta_by_len)); | |
257 | |
258 // TypedArray with fake `length`. | |
259 ta = new type(1); | |
260 var defValue = ta[0]; | |
261 var expected = new Array(4000); | |
262 expected[0] = defValue; | |
263 | |
264 Object.defineProperty(ta, "length", { value: 4000 }); | |
265 ta[Symbol.isConcatSpreadable] = true; | |
266 assertEquals(expected, [].concat(ta)); | |
267 } | |
268 | |
269 (function testConcatSmallTypedArray() { | |
270 var max = [Math.pow(2, 8), Math.pow(2, 16), Math.pow(2, 32), false, false]; | |
271 [ | |
272 Uint8Array, | |
273 Uint16Array, | |
274 Uint32Array, | |
275 Float32Array, | |
276 Float64Array | |
277 ].forEach(function(ctor, i) { | |
278 testConcatTypedArray(ctor, 1, max[i]); | |
279 }); | |
280 })(); | |
281 | |
282 | |
283 (function testConcatLargeTypedArray() { | |
284 var max = [Math.pow(2, 8), Math.pow(2, 16), Math.pow(2, 32), false, false]; | |
285 [ | |
286 Uint8Array, | |
287 Uint16Array, | |
288 Uint32Array, | |
289 Float32Array, | |
290 Float64Array | |
291 ].forEach(function(ctor, i) { | |
292 testConcatTypedArray(ctor, 4000, max[i]); | |
293 }); | |
294 })(); | |
295 | |
296 | |
297 (function testConcatStrictArguments() { | |
298 var args = (function(a, b, c) { "use strict"; return arguments; })(1,2,3); | |
299 args[Symbol.isConcatSpreadable] = true; | |
300 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args)); | |
301 | |
302 Object.defineProperty(args, "length", { value: 6 }); | |
303 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args)); | |
304 })(); | |
305 | |
306 | |
307 (function testConcatSloppyArguments() { | |
308 var args = (function(a, b, c) { return arguments; })(1,2,3); | |
309 args[Symbol.isConcatSpreadable] = true; | |
310 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args)); | |
311 | |
312 Object.defineProperty(args, "length", { value: 6 }); | |
313 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args)); | |
314 })(); | |
315 | |
316 | |
317 (function testConcatSloppyArgumentsWithDupes() { | |
318 var args = (function(a, a, a) { return arguments; })(1,2,3); | |
319 args[Symbol.isConcatSpreadable] = true; | |
320 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args)); | |
321 | |
322 Object.defineProperty(args, "length", { value: 6 }); | |
323 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args)); | |
324 })(); | |
325 | |
326 | |
327 (function testConcatSloppyArgumentsThrows() { | |
328 function MyError() {} | |
329 var args = (function(a) { return arguments; })(1,2,3); | |
330 Object.defineProperty(args, 0, { | |
331 get: function() { throw new MyError(); } | |
332 }); | |
333 args[Symbol.isConcatSpreadable] = true; | |
334 assertThrows(function() { | |
335 return [].concat(args, args); | |
336 }, MyError); | |
337 })(); | |
338 | |
339 | |
340 (function testConcatHoleySloppyArguments() { | |
341 var args = (function(a) { return arguments; })(1,2,3); | |
342 delete args[1]; | |
343 args[Symbol.isConcatSpreadable] = true; | |
344 assertEquals([1, void 0, 3, 1, void 0, 3], [].concat(args, args)); | |
345 })(); | |
346 | |
347 | |
348 (function testConcatSpreadableStringWrapper() { | |
349 "use strict"; | |
350 var str1 = new String("yuck\uD83D\uDCA9") | |
351 // String wrapper objects are not concat-spreadable by default | |
352 assertEquals([str1], [].concat(str1)); | |
353 | |
354 // String wrapper objects may be individually concat-spreadable | |
355 str1[Symbol.isConcatSpreadable] = true; | |
356 assertEquals(["y", "u", "c", "k", "\uD83D", "\uDCA9"], | |
357 [].concat(str1)); | |
358 | |
359 String.prototype[Symbol.isConcatSpreadable] = true; | |
360 // String wrapper objects may be concat-spreadable | |
361 assertEquals(["y", "u", "c", "k", "\uD83D", "\uDCA9"], | |
362 [].concat(new String("yuck\uD83D\uDCA9"))); | |
363 | |
364 // String values are never concat-spreadable | |
365 assertEquals(["yuck\uD83D\uDCA9"], [].concat("yuck\uD83D\uDCA9")); | |
366 delete String.prototype[Symbol.isConcatSpreadable]; | |
367 })(); | |
368 | |
369 | |
370 (function testConcatSpreadableBooleanWrapper() { | |
371 "use strict"; | |
372 var bool = new Boolean(true) | |
373 // Boolean wrapper objects are not concat-spreadable by default | |
374 assertEquals([bool], [].concat(bool)); | |
375 | |
376 // Boolean wrapper objects may be individually concat-spreadable | |
377 bool[Symbol.isConcatSpreadable] = true; | |
378 bool.length = 3; | |
379 bool[0] = 1, bool[1] = 2, bool[2] = 3; | |
380 assertEquals([1, 2, 3], [].concat(bool)); | |
381 | |
382 Boolean.prototype[Symbol.isConcatSpreadable] = true; | |
383 // Boolean wrapper objects may be concat-spreadable | |
384 assertEquals([], [].concat(new Boolean(true))); | |
385 Boolean.prototype[0] = 1; | |
386 Boolean.prototype[1] = 2; | |
387 Boolean.prototype[2] = 3; | |
388 Boolean.prototype.length = 3; | |
389 assertEquals([1,2,3], [].concat(new Boolean(true))); | |
390 | |
391 // Boolean values are never concat-spreadable | |
392 assertEquals([true], [].concat(true)); | |
393 delete Boolean.prototype[Symbol.isConcatSpreadable]; | |
394 delete Boolean.prototype[0]; | |
395 delete Boolean.prototype[1]; | |
396 delete Boolean.prototype[2]; | |
397 delete Boolean.prototype.length; | |
398 })(); | |
399 | |
400 | |
401 (function testConcatSpreadableNumberWrapper() { | |
402 "use strict"; | |
403 var num = new Number(true) | |
404 // Number wrapper objects are not concat-spreadable by default | |
405 assertEquals([num], [].concat(num)); | |
406 | |
407 // Number wrapper objects may be individually concat-spreadable | |
408 num[Symbol.isConcatSpreadable] = true; | |
409 num.length = 3; | |
410 num[0] = 1, num[1] = 2, num[2] = 3; | |
411 assertEquals([1, 2, 3], [].concat(num)); | |
412 | |
413 Number.prototype[Symbol.isConcatSpreadable] = true; | |
414 // Number wrapper objects may be concat-spreadable | |
415 assertEquals([], [].concat(new Number(123))); | |
416 Number.prototype[0] = 1; | |
417 Number.prototype[1] = 2; | |
418 Number.prototype[2] = 3; | |
419 Number.prototype.length = 3; | |
420 assertEquals([1,2,3], [].concat(new Number(123))); | |
421 | |
422 // Number values are never concat-spreadable | |
423 assertEquals([true], [].concat(true)); | |
424 delete Number.prototype[Symbol.isConcatSpreadable]; | |
425 delete Number.prototype[0]; | |
426 delete Number.prototype[1]; | |
427 delete Number.prototype[2]; | |
428 delete Number.prototype.length; | |
429 })(); | |
430 | |
431 | |
432 (function testConcatSpreadableFunction() { | |
433 "use strict"; | |
434 var fn = function(a, b, c) {} | |
435 // Functions are not concat-spreadable by default | |
436 assertEquals([fn], [].concat(fn)); | |
437 | |
438 // Functions may be individually concat-spreadable | |
439 fn[Symbol.isConcatSpreadable] = true; | |
440 fn[0] = 1, fn[1] = 2, fn[2] = 3; | |
441 assertEquals([1, 2, 3], [].concat(fn)); | |
442 | |
443 Function.prototype[Symbol.isConcatSpreadable] = true; | |
444 // Functions may be concat-spreadable | |
445 assertEquals([void 0, void 0, void 0], [].concat(function(a,b,c) {})); | |
446 Function.prototype[0] = 1; | |
447 Function.prototype[1] = 2; | |
448 Function.prototype[2] = 3; | |
449 assertEquals([1,2,3], [].concat(function(a, b, c) {})); | |
450 | |
451 delete Function.prototype[Symbol.isConcatSpreadable]; | |
452 delete Function.prototype[0]; | |
453 delete Function.prototype[1]; | |
454 delete Function.prototype[2]; | |
455 })(); | |
456 | |
457 | |
458 (function testConcatSpreadableRegExp() { | |
459 "use strict"; | |
460 var re = /abc/; | |
461 // RegExps are not concat-spreadable by default | |
462 assertEquals([re], [].concat(re)); | |
463 | |
464 // RegExps may be individually concat-spreadable | |
465 re[Symbol.isConcatSpreadable] = true; | |
466 re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3; | |
467 assertEquals([1, 2, 3], [].concat(re)); | |
468 | |
469 // RegExps may be concat-spreadable | |
470 RegExp.prototype[Symbol.isConcatSpreadable] = true; | |
471 RegExp.prototype.length = 3; | |
472 | |
473 assertEquals([void 0, void 0, void 0], [].concat(/abc/)); | |
474 RegExp.prototype[0] = 1; | |
475 RegExp.prototype[1] = 2; | |
476 RegExp.prototype[2] = 3; | |
477 assertEquals([1,2,3], [].concat(/abc/)); | |
478 | |
479 delete RegExp.prototype[Symbol.isConcatSpreadable]; | |
480 delete RegExp.prototype[0]; | |
481 delete RegExp.prototype[1]; | |
482 delete RegExp.prototype[2]; | |
483 delete RegExp.prototype.length; | |
484 })(); | |
485 | |
486 | |
487 (function testArrayConcatSpreadableSparseObject() { | |
488 "use strict"; | |
489 var obj = { length: 5 }; | |
490 obj[Symbol.isConcatSpreadable] = true; | |
491 assertEquals([void 0, void 0, void 0, void 0, void 0], [].concat(obj)); | |
492 | |
493 obj.length = 4000; | |
494 assertEquals(new Array(4000), [].concat(obj)); | |
495 })(); | |
496 | |
497 | |
498 // ES5 tests | |
499 (function testArrayConcatES5() { | |
500 "use strict"; | |
501 var poses; | |
502 var pos; | |
503 | |
504 poses = [140, 4000000000]; | |
505 while (pos = poses.shift()) { | |
506 var a = new Array(pos); | |
507 var array_proto = []; | |
508 a.__proto__ = array_proto; | |
509 assertEquals(pos, a.length); | |
510 a.push('foo'); | |
511 assertEquals(pos + 1, a.length); | |
512 var b = ['bar']; | |
513 var c = a.concat(b); | |
514 assertEquals(pos + 2, c.length); | |
515 assertEquals("undefined", typeof(c[pos - 1])); | |
516 assertEquals("foo", c[pos]); | |
517 assertEquals("bar", c[pos + 1]); | |
518 | |
519 // Can we fool the system by putting a number in a string? | |
520 var onetwofour = "124"; | |
521 a[onetwofour] = 'doo'; | |
522 assertEquals(a[124], 'doo'); | |
523 c = a.concat(b); | |
524 assertEquals(c[124], 'doo'); | |
525 | |
526 // If we put a number in the prototype, then the spec says it should be | |
527 // copied on concat. | |
528 array_proto["123"] = 'baz'; | |
529 assertEquals(a[123], 'baz'); | |
530 | |
531 c = a.concat(b); | |
532 assertEquals(pos + 2, c.length); | |
533 assertEquals("baz", c[123]); | |
534 assertEquals("undefined", typeof(c[pos - 1])); | |
535 assertEquals("foo", c[pos]); | |
536 assertEquals("bar", c[pos + 1]); | |
537 | |
538 // When we take the number off the prototype it disappears from a, but | |
539 // the concat put it in c itself. | |
540 array_proto["123"] = undefined; | |
541 assertEquals("undefined", typeof(a[123])); | |
542 assertEquals("baz", c[123]); | |
543 | |
544 // If the element of prototype is shadowed, the element on the instance | |
545 // should be copied, but not the one on the prototype. | |
546 array_proto[123] = 'baz'; | |
547 a[123] = 'xyz'; | |
548 assertEquals('xyz', a[123]); | |
549 c = a.concat(b); | |
550 assertEquals('xyz', c[123]); | |
551 | |
552 // Non-numeric properties on the prototype or the array shouldn't get | |
553 // copied. | |
554 array_proto.moe = 'joe'; | |
555 a.ben = 'jerry'; | |
556 assertEquals(a["moe"], 'joe'); | |
557 assertEquals(a["ben"], 'jerry'); | |
558 c = a.concat(b); | |
559 // ben was not copied | |
560 assertEquals("undefined", typeof(c.ben)); | |
561 | |
562 // When we take moe off the prototype it disappears from all arrays. | |
563 array_proto.moe = undefined; | |
564 assertEquals("undefined", typeof(c.moe)); | |
565 | |
566 // Negative indices don't get concated. | |
567 a[-1] = 'minus1'; | |
568 assertEquals("minus1", a[-1]); | |
569 assertEquals("undefined", typeof(a[0xffffffff])); | |
570 c = a.concat(b); | |
571 assertEquals("undefined", typeof(c[-1])); | |
572 assertEquals("undefined", typeof(c[0xffffffff])); | |
573 assertEquals(c.length, a.length + 1); | |
574 } | |
575 | |
576 poses = [140, 4000000000]; | |
577 while (pos = poses.shift()) { | |
578 var a = new Array(pos); | |
579 assertEquals(pos, a.length); | |
580 a.push('foo'); | |
581 assertEquals(pos + 1, a.length); | |
582 var b = ['bar']; | |
583 var c = a.concat(b); | |
584 assertEquals(pos + 2, c.length); | |
585 assertEquals("undefined", typeof(c[pos - 1])); | |
586 assertEquals("foo", c[pos]); | |
587 assertEquals("bar", c[pos + 1]); | |
588 | |
589 // Can we fool the system by putting a number in a string? | |
590 var onetwofour = "124"; | |
591 a[onetwofour] = 'doo'; | |
592 assertEquals(a[124], 'doo'); | |
593 c = a.concat(b); | |
594 assertEquals(c[124], 'doo'); | |
595 | |
596 // If we put a number in the prototype, then the spec says it should be | |
597 // copied on concat. | |
598 Array.prototype["123"] = 'baz'; | |
599 assertEquals(a[123], 'baz'); | |
600 | |
601 c = a.concat(b); | |
602 assertEquals(pos + 2, c.length); | |
603 assertEquals("baz", c[123]); | |
604 assertEquals("undefined", typeof(c[pos - 1])); | |
605 assertEquals("foo", c[pos]); | |
606 assertEquals("bar", c[pos + 1]); | |
607 | |
608 // When we take the number off the prototype it disappears from a, but | |
609 // the concat put it in c itself. | |
610 Array.prototype["123"] = undefined; | |
611 assertEquals("undefined", typeof(a[123])); | |
612 assertEquals("baz", c[123]); | |
613 | |
614 // If the element of prototype is shadowed, the element on the instance | |
615 // should be copied, but not the one on the prototype. | |
616 Array.prototype[123] = 'baz'; | |
617 a[123] = 'xyz'; | |
618 assertEquals('xyz', a[123]); | |
619 c = a.concat(b); | |
620 assertEquals('xyz', c[123]); | |
621 | |
622 // Non-numeric properties on the prototype or the array shouldn't get | |
623 // copied. | |
624 Array.prototype.moe = 'joe'; | |
625 a.ben = 'jerry'; | |
626 assertEquals(a["moe"], 'joe'); | |
627 assertEquals(a["ben"], 'jerry'); | |
628 c = a.concat(b); | |
629 // ben was not copied | |
630 assertEquals("undefined", typeof(c.ben)); | |
631 // moe was not copied, but we can see it through the prototype | |
632 assertEquals("joe", c.moe); | |
633 | |
634 // When we take moe off the prototype it disappears from all arrays. | |
635 Array.prototype.moe = undefined; | |
636 assertEquals("undefined", typeof(c.moe)); | |
637 | |
638 // Negative indices don't get concated. | |
639 a[-1] = 'minus1'; | |
640 assertEquals("minus1", a[-1]); | |
641 assertEquals("undefined", typeof(a[0xffffffff])); | |
642 c = a.concat(b); | |
643 assertEquals("undefined", typeof(c[-1])); | |
644 assertEquals("undefined", typeof(c[0xffffffff])); | |
645 assertEquals(c.length, a.length + 1); | |
646 | |
647 } | |
648 | |
649 a = []; | |
650 c = a.concat('Hello'); | |
651 assertEquals(1, c.length); | |
652 assertEquals("Hello", c[0]); | |
653 assertEquals("Hello", c.toString()); | |
654 | |
655 // Check that concat preserves holes. | |
656 var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0]) | |
657 assertEquals(9, holey.length); // hole in embedded array is ignored | |
658 for (var i = 0; i < holey.length; i++) { | |
659 if (i == 2 || i == 5) { | |
660 assertFalse(i in holey); | |
661 } else { | |
662 assertTrue(i in holey); | |
663 } | |
664 } | |
665 | |
666 // Polluted prototype from prior tests. | |
667 delete Array.prototype[123]; | |
668 | |
669 // Check that concat reads getters in the correct order. | |
670 var arr1 = [,2]; | |
671 var arr2 = [1,3]; | |
672 var r1 = [].concat(arr1, arr2); // [,2,1,3] | |
673 assertEquals([,2,1,3], r1); | |
674 | |
675 // Make first array change length of second array. | |
676 Object.defineProperty(arr1, 0, {get: function() { | |
677 arr2.push("X"); | |
678 return undefined; | |
679 }, configurable: true}) | |
680 var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] | |
681 assertEquals([undefined,2,1,3,"X"], r2); | |
682 | |
683 // Make first array change length of second array massively. | |
684 arr2.length = 2; | |
685 Object.defineProperty(arr1, 0, {get: function() { | |
686 arr2[500000] = "X"; | |
687 return undefined; | |
688 }, configurable: true}) | |
689 var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] | |
690 var expected = [undefined,2,1,3]; | |
691 expected[500000 + 2] = "X"; | |
692 | |
693 assertEquals(expected, r3); | |
694 | |
695 var arr3 = []; | |
696 var trace = []; | |
697 var expectedTrace = [] | |
698 function mkGetter(i) { return function() { trace.push(i); }; } | |
699 arr3.length = 10000; | |
700 for (var i = 0; i < 100; i++) { | |
701 Object.defineProperty(arr3, i * i, {get: mkGetter(i)}); | |
702 expectedTrace[i] = i; | |
703 expectedTrace[100 + i] = i; | |
704 } | |
705 var r4 = [0].concat(arr3, arr3); | |
706 assertEquals(1 + arr3.length * 2, r4.length); | |
707 assertEquals(expectedTrace, trace); | |
708 | |
709 // Clean up. | |
710 delete Array.prototype[123]; | |
711 delete Array.prototype["123"]; | |
712 delete Array.prototype["moe"]; | |
713 })(); | |
714 | |
715 | |
716 | |
717 | |
718 //////////////////////////////////////////////////////////////////////////////// | |
719 // Tests with proxies | |
720 | |
721 // Note: concat does not currently support species so there is no difference | |
722 // between [].concat(foo) and Array.prototype.concat.apply(foo). | |
723 | |
724 | |
725 var log = []; | |
726 var logger = {}; | |
727 var handler = new Proxy({}, logger); | |
728 | |
729 logger.get = function(t, trap, r) { | |
730 return function(...args) { | |
731 log.push([trap, ...args]); | |
732 return Reflect[trap](...args); | |
733 } | |
734 }; | |
735 | |
736 | |
737 (function testUnspreadableNonArrayLikeProxy() { | |
738 var target = {0: "a", 1: "b"}; | |
739 var obj = new Proxy(target, handler); | |
740 | |
741 log.length = 0; | |
742 assertEquals([obj], [].concat(obj)); | |
743 assertEquals(1, log.length); | |
744 for (var i in log) assertSame(target, log[i][1]); | |
745 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
746 | |
747 log.length = 0; | |
748 assertEquals([obj], Array.prototype.concat.apply(obj)); | |
749 assertEquals(1, log.length); | |
750 for (var i in log) assertSame(target, log[i][1]); | |
751 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
752 })(); | |
753 | |
754 | |
755 (function testSpreadableNonArrayLikeProxy() { | |
756 var target = {0: "a", 1: "b", [Symbol.isConcatSpreadable]: "truish"}; | |
757 var obj = new Proxy(target, handler); | |
758 | |
759 log.length = 0; | |
760 assertEquals([], [].concat(obj)); | |
761 assertEquals(2, log.length); | |
762 for (var i in log) assertSame(target, log[i][1]); | |
763 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
764 assertEquals(["get", target, "length", obj], log[1]); | |
765 | |
766 log.length = 0; | |
767 assertEquals([], Array.prototype.concat.apply(obj)); | |
768 assertEquals(2, log.length); | |
769 for (var i in log) assertSame(target, log[i][1]); | |
770 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
771 assertEquals(["get", target, "length", obj], log[1]); | |
772 | |
773 target.length = 3; | |
774 | |
775 log.length = 0; | |
776 assertEquals(["a", "b", undefined], [].concat(obj)); | |
777 assertEquals(7, log.length); | |
778 for (var i in log) assertSame(target, log[i][1]); | |
779 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
780 assertEquals(["get", target, "length", obj], log[1]); | |
781 assertEquals(["has", target, "0"], log[2]); | |
782 assertEquals(["get", target, "0", obj], log[3]); | |
783 assertEquals(["has", target, "1"], log[4]); | |
784 assertEquals(["get", target, "1", obj], log[5]); | |
785 assertEquals(["has", target, "2"], log[6]); | |
786 | |
787 log.length = 0; | |
788 assertEquals(["a", "b", undefined], Array.prototype.concat.apply(obj)); | |
789 assertEquals(7, log.length); | |
790 for (var i in log) assertSame(target, log[i][1]); | |
791 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
792 assertEquals(["get", target, "length", obj], log[1]); | |
793 assertEquals(["has", target, "0"], log[2]); | |
794 assertEquals(["get", target, "0", obj], log[3]); | |
795 assertEquals(["has", target, "1"], log[4]); | |
796 assertEquals(["get", target, "1", obj], log[5]); | |
797 assertEquals(["has", target, "2"], log[6]); | |
798 })(); | |
799 | |
800 | |
801 (function testUnspreadableArrayLikeProxy() { | |
802 var target = ["a", "b"]; | |
803 target[Symbol.isConcatSpreadable] = ""; | |
804 var obj = new Proxy(target, handler); | |
805 | |
806 log.length = 0; | |
807 assertEquals([obj], [].concat(obj)); | |
808 assertEquals(1, log.length); | |
809 for (var i in log) assertSame(target, log[i][1]); | |
810 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
811 | |
812 log.length = 0; | |
813 assertEquals([obj], Array.prototype.concat.apply(obj)); | |
814 assertEquals(1, log.length); | |
815 for (var i in log) assertSame(target, log[i][1]); | |
816 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
817 })(); | |
818 | |
819 | |
820 (function testSpreadableArrayLikeProxy() { | |
821 var target = ["a", "b"]; | |
822 target[Symbol.isConcatSpreadable] = undefined; | |
823 var obj = new Proxy(target, handler); | |
824 | |
825 log.length = 0; | |
826 assertEquals(["a", "b"], [].concat(obj)); | |
827 assertEquals(6, log.length); | |
828 for (var i in log) assertSame(target, log[i][1]); | |
829 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
830 assertEquals(["get", target, "length", obj], log[1]); | |
831 assertEquals(["has", target, "0"], log[2]); | |
832 assertEquals(["get", target, "0", obj], log[3]); | |
833 assertEquals(["has", target, "1"], log[4]); | |
834 assertEquals(["get", target, "1", obj], log[5]); | |
835 | |
836 log.length = 0; | |
837 assertEquals(["a", "b"], Array.prototype.concat.apply(obj)); | |
838 assertEquals(6, log.length); | |
839 for (var i in log) assertSame(target, log[i][1]); | |
840 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]); | |
841 assertEquals(["get", target, "length", obj], log[1]); | |
842 assertEquals(["has", target, "0"], log[2]); | |
843 assertEquals(["get", target, "0", obj], log[3]); | |
844 assertEquals(["has", target, "1"], log[4]); | |
845 assertEquals(["get", target, "1", obj], log[5]); | |
846 })(); | |
847 | |
848 | |
849 (function testSpreadableArrayLikeProxyWithNontrivialLength() { | |
850 var getTrap = function(t, key) { | |
851 if (key === "length") return {[Symbol.toPrimitive]() {return 3}}; | |
852 if (key === "2") return "baz"; | |
853 if (key === "3") return "bar"; | |
854 }; | |
855 var target = []; | |
856 var obj = new Proxy(target, {get: getTrap, has: () => true}); | |
857 | |
858 assertEquals([undefined, undefined, "baz"], [].concat(obj)); | |
859 assertEquals([undefined, undefined, "baz"], Array.prototype.concat.apply(obj)) | |
860 })(); | |
861 | |
862 | |
863 (function testSpreadableArrayLikeProxyWithBogusLength() { | |
864 var getTrap = function(t, key) { | |
865 if (key === "length") return Symbol(); | |
866 if (key === "2") return "baz"; | |
867 if (key === "3") return "bar"; | |
868 }; | |
869 var target = []; | |
870 var obj = new Proxy(target, {get: getTrap, has: () => true}); | |
871 | |
872 assertThrows(() => [].concat(obj), TypeError); | |
873 assertThrows(() => Array.prototype.concat.apply(obj), TypeError); | |
874 })(); | |
OLD | NEW |