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 splice, shift, unshift, slice and join on small 30 * and large arrays. Some of these methods are specified such that they 31 * should work on other objects too, so we test that too. 32 */ 33 34var LARGE = 4000000; 35var VERYLARGE = 4000000000; 36 37// Nicer for firefox 1.5. Unless you uncomment the following two lines, 38// smjs will appear to hang on this file. 39//var LARGE = 40000; 40//var VERYLARGE = 40000; 41 42var fourhundredth = LARGE/400; 43 44function PseudoArray() { 45}; 46 47for (var use_real_arrays = 0; use_real_arrays <= 1; use_real_arrays++) { 48 var poses = [0, 140, 20000, VERYLARGE]; 49 var the_prototype; 50 var new_function; 51 var push_function; 52 var concat_function; 53 var slice_function; 54 var splice_function; 55 var splice_function_2; 56 var unshift_function; 57 var unshift_function_2; 58 var shift_function; 59 if (use_real_arrays) { 60 new_function = function(length) { 61 return new Array(length); 62 }; 63 the_prototype = Array.prototype; 64 push_function = function(array, elt) { 65 return array.push(elt); 66 }; 67 concat_function = function(array, other) { 68 return array.concat(other); 69 }; 70 slice_function = function(array, start, len) { 71 return array.slice(start, len); 72 }; 73 splice_function = function(array, start, len) { 74 return array.splice(start, len); 75 }; 76 splice_function_2 = function(array, start, len, elt) { 77 return array.splice(start, len, elt); 78 }; 79 unshift_function = function(array, elt) { 80 return array.unshift(elt); 81 }; 82 unshift_function_2 = function(array, elt1, elt2) { 83 return array.unshift(elt1, elt2); 84 }; 85 shift_function = function(array) { 86 return array.shift(); 87 }; 88 } else { 89 // Don't run largest size on non-arrays or we'll be here for ever. 90 poses.pop(); 91 new_function = function(length) { 92 var obj = new PseudoArray(); 93 obj.length = length; 94 return obj; 95 }; 96 the_prototype = PseudoArray.prototype; 97 push_function = function(array, elt) { 98 array[array.length] = elt; 99 array.length++; 100 }; 101 concat_function = function(array, other) { 102 return Array.prototype.concat.call(array, other); 103 }; 104 slice_function = function(array, start, len) { 105 return Array.prototype.slice.call(array, start, len); 106 }; 107 splice_function = function(array, start, len) { 108 return Array.prototype.splice.call(array, start, len); 109 }; 110 splice_function_2 = function(array, start, len, elt) { 111 return Array.prototype.splice.call(array, start, len, elt); 112 }; 113 unshift_function = function(array, elt) { 114 return Array.prototype.unshift.call(array, elt); 115 }; 116 unshift_function_2 = function(array, elt1, elt2) { 117 return Array.prototype.unshift.call(array, elt1, elt2); 118 }; 119 shift_function = function(array) { 120 return Array.prototype.shift.call(array); 121 }; 122 } 123 124 for (var pos_pos = 0; pos_pos < poses.length; pos_pos++) { 125 var pos = poses[pos_pos]; 126 if (pos > 100) { 127 var a = new_function(pos); 128 assertEquals(pos, a.length); 129 push_function(a, 'foo'); 130 assertEquals(pos + 1, a.length); 131 var b = ['bar']; 132 // Delete a huge number of holes. 133 var c = splice_function(a, 10, pos - 20); 134 assertEquals(pos - 20, c.length); 135 assertEquals(21, a.length); 136 } 137 138 // Add a numeric property to the prototype of the array class. This 139 // allows us to test some borderline stuff relative to the standard. 140 the_prototype["" + (pos + 1)] = 'baz'; 141 142 if (use_real_arrays) { 143 // It seems quite clear from ECMAScript spec 15.4.4.5. Just call Get on 144 // every integer in the range. 145 // IE, Safari get this right. 146 // FF, Opera get this wrong. 147 var a = ['zero', ,'two']; 148 if (pos == 0) { 149 assertEquals("zero,baz,two", a.join(",")); 150 } 151 152 // Concat only applies to real arrays, unlike most of the other methods. 153 var a = new_function(pos); 154 push_function(a, "con"); 155 assertEquals("con", a[pos]); 156 assertEquals(pos + 1, a.length); 157 var b = new_function(0); 158 push_function(b, "cat"); 159 assertEquals("cat", b[0]); 160 var ab = concat_function(a, b); 161 assertEquals("con", ab[pos]); 162 assertEquals(pos + 2, ab.length); 163 assertEquals("cat", ab[pos + 1]); 164 var ba = concat_function(b, a); 165 assertEquals("con", ba[pos + 1]); 166 assertEquals(pos + 2, ba.length); 167 assertEquals("cat", ba[0]); 168 169 // Join with '' as separator. 170 var join = a.join(''); 171 assertEquals("con", join); 172 join = b.join(''); 173 assertEquals("cat", join); 174 join = ab.join(''); 175 assertEquals("concat", join); 176 join = ba.join(''); 177 assertEquals("catcon", join); 178 179 var sparse = []; 180 sparse[pos + 1000] = 'is '; 181 sparse[pos + 271828] = 'time '; 182 sparse[pos + 31415] = 'the '; 183 sparse[pos + 012260199] = 'all '; 184 sparse[-1] = 'foo'; 185 sparse[pos + 22591927] = 'good '; 186 sparse[pos + 1618033] = 'for '; 187 sparse[pos + 91] = ': Now '; 188 sparse[pos + 86720199] = 'men.'; 189 sparse.hest = 'fisk'; 190 191 assertEquals("baz: Now is the time for all good men.", sparse.join('')); 192 } 193 194 a = new_function(pos); 195 push_function(a, 'zero'); 196 push_function(a, void 0); 197 push_function(a, 'two'); 198 199 // Splice works differently from join. 200 // IE, Safari get this wrong. 201 // FF, Opera get this right. 202 // 15.4.4.12 line 24 says the object itself has to have the property... 203 var zero = splice_function(a, pos, 1); 204 assertEquals("undefined", typeof(a[pos])); 205 assertEquals("two", a[pos+1], "pos1:" + pos); 206 assertEquals(pos + 2, a.length, "a length"); 207 assertEquals(1, zero.length, "zero length"); 208 assertEquals("zero", zero[0]); 209 210 // 15.4.4.12 line 41 says the object itself has to have the property... 211 a = new_function(pos); 212 push_function(a, 'zero'); 213 push_function(a, void 0); 214 push_function(a, 'two'); 215 var nothing = splice_function_2(a, pos, 0, 'minus1'); 216 assertEquals("minus1", a[pos]); 217 assertEquals("zero", a[pos+1]); 218 assertEquals("undefined", typeof(a[pos+2]), "toot!"); 219 assertEquals("two", a[pos+3], "pos3"); 220 assertEquals(pos + 4, a.length); 221 assertEquals(1, zero.length); 222 assertEquals("zero", zero[0]); 223 224 // 15.4.4.12 line 10 says the object itself has to have the property... 225 a = new_function(pos); 226 push_function(a, 'zero'); 227 push_function(a, void 0); 228 push_function(a, 'two'); 229 var one = splice_function(a, pos + 1, 1); 230 assertEquals("", one.join(",")); 231 assertEquals(pos + 2, a.length); 232 assertEquals("zero", a[pos]); 233 assertEquals("two", a[pos+1]); 234 235 // Set things back to the way they were. 236 the_prototype[pos + 1] = undefined; 237 238 // Unshift. 239 var a = new_function(pos); 240 push_function(a, "foo"); 241 assertEquals("foo", a[pos]); 242 assertEquals(pos + 1, a.length); 243 unshift_function(a, "bar"); 244 assertEquals("foo", a[pos+1]); 245 assertEquals(pos + 2, a.length); 246 assertEquals("bar", a[0]); 247 unshift_function_2(a, "baz", "boo"); 248 assertEquals("foo", a[pos+3]); 249 assertEquals(pos + 4, a.length); 250 assertEquals("baz", a[0]); 251 assertEquals("boo", a[1]); 252 assertEquals("bar", a[2]); 253 254 // Shift. 255 var baz = shift_function(a); 256 assertEquals("baz", baz); 257 assertEquals("boo", a[0]); 258 assertEquals(pos + 3, a.length); 259 assertEquals("foo", a[pos + 2]); 260 261 // Slice. 262 var bar = slice_function(a, 1, 0); // don't throw an exception please. 263 bar = slice_function(a, 1, 2); 264 assertEquals("bar", bar[0]); 265 assertEquals(1, bar.length); 266 assertEquals("bar", a[1]); 267 268 } 269} 270 271// Lets see if performance is reasonable. 272 273var a = new Array(LARGE + 10); 274for (var i = 0; i < a.length; i += 1000) { 275 a[i] = i; 276} 277 278// Take something near the end of the array. 279for (var i = 0; i < 10; i++) { 280 var top = a.splice(LARGE, 5); 281 assertEquals(5, top.length); 282 assertEquals(LARGE, top[0]); 283 assertEquals("undefined", typeof(top[1])); 284 assertEquals(LARGE + 5, a.length); 285 a.splice(LARGE, 0, LARGE); 286 a.length = LARGE + 10; 287} 288 289var a = new Array(LARGE + 10); 290for (var i = 0; i < a.length; i += fourhundredth) { 291 a[i] = i; 292} 293 294// Take something near the middle of the array. 295for (var i = 0; i < 10; i++) { 296 var top = a.splice(LARGE >> 1, 5); 297 assertEquals(5, top.length); 298 assertEquals(LARGE >> 1, top[0]); 299 assertEquals("undefined", typeof(top[1])); 300 assertEquals(LARGE + 5, a.length); 301 a.splice(LARGE >> 1, 0, LARGE >> 1, void 0, void 0, void 0, void 0); 302} 303 304 305// Test http://b/issue?id=1202711 306arr = [0]; 307arr.length = 2; 308Array.prototype[1] = 1; 309assertEquals(1, arr.pop()); 310assertEquals(0, arr.pop()); 311Array.prototype[1] = undefined; 312 313// Test http://code.google.com/p/chromium/issues/detail?id=21860 314Array.prototype.push.apply([], [1].splice(0, -(-1 % 5))); 315 316 317// Check that the Array functions work also properly on non-Arrays 318var receiver; 319 320receiver = 'a string'; 321assertThrows(function(){ 322 Array.prototype.push.call(receiver); 323}); 324 325receiver = 0; 326assertEquals(undefined, receiver.length); 327assertEquals(0, Array.prototype.push.call(receiver)); 328assertEquals(1, Array.prototype.push.call(receiver, 'first')); 329assertEquals(undefined, receiver.length); 330 331receiver = {}; 332assertEquals(undefined, receiver.length); 333assertEquals(0, Array.prototype.push.call(receiver)); 334assertEquals(0, Array.prototype.push.call(receiver)); 335assertEquals(0, receiver.length); 336assertEquals(1, Array.prototype.push.call(receiver, 'first')); 337assertEquals(1, receiver.length); 338assertEquals('first', receiver[0]); 339assertEquals(2, Array.prototype.push.call(receiver, 'second')); 340assertEquals(2, receiver.length); 341assertEquals('first', receiver[0]); 342assertEquals('second', receiver[1]); 343 344receiver = {'length': 10}; 345assertEquals(10, Array.prototype.push.call(receiver)); 346assertEquals(10, receiver.length); 347assertEquals(11, Array.prototype.push.call(receiver, 'first')); 348assertEquals(11, receiver.length); 349assertEquals('first', receiver[10]); 350assertEquals(13, Array.prototype.push.call(receiver, 'second', 'third')); 351assertEquals(13, receiver.length); 352assertEquals('first', receiver[10]); 353assertEquals('second', receiver[11]); 354assertEquals('third', receiver[12]); 355 356receiver = { 357 get length() { return 10; }, 358 set length(l) {} 359}; 360assertEquals(10, Array.prototype.push.call(receiver)); 361assertEquals(10, receiver.length); 362assertEquals(11, Array.prototype.push.call(receiver, 'first')); 363assertEquals(10, receiver.length); 364assertEquals('first', receiver[10]); 365assertEquals(12, Array.prototype.push.call(receiver, 'second', 'third')); 366assertEquals(10, receiver.length); 367assertEquals('second', receiver[10]); 368assertEquals('third', receiver[11]); 369 370// readonly length 371receiver = { 372 get length() { return 10; }, 373}; 374assertThrows(function(){ 375 Array.prototype.push.call(receiver); 376}); 377 378receiver = { 379 set length(l) {} 380}; 381assertEquals(0, Array.prototype.push.call(receiver)); 382assertEquals(undefined, receiver.length); 383assertEquals(1, Array.prototype.push.call(receiver, 'first')); 384assertEquals(undefined, receiver.length); 385assertEquals(2, Array.prototype.push.call(receiver, 'third', 'second')); 386assertEquals(undefined, receiver.length); 387