blob: 286aefd29a5c3d3933ab5c25ab4f322434e2eaaf [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
5// Flags: --harmony-arrays --harmony-classes
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};
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";
197 // TODO(caitp): when concat is called on instances of classes which extend
198 // Array, they should:
199 //
200 // - return an instance of the class, rather than an Array instance (if from
201 // same Realm)
202 // - always treat such classes as concat-spreadable
203})();
204
205
206(function testConcatNonArray() {
207 "use strict";
208 class NonArray {
209 constructor() { Array.apply(this, arguments); }
210 };
211
212 var obj = new NonArray(1,2,3);
213 var result = Array.prototype.concat.call(obj, 4, 5, 6);
214 assertEquals(Array, result.constructor);
215 assertEquals([obj,4,5,6], result);
216 assertFalse(result instanceof NonArray);
217})();
218
219
220function testConcatTypedArray(type, elems, modulo) {
221 "use strict";
222 var items = new Array(elems);
223 var ta_by_len = new type(elems);
224 for (var i = 0; i < elems; ++i) {
225 ta_by_len[i] = items[i] = modulo === false ? i : elems % modulo;
226 }
227 var ta = new type(items);
228 assertEquals([ta, ta], [].concat(ta, ta));
229 ta[Symbol.isConcatSpreadable] = true;
230 assertEquals(items, [].concat(ta));
231
232 assertEquals([ta_by_len, ta_by_len], [].concat(ta_by_len, ta_by_len));
233 ta_by_len[Symbol.isConcatSpreadable] = true;
234 assertEquals(items, [].concat(ta_by_len));
235
236 // TypedArray with fake `length`.
237 ta = new type(1);
238 var defValue = ta[0];
239 var expected = new Array(4000);
240 expected[0] = defValue;
241
242 Object.defineProperty(ta, "length", { value: 4000 });
243 ta[Symbol.isConcatSpreadable] = true;
244 assertEquals(expected, [].concat(ta));
245}
246
247(function testConcatSmallTypedArray() {
248 var max = [2^8, 2^16, 2^32, false, false];
249 [
250 Uint8Array,
251 Uint16Array,
252 Uint32Array,
253 Float32Array,
254 Float64Array
255 ].forEach(function(ctor, i) {
256 testConcatTypedArray(ctor, 1, max[i]);
257 });
258})();
259
260
261(function testConcatLargeTypedArray() {
262 var max = [2^8, 2^16, 2^32, false, false];
263 [
264 Uint8Array,
265 Uint16Array,
266 Uint32Array,
267 Float32Array,
268 Float64Array
269 ].forEach(function(ctor, i) {
270 testConcatTypedArray(ctor, 4000, max[i]);
271 });
272})();
273
274
275(function testConcatStrictArguments() {
276 var args = (function(a, b, c) { "use strict"; return arguments; })(1,2,3);
277 args[Symbol.isConcatSpreadable] = true;
278 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args));
279
280 Object.defineProperty(args, "length", { value: 6 });
281 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args));
282})();
283
284
285(function testConcatSloppyArguments() {
286 var args = (function(a, b, c) { return arguments; })(1,2,3);
287 args[Symbol.isConcatSpreadable] = true;
288 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args));
289
290 Object.defineProperty(args, "length", { value: 6 });
291 assertEquals([1, 2, 3, void 0, void 0, void 0], [].concat(args));
292})();
293
294
295(function testConcatSloppyArgumentsWithDupes() {
296 var args = (function(a, a, a) { 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 testConcatSloppyArgumentsThrows() {
306 function MyError() {}
307 var args = (function(a) { return arguments; })(1,2,3);
308 Object.defineProperty(args, 0, {
309 get: function() { throw new MyError(); }
310 });
311 args[Symbol.isConcatSpreadable] = true;
312 assertThrows(function() {
313 return [].concat(args, args);
314 }, MyError);
315})();
316
317
318(function testConcatHoleySloppyArguments() {
319 var args = (function(a) { return arguments; })(1,2,3);
320 delete args[1];
321 args[Symbol.isConcatSpreadable] = true;
322 assertEquals([1, void 0, 3, 1, void 0, 3], [].concat(args, args));
323})();
324
325
326(function testConcatSpreadableStringWrapper() {
327 "use strict";
328 var str1 = new String("yuck\uD83D\uDCA9")
329 // String wrapper objects are not concat-spreadable by default
330 assertEquals([str1], [].concat(str1));
331
332 // String wrapper objects may be individually concat-spreadable
333 str1[Symbol.isConcatSpreadable] = true;
334 assertEquals(["y", "u", "c", "k", "\uD83D", "\uDCA9"],
335 [].concat(str1));
336
337 String.prototype[Symbol.isConcatSpreadable] = true;
338 // String wrapper objects may be concat-spreadable
339 assertEquals(["y", "u", "c", "k", "\uD83D", "\uDCA9"],
340 [].concat(new String("yuck\uD83D\uDCA9")));
341
342 // String values are never concat-spreadable
343 assertEquals(["yuck\uD83D\uDCA9"], [].concat("yuck\uD83D\uDCA9"));
344 delete String.prototype[Symbol.isConcatSpreadable];
345})();
346
347
348(function testConcatSpreadableBooleanWrapper() {
349 "use strict";
350 var bool = new Boolean(true)
351 // Boolean wrapper objects are not concat-spreadable by default
352 assertEquals([bool], [].concat(bool));
353
354 // Boolean wrapper objects may be individually concat-spreadable
355 bool[Symbol.isConcatSpreadable] = true;
356 bool.length = 3;
357 bool[0] = 1, bool[1] = 2, bool[2] = 3;
358 assertEquals([1, 2, 3], [].concat(bool));
359
360 Boolean.prototype[Symbol.isConcatSpreadable] = true;
361 // Boolean wrapper objects may be concat-spreadable
362 assertEquals([], [].concat(new Boolean(true)));
363 Boolean.prototype[0] = 1;
364 Boolean.prototype[1] = 2;
365 Boolean.prototype[2] = 3;
366 Boolean.prototype.length = 3;
367 assertEquals([1,2,3], [].concat(new Boolean(true)));
368
369 // Boolean values are never concat-spreadable
370 assertEquals([true], [].concat(true));
371 delete Boolean.prototype[Symbol.isConcatSpreadable];
372 delete Boolean.prototype[0];
373 delete Boolean.prototype[1];
374 delete Boolean.prototype[2];
375 delete Boolean.prototype.length;
376})();
377
378
379(function testConcatSpreadableNumberWrapper() {
380 "use strict";
381 var num = new Number(true)
382 // Number wrapper objects are not concat-spreadable by default
383 assertEquals([num], [].concat(num));
384
385 // Number wrapper objects may be individually concat-spreadable
386 num[Symbol.isConcatSpreadable] = true;
387 num.length = 3;
388 num[0] = 1, num[1] = 2, num[2] = 3;
389 assertEquals([1, 2, 3], [].concat(num));
390
391 Number.prototype[Symbol.isConcatSpreadable] = true;
392 // Number wrapper objects may be concat-spreadable
393 assertEquals([], [].concat(new Number(123)));
394 Number.prototype[0] = 1;
395 Number.prototype[1] = 2;
396 Number.prototype[2] = 3;
397 Number.prototype.length = 3;
398 assertEquals([1,2,3], [].concat(new Number(123)));
399
400 // Number values are never concat-spreadable
401 assertEquals([true], [].concat(true));
402 delete Number.prototype[Symbol.isConcatSpreadable];
403 delete Number.prototype[0];
404 delete Number.prototype[1];
405 delete Number.prototype[2];
406 delete Number.prototype.length;
407})();
408
409
410(function testConcatSpreadableFunction() {
411 "use strict";
412 var fn = function(a, b, c) {}
413 // Functions are not concat-spreadable by default
414 assertEquals([fn], [].concat(fn));
415
416 // Functions may be individually concat-spreadable
417 fn[Symbol.isConcatSpreadable] = true;
418 fn[0] = 1, fn[1] = 2, fn[2] = 3;
419 assertEquals([1, 2, 3], [].concat(fn));
420
421 Function.prototype[Symbol.isConcatSpreadable] = true;
422 // Functions may be concat-spreadable
423 assertEquals([void 0, void 0, void 0], [].concat(function(a,b,c) {}));
424 Function.prototype[0] = 1;
425 Function.prototype[1] = 2;
426 Function.prototype[2] = 3;
427 assertEquals([1,2,3], [].concat(function(a, b, c) {}));
428
429 delete Function.prototype[Symbol.isConcatSpreadable];
430 delete Function.prototype[0];
431 delete Function.prototype[1];
432 delete Function.prototype[2];
433})();
434
435
436(function testConcatSpreadableRegExp() {
437 "use strict";
438 var re = /abc/;
439 // RegExps are not concat-spreadable by default
440 assertEquals([re], [].concat(re));
441
442 // RegExps may be individually concat-spreadable
443 re[Symbol.isConcatSpreadable] = true;
444 re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3;
445 assertEquals([1, 2, 3], [].concat(re));
446
447 // RegExps may be concat-spreadable
448 RegExp.prototype[Symbol.isConcatSpreadable] = true;
449 RegExp.prototype.length = 3;
450
451 assertEquals([void 0, void 0, void 0], [].concat(/abc/));
452 RegExp.prototype[0] = 1;
453 RegExp.prototype[1] = 2;
454 RegExp.prototype[2] = 3;
455 assertEquals([1,2,3], [].concat(/abc/));
456
457 delete RegExp.prototype[Symbol.isConcatSpreadable];
458 delete RegExp.prototype[0];
459 delete RegExp.prototype[1];
460 delete RegExp.prototype[2];
461 delete RegExp.prototype.length;
462})();
463
464
465(function testArrayConcatSpreadableSparseObject() {
466 "use strict";
467 var obj = { length: 5 };
468 obj[Symbol.isConcatSpreadable] = true;
469 assertEquals([void 0, void 0, void 0, void 0, void 0], [].concat(obj));
470
471 obj.length = 4000;
472 assertEquals(new Array(4000), [].concat(obj));
473})();
474
475
476// ES5 tests
477(function testArrayConcatES5() {
478 "use strict";
479 var poses;
480 var pos;
481
482 poses = [140, 4000000000];
483 while (pos = poses.shift()) {
484 var a = new Array(pos);
485 var array_proto = [];
486 a.__proto__ = array_proto;
487 assertEquals(pos, a.length);
488 a.push('foo');
489 assertEquals(pos + 1, a.length);
490 var b = ['bar'];
491 var c = a.concat(b);
492 assertEquals(pos + 2, c.length);
493 assertEquals("undefined", typeof(c[pos - 1]));
494 assertEquals("foo", c[pos]);
495 assertEquals("bar", c[pos + 1]);
496
497 // Can we fool the system by putting a number in a string?
498 var onetwofour = "124";
499 a[onetwofour] = 'doo';
500 assertEquals(a[124], 'doo');
501 c = a.concat(b);
502 assertEquals(c[124], 'doo');
503
504 // If we put a number in the prototype, then the spec says it should be
505 // copied on concat.
506 array_proto["123"] = 'baz';
507 assertEquals(a[123], 'baz');
508
509 c = a.concat(b);
510 assertEquals(pos + 2, c.length);
511 assertEquals("baz", c[123]);
512 assertEquals("undefined", typeof(c[pos - 1]));
513 assertEquals("foo", c[pos]);
514 assertEquals("bar", c[pos + 1]);
515
516 // When we take the number off the prototype it disappears from a, but
517 // the concat put it in c itself.
518 array_proto["123"] = undefined;
519 assertEquals("undefined", typeof(a[123]));
520 assertEquals("baz", c[123]);
521
522 // If the element of prototype is shadowed, the element on the instance
523 // should be copied, but not the one on the prototype.
524 array_proto[123] = 'baz';
525 a[123] = 'xyz';
526 assertEquals('xyz', a[123]);
527 c = a.concat(b);
528 assertEquals('xyz', c[123]);
529
530 // Non-numeric properties on the prototype or the array shouldn't get
531 // copied.
532 array_proto.moe = 'joe';
533 a.ben = 'jerry';
534 assertEquals(a["moe"], 'joe');
535 assertEquals(a["ben"], 'jerry');
536 c = a.concat(b);
537 // ben was not copied
538 assertEquals("undefined", typeof(c.ben));
539
540 // When we take moe off the prototype it disappears from all arrays.
541 array_proto.moe = undefined;
542 assertEquals("undefined", typeof(c.moe));
543
544 // Negative indices don't get concated.
545 a[-1] = 'minus1';
546 assertEquals("minus1", a[-1]);
547 assertEquals("undefined", typeof(a[0xffffffff]));
548 c = a.concat(b);
549 assertEquals("undefined", typeof(c[-1]));
550 assertEquals("undefined", typeof(c[0xffffffff]));
551 assertEquals(c.length, a.length + 1);
552 }
553
554 poses = [140, 4000000000];
555 while (pos = poses.shift()) {
556 var a = new Array(pos);
557 assertEquals(pos, a.length);
558 a.push('foo');
559 assertEquals(pos + 1, a.length);
560 var b = ['bar'];
561 var c = a.concat(b);
562 assertEquals(pos + 2, c.length);
563 assertEquals("undefined", typeof(c[pos - 1]));
564 assertEquals("foo", c[pos]);
565 assertEquals("bar", c[pos + 1]);
566
567 // Can we fool the system by putting a number in a string?
568 var onetwofour = "124";
569 a[onetwofour] = 'doo';
570 assertEquals(a[124], 'doo');
571 c = a.concat(b);
572 assertEquals(c[124], 'doo');
573
574 // If we put a number in the prototype, then the spec says it should be
575 // copied on concat.
576 Array.prototype["123"] = 'baz';
577 assertEquals(a[123], 'baz');
578
579 c = a.concat(b);
580 assertEquals(pos + 2, c.length);
581 assertEquals("baz", c[123]);
582 assertEquals("undefined", typeof(c[pos - 1]));
583 assertEquals("foo", c[pos]);
584 assertEquals("bar", c[pos + 1]);
585
586 // When we take the number off the prototype it disappears from a, but
587 // the concat put it in c itself.
588 Array.prototype["123"] = undefined;
589 assertEquals("undefined", typeof(a[123]));
590 assertEquals("baz", c[123]);
591
592 // If the element of prototype is shadowed, the element on the instance
593 // should be copied, but not the one on the prototype.
594 Array.prototype[123] = 'baz';
595 a[123] = 'xyz';
596 assertEquals('xyz', a[123]);
597 c = a.concat(b);
598 assertEquals('xyz', c[123]);
599
600 // Non-numeric properties on the prototype or the array shouldn't get
601 // copied.
602 Array.prototype.moe = 'joe';
603 a.ben = 'jerry';
604 assertEquals(a["moe"], 'joe');
605 assertEquals(a["ben"], 'jerry');
606 c = a.concat(b);
607 // ben was not copied
608 assertEquals("undefined", typeof(c.ben));
609 // moe was not copied, but we can see it through the prototype
610 assertEquals("joe", c.moe);
611
612 // When we take moe off the prototype it disappears from all arrays.
613 Array.prototype.moe = undefined;
614 assertEquals("undefined", typeof(c.moe));
615
616 // Negative indices don't get concated.
617 a[-1] = 'minus1';
618 assertEquals("minus1", a[-1]);
619 assertEquals("undefined", typeof(a[0xffffffff]));
620 c = a.concat(b);
621 assertEquals("undefined", typeof(c[-1]));
622 assertEquals("undefined", typeof(c[0xffffffff]));
623 assertEquals(c.length, a.length + 1);
624
625 }
626
627 a = [];
628 c = a.concat('Hello');
629 assertEquals(1, c.length);
630 assertEquals("Hello", c[0]);
631 assertEquals("Hello", c.toString());
632
633 // Check that concat preserves holes.
634 var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0])
635 assertEquals(9, holey.length); // hole in embedded array is ignored
636 for (var i = 0; i < holey.length; i++) {
637 if (i == 2 || i == 5) {
638 assertFalse(i in holey);
639 } else {
640 assertTrue(i in holey);
641 }
642 }
643
644 // Polluted prototype from prior tests.
645 delete Array.prototype[123];
646
647 // Check that concat reads getters in the correct order.
648 var arr1 = [,2];
649 var arr2 = [1,3];
650 var r1 = [].concat(arr1, arr2); // [,2,1,3]
651 assertEquals([,2,1,3], r1);
652
653 // Make first array change length of second array.
654 Object.defineProperty(arr1, 0, {get: function() {
655 arr2.push("X");
656 return undefined;
657 }, configurable: true})
658 var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"]
659 assertEquals([undefined,2,1,3,"X"], r2);
660
661 // Make first array change length of second array massively.
662 arr2.length = 2;
663 Object.defineProperty(arr1, 0, {get: function() {
664 arr2[500000] = "X";
665 return undefined;
666 }, configurable: true})
667 var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"]
668 var expected = [undefined,2,1,3];
669 expected[500000 + 2] = "X";
670
671 assertEquals(expected, r3);
672
673 var arr3 = [];
674 var trace = [];
675 var expectedTrace = []
676 function mkGetter(i) { return function() { trace.push(i); }; }
677 arr3.length = 10000;
678 for (var i = 0; i < 100; i++) {
679 Object.defineProperty(arr3, i * i, {get: mkGetter(i)});
680 expectedTrace[i] = i;
681 expectedTrace[100 + i] = i;
682 }
683 var r4 = [0].concat(arr3, arr3);
684 assertEquals(1 + arr3.length * 2, r4.length);
685 assertEquals(expectedTrace, trace);
686})();