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