// 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 --max-opt-count=100 --noalways-opt // Flags: --crankshaft // We specify max-opt-count because we opt/deopt the same function many // times. // 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; optimize = empty_func; clearFunctionTypeFeedback = empty_func; deoptimizeFunction = empty_func; } else { optimize = function(name) { %OptimizeFunctionOnNextCall(name); } clearFunctionTypeFeedback = function(name) { %ClearFunctionFeedback(name); } deoptimizeFunction = function(name) { %DeoptimizeFunction(name); } } function base_setter_test(create_func, index, store_value) { var calls = 0; // Testcase: setter in prototype chain foo = function(a) { a[index] = store_value; } var a = create_func(); var ap = []; ap.__defineSetter__(index, function() { calls++; }); foo(a); foo(a); foo(a); delete a[index]; assertEquals(0, calls); a.__proto__ = ap; foo(a); assertEquals(1, calls); optimize(foo); foo(a); assertEquals(2, calls); assertOptimized(foo); // Testcase: setter added on prototype chain object already in place. clearFunctionTypeFeedback(foo); deoptimizeFunction(foo); clearFunctionTypeFeedback(foo); calls = 0; a = create_func(); var apap = []; a.__proto__ = apap; foo(a); foo(a); foo(a); delete a[index]; apap.__defineSetter__(index, function() { calls++; }); foo(a); foo(a); foo(a); assertEquals(3, calls); // Testcase: setter "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[index]; 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: setter added after optimization (feedback is monomorphic) clearFunctionTypeFeedback(foo); deoptimizeFunction(foo); clearFunctionTypeFeedback(foo); calls = 0; a = create_func(); ap2 = []; a.__proto__ = ap2; foo(a); foo(a); foo(a); optimize(foo); foo(a); assertOptimized(foo); delete a[index]; ap2.__proto__ = ap; foo(a); assertUnoptimized(foo); // map shape change should deopt foo. assertEquals(1, calls); // Testcase: adding additional setters to a prototype chain that already has // one shouldn't deopt anything. (ie, we aren't changing the map shape). clearFunctionTypeFeedback(foo); calls = 0; a = create_func(); a.__proto__ = ap2; bar = function(a) { a[index+1] = store_value; } bar(a); bar(a); bar(a); // store should be generic optimize(bar); bar(a); assertOptimized(bar); assertEquals(0, calls); delete a[index+1]; ap2.__defineSetter__(index+1, function() { calls++; }); bar(a); assertOptimized(bar); assertEquals(1, calls); } // Verify that map transitions don't confuse us. create_func_smi = function() { return [,,,,,,5]; } create_func_double = function() { return [0,,3.2,,,,5.5]; } create_func_fast = function() { return [,,,,,,true]; } create_func_dictionary = function() { var a = []; a.length = 100000; return a; } var cf = [create_func_smi, create_func_double, create_func_fast, create_func_dictionary]; var values = [3, 3.5, true]; for(var c = 0; c < cf.length; c++) { for(var s = 0; s < values.length; s++) { base_setter_test(cf[c], 0, values[s]); base_setter_test(cf[c], 1, values[s]); } }