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, 2, 3, 4, 5], 'a', 1, 2, 3, 4, 5); 232 func(['a', 1, undefined], 'a', 1, undefined); 233 func(['a', 1, undefined, void(0)], 'a', 1, undefined, void(0)); 234 })(); 235 236 // Check slicing on arguments object when missing arguments get assigined. 237 (function() { 238 function func(x, y) { 239 assertEquals(1, arguments.length); 240 assertEquals(undefined, y); 241 y = 239; 242 assertEquals(1, arguments.length); // arguments length is the same. 243 assertEquals([x], Array.prototype.slice.call(arguments, 0)); 244 } 245 246 func('a'); 247 })(); 248 249 // Check slicing on arguments object when length property has been set. 250 (function() { 251 function func(x, y) { 252 assertEquals(1, arguments.length); 253 arguments.length = 7; 254 assertEquals([x,,,,,,,], Array.prototype.slice.call(arguments, 0)); 255 } 256 257 func('a'); 258 })(); 259 260 // Check slicing on arguments object when length property has been set to 261 // some strange value. 262 (function() { 263 function func(x, y) { 264 assertEquals(1, arguments.length); 265 arguments.length = 'foobar'; 266 assertEquals([], Array.prototype.slice.call(arguments, 0)); 267 } 268 269 func('a'); 270 })(); 271 272 // Check slicing on arguments object when extra argument has been added 273 // via indexed assignment. 274 (function() { 275 function func(x, y) { 276 assertEquals(1, arguments.length); 277 arguments[3] = 239; 278 assertEquals([x], Array.prototype.slice.call(arguments, 0)); 279 } 280 281 func('a'); 282 })(); 283 284 // Check slicing on arguments object when argument has been deleted by index. 285 (function() { 286 function func(x, y, z) { 287 assertEquals(3, arguments.length); 288 delete arguments[1]; 289 assertEquals([x,,z], Array.prototype.slice.call(arguments, 0)); 290 } 291 292 func('a', 'b', 'c'); 293 })(); 294 295 // Check slicing of holey objects with elements in the prototype 296 (function() { 297 function f() { 298 delete arguments[1]; 299 arguments.__proto__[1] = 5; 300 var result = Array.prototype.slice.call(arguments); 301 delete arguments.__proto__[1]; 302 assertEquals([1,5,3], result); 303 } 304 f(1,2,3); 305 })(); 306