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