|  | // Copyright 2011 the V8 project authors. All rights reserved. | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are | 
|  | // met: | 
|  | // | 
|  | //     * Redistributions of source code must retain the above copyright | 
|  | //       notice, this list of conditions and the following disclaimer. | 
|  | //     * Redistributions in binary form must reproduce the above | 
|  | //       copyright notice, this list of conditions and the following | 
|  | //       disclaimer in the documentation and/or other materials provided | 
|  | //       with the distribution. | 
|  | //     * Neither the name of Google Inc. nor the names of its | 
|  | //       contributors may be used to endorse or promote products derived | 
|  | //       from this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | // Flags: --allow-natives-syntax --noalways-opt --opt | 
|  |  | 
|  | // It's nice to run this in other browsers too. | 
|  | var standalone = false; | 
|  | if (standalone) { | 
|  | assertTrue = function(val) { | 
|  | if (val != true) { | 
|  | print("FAILURE"); | 
|  | } | 
|  | } | 
|  |  | 
|  | assertFalse = function(val) { | 
|  | if (val != false) { | 
|  | print("FAILURE"); | 
|  | } | 
|  | } | 
|  |  | 
|  | assertEquals = function(expected, val) { | 
|  | if (expected !== val) { | 
|  | print("FAILURE"); | 
|  | } | 
|  | } | 
|  |  | 
|  | empty_func = function(name) { } | 
|  | assertUnoptimized = empty_func; | 
|  | assertOptimized = empty_func; | 
|  |  | 
|  | prepareForOptimize = emtpy_func; | 
|  | optimize = empty_func; | 
|  | clearFunctionTypeFeedback = empty_func; | 
|  | deoptimizeFunction = empty_func; | 
|  | } else { | 
|  | optimize = function(name) { | 
|  | %OptimizeFunctionOnNextCall(name); | 
|  | } | 
|  | prepareForOptimize = function(name) { | 
|  | %PrepareFunctionForOptimization(name); | 
|  | } | 
|  | clearFunctionTypeFeedback = function(name) { | 
|  | %ClearFunctionFeedback(name); | 
|  | } | 
|  | deoptimizeFunction = function(name) { | 
|  | %DeoptimizeFunction(name); | 
|  | } | 
|  | } | 
|  |  | 
|  | function base_getter_test(create_func) { | 
|  | var calls = 0; | 
|  |  | 
|  | // Testcase: setter in prototype chain | 
|  | foo = function(a) { var x = a[0]; return x + 3; } | 
|  | var a = create_func(); | 
|  | var ap = []; | 
|  | ap.__defineGetter__(0, function() { calls++; return 0; }); | 
|  |  | 
|  | prepareForOptimize(foo); | 
|  | foo(a); | 
|  | assertUnoptimized(foo); | 
|  | // Smi and Double elements transition the KeyedLoadIC to Generic state | 
|  | // here, because they miss twice with the same map when loading the hole. | 
|  | // For HOLEY_ELEMENTS, however, the IC knows how to convert the hole | 
|  | // to undefined if the prototype is the original array prototype, so it | 
|  | // stays monomorphic for now... | 
|  | foo(a); | 
|  | foo(a); | 
|  | delete a[0]; | 
|  |  | 
|  | assertEquals(0, calls); | 
|  | a.__proto__ = ap; | 
|  | // ...and later becomes polymorphic when it sees a second map. Optimized | 
|  | // code will therefore inline the elements access, and deopt right away | 
|  | // when it loads the hole from index [0]. | 
|  | // Possible solutions: | 
|  | // - remove the convert_hole_to_undefined flag from the IC, to force it | 
|  | //   into generic state for all elements kinds. Cost: slower ICs in code | 
|  | //   that doesn't get optimized. | 
|  | // - teach Turbofan about the same trick: for holey elements with the | 
|  | //   original array prototype, convert hole to undefined inline. Cost: | 
|  | //   larger optimized code size, because the loads for different maps with | 
|  | //   the same elements kind can no longer be consolidated if they handle | 
|  | //   the hole differently. | 
|  | // - call "foo" twice after setting a.__proto__ and before optimizing it; | 
|  | //   this is the simplest fix so let's do that for now. | 
|  | foo(a); | 
|  | assertEquals(1, calls); | 
|  | foo(a); | 
|  | assertEquals(2, calls); | 
|  | optimize(foo); | 
|  | foo(a); | 
|  | assertEquals(3, calls); | 
|  | assertOptimized(foo); | 
|  |  | 
|  | // Testcase: getter "deep" in prototype chain. | 
|  | clearFunctionTypeFeedback(foo); | 
|  | deoptimizeFunction(foo); | 
|  | clearFunctionTypeFeedback(foo); | 
|  | calls = 0; | 
|  |  | 
|  | a = create_func(); | 
|  | var ap2 = []; | 
|  | a.__proto__ = ap2; | 
|  | foo(a); | 
|  | foo(a); | 
|  | foo(a); | 
|  | delete a[0]; | 
|  |  | 
|  | assertEquals(0, calls); | 
|  |  | 
|  | ap2.__proto__ = ap;  // "sneak" in a callback. | 
|  | // The sneak case should be caught by unoptimized code too. | 
|  | assertUnoptimized(foo); | 
|  | foo(a); | 
|  | foo(a); | 
|  | foo(a); | 
|  | assertEquals(3, calls); | 
|  |  | 
|  | // Testcase: getter added after optimization (feedback is monomorphic) | 
|  | clearFunctionTypeFeedback(foo); | 
|  | deoptimizeFunction(foo); | 
|  | clearFunctionTypeFeedback(foo); | 
|  | calls = 0; | 
|  |  | 
|  | a = create_func(); | 
|  | ap2 = []; | 
|  | a.__proto__ = ap2; | 
|  | prepareForOptimize(foo); | 
|  | foo(a); | 
|  | foo(a); | 
|  | foo(a); | 
|  | optimize(foo); | 
|  | foo(a); | 
|  | assertOptimized(foo); | 
|  | delete a[0]; | 
|  | ap2.__proto__ = ap; | 
|  | foo(a); | 
|  | assertOptimized(foo);  // getters don't require deopt on shape change. | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // Testcase: adding additional getters to a prototype chain that already has | 
|  | // one shouldn't deopt anything. | 
|  | clearFunctionTypeFeedback(foo); | 
|  | calls = 0; | 
|  |  | 
|  | a = create_func(); | 
|  | a.__proto__ = ap2; | 
|  | bar = function(a) { return a[3] + 600; } | 
|  | prepareForOptimize(bar); | 
|  | bar(a); | 
|  | bar(a); | 
|  | bar(a); | 
|  | optimize(bar); | 
|  | bar(a); | 
|  | assertOptimized(bar); | 
|  | assertEquals(0, calls); | 
|  | delete a[3]; | 
|  | ap2.__defineGetter__(3, function() { calls++; return 0; }); | 
|  | bar(a); | 
|  | assertOptimized(bar); | 
|  | assertEquals(1, calls); | 
|  |  | 
|  | // Reset the state of foo and bar. | 
|  | clearFunctionTypeFeedback(foo); | 
|  | deoptimizeFunction(foo); | 
|  | clearFunctionTypeFeedback(foo); | 
|  |  | 
|  | clearFunctionTypeFeedback(bar); | 
|  | deoptimizeFunction(bar); | 
|  | clearFunctionTypeFeedback(bar); | 
|  | } | 
|  |  | 
|  | // Verify that map transitions don't confuse us. | 
|  | create_func_smi = function() { return [,,,,,,5]; } | 
|  | create_func_double = function() { return [,,,,,,5.5]; } | 
|  | create_func_fast = function() { return [,,,,,,true]; } | 
|  |  | 
|  | var cf = [create_func_smi, | 
|  | create_func_double, | 
|  | create_func_fast]; | 
|  |  | 
|  | for(var c = 0; c < cf.length; c++) { | 
|  | base_getter_test(cf[c]); | 
|  | } | 
|  |  | 
|  | // A special test for LoadKeyedHoleMode. Ensure that optimized is generated | 
|  | // which sets ALLOW_RETURN_HOLE, then add a setter on the prototype that should | 
|  | // cause the function to deoptimize. | 
|  |  | 
|  | var a = [3.5,,,3.5]; | 
|  | fun = function(a) { return a[0] + 5.5; } | 
|  | prepareForOptimize(fun); | 
|  | fun(a); | 
|  | fun(a); | 
|  | fun(a);  // should have a monomorphic KeyedLoadIC. | 
|  | optimize(fun); | 
|  | fun(a); | 
|  | assertOptimized(fun); | 
|  |  | 
|  | // returning undefined shouldn't phase us. | 
|  | delete a[0]; | 
|  | fun(a); | 
|  | assertOptimized(fun); | 
|  |  | 
|  | // but messing up the prototype chain will. | 
|  | a.__proto__ = []; | 
|  | fun(a); | 
|  | assertUnoptimized(fun); | 
|  |  | 
|  | // Construct a non-trivial prototype chain. | 
|  | var a = [3.5,,,,3.5]; | 
|  | var ap = [,,3.5]; | 
|  | ap.__proto__ = a.__proto__; | 
|  | a.__proto__ = ap; | 
|  | prepareForOptimize(fun); | 
|  | fun(a); | 
|  | optimize(fun); | 
|  | fun(a); | 
|  | assertOptimized(fun); | 
|  |  | 
|  | var calls = 0; | 
|  | delete a[0]; | 
|  | ap.__defineGetter__(0, function() { calls++; return 0; }); | 
|  | fun(a); | 
|  | assertEquals(1, calls); | 
|  | assertUnoptimized(fun); |