// 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
// 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) {
%ClearFunctionTypeFeedback(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 < 3; c++) {
for(var s = 0; s < 3; s++) {
base_setter_test(cf[c], 0, values[s]);
base_setter_test(cf[c], 1, values[s]);
}
}