blob: bc9e1a00cc946ce4972da193ee4567d5925ee8e9 [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
Ben Murdoch342c50c2016-05-18 11:27:45 +01005// Flags: --harmony-proxies --harmony-reflect
Emily Bernier958fae72015-03-24 16:35:39 -04006
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};
140obj[Symbol.isConcatSpreadable] = true;
141assertThrows(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";
Ben Murdoch014dc512016-03-22 12:00:34 +0000197 // 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
Emily Bernier958fae72015-03-24 16:35:39 -0400206 // 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
Ben Murdoch014dc512016-03-22 12:00:34 +0000215(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
Emily Bernier958fae72015-03-24 16:35:39 -0400228(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
242function 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() {
Ben Murdoch342c50c2016-05-18 11:27:45 +0100270 var length = 1;
271 testConcatTypedArray(Uint8Array, length, Math.pow(2, 8));
272 testConcatTypedArray(Uint16Array, length, Math.pow(2, 16));
273 testConcatTypedArray(Uint32Array, length, Math.pow(2, 32));
274 testConcatTypedArray(Float32Array, length, false);
275 testConcatTypedArray(Float64Array, length, false);
Emily Bernier958fae72015-03-24 16:35:39 -0400276})();
277
278
279(function testConcatLargeTypedArray() {
Ben Murdoch342c50c2016-05-18 11:27:45 +0100280 var length = 4000;
281 testConcatTypedArray(Uint8Array, length, Math.pow(2, 8));
282 testConcatTypedArray(Uint16Array, length, Math.pow(2, 16));
283 testConcatTypedArray(Uint32Array, length, Math.pow(2, 32));
284 testConcatTypedArray(Float32Array, length, false);
285 testConcatTypedArray(Float64Array, length, false);
Emily Bernier958fae72015-03-24 16:35:39 -0400286})();
287
288
289(function testConcatStrictArguments() {
290 var args = (function(a, b, c) { "use strict"; return arguments; })(1,2,3);
291 args[Symbol.isConcatSpreadable] = true;
292 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args));
293
294 Object.defineProperty(args, "length", { value: 6 });
295 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args));
296})();
297
298
299(function testConcatSloppyArguments() {
300 var args = (function(a, b, c) { return arguments; })(1,2,3);
301 args[Symbol.isConcatSpreadable] = true;
302 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args));
303
304 Object.defineProperty(args, "length", { value: 6 });
305 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args));
306})();
307
308
309(function testConcatSloppyArgumentsWithDupes() {
310 var args = (function(a, a, a) { return arguments; })(1,2,3);
311 args[Symbol.isConcatSpreadable] = true;
312 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args));
313
314 Object.defineProperty(args, "length", { value: 6 });
315 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args));
316})();
317
318
319(function testConcatSloppyArgumentsThrows() {
320 function MyError() {}
321 var args = (function(a) { return arguments; })(1,2,3);
322 Object.defineProperty(args, 0, {
323 get: function() { throw new MyError(); }
324 });
325 args[Symbol.isConcatSpreadable] = true;
326 assertThrows(function() {
327 return [].concat(args, args);
328 }, MyError);
329})();
330
331
332(function testConcatHoleySloppyArguments() {
333 var args = (function(a) { return arguments; })(1,2,3);
334 delete args[1];
335 args[Symbol.isConcatSpreadable] = true;
336 assertEquals([1, void 0, 3, 1, void 0, 3], [].concat(args, args));
337})();
338
339
340(function testConcatSpreadableStringWrapper() {
341 "use strict";
342 var str1 = new String("yuck\uD83D\uDCA9")
343 // String wrapper objects are not concat-spreadable by default
344 assertEquals([str1], [].concat(str1));
345
346 // String wrapper objects may be individually concat-spreadable
347 str1[Symbol.isConcatSpreadable] = true;
348 assertEquals(["y", "u", "c", "k", "\uD83D", "\uDCA9"],
349 [].concat(str1));
350
351 String.prototype[Symbol.isConcatSpreadable] = true;
352 // String wrapper objects may be concat-spreadable
353 assertEquals(["y", "u", "c", "k", "\uD83D", "\uDCA9"],
354 [].concat(new String("yuck\uD83D\uDCA9")));
355
356 // String values are never concat-spreadable
357 assertEquals(["yuck\uD83D\uDCA9"], [].concat("yuck\uD83D\uDCA9"));
358 delete String.prototype[Symbol.isConcatSpreadable];
359})();
360
361
362(function testConcatSpreadableBooleanWrapper() {
363 "use strict";
364 var bool = new Boolean(true)
365 // Boolean wrapper objects are not concat-spreadable by default
366 assertEquals([bool], [].concat(bool));
367
368 // Boolean wrapper objects may be individually concat-spreadable
369 bool[Symbol.isConcatSpreadable] = true;
370 bool.length = 3;
371 bool[0] = 1, bool[1] = 2, bool[2] = 3;
372 assertEquals([1, 2, 3], [].concat(bool));
373
374 Boolean.prototype[Symbol.isConcatSpreadable] = true;
375 // Boolean wrapper objects may be concat-spreadable
376 assertEquals([], [].concat(new Boolean(true)));
377 Boolean.prototype[0] = 1;
378 Boolean.prototype[1] = 2;
379 Boolean.prototype[2] = 3;
380 Boolean.prototype.length = 3;
381 assertEquals([1,2,3], [].concat(new Boolean(true)));
382
383 // Boolean values are never concat-spreadable
384 assertEquals([true], [].concat(true));
385 delete Boolean.prototype[Symbol.isConcatSpreadable];
386 delete Boolean.prototype[0];
387 delete Boolean.prototype[1];
388 delete Boolean.prototype[2];
389 delete Boolean.prototype.length;
390})();
391
392
393(function testConcatSpreadableNumberWrapper() {
394 "use strict";
395 var num = new Number(true)
396 // Number wrapper objects are not concat-spreadable by default
397 assertEquals([num], [].concat(num));
398
399 // Number wrapper objects may be individually concat-spreadable
400 num[Symbol.isConcatSpreadable] = true;
401 num.length = 3;
402 num[0] = 1, num[1] = 2, num[2] = 3;
403 assertEquals([1, 2, 3], [].concat(num));
404
405 Number.prototype[Symbol.isConcatSpreadable] = true;
406 // Number wrapper objects may be concat-spreadable
407 assertEquals([], [].concat(new Number(123)));
408 Number.prototype[0] = 1;
409 Number.prototype[1] = 2;
410 Number.prototype[2] = 3;
411 Number.prototype.length = 3;
412 assertEquals([1,2,3], [].concat(new Number(123)));
413
414 // Number values are never concat-spreadable
415 assertEquals([true], [].concat(true));
416 delete Number.prototype[Symbol.isConcatSpreadable];
417 delete Number.prototype[0];
418 delete Number.prototype[1];
419 delete Number.prototype[2];
420 delete Number.prototype.length;
421})();
422
423
424(function testConcatSpreadableFunction() {
425 "use strict";
426 var fn = function(a, b, c) {}
427 // Functions are not concat-spreadable by default
428 assertEquals([fn], [].concat(fn));
429
430 // Functions may be individually concat-spreadable
431 fn[Symbol.isConcatSpreadable] = true;
432 fn[0] = 1, fn[1] = 2, fn[2] = 3;
433 assertEquals([1, 2, 3], [].concat(fn));
434
435 Function.prototype[Symbol.isConcatSpreadable] = true;
436 // Functions may be concat-spreadable
437 assertEquals([void 0, void 0, void 0], [].concat(function(a,b,c) {}));
438 Function.prototype[0] = 1;
439 Function.prototype[1] = 2;
440 Function.prototype[2] = 3;
441 assertEquals([1,2,3], [].concat(function(a, b, c) {}));
442
443 delete Function.prototype[Symbol.isConcatSpreadable];
444 delete Function.prototype[0];
445 delete Function.prototype[1];
446 delete Function.prototype[2];
447})();
448
449
450(function testConcatSpreadableRegExp() {
451 "use strict";
452 var re = /abc/;
453 // RegExps are not concat-spreadable by default
454 assertEquals([re], [].concat(re));
455
456 // RegExps may be individually concat-spreadable
457 re[Symbol.isConcatSpreadable] = true;
458 re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3;
459 assertEquals([1, 2, 3], [].concat(re));
460
461 // RegExps may be concat-spreadable
462 RegExp.prototype[Symbol.isConcatSpreadable] = true;
463 RegExp.prototype.length = 3;
464
465 assertEquals([void 0, void 0, void 0], [].concat(/abc/));
466 RegExp.prototype[0] = 1;
467 RegExp.prototype[1] = 2;
468 RegExp.prototype[2] = 3;
469 assertEquals([1,2,3], [].concat(/abc/));
470
471 delete RegExp.prototype[Symbol.isConcatSpreadable];
472 delete RegExp.prototype[0];
473 delete RegExp.prototype[1];
474 delete RegExp.prototype[2];
475 delete RegExp.prototype.length;
476})();
477
478
479(function testArrayConcatSpreadableSparseObject() {
480 "use strict";
481 var obj = { length: 5 };
482 obj[Symbol.isConcatSpreadable] = true;
483 assertEquals([void 0, void 0, void 0, void 0, void 0], [].concat(obj));
484
485 obj.length = 4000;
486 assertEquals(new Array(4000), [].concat(obj));
487})();
488
489
490// ES5 tests
491(function testArrayConcatES5() {
492 "use strict";
493 var poses;
494 var pos;
495
496 poses = [140, 4000000000];
497 while (pos = poses.shift()) {
498 var a = new Array(pos);
499 var array_proto = [];
500 a.__proto__ = array_proto;
501 assertEquals(pos, a.length);
502 a.push('foo');
503 assertEquals(pos + 1, a.length);
504 var b = ['bar'];
505 var c = a.concat(b);
506 assertEquals(pos + 2, c.length);
507 assertEquals("undefined", typeof(c[pos - 1]));
508 assertEquals("foo", c[pos]);
509 assertEquals("bar", c[pos + 1]);
510
511 // Can we fool the system by putting a number in a string?
512 var onetwofour = "124";
513 a[onetwofour] = 'doo';
514 assertEquals(a[124], 'doo');
515 c = a.concat(b);
516 assertEquals(c[124], 'doo');
517
518 // If we put a number in the prototype, then the spec says it should be
519 // copied on concat.
520 array_proto["123"] = 'baz';
521 assertEquals(a[123], 'baz');
522
523 c = a.concat(b);
524 assertEquals(pos + 2, c.length);
525 assertEquals("baz", c[123]);
526 assertEquals("undefined", typeof(c[pos - 1]));
527 assertEquals("foo", c[pos]);
528 assertEquals("bar", c[pos + 1]);
529
530 // When we take the number off the prototype it disappears from a, but
531 // the concat put it in c itself.
532 array_proto["123"] = undefined;
533 assertEquals("undefined", typeof(a[123]));
534 assertEquals("baz", c[123]);
535
536 // If the element of prototype is shadowed, the element on the instance
537 // should be copied, but not the one on the prototype.
538 array_proto[123] = 'baz';
539 a[123] = 'xyz';
540 assertEquals('xyz', a[123]);
541 c = a.concat(b);
542 assertEquals('xyz', c[123]);
543
544 // Non-numeric properties on the prototype or the array shouldn't get
545 // copied.
546 array_proto.moe = 'joe';
547 a.ben = 'jerry';
548 assertEquals(a["moe"], 'joe');
549 assertEquals(a["ben"], 'jerry');
550 c = a.concat(b);
551 // ben was not copied
552 assertEquals("undefined", typeof(c.ben));
553
554 // When we take moe off the prototype it disappears from all arrays.
555 array_proto.moe = undefined;
556 assertEquals("undefined", typeof(c.moe));
557
558 // Negative indices don't get concated.
559 a[-1] = 'minus1';
560 assertEquals("minus1", a[-1]);
561 assertEquals("undefined", typeof(a[0xffffffff]));
562 c = a.concat(b);
563 assertEquals("undefined", typeof(c[-1]));
564 assertEquals("undefined", typeof(c[0xffffffff]));
565 assertEquals(c.length, a.length + 1);
566 }
567
568 poses = [140, 4000000000];
569 while (pos = poses.shift()) {
570 var a = new Array(pos);
571 assertEquals(pos, a.length);
572 a.push('foo');
573 assertEquals(pos + 1, a.length);
574 var b = ['bar'];
575 var c = a.concat(b);
576 assertEquals(pos + 2, c.length);
577 assertEquals("undefined", typeof(c[pos - 1]));
578 assertEquals("foo", c[pos]);
579 assertEquals("bar", c[pos + 1]);
580
581 // Can we fool the system by putting a number in a string?
582 var onetwofour = "124";
583 a[onetwofour] = 'doo';
584 assertEquals(a[124], 'doo');
585 c = a.concat(b);
586 assertEquals(c[124], 'doo');
587
588 // If we put a number in the prototype, then the spec says it should be
589 // copied on concat.
590 Array.prototype["123"] = 'baz';
591 assertEquals(a[123], 'baz');
592
593 c = a.concat(b);
594 assertEquals(pos + 2, c.length);
595 assertEquals("baz", c[123]);
596 assertEquals("undefined", typeof(c[pos - 1]));
597 assertEquals("foo", c[pos]);
598 assertEquals("bar", c[pos + 1]);
599
600 // When we take the number off the prototype it disappears from a, but
601 // the concat put it in c itself.
602 Array.prototype["123"] = undefined;
603 assertEquals("undefined", typeof(a[123]));
604 assertEquals("baz", c[123]);
605
606 // If the element of prototype is shadowed, the element on the instance
607 // should be copied, but not the one on the prototype.
608 Array.prototype[123] = 'baz';
609 a[123] = 'xyz';
610 assertEquals('xyz', a[123]);
611 c = a.concat(b);
612 assertEquals('xyz', c[123]);
613
614 // Non-numeric properties on the prototype or the array shouldn't get
615 // copied.
616 Array.prototype.moe = 'joe';
617 a.ben = 'jerry';
618 assertEquals(a["moe"], 'joe');
619 assertEquals(a["ben"], 'jerry');
620 c = a.concat(b);
621 // ben was not copied
622 assertEquals("undefined", typeof(c.ben));
623 // moe was not copied, but we can see it through the prototype
624 assertEquals("joe", c.moe);
625
626 // When we take moe off the prototype it disappears from all arrays.
627 Array.prototype.moe = undefined;
628 assertEquals("undefined", typeof(c.moe));
629
630 // Negative indices don't get concated.
631 a[-1] = 'minus1';
632 assertEquals("minus1", a[-1]);
633 assertEquals("undefined", typeof(a[0xffffffff]));
634 c = a.concat(b);
635 assertEquals("undefined", typeof(c[-1]));
636 assertEquals("undefined", typeof(c[0xffffffff]));
637 assertEquals(c.length, a.length + 1);
638
639 }
640
641 a = [];
642 c = a.concat('Hello');
643 assertEquals(1, c.length);
644 assertEquals("Hello", c[0]);
645 assertEquals("Hello", c.toString());
646
647 // Check that concat preserves holes.
648 var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0])
649 assertEquals(9, holey.length); // hole in embedded array is ignored
650 for (var i = 0; i < holey.length; i++) {
651 if (i == 2 || i == 5) {
652 assertFalse(i in holey);
653 } else {
654 assertTrue(i in holey);
655 }
656 }
657
658 // Polluted prototype from prior tests.
659 delete Array.prototype[123];
660
661 // Check that concat reads getters in the correct order.
662 var arr1 = [,2];
663 var arr2 = [1,3];
664 var r1 = [].concat(arr1, arr2); // [,2,1,3]
665 assertEquals([,2,1,3], r1);
666
667 // Make first array change length of second array.
668 Object.defineProperty(arr1, 0, {get: function() {
669 arr2.push("X");
670 return undefined;
671 }, configurable: true})
672 var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"]
673 assertEquals([undefined,2,1,3,"X"], r2);
674
675 // Make first array change length of second array massively.
676 arr2.length = 2;
677 Object.defineProperty(arr1, 0, {get: function() {
678 arr2[500000] = "X";
679 return undefined;
680 }, configurable: true})
681 var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"]
682 var expected = [undefined,2,1,3];
683 expected[500000 + 2] = "X";
684
685 assertEquals(expected, r3);
686
687 var arr3 = [];
688 var trace = [];
689 var expectedTrace = []
690 function mkGetter(i) { return function() { trace.push(i); }; }
691 arr3.length = 10000;
692 for (var i = 0; i < 100; i++) {
693 Object.defineProperty(arr3, i * i, {get: mkGetter(i)});
694 expectedTrace[i] = i;
695 expectedTrace[100 + i] = i;
696 }
697 var r4 = [0].concat(arr3, arr3);
698 assertEquals(1 + arr3.length * 2, r4.length);
699 assertEquals(expectedTrace, trace);
Ben Murdoch014dc512016-03-22 12:00:34 +0000700
701 // Clean up.
702 delete Array.prototype[123];
703 delete Array.prototype["123"];
704 delete Array.prototype["moe"];
705})();
706
707
708
709
710////////////////////////////////////////////////////////////////////////////////
711// Tests with proxies
712
713// Note: concat does not currently support species so there is no difference
714// between [].concat(foo) and Array.prototype.concat.apply(foo).
715
716
717var log = [];
718var logger = {};
719var handler = new Proxy({}, logger);
720
721logger.get = function(t, trap, r) {
722 return function(...args) {
723 log.push([trap, ...args]);
724 return Reflect[trap](...args);
725 }
726};
727
728
729(function testUnspreadableNonArrayLikeProxy() {
730 var target = {0: "a", 1: "b"};
731 var obj = new Proxy(target, handler);
732
733 log.length = 0;
734 assertEquals([obj], [].concat(obj));
735 assertEquals(1, log.length);
736 for (var i in log) assertSame(target, log[i][1]);
737 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
738
739 log.length = 0;
740 assertEquals([obj], Array.prototype.concat.apply(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
746
747(function testSpreadableNonArrayLikeProxy() {
748 var target = {0: "a", 1: "b", [Symbol.isConcatSpreadable]: "truish"};
749 var obj = new Proxy(target, handler);
750
751 log.length = 0;
752 assertEquals([], [].concat(obj));
753 assertEquals(2, log.length);
754 for (var i in log) assertSame(target, log[i][1]);
755 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
756 assertEquals(["get", target, "length", obj], log[1]);
757
758 log.length = 0;
759 assertEquals([], Array.prototype.concat.apply(obj));
760 assertEquals(2, log.length);
761 for (var i in log) assertSame(target, log[i][1]);
762 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
763 assertEquals(["get", target, "length", obj], log[1]);
764
765 target.length = 3;
766
767 log.length = 0;
768 assertEquals(["a", "b", undefined], [].concat(obj));
769 assertEquals(7, log.length);
770 for (var i in log) assertSame(target, log[i][1]);
771 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
772 assertEquals(["get", target, "length", obj], log[1]);
773 assertEquals(["has", target, "0"], log[2]);
774 assertEquals(["get", target, "0", obj], log[3]);
775 assertEquals(["has", target, "1"], log[4]);
776 assertEquals(["get", target, "1", obj], log[5]);
777 assertEquals(["has", target, "2"], log[6]);
778
779 log.length = 0;
780 assertEquals(["a", "b", undefined], Array.prototype.concat.apply(obj));
781 assertEquals(7, log.length);
782 for (var i in log) assertSame(target, log[i][1]);
783 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
784 assertEquals(["get", target, "length", obj], log[1]);
785 assertEquals(["has", target, "0"], log[2]);
786 assertEquals(["get", target, "0", obj], log[3]);
787 assertEquals(["has", target, "1"], log[4]);
788 assertEquals(["get", target, "1", obj], log[5]);
789 assertEquals(["has", target, "2"], log[6]);
790})();
791
792
793(function testUnspreadableArrayLikeProxy() {
794 var target = ["a", "b"];
795 target[Symbol.isConcatSpreadable] = "";
796 var obj = new Proxy(target, handler);
797
798 log.length = 0;
799 assertEquals([obj], [].concat(obj));
800 assertEquals(1, log.length);
801 for (var i in log) assertSame(target, log[i][1]);
802 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
803
804 log.length = 0;
805 assertEquals([obj], Array.prototype.concat.apply(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
811
812(function testSpreadableArrayLikeProxy() {
813 var target = ["a", "b"];
814 target[Symbol.isConcatSpreadable] = undefined;
815 var obj = new Proxy(target, handler);
816
817 log.length = 0;
818 assertEquals(["a", "b"], [].concat(obj));
819 assertEquals(6, log.length);
820 for (var i in log) assertSame(target, log[i][1]);
821 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
822 assertEquals(["get", target, "length", obj], log[1]);
823 assertEquals(["has", target, "0"], log[2]);
824 assertEquals(["get", target, "0", obj], log[3]);
825 assertEquals(["has", target, "1"], log[4]);
826 assertEquals(["get", target, "1", obj], log[5]);
827
828 log.length = 0;
829 assertEquals(["a", "b"], Array.prototype.concat.apply(obj));
830 assertEquals(6, log.length);
831 for (var i in log) assertSame(target, log[i][1]);
832 assertEquals(["get", target, Symbol.isConcatSpreadable, obj], log[0]);
833 assertEquals(["get", target, "length", obj], log[1]);
834 assertEquals(["has", target, "0"], log[2]);
835 assertEquals(["get", target, "0", obj], log[3]);
836 assertEquals(["has", target, "1"], log[4]);
837 assertEquals(["get", target, "1", obj], log[5]);
838})();
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})();