array-slice-clone.js 10.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --allow-natives-syntax --opt


// Test CloneFastJSArray inserted by JSCallReducer for Array.prototype.slice.
// CloneFastJSArray produces COW arrays if the original array is COW.

// Trigger JSCallReducer on slice() and slice(0)
(function() {
  const arr = [1,2,3,4,5];

  function slice() {
    return arr.slice();
  }

  function slice0() {
    return arr.slice(0);
  }

23
  %PrepareFunctionForOptimization(slice0);
24
  %PrepareFunctionForOptimization(slice);
25

26 27 28 29 30 31
  assertEquals(arr, slice());
  assertFalse(arr === slice());
  assertEquals(slice(), slice0());
  assertEquals(slice0(), slice());

  %OptimizeFunctionOnNextCall(slice0);
32
  assertEquals(slice(), slice0());
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
  %OptimizeFunctionOnNextCall(slice);

  assertEquals(slice(), slice0());
  assertOptimized(slice); assertOptimized(slice0);
})();

// This will cause deopt of slice by a CheckMap installed by
// JSNativeContextSpecialization::ReduceNamedAccess
(function() {
  const arr = [1,2,3,4,5];

  function slice() {
    return arr.slice();
  }

48 49
  %PrepareFunctionForOptimization(slice);

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
  assertEquals(arr, slice());
  assertEquals(slice(), arr);

  %OptimizeFunctionOnNextCall(slice);
  slice();

  // Trigger deopt here
  arr.push(7.2);
  assertEquals(slice()[5], 7.2);
})();

// There should not be a deopt cycle.
(function() {
  const arr = [1,2,3,4,5];

  function slice() {
    return arr.slice();
  }

69 70
  %PrepareFunctionForOptimization(slice);

71 72 73 74 75 76 77 78 79 80 81
  assertEquals(arr, slice());
  assertEquals(slice(), arr);

  %OptimizeFunctionOnNextCall(slice);
  // Trigger opt
  assertEquals(slice(), arr);

  // Trigger deopt by CheckMap from JSNativeContextSpecialization
  arr.push(7.2);
  slice();

82
  %PrepareFunctionForOptimization(slice);
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
  %OptimizeFunctionOnNextCall(slice);
  // Trigger opt again
  slice();

  // Should not deopt again
  arr.push(8.2);
  slice();
  assertOptimized(slice);
})();

// JSCallReducer will not reduce because the species has been modified
(function() {
  const array = [3,4,5];

  function slice(){
    return array.slice();
  }

  class MyArray extends Array {};
  array.constructor = MyArray;

104 105
  %PrepareFunctionForOptimization(slice);

106 107 108 109 110 111 112 113 114 115 116 117 118 119
  slice(); slice();

  %OptimizeFunctionOnNextCall(slice);
  var narr = slice();
  assertInstanceof(narr, MyArray);
})();

(function() {
  const array = [3,4,5];

  function slice(){
    return array.slice();
  }

120 121
  %PrepareFunctionForOptimization(slice);

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  slice(); slice();

  %OptimizeFunctionOnNextCall(slice);

  slice();

  class MyArray extends Array {};
  array.constructor = MyArray;
  // deopt
  var narr = slice();
  // if not deopt, narr will be instanceof Array
  assertTrue(narr instanceof MyArray);
})();

// JSCallReducer adds check for UnreliableReceiverMaps
(function() {
  const arr = [1,2,3,4,5];

  function slice() {
    return arr.slice();
  }

144 145
  %PrepareFunctionForOptimization(slice);

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  slice(); slice();
  arr.foo = 6.2;

  %OptimizeFunctionOnNextCall(slice);
  // JSCallReducer will add check for UnreliableReceiverMaps
  slice();

  // Trigger deopt because of DependOnStableMaps
  // installed by JSNativeContextSpecialization,
  // but not the check installed by ReduceArrayPrototypeSlice itself
  arr.bar = 7.2;

  let narr = slice();
  assertEquals(arr, narr);
  assertEquals(narr.foo, undefined);
  assertEquals(narr.bar, undefined);
})();

// Multiple maps
(function() {
  const iarr = [1,2,3];
  const darr = [2.1, 3.3, 0.2];

  function slice(arr) {
    return arr.slice();
  }

173 174
  %PrepareFunctionForOptimization(slice);

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
  slice(iarr); slice(darr);
  slice(iarr); slice(darr);

  %OptimizeFunctionOnNextCall(slice);
  // The optimization works for both maps
  assertEquals(iarr, slice(iarr));
  assertEquals(darr, slice(darr));
  assertOptimized(slice);
})();

// Tests for the branch of CanInlineArrayIteratingBuiltin

// JSCallReducer will not reduce to CloneFastJSArray
// if array's prototype is not JS_ARRAY_TYPE
(function () {
  class MyArray extends Array {
    constructor() {
      super();
      this[6]= 6;
    }
  }
  let array = new MyArray(3, 5, 4);

  function slice() {
    return array.slice();
  }

202 203
  %PrepareFunctionForOptimization(slice);

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  assertEquals(slice(),array);
  slice();

  %OptimizeFunctionOnNextCall(slice);
  let narr = slice();
  // here, slice supposes to call MyArray's constructor.
  // If we optimize with CloneFastJSArray, Array's constructor is called instead.
  assertEquals(narr[6], 6);
  assertTrue(narr instanceof MyArray);
})();

// JSCallReducer will not reduce to CloneFastJSArray
// if array's instance type is not JS_ARRAY_TYPE.
// CloneFastJSArray does not work with non JS_ARRAY_TYPE.
// Check : receiver_map->instance_type() == JS_ARRAY_TYPE
(function () {
  var x = {"0" : 0, "2": 2} ;
  x.__proto__ = Array.prototype;

  function slice() {
    return x.slice();
  }

227 228
  %PrepareFunctionForOptimization(slice);

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
  slice(); slice();

  %OptimizeFunctionOnNextCall(slice);
  assertEquals(slice(), []);
})();

// JSCallReducer will not reduce to CloneFastJSArray
// since array is not Fast Elements Kind
// Check : IsFastElementsKind(receiver_map->elements_kind())
(function () {
  var array = [3, 4, 5];

  function slice() {
    return array.slice();
  }

245 246
  %PrepareFunctionForOptimization(slice);

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
  assertEquals(slice(),array);
  slice();

  // a sparse array switches to Dictionary Elements
  array[9999] = 0;
  %OptimizeFunctionOnNextCall(slice);
  var narr = slice();
  assertEquals(narr, array);
})();

(function () {
  var array = [3, 4, 5];

  function slice() {
    return array.slice();
  }

264 265
  %PrepareFunctionForOptimization(slice);

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
  assertEquals(slice(),array);
  slice();

  %OptimizeFunctionOnNextCall(slice);
  slice();

  // a sparse array switches to Dictionary Elements
  array[9999] = 0;
  // trigger deopt because map changes
  assertEquals(slice(),array);
})();

// JSCallReducer will not reduce to CloneFastJSArray
// if array is used as a prototype and has unstable map
(function () {
  var array = [3, 5, 4];

  function slice(arr) {
    return arr.slice();
  }

287 288
  %PrepareFunctionForOptimization(slice);

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
  // make array's map is_prototype_map()
  var x = {__proto__ : array};

  assertEquals(slice(array),array);
  slice(array);

  // make array's map unstable
  array.push(6.3);
  slice(array);

  %OptimizeFunctionOnNextCall(slice);

  assertEquals(slice(array),array);
})();

// JSCallReducer will not reduce to CloneFastJSArray
// if the Array prototype got some elements.
// Check: isolate->IsNoElementsProtectorIntact()
(function () {
  var array = [, 6, 6];

  function slice() {
    return array.slice();
  }

314 315
  %PrepareFunctionForOptimization(slice);

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
  assertEquals(slice(),array);
  slice();

  array.__proto__.push(6);

  %OptimizeFunctionOnNextCall(slice);

  // if we optimized, we would get [ , 6, 6]
  // here, slice copies elements from both the object and the prototype
  let narr = slice();
  assertNotEquals(Object.getOwnPropertyDescriptor(narr,0), undefined);
  assertEquals(narr, [6, 6, 6]);
})();

(function () {
  var array = [, 6, 6];

  function slice() {
    return array.slice();
  }

337 338
  %PrepareFunctionForOptimization(slice);

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
  assertEquals(slice(),array);
  slice();

  %OptimizeFunctionOnNextCall(slice);
  slice();

  // Deopt
  array.__proto__.push(6);
  let narr = slice();
  assertNotEquals(Object.getOwnPropertyDescriptor(narr, 0), undefined);
  assertEquals(narr[0], 6);
})();

// JSCallReducer will not reduce to CloneFastJSArray
// if the Array prototype is not original
// Check: isolate->IsAnyInitialArrayPrototype(receiver_prototype)
(function () {
  var array = [6, , 6];

  function slice() {
    return array.slice();
  }

362 363
  %PrepareFunctionForOptimization(slice);

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  assertEquals(slice(),array);
  slice();

  // change the prototype
  array.__proto__ = [ , 6, ];

  %OptimizeFunctionOnNextCall(slice);
  let narr = slice();
  // if optimized, we would get [6, , 6]
  assertNotEquals(Object.getOwnPropertyDescriptor(narr, 1), undefined);
  assertEquals(narr, [6,6,6]);
})();

(function () {
  var array = [6, ,6];

  function slice() {
    return array.slice();
  }

384 385
  %PrepareFunctionForOptimization(slice);

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
  assertEquals(slice(),array);
  slice();

  %OptimizeFunctionOnNextCall(slice);
  slice();

  // change the prototype
  array.__proto__ = [,6,];
  // deopt because of map changed
  let narr = slice();

  // if optimized, we would get [6, , 6]
  assertNotEquals(Object.getOwnPropertyDescriptor(narr, 1), undefined);
  assertEquals(narr, [6,6,6]);
})();
401

402
// Packed
403 404 405
// Trigger JSCallReducer on slice() and slice(0)
(function() {
  // Non-extensible:
406
  var arr = Object.preventExtensions([1,2,'a',4,5]);
407 408 409 410 411 412 413 414 415 416 417

  function slice() {
    return arr.slice();
  }

  function slice0() {
    return arr.slice(0);
  }

  function test() {
    %PrepareFunctionForOptimization(slice0);
418
    %PrepareFunctionForOptimization(slice);
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434

    assertEquals(arr, slice());
    assertFalse(arr === slice());
    assertEquals(slice(), slice0());
    assertEquals(slice0(), slice());

    %OptimizeFunctionOnNextCall(slice0);
    assertEquals(slice(), slice0());
    %OptimizeFunctionOnNextCall(slice);

    assertEquals(slice(), slice0());
    assertOptimized(slice); assertOptimized(slice0);
  }
  test();

  // Sealed
435
  arr = Object.seal([1,2,'a',4,5]);
436 437 438
  test();

  // Frozen
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
  arr = Object.freeze([1,2,'a',4,5]);
  test();
})();

// Holey
// Trigger JSCallReducer on slice() and slice(0)
(function() {
  // Non-extensible:
  var arr = Object.preventExtensions([,1,2,'a',4,5]);

  function slice() {
    return arr.slice();
  }

  function slice0() {
    return arr.slice(0);
  }

  function test() {
458 459
    %PrepareFunctionForOptimization(slice0);
    %PrepareFunctionForOptimization(slice);
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
    assertEquals(arr, slice());
    assertFalse(arr === slice());
    assertEquals(slice(), slice0());
    assertEquals(slice0(), slice());

    %OptimizeFunctionOnNextCall(slice0);
    assertEquals(slice(), slice0());
    %OptimizeFunctionOnNextCall(slice);

    assertEquals(slice(), slice0());
    assertOptimized(slice0);
    assertOptimized(slice);
  }
  test();

  // Sealed
  arr = Object.seal([,1,2,'a',4,5]);
  test();

  // Frozen
  arr = Object.freeze([,1,2,'a',4,5]);
481 482
  test();
})();