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