| OLD | NEW |
| (Empty) |
| 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 }; | |
| 140 obj[Symbol.isConcatSpreadable] = true; | |
| 141 assertThrows(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 | |
| 242 function 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 | |
| 725 var log = []; | |
| 726 var logger = {}; | |
| 727 var handler = new Proxy({}, logger); | |
| 728 | |
| 729 logger.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 })(); | |
| OLD | NEW |