1 // Copyright 2008 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 /** 29 * @fileoverview Test concat on small and large arrays 30 */ 31 32 33 (function testStringWrapperConcat() { 34 var concat = Array.prototype.concat; 35 var str = new String('abcd'); 36 assertEquals([1,2,3,new String('abcd')], [1, 2, 3].concat(str)); 37 assertEquals([new String("abcd")], concat.call(str)); 38 39 var array = [1, 2, 3]; 40 array.__proto__ = str; 41 array.length = 4; 42 assertEquals([1,2,3,'d'], concat.call(array)); 43 })() 44 45 var poses; 46 47 poses = [140, 4000000000]; 48 while (pos = poses.shift()) { 49 var a = new Array(pos); 50 var array_proto = []; 51 a.__proto__ = array_proto; 52 assertEquals(pos, a.length); 53 a.push('foo'); 54 assertEquals(pos + 1, a.length); 55 var b = ['bar']; 56 var c = a.concat(b); 57 assertEquals(pos + 2, c.length); 58 assertEquals("undefined", typeof(c[pos - 1])); 59 assertEquals("foo", c[pos]); 60 assertEquals("bar", c[pos + 1]); 61 62 // Can we fool the system by putting a number in a string? 63 var onetwofour = "124"; 64 a[onetwofour] = 'doo'; 65 assertEquals(a[124], 'doo'); 66 c = a.concat(b); 67 assertEquals(c[124], 'doo'); 68 69 // If we put a number in the prototype, then the spec says it should be 70 // copied on concat. 71 array_proto["123"] = 'baz'; 72 assertEquals(a[123], 'baz'); 73 74 c = a.concat(b); 75 assertEquals(pos + 2, c.length); 76 assertEquals("baz", c[123]); 77 assertEquals("undefined", typeof(c[pos - 1])); 78 assertEquals("foo", c[pos]); 79 assertEquals("bar", c[pos + 1]); 80 81 // When we take the number off the prototype it disappears from a, but 82 // the concat put it in c itself. 83 array_proto["123"] = undefined; 84 assertEquals("undefined", typeof(a[123])); 85 assertEquals("baz", c[123]); 86 87 // If the element of prototype is shadowed, the element on the instance 88 // should be copied, but not the one on the prototype. 89 array_proto[123] = 'baz'; 90 a[123] = 'xyz'; 91 assertEquals('xyz', a[123]); 92 c = a.concat(b); 93 assertEquals('xyz', c[123]); 94 95 // Non-numeric properties on the prototype or the array shouldn't get 96 // copied. 97 array_proto.moe = 'joe'; 98 a.ben = 'jerry'; 99 assertEquals(a["moe"], 'joe'); 100 assertEquals(a["ben"], 'jerry'); 101 c = a.concat(b); 102 // ben was not copied 103 assertEquals("undefined", typeof(c.ben)); 104 105 // When we take moe off the prototype it disappears from all arrays. 106 array_proto.moe = undefined; 107 assertEquals("undefined", typeof(c.moe)); 108 109 // Negative indices don't get concated. 110 a[-1] = 'minus1'; 111 assertEquals("minus1", a[-1]); 112 assertEquals("undefined", typeof(a[0xffffffff])); 113 c = a.concat(b); 114 assertEquals("undefined", typeof(c[-1])); 115 assertEquals("undefined", typeof(c[0xffffffff])); 116 assertEquals(c.length, a.length + 1); 117 } 118 119 poses = [140, 4000000000]; 120 while (pos = poses.shift()) { 121 var a = new Array(pos); 122 assertEquals(pos, a.length); 123 a.push('foo'); 124 assertEquals(pos + 1, a.length); 125 var b = ['bar']; 126 var c = a.concat(b); 127 assertEquals(pos + 2, c.length); 128 assertEquals("undefined", typeof(c[pos - 1])); 129 assertEquals("foo", c[pos]); 130 assertEquals("bar", c[pos + 1]); 131 132 // Can we fool the system by putting a number in a string? 133 var onetwofour = "124"; 134 a[onetwofour] = 'doo'; 135 assertEquals(a[124], 'doo'); 136 c = a.concat(b); 137 assertEquals(c[124], 'doo'); 138 139 // If we put a number in the prototype, then the spec says it should be 140 // copied on concat. 141 Array.prototype["123"] = 'baz'; 142 assertEquals(a[123], 'baz'); 143 144 c = a.concat(b); 145 assertEquals(pos + 2, c.length); 146 assertEquals("baz", c[123]); 147 assertEquals("undefined", typeof(c[pos - 1])); 148 assertEquals("foo", c[pos]); 149 assertEquals("bar", c[pos + 1]); 150 151 // When we take the number off the prototype it disappears from a, but 152 // the concat put it in c itself. 153 Array.prototype["123"] = undefined; 154 assertEquals("undefined", typeof(a[123])); 155 assertEquals("baz", c[123]); 156 157 // If the element of prototype is shadowed, the element on the instance 158 // should be copied, but not the one on the prototype. 159 Array.prototype[123] = 'baz'; 160 a[123] = 'xyz'; 161 assertEquals('xyz', a[123]); 162 c = a.concat(b); 163 assertEquals('xyz', c[123]); 164 165 // Non-numeric properties on the prototype or the array shouldn't get 166 // copied. 167 Array.prototype.moe = 'joe'; 168 a.ben = 'jerry'; 169 assertEquals(a["moe"], 'joe'); 170 assertEquals(a["ben"], 'jerry'); 171 c = a.concat(b); 172 // ben was not copied 173 assertEquals("undefined", typeof(c.ben)); 174 // moe was not copied, but we can see it through the prototype 175 assertEquals("joe", c.moe); 176 177 // When we take moe off the prototype it disappears from all arrays. 178 Array.prototype.moe = undefined; 179 assertEquals("undefined", typeof(c.moe)); 180 181 // Negative indices don't get concated. 182 a[-1] = 'minus1'; 183 assertEquals("minus1", a[-1]); 184 assertEquals("undefined", typeof(a[0xffffffff])); 185 c = a.concat(b); 186 assertEquals("undefined", typeof(c[-1])); 187 assertEquals("undefined", typeof(c[0xffffffff])); 188 assertEquals(c.length, a.length + 1); 189 190 } 191 192 a = []; 193 c = a.concat('Hello'); 194 assertEquals(1, c.length); 195 assertEquals("Hello", c[0]); 196 assertEquals("Hello", c.toString()); 197 198 // Check that concat preserves holes. 199 var holey = [void 0,'a',,'c'].concat(['d',,'f',[0,,2],void 0]) 200 assertEquals(9, holey.length); // hole in embedded array is ignored 201 for (var i = 0; i < holey.length; i++) { 202 if (i == 2 || i == 5) { 203 assertFalse(i in holey); 204 } else { 205 assertTrue(i in holey); 206 } 207 } 208 209 // Polluted prototype from prior tests. 210 delete Array.prototype[123]; 211 212 // Check that concat reads getters in the correct order. 213 var arr1 = [,2]; 214 var arr2 = [1,3]; 215 var r1 = [].concat(arr1, arr2); // [,2,1,3] 216 assertEquals([,2,1,3], r1); 217 218 // Make first array change length of second array. 219 Object.defineProperty(arr1, 0, {get: function() { 220 arr2.push("X"); 221 return undefined; 222 }, configurable: true}) 223 var r2 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] 224 assertEquals([undefined,2,1,3,"X"], r2); 225 226 // Make first array change length of second array massively. 227 arr2.length = 2; 228 Object.defineProperty(arr1, 0, {get: function() { 229 arr2[500000] = "X"; 230 return undefined; 231 }, configurable: true}) 232 var r3 = [].concat(arr1, arr2); // [undefined,2,1,3,"X"] 233 var expected = [undefined,2,1,3]; 234 expected[500000 + 2] = "X"; 235 236 assertEquals(expected, r3); 237 238 var arr3 = []; 239 var trace = []; 240 var expectedTrace = [] 241 function mkGetter(i) { return function() { trace.push(i); }; } 242 arr3.length = 10000; 243 for (var i = 0; i < 100; i++) { 244 Object.defineProperty(arr3, i * i, {get: mkGetter(i)}); 245 expectedTrace[i] = i; 246 expectedTrace[100 + i] = i; 247 } 248 var r4 = [0].concat(arr3, arr3); 249 assertEquals(1 + arr3.length * 2, r4.length); 250 assertEquals(expectedTrace, trace); 251