Home | History | Annotate | Download | only in es6
      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 };
    146 obj[Symbol.isConcatSpreadable] = true;
    147 assertThrows(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 
    248 function 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 
    723 var log = [];
    724 var logger = {};
    725 var handler = new Proxy({}, logger);
    726 
    727 logger.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