1// Copyright 2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Flags: --allow-natives-syntax --max-opt-count=100 --noalways-opt
29// Flags: --nocollect-maps
30
31// We specify max-opt-count because we opt/deopt the same function many
32// times.
33
34// We specify nocollect-maps because in gcstress we can end up deoptimizing
35// a function in a gc in the stack guard at the beginning of the (optimized)
36// function due to leftover map clearing work that results in deoptimizing
37// dependent code from those maps. The choice is to insert strategic gc()
38// calls or specify this flag.
39
40// It's nice to run this in other browsers too.
41var standalone = false;
42if (standalone) {
43  assertTrue = function(val) {
44    if (val != true) {
45      print("FAILURE");
46    }
47  }
48
49  assertFalse = function(val) {
50    if (val != false) {
51      print("FAILURE");
52    }
53  }
54
55  assertEquals = function(expected, val) {
56    if (expected !== val) {
57      print("FAILURE");
58    }
59  }
60
61  empty_func = function(name) { }
62  assertUnoptimized = empty_func;
63  assertOptimized = empty_func;
64
65  optimize = empty_func;
66  clearFunctionTypeFeedback = empty_func;
67  deoptimizeFunction = empty_func;
68} else {
69  optimize = function(name) {
70    %OptimizeFunctionOnNextCall(name);
71  }
72  clearFunctionTypeFeedback = function(name) {
73    %ClearFunctionTypeFeedback(name);
74  }
75  deoptimizeFunction = function(name) {
76    %DeoptimizeFunction(name);
77  }
78}
79
80function base_getter_test(create_func) {
81  var calls = 0;
82
83  // Testcase: setter in prototype chain
84  foo = function(a) { var x = a[0]; return x + 3; }
85  var a = create_func();
86  var ap = [];
87  ap.__defineGetter__(0, function() { calls++; return 0; });
88
89  foo(a);
90  foo(a);
91  foo(a);
92  delete a[0];
93
94  assertEquals(0, calls);
95  a.__proto__ = ap;
96  foo(a);
97  assertEquals(1, calls);
98  optimize(foo);
99  foo(a);
100  assertEquals(2, calls);
101  assertOptimized(foo);
102
103  // Testcase: getter "deep" in prototype chain.
104  clearFunctionTypeFeedback(foo);
105  deoptimizeFunction(foo);
106  clearFunctionTypeFeedback(foo);
107  calls = 0;
108
109  a = create_func();
110  var ap2 = [];
111  a.__proto__ = ap2;
112  foo(a);
113  foo(a);
114  foo(a);
115  delete a[0];
116
117  assertEquals(0, calls);
118
119  ap2.__proto__ = ap;  // "sneak" in a callback.
120  // The sneak case should be caught by unoptimized code too.
121  assertUnoptimized(foo);
122  foo(a);
123  foo(a);
124  foo(a);
125  assertEquals(3, calls);
126
127  // Testcase: getter added after optimization (feedback is monomorphic)
128  clearFunctionTypeFeedback(foo);
129  deoptimizeFunction(foo);
130  clearFunctionTypeFeedback(foo);
131  calls = 0;
132
133  a = create_func();
134  ap2 = [];
135  a.__proto__ = ap2;
136  foo(a);
137  foo(a);
138  foo(a);
139  optimize(foo);
140  foo(a);
141  assertOptimized(foo);
142  delete a[0];
143  ap2.__proto__ = ap;
144  foo(a);
145  assertOptimized(foo);  // getters don't require deopt on shape change.
146  assertEquals(1, calls);
147
148  // Testcase: adding additional getters to a prototype chain that already has
149  // one shouldn't deopt anything.
150  clearFunctionTypeFeedback(foo);
151  calls = 0;
152
153  a = create_func();
154  a.__proto__ = ap2;
155  bar = function(a) { return a[3] + 600; }
156  bar(a);
157  bar(a);
158  bar(a);
159  optimize(bar);
160  bar(a);
161  assertOptimized(bar);
162  assertEquals(0, calls);
163  delete a[3];
164  ap2.__defineGetter__(3, function() { calls++; return 0; });
165  bar(a);
166  assertOptimized(bar);
167  assertEquals(1, calls);
168}
169
170// Verify that map transitions don't confuse us.
171create_func_smi = function() { return [,,,,,,5]; }
172create_func_double = function() { return [,,,,,,5.5]; }
173create_func_fast = function() { return [,,,,,,true]; }
174
175var cf = [create_func_smi,
176          create_func_double,
177          create_func_fast];
178
179for(var c = 0; c < 3; c++) {
180  base_getter_test(cf[c]);
181}
182
183// A special test for LoadKeyedHoleMode. Ensure that optimized is generated
184// which sets ALLOW_RETURN_HOLE, then add a setter on the prototype that should
185// cause the function to deoptimize.
186
187var a = [3.5,,,3.5];
188fun = function(a) { return a[0] + 5.5; }
189fun(a);
190fun(a);
191fun(a);  // should have a monomorphic KeyedLoadIC.
192optimize(fun);
193fun(a);
194assertOptimized(fun);
195
196// returning undefined shouldn't phase us.
197delete a[0];
198fun(a);
199assertOptimized(fun);
200
201// but messing up the prototype chain will.
202a.__proto__ = [];
203fun(a);
204assertUnoptimized(fun);
205
206// Construct a non-trivial prototype chain.
207var a = [3.5,,,,3.5];
208var ap = [,,3.5];
209ap.__proto__ = a.__proto__;
210a.__proto__ = ap;
211fun(a);
212optimize(fun);
213fun(a);
214assertOptimized(fun);
215
216var calls = 0;
217delete a[0];
218ap.__defineGetter__(0, function() { calls++; return 0; });
219fun(a);
220assertEquals(1, calls);
221assertUnoptimized(fun);
222