blob: fc002da2c3dc2efb790a2d5059c187f147742801 [file] [log] [blame]
// 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);
}
%PrepareFunctionForOptimization(slice0);
%PrepareFunctionForOptimization(slice);
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);
})();
// 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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
assertEquals(arr, slice());
assertEquals(slice(), arr);
%OptimizeFunctionOnNextCall(slice);
// Trigger opt
assertEquals(slice(), arr);
// Trigger deopt by CheckMap from JSNativeContextSpecialization
arr.push(7.2);
slice();
%PrepareFunctionForOptimization(slice);
%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;
%PrepareFunctionForOptimization(slice);
slice(); slice();
%OptimizeFunctionOnNextCall(slice);
var narr = slice();
assertInstanceof(narr, MyArray);
})();
(function() {
const array = [3,4,5];
function slice(){
return array.slice();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
// 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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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();
}
%PrepareFunctionForOptimization(slice);
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]);
})();
// Packed
// 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() {
%PrepareFunctionForOptimization(slice0);
%PrepareFunctionForOptimization(slice);
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
arr = Object.seal([1,2,'a',4,5]);
test();
// Frozen
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() {
%PrepareFunctionForOptimization(slice0);
%PrepareFunctionForOptimization(slice);
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]);
test();
})();