| // Copyright 2008 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| /** |
| * @fileoverview Test concat on small and large arrays |
| */ |
| |
| |
| (function testStringWrapperConcat() { |
| var concat = Array.prototype.concat; |
| var str = new String('abcd'); |
| assertEquals([1,2,3,new String('abcd')], [1, 2, 3].concat(str)); |
| assertEquals([new String("abcd")], concat.call(str)); |
| |
| var array = [1, 2, 3]; |
| array.__proto__ = str; |
| array.length = 4; |
| assertEquals([1,2,3,'d'], concat.call(array)); |
| })() |
| |
| var poses; |
| |
| poses = [140, 4000000000]; |
| while (pos = poses.shift()) { |
| var a = new Array(pos); |
| var array_proto = []; |
| a.__proto__ = array_proto; |
| assertEquals(pos, a.length); |
| a.push('foo'); |
| assertEquals(pos + 1, a.length); |
| var b = ['bar']; |
| var c = a.concat(b); |
| assertEquals(pos + 2, c.length); |
| assertEquals("undefined", typeof(c[pos - 1])); |
| assertEquals("foo", c[pos]); |
| assertEquals("bar", c[pos + 1]); |
| |
| // Can we fool the system by putting a number in a string? |
| var onetwofour = "124"; |
| a[onetwofour] = 'doo'; |
| assertEquals(a[124], 'doo'); |
| c = a.concat(b); |
| assertEquals(c[124], 'doo'); |
| |
| // If we put a number in the prototype, then the spec says it should be |
| // copied on concat. |
| array_proto["123"] = 'baz'; |
| assertEquals(a[123], 'baz'); |
| |
| c = a.concat(b); |
| assertEquals(pos + 2, c.length); |
| assertEquals("baz", c[123]); |
| assertEquals("undefined", typeof(c[pos - 1])); |
| assertEquals("foo", c[pos]); |
| assertEquals("bar", c[pos + 1]); |
| |
| // When we take the number off the prototype it disappears from a, but |
| // the concat put it in c itself. |
| array_proto["123"] = undefined; |
| assertEquals("undefined", typeof(a[123])); |
| assertEquals("baz", c[123]); |
| |
| // If the element of prototype is shadowed, the element on the instance |
| // should be copied, but not the one on the prototype. |
| array_proto[123] = 'baz'; |
| a[123] = 'xyz'; |
| assertEquals('xyz', a[123]); |
| c = a.concat(b); |
| assertEquals('xyz', c[123]); |
| |
| // Non-numeric properties on the prototype or the array shouldn't get |
| // copied. |
| array_proto.moe = 'joe'; |
| a.ben = 'jerry'; |
| assertEquals(a["moe"], 'joe'); |
| assertEquals(a["ben"], 'jerry'); |
| c = a.concat(b); |
| // ben was not copied |
| assertEquals("undefined", typeof(c.ben)); |
| |
| // When we take moe off the prototype it disappears from all arrays. |
| array_proto.moe = undefined; |
| assertEquals("undefined", typeof(c.moe)); |
| |
| // Negative indices don't get concated. |
| a[-1] = 'minus1'; |
| assertEquals("minus1", a[-1]); |
| assertEquals("undefined", typeof(a[0xffffffff])); |
| c = a.concat(b); |
| assertEquals("undefined", typeof(c[-1])); |
| assertEquals("undefined", typeof(c[0xffffffff])); |
| assertEquals(c.length, a.length + 1); |
| } |
| |
| poses = [140, 4000000000]; |
| while (pos = poses.shift()) { |
| var a = new Array(pos); |
| assertEquals(pos, a.length); |
| a.push('foo'); |
| assertEquals(pos + 1, a.length); |
| var b = ['bar']; |
| var c = a.concat(b); |
| assertEquals(pos + 2, c.length); |
| assertEquals("undefined", typeof(c[pos - 1])); |
| assertEquals("foo", c[pos]); |
| assertEquals("bar", c[pos + 1]); |
| |
| // Can we fool the system by putting a number in a string? |
| var onetwofour = "124"; |
| a[onetwofour] = 'doo'; |
| assertEquals(a[124], 'doo'); |
| c = a.concat(b); |
| assertEquals(c[124], 'doo'); |
| |
| // If we put a number in the prototype, then the spec says it should be |
| // copied on concat. |
| Array.prototype["123"] = 'baz'; |
| assertEquals(a[123], 'baz'); |
| |
| c = a.concat(b); |
| assertEquals(pos + 2, c.length); |
| assertEquals("baz", c[123]); |
| assertEquals("undefined", typeof(c[pos - 1])); |
| assertEquals("foo", c[pos]); |
| assertEquals("bar", c[pos + 1]); |
| |
| // When we take the number off the prototype it disappears from a, but |
| // the concat put it in c itself. |
| Array.prototype["123"] = undefined; |
| assertEquals("undefined", typeof(a[123])); |
| assertEquals("baz", c[123]); |
| |
| // If the element of prototype is shadowed, the element on the instance |
| // should be copied, but not the one on the prototype. |
| Array.prototype[123] = 'baz'; |
| a[123] = 'xyz'; |
| assertEquals('xyz', a[123]); |
| c = a.concat(b); |
| assertEquals('xyz', c[123]); |
| |
| // Non-numeric properties on the prototype or the array shouldn't get |
| // copied. |
| Array.prototype.moe = 'joe'; |
| a.ben = 'jerry'; |
| assertEquals(a["moe"], 'joe'); |
| assertEquals(a["ben"], 'jerry'); |
| c = a.concat(b); |
| // ben was not copied |
| assertEquals("undefined", typeof(c.ben)); |
| // moe was not copied, but we can see it through the prototype |
| assertEquals("joe", c.moe); |
| |
| // When we take moe off the prototype it disappears from all arrays. |
| Array.prototype.moe = undefined; |
| assertEquals("undefined", typeof(c.moe)); |
| |
| // Negative indices don't get concated. |
| a[-1] = 'minus1'; |
| assertEquals("minus1", a[-1]); |
| assertEquals("undefined", typeof(a[0xffffffff])); |
| c = a.concat(b); |
| assertEquals("undefined", typeof(c[-1])); |
| assertEquals("undefined", typeof(c[0xffffffff])); |
| assertEquals(c.length, a.length + 1); |
| |
| } |
| |
| a = []; |
| c = a.concat('Hello'); |
| assertEquals(1, c.length); |
| assertEquals("Hello", c[0]); |
| assertEquals("Hello", c.toString()); |
| |
| // Check that concat preserves holes. |
| var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0]) |
| assertEquals(9, holey.length); // hole in embedded array is ignored |
| for (var i = 0; i < holey.length; i++) { |
| if (i == 2 || i == 5) { |
| assertFalse(i in holey); |
| } else { |
| assertTrue(i in holey); |
| } |
| } |
| |
| // Polluted prototype from prior tests. |
| delete Array.prototype[123]; |
| |
| // Check that concat reads getters in the correct order. |
| var arr1 = [,2]; |
| var arr2 = [1,3]; |
| var r1 = [].concat(arr1, arr2); // [,2,1,3] |
| assertEquals([,2,1,3], r1); |
| |
| // Make first array change length of second array. |
| Object.defineProperty(arr1, 0, {get: function() { |
| arr2.push("X"); |
| return undefined; |
| }, configurable: true}) |
| var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] |
| assertEquals([undefined,2,1,3,"X"], r2); |
| |
| // Make first array change length of second array massively. |
| arr2.length = 2; |
| Object.defineProperty(arr1, 0, {get: function() { |
| arr2[500000] = "X"; |
| return undefined; |
| }, configurable: true}) |
| var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] |
| var expected = [undefined,2,1,3]; |
| expected[500000 + 2] = "X"; |
| |
| assertEquals(expected, r3); |
| |
| var arr3 = []; |
| var trace = []; |
| var expectedTrace = [] |
| function mkGetter(i) { return function() { trace.push(i); }; } |
| arr3.length = 10000; |
| for (var i = 0; i < 100; i++) { |
| Object.defineProperty(arr3, i * i, {get: mkGetter(i)}); |
| expectedTrace[i] = i; |
| expectedTrace[100 + i] = i; |
| } |
| var r4 = [0].concat(arr3, arr3); |
| assertEquals(1 + arr3.length * 2, r4.length); |
| assertEquals(expectedTrace, trace); |