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