| // 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(); |
| })(); |