1 // Copyright 2010 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 // Check that slicing array of holes keeps it as array of holes 29 (function() { 30 var array = new Array(10); 31 for (var i = 0; i < 7; i++) { 32 var sliced = array.slice(); 33 assertEquals(array.length, sliced.length); 34 assertFalse(0 in sliced); 35 } 36 })(); 37 38 39 // Check various variants of empty array's slicing. 40 (function() { 41 for (var i = 0; i < 7; i++) { 42 assertEquals([], [].slice(0, 0)); 43 assertEquals([], [].slice(1, 0)); 44 assertEquals([], [].slice(0, 1)); 45 assertEquals([], [].slice(-1, 0)); 46 } 47 })(); 48 49 50 // Check various forms of arguments omission. 51 (function() { 52 var array = new Array(7); 53 54 for (var i = 0; i < 7; i++) { 55 assertEquals(array, array.slice()); 56 assertEquals(array, array.slice(0)); 57 assertEquals(array, array.slice(undefined)); 58 assertEquals(array, array.slice("foobar")); 59 assertEquals(array, array.slice(undefined, undefined)); 60 } 61 })(); 62 63 64 // Check variants of negatives and positive indices. 65 (function() { 66 var array = new Array(7); 67 68 for (var i = 0; i < 7; i++) { 69 assertEquals(7, array.slice(-100).length); 70 assertEquals(3, array.slice(-3).length); 71 assertEquals(3, array.slice(4).length); 72 assertEquals(1, array.slice(6).length); 73 assertEquals(0, array.slice(7).length); 74 assertEquals(0, array.slice(8).length); 75 assertEquals(0, array.slice(100).length); 76 77 assertEquals(0, array.slice(0, -100).length); 78 assertEquals(4, array.slice(0, -3).length); 79 assertEquals(4, array.slice(0, 4).length); 80 assertEquals(6, array.slice(0, 6).length); 81 assertEquals(7, array.slice(0, 7).length); 82 assertEquals(7, array.slice(0, 8).length); 83 assertEquals(7, array.slice(0, 100).length); 84 85 // Some exotic cases. 86 87 obj = { toString: function() { throw 'Exception'; } }; 88 89 // More than 2 arguments: 90 assertEquals(7, array.slice(0, 7, obj, null, undefined).length); 91 92 // Custom conversion: 93 assertEquals(1, array.slice({valueOf: function() { return 1; }}, 94 {toString: function() { return 2; }}).length); 95 96 // Throwing an exception in conversion: 97 try { 98 assertEquals(7, array.slice(0, obj).length); 99 throw 'Should have thrown'; 100 } catch (e) { 101 assertEquals('Exception', e); 102 } 103 } 104 })(); 105 106 107 // Nasty: modify the array in ToInteger. 108 (function() { 109 var array = []; 110 var expected = [] 111 bad_guy = { valueOf: function() { array.push(array.length); return -1; } }; 112 113 for (var i = 0; i < 13; i++) { 114 var sliced = array.slice(bad_guy); 115 expected.push(i); 116 assertEquals(expected, array); 117 // According to the spec (15.4.4.10), length is calculated before 118 // performing ToInteger on arguments. 119 if (i == 0) { 120 assertEquals([], sliced); // Length was 0, nothing to get. 121 } else { 122 // Actually out of array [0..i] we get [i - 1] as length is i. 123 assertEquals([i - 1], sliced); 124 } 125 } 126 })(); 127 128 129 // Now check the case with array of holes and some elements on prototype. 130 // Note: that is important that this test runs before the next one 131 // as the next one tampers Array.prototype. 132 (function() { 133 var len = 9; 134 var array = new Array(len); 135 136 var at3 = "@3"; 137 var at7 = "@7"; 138 139 for (var i = 0; i < 7; i++) { 140 var array_proto = []; 141 array_proto[3] = at3; 142 array_proto[7] = at7; 143 array.__proto__ = array_proto; 144 145 assertEquals(len, array.length); 146 for (var i = 0; i < array.length; i++) { 147 assertEquals(array[i], array_proto[i]); 148 } 149 150 var sliced = array.slice(); 151 152 assertEquals(len, sliced.length); 153 154 assertTrue(delete array_proto[3]); 155 assertTrue(delete array_proto[7]); 156 157 // Note that slice copies values from prototype into the array. 158 assertEquals(array[3], undefined); 159 assertFalse(array.hasOwnProperty(3)); 160 assertEquals(sliced[3], at3); 161 assertTrue(sliced.hasOwnProperty(3)); 162 163 assertEquals(array[7], undefined); 164 assertFalse(array.hasOwnProperty(7)); 165 assertEquals(sliced[7], at7); 166 assertTrue(sliced.hasOwnProperty(7)); 167 168 // ... but keeps the rest as holes: 169 array_proto[5] = "@5"; 170 assertEquals(array[5], array_proto[5]); 171 assertFalse(array.hasOwnProperty(5)); 172 } 173 })(); 174 175 176 // Now check the case with array of holes and some elements on prototype. 177 (function() { 178 var len = 9; 179 var array = new Array(len); 180 181 var at3 = "@3"; 182 var at7 = "@7"; 183 184 for (var i = 0; i < 7; i++) { 185 Array.prototype[3] = at3; 186 Array.prototype[7] = at7; 187 188 assertEquals(len, array.length); 189 for (var i = 0; i < array.length; i++) { 190 assertEquals(array[i], Array.prototype[i]); 191 } 192 193 var sliced = array.slice(); 194 195 assertEquals(len, sliced.length); 196 197 assertTrue(delete Array.prototype[3]); 198 assertTrue(delete Array.prototype[7]); 199 200 // Note that slice copies values from prototype into the array. 201 assertEquals(array[3], undefined); 202 assertFalse(array.hasOwnProperty(3)); 203 assertEquals(sliced[3], at3); 204 assertTrue(sliced.hasOwnProperty(3)); 205 206 assertEquals(array[7], undefined); 207 assertFalse(array.hasOwnProperty(7)); 208 assertEquals(sliced[7], at7); 209 assertTrue(sliced.hasOwnProperty(7)); 210 211 // ... but keeps the rest as holes: 212 Array.prototype[5] = "@5"; 213 assertEquals(array[5], Array.prototype[5]); 214 assertFalse(array.hasOwnProperty(5)); 215 assertEquals(sliced[5], Array.prototype[5]); 216 assertFalse(sliced.hasOwnProperty(5)); 217 218 assertTrue(delete Array.prototype[5]); 219 } 220 })(); 221 222 // Check slicing on arguments object. 223 (function() { 224 function func(expected, a0, a1, a2) { 225 assertEquals(expected, Array.prototype.slice.call(arguments, 1)); 226 } 227 228 func([]); 229 func(['a'], 'a'); 230 func(['a', 1], 'a', 1); 231 func(['a', 1, undefined], 'a', 1, undefined); 232 func(['a', 1, undefined, void(0)], 'a', 1, undefined, void(0)); 233 })(); 234 235 // Check slicing on arguments object when missing arguments get assigined. 236 (function() { 237 function func(x, y) { 238 assertEquals(1, arguments.length); 239 assertEquals(undefined, y); 240 y = 239; 241 assertEquals(1, arguments.length); // arguments length is the same. 242 assertEquals([x], Array.prototype.slice.call(arguments, 0)); 243 } 244 245 func('a'); 246 })(); 247 248 // Check slicing on arguments object when length property has been set. 249 (function() { 250 function func(x, y) { 251 assertEquals(1, arguments.length); 252 arguments.length = 7; 253 assertEquals([x,,,,,,,], Array.prototype.slice.call(arguments, 0)); 254 } 255 256 func('a'); 257 })(); 258 259 // Check slicing on arguments object when length property has been set to 260 // some strange value. 261 (function() { 262 function func(x, y) { 263 assertEquals(1, arguments.length); 264 arguments.length = 'foobar'; 265 assertEquals([], Array.prototype.slice.call(arguments, 0)); 266 } 267 268 func('a'); 269 })(); 270 271 // Check slicing on arguments object when extra argument has been added 272 // via indexed assignment. 273 (function() { 274 function func(x, y) { 275 assertEquals(1, arguments.length); 276 arguments[3] = 239; 277 assertEquals([x], Array.prototype.slice.call(arguments, 0)); 278 } 279 280 func('a'); 281 })(); 282 283 // Check slicing on arguments object when argument has been deleted by index. 284 (function() { 285 function func(x, y, z) { 286 assertEquals(3, arguments.length); 287 delete arguments[1]; 288 assertEquals([x,,z], Array.prototype.slice.call(arguments, 0)); 289 } 290 291 func('a', 'b', 'c'); 292 })(); 293 294 // Check slicing of holey objects with elements in the prototype 295 (function() { 296 function f() { 297 delete arguments[1]; 298 arguments.__proto__[1] = 5; 299 var result = Array.prototype.slice.call(arguments); 300 delete arguments.__proto__[1]; 301 assertEquals([1,5,3], result); 302 } 303 f(1,2,3); 304 })(); 305