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