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-arrays --harmony-classes |
| 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 testConcatHoleyArray() { |
| 44 "use strict"; |
| 45 var arr = []; |
| 46 arr[4] = "Item 4"; |
| 47 arr[8] = "Item 8"; |
| 48 var arr2 = [".", "!", "?"]; |
| 49 assertEquals([void 0, void 0, void 0, void 0, "Item 4", void 0, void 0, |
| 50 void 0, "Item 8", ".", "!", "?"], arr.concat(arr2)); |
| 51 })(); |
| 52 |
| 53 |
| 54 (function testIsConcatSpreadableGetterThrows() { |
| 55 "use strict"; |
| 56 function MyError() {} |
| 57 var obj = {}; |
| 58 Object.defineProperty(obj, Symbol.isConcatSpreadable, { |
| 59 get: function() { throw new MyError(); } |
| 60 }); |
| 61 |
| 62 assertThrows(function() { |
| 63 [].concat(obj); |
| 64 }, MyError); |
| 65 |
| 66 assertThrows(function() { |
| 67 Array.prototype.concat.call(obj, 1, 2, 3); |
| 68 }, MyError); |
| 69 })(); |
| 70 |
| 71 |
| 72 (function testConcatLengthThrows() { |
| 73 "use strict"; |
| 74 function MyError() {} |
| 75 var obj = {}; |
| 76 obj[Symbol.isConcatSpreadable] = true; |
| 77 Object.defineProperty(obj, "length", { |
| 78 get: function() { throw new MyError(); } |
| 79 }); |
| 80 |
| 81 assertThrows(function() { |
| 82 [].concat(obj); |
| 83 }, MyError); |
| 84 |
| 85 assertThrows(function() { |
| 86 Array.prototype.concat.call(obj, 1, 2, 3); |
| 87 }, MyError); |
| 88 })(); |
| 89 |
| 90 |
| 91 (function testConcatArraySubclass() { |
| 92 "use strict"; |
| 93 // TODO(caitp): when concat is called on instances of classes which extend |
| 94 // Array, they should: |
| 95 // |
| 96 // - return an instance of the class, rather than an Array instance (if from |
| 97 // same Realm) |
| 98 // - always treat such classes as concat-spreadable |
| 99 })(); |
| 100 |
| 101 |
| 102 (function testConcatNonArray() { |
| 103 "use strict"; |
| 104 class NonArray { |
| 105 constructor() { Array.apply(this, arguments); } |
| 106 }; |
| 107 |
| 108 var obj = new NonArray(1,2,3); |
| 109 var result = Array.prototype.concat.call(obj, 4, 5, 6); |
| 110 assertEquals(Array, result.constructor); |
| 111 assertEquals([obj,4,5,6], result); |
| 112 assertFalse(result instanceof NonArray); |
| 113 })(); |
| 114 |
| 115 |
| 116 function testConcatTypedArray(type, elems, modulo) { |
| 117 "use strict"; |
| 118 var items = new Array(elems); |
| 119 var ta_by_len = new type(elems); |
| 120 for (var i = 0; i < elems; ++i) { |
| 121 ta_by_len[i] = items[i] = modulo === false ? i : elems % modulo; |
| 122 } |
| 123 var ta = new type(items); |
| 124 assertEquals([ta, ta], [].concat(ta, ta)); |
| 125 ta[Symbol.isConcatSpreadable] = true; |
| 126 assertEquals(items, [].concat(ta)); |
| 127 |
| 128 assertEquals([ta_by_len, ta_by_len], [].concat(ta_by_len, ta_by_len)); |
| 129 ta_by_len[Symbol.isConcatSpreadable] = true; |
| 130 assertEquals(items, [].concat(ta_by_len)); |
| 131 } |
| 132 |
| 133 (function testConcatSmallTypedArray() { |
| 134 var max = [2^8, 2^16, 2^32, false, false]; |
| 135 [ |
| 136 Uint8Array, |
| 137 Uint16Array, |
| 138 Uint32Array, |
| 139 Float32Array, |
| 140 Float64Array |
| 141 ].forEach(function(ctor, i) { |
| 142 testConcatTypedArray(ctor, 1, max[i]); |
| 143 }); |
| 144 })(); |
| 145 |
| 146 |
| 147 (function testConcatLargeTypedArray() { |
| 148 var max = [2^8, 2^16, 2^32, false, false]; |
| 149 [ |
| 150 Uint8Array, |
| 151 Uint16Array, |
| 152 Uint32Array, |
| 153 Float32Array, |
| 154 Float64Array |
| 155 ].forEach(function(ctor, i) { |
| 156 testConcatTypedArray(ctor, 4000, max[i]); |
| 157 }); |
| 158 })(); |
| 159 |
| 160 |
| 161 (function testConcatStrictArguments() { |
| 162 var args = (function(a, b, c) { "use strict"; return arguments; })(1,2,3); |
| 163 args[Symbol.isConcatSpreadable] = true; |
| 164 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args)); |
| 165 })(); |
| 166 |
| 167 |
| 168 (function testConcatSloppyArguments() { |
| 169 var args = (function(a, b, c) { return arguments; })(1,2,3); |
| 170 args[Symbol.isConcatSpreadable] = true; |
| 171 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args)); |
| 172 })(); |
| 173 |
| 174 |
| 175 (function testConcatSloppyArgumentsWithDupes() { |
| 176 var args = (function(a, a, a) { return arguments; })(1,2,3); |
| 177 args[Symbol.isConcatSpreadable] = true; |
| 178 assertEquals([1, 2, 3, 1, 2, 3], [].concat(args, args)); |
| 179 })(); |
| 180 |
| 181 |
| 182 (function testConcatSloppyArgumentsThrows() { |
| 183 function MyError() {} |
| 184 var args = (function(a) { return arguments; })(1,2,3); |
| 185 Object.defineProperty(args, 0, { |
| 186 get: function() { throw new MyError(); } |
| 187 }); |
| 188 args[Symbol.isConcatSpreadable] = true; |
| 189 assertThrows(function() { |
| 190 return [].concat(args, args); |
| 191 }, MyError); |
| 192 })(); |
| 193 |
| 194 |
| 195 (function testConcatHoleySloppyArguments() { |
| 196 var args = (function(a) { return arguments; })(1,2,3); |
| 197 delete args[1]; |
| 198 args[Symbol.isConcatSpreadable] = true; |
| 199 assertEquals([1, void 0, 3, 1, void 0, 3], [].concat(args, args)); |
| 200 })(); |
| 201 |
| 202 |
| 203 // ES5 tests |
| 204 (function testArrayConcatES5() { |
| 205 "use strict"; |
| 206 var poses; |
| 207 var pos; |
| 208 |
| 209 poses = [140, 4000000000]; |
| 210 while (pos = poses.shift()) { |
| 211 var a = new Array(pos); |
| 212 var array_proto = []; |
| 213 a.__proto__ = array_proto; |
| 214 assertEquals(pos, a.length); |
| 215 a.push('foo'); |
| 216 assertEquals(pos + 1, a.length); |
| 217 var b = ['bar']; |
| 218 var c = a.concat(b); |
| 219 assertEquals(pos + 2, c.length); |
| 220 assertEquals("undefined", typeof(c[pos - 1])); |
| 221 assertEquals("foo", c[pos]); |
| 222 assertEquals("bar", c[pos + 1]); |
| 223 |
| 224 // Can we fool the system by putting a number in a string? |
| 225 var onetwofour = "124"; |
| 226 a[onetwofour] = 'doo'; |
| 227 assertEquals(a[124], 'doo'); |
| 228 c = a.concat(b); |
| 229 assertEquals(c[124], 'doo'); |
| 230 |
| 231 // If we put a number in the prototype, then the spec says it should be |
| 232 // copied on concat. |
| 233 array_proto["123"] = 'baz'; |
| 234 assertEquals(a[123], 'baz'); |
| 235 |
| 236 c = a.concat(b); |
| 237 assertEquals(pos + 2, c.length); |
| 238 assertEquals("baz", c[123]); |
| 239 assertEquals("undefined", typeof(c[pos - 1])); |
| 240 assertEquals("foo", c[pos]); |
| 241 assertEquals("bar", c[pos + 1]); |
| 242 |
| 243 // When we take the number off the prototype it disappears from a, but |
| 244 // the concat put it in c itself. |
| 245 array_proto["123"] = undefined; |
| 246 assertEquals("undefined", typeof(a[123])); |
| 247 assertEquals("baz", c[123]); |
| 248 |
| 249 // If the element of prototype is shadowed, the element on the instance |
| 250 // should be copied, but not the one on the prototype. |
| 251 array_proto[123] = 'baz'; |
| 252 a[123] = 'xyz'; |
| 253 assertEquals('xyz', a[123]); |
| 254 c = a.concat(b); |
| 255 assertEquals('xyz', c[123]); |
| 256 |
| 257 // Non-numeric properties on the prototype or the array shouldn't get |
| 258 // copied. |
| 259 array_proto.moe = 'joe'; |
| 260 a.ben = 'jerry'; |
| 261 assertEquals(a["moe"], 'joe'); |
| 262 assertEquals(a["ben"], 'jerry'); |
| 263 c = a.concat(b); |
| 264 // ben was not copied |
| 265 assertEquals("undefined", typeof(c.ben)); |
| 266 |
| 267 // When we take moe off the prototype it disappears from all arrays. |
| 268 array_proto.moe = undefined; |
| 269 assertEquals("undefined", typeof(c.moe)); |
| 270 |
| 271 // Negative indices don't get concated. |
| 272 a[-1] = 'minus1'; |
| 273 assertEquals("minus1", a[-1]); |
| 274 assertEquals("undefined", typeof(a[0xffffffff])); |
| 275 c = a.concat(b); |
| 276 assertEquals("undefined", typeof(c[-1])); |
| 277 assertEquals("undefined", typeof(c[0xffffffff])); |
| 278 assertEquals(c.length, a.length + 1); |
| 279 } |
| 280 |
| 281 poses = [140, 4000000000]; |
| 282 while (pos = poses.shift()) { |
| 283 var a = new Array(pos); |
| 284 assertEquals(pos, a.length); |
| 285 a.push('foo'); |
| 286 assertEquals(pos + 1, a.length); |
| 287 var b = ['bar']; |
| 288 var c = a.concat(b); |
| 289 assertEquals(pos + 2, c.length); |
| 290 assertEquals("undefined", typeof(c[pos - 1])); |
| 291 assertEquals("foo", c[pos]); |
| 292 assertEquals("bar", c[pos + 1]); |
| 293 |
| 294 // Can we fool the system by putting a number in a string? |
| 295 var onetwofour = "124"; |
| 296 a[onetwofour] = 'doo'; |
| 297 assertEquals(a[124], 'doo'); |
| 298 c = a.concat(b); |
| 299 assertEquals(c[124], 'doo'); |
| 300 |
| 301 // If we put a number in the prototype, then the spec says it should be |
| 302 // copied on concat. |
| 303 Array.prototype["123"] = 'baz'; |
| 304 assertEquals(a[123], 'baz'); |
| 305 |
| 306 c = a.concat(b); |
| 307 assertEquals(pos + 2, c.length); |
| 308 assertEquals("baz", c[123]); |
| 309 assertEquals("undefined", typeof(c[pos - 1])); |
| 310 assertEquals("foo", c[pos]); |
| 311 assertEquals("bar", c[pos + 1]); |
| 312 |
| 313 // When we take the number off the prototype it disappears from a, but |
| 314 // the concat put it in c itself. |
| 315 Array.prototype["123"] = undefined; |
| 316 assertEquals("undefined", typeof(a[123])); |
| 317 assertEquals("baz", c[123]); |
| 318 |
| 319 // If the element of prototype is shadowed, the element on the instance |
| 320 // should be copied, but not the one on the prototype. |
| 321 Array.prototype[123] = 'baz'; |
| 322 a[123] = 'xyz'; |
| 323 assertEquals('xyz', a[123]); |
| 324 c = a.concat(b); |
| 325 assertEquals('xyz', c[123]); |
| 326 |
| 327 // Non-numeric properties on the prototype or the array shouldn't get |
| 328 // copied. |
| 329 Array.prototype.moe = 'joe'; |
| 330 a.ben = 'jerry'; |
| 331 assertEquals(a["moe"], 'joe'); |
| 332 assertEquals(a["ben"], 'jerry'); |
| 333 c = a.concat(b); |
| 334 // ben was not copied |
| 335 assertEquals("undefined", typeof(c.ben)); |
| 336 // moe was not copied, but we can see it through the prototype |
| 337 assertEquals("joe", c.moe); |
| 338 |
| 339 // When we take moe off the prototype it disappears from all arrays. |
| 340 Array.prototype.moe = undefined; |
| 341 assertEquals("undefined", typeof(c.moe)); |
| 342 |
| 343 // Negative indices don't get concated. |
| 344 a[-1] = 'minus1'; |
| 345 assertEquals("minus1", a[-1]); |
| 346 assertEquals("undefined", typeof(a[0xffffffff])); |
| 347 c = a.concat(b); |
| 348 assertEquals("undefined", typeof(c[-1])); |
| 349 assertEquals("undefined", typeof(c[0xffffffff])); |
| 350 assertEquals(c.length, a.length + 1); |
| 351 |
| 352 } |
| 353 |
| 354 a = []; |
| 355 c = a.concat('Hello'); |
| 356 assertEquals(1, c.length); |
| 357 assertEquals("Hello", c[0]); |
| 358 assertEquals("Hello", c.toString()); |
| 359 |
| 360 // Check that concat preserves holes. |
| 361 var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0]) |
| 362 assertEquals(9, holey.length); // hole in embedded array is ignored |
| 363 for (var i = 0; i < holey.length; i++) { |
| 364 if (i == 2 || i == 5) { |
| 365 assertFalse(i in holey); |
| 366 } else { |
| 367 assertTrue(i in holey); |
| 368 } |
| 369 } |
| 370 |
| 371 // Polluted prototype from prior tests. |
| 372 delete Array.prototype[123]; |
| 373 |
| 374 // Check that concat reads getters in the correct order. |
| 375 var arr1 = [,2]; |
| 376 var arr2 = [1,3]; |
| 377 var r1 = [].concat(arr1, arr2); // [,2,1,3] |
| 378 assertEquals([,2,1,3], r1); |
| 379 |
| 380 // Make first array change length of second array. |
| 381 Object.defineProperty(arr1, 0, {get: function() { |
| 382 arr2.push("X"); |
| 383 return undefined; |
| 384 }, configurable: true}) |
| 385 var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] |
| 386 assertEquals([undefined,2,1,3,"X"], r2); |
| 387 |
| 388 // Make first array change length of second array massively. |
| 389 arr2.length = 2; |
| 390 Object.defineProperty(arr1, 0, {get: function() { |
| 391 arr2[500000] = "X"; |
| 392 return undefined; |
| 393 }, configurable: true}) |
| 394 var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] |
| 395 var expected = [undefined,2,1,3]; |
| 396 expected[500000 + 2] = "X"; |
| 397 |
| 398 assertEquals(expected, r3); |
| 399 |
| 400 var arr3 = []; |
| 401 var trace = []; |
| 402 var expectedTrace = [] |
| 403 function mkGetter(i) { return function() { trace.push(i); }; } |
| 404 arr3.length = 10000; |
| 405 for (var i = 0; i < 100; i++) { |
| 406 Object.defineProperty(arr3, i * i, {get: mkGetter(i)}); |
| 407 expectedTrace[i] = i; |
| 408 expectedTrace[100 + i] = i; |
| 409 } |
| 410 var r4 = [0].concat(arr3, arr3); |
| 411 assertEquals(1 + arr3.length * 2, r4.length); |
| 412 assertEquals(expectedTrace, trace); |
| 413 })(); |
OLD | NEW |