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