1// Copyright 2012 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 --inline-accessors --max-opt-count=100
29
30var accessorCallCount, setterArgument, setterValue, obj, forceDeopt;
31
32// -----------------------------------------------------------------------------
33// Helpers for testing inlining of getters.
34
35function TestInlinedGetter(context, obj, expected) {
36  forceDeopt = { deopt: 0 };
37  accessorCallCount = 0;
38
39  assertEquals(expected, context(obj));
40  assertEquals(1, accessorCallCount);
41
42  assertEquals(expected, context(obj));
43  assertEquals(2, accessorCallCount);
44
45  %OptimizeFunctionOnNextCall(context);
46  assertEquals(expected, context(obj));
47  assertEquals(3, accessorCallCount);
48
49  forceDeopt = { /* empty*/ };
50  assertEquals(expected, context(obj));
51  assertEquals(4, accessorCallCount);
52}
53
54
55function value_context_for_getter(obj) {
56  return obj.getterProperty;
57}
58
59function test_context_for_getter(obj) {
60  if (obj.getterProperty) {
61    return 111;
62  } else {
63    return 222;
64  }
65}
66
67function effect_context_for_getter(obj) {
68  obj.getterProperty;
69  return 5678;
70}
71
72function TryGetter(context, getter, obj, expected, expectException) {
73  try {
74    TestInlinedGetter(context, obj, expected);
75    assertFalse(expectException);
76  } catch (exception) {
77    assertTrue(expectException);
78    assertEquals(7, exception.stack.split('\n').length);
79  }
80  %DeoptimizeFunction(context);
81  %ClearFunctionTypeFeedback(context);
82  %ClearFunctionTypeFeedback(getter);
83}
84
85function TestGetterInAllContexts(getter, obj, expected, expectException) {
86  TryGetter(value_context_for_getter, getter, obj, expected, expectException);
87  TryGetter(test_context_for_getter, getter, obj, expected ? 111 : 222,
88            expectException);
89  TryGetter(effect_context_for_getter, getter, obj, 5678, expectException);
90}
91
92// -----------------------------------------------------------------------------
93// Test getter returning something 'true'ish in all contexts.
94
95function getter1() {
96  assertSame(obj, this);
97  accessorCallCount++;
98  forceDeopt.deopt;
99  return 1234;
100}
101
102function ConstrG1() { }
103obj = Object.defineProperty(new ConstrG1(), "getterProperty", { get: getter1 });
104TestGetterInAllContexts(getter1, obj, 1234, false);
105obj = Object.create(obj);
106TestGetterInAllContexts(getter1, obj, 1234, false);
107
108// -----------------------------------------------------------------------------
109// Test getter returning false in all contexts.
110
111function getter2() {
112  assertSame(obj, this);
113  accessorCallCount++;
114  forceDeopt.deopt;
115  return false;
116}
117
118function ConstrG2() { }
119obj = Object.defineProperty(new ConstrG2(), "getterProperty", { get: getter2 });
120TestGetterInAllContexts(getter2, obj, false, false);
121obj = Object.create(obj);
122TestGetterInAllContexts(getter2, obj, false, false);
123
124// -----------------------------------------------------------------------------
125// Test getter without a return in all contexts.
126
127function getter3() {
128  assertSame(obj, this);
129  accessorCallCount++;
130  forceDeopt.deopt;
131}
132
133function ConstrG3() { }
134obj = Object.defineProperty(new ConstrG3(), "getterProperty", { get: getter3 });
135TestGetterInAllContexts(getter3, obj, undefined, false);
136obj = Object.create(obj);
137TestGetterInAllContexts(getter3, obj, undefined, false);
138
139// -----------------------------------------------------------------------------
140// Test getter with too many arguments without a return in all contexts.
141
142function getter4(a) {
143  assertSame(obj, this);
144  assertEquals(undefined, a);
145  accessorCallCount++;
146  forceDeopt.deopt;
147}
148
149function ConstrG4() { }
150obj = Object.defineProperty(new ConstrG4(), "getterProperty", { get: getter4 });
151TestGetterInAllContexts(getter4, obj, undefined, false);
152obj = Object.create(obj);
153TestGetterInAllContexts(getter4, obj, undefined, false);
154
155// -----------------------------------------------------------------------------
156// Test getter with too many arguments with a return in all contexts.
157
158function getter5(a) {
159  assertSame(obj, this);
160  assertEquals(undefined, a);
161  accessorCallCount++;
162  forceDeopt.deopt;
163  return 9876;
164}
165
166function ConstrG5() { }
167obj = Object.defineProperty(new ConstrG5(), "getterProperty", { get: getter5 });
168TestGetterInAllContexts(getter5, obj, 9876, false);
169obj = Object.create(obj);
170TestGetterInAllContexts(getter5, obj, 9876, false);
171
172// -----------------------------------------------------------------------------
173// Test getter which throws from optimized code.
174
175function getter6() {
176  assertSame(obj, this);
177  accessorCallCount++;
178  forceDeopt.deopt;
179  if (accessorCallCount == 4) { 123 in null; }
180  return 13579;
181}
182
183function ConstrG6() { }
184obj = Object.defineProperty(new ConstrG6(), "getterProperty", { get: getter6 });
185TestGetterInAllContexts(getter6, obj, 13579, true);
186obj = Object.create(obj);
187TestGetterInAllContexts(getter6, obj, 13579, true);
188
189// -----------------------------------------------------------------------------
190// Helpers for testing inlining of setters.
191
192function TestInlinedSetter(context, obj, value, expected) {
193  forceDeopt = { deopt: 0 };
194  accessorCallCount = 0;
195  setterArgument = value;
196
197  assertEquals(expected, context(obj, value));
198  assertEquals(value, setterValue);
199  assertEquals(1, accessorCallCount);
200
201  assertEquals(expected, context(obj, value));
202  assertEquals(value, setterValue);
203  assertEquals(2, accessorCallCount);
204
205  %OptimizeFunctionOnNextCall(context);
206  assertEquals(expected, context(obj, value));
207  assertEquals(value, setterValue);
208  assertEquals(3, accessorCallCount);
209
210  forceDeopt = { /* empty*/ };
211  assertEquals(expected, context(obj, value));
212  assertEquals(value, setterValue);
213  assertEquals(4, accessorCallCount);
214}
215
216function value_context_for_setter(obj, value) {
217  return obj.setterProperty = value;
218}
219
220function test_context_for_setter(obj, value) {
221  if (obj.setterProperty = value) {
222    return 333;
223  } else {
224    return 444;
225  }
226}
227
228function effect_context_for_setter(obj, value) {
229  obj.setterProperty = value;
230  return 666;
231}
232
233function TrySetter(context, setter, obj, expectException, value, expected) {
234  try {
235    TestInlinedSetter(context, obj, value, expected);
236    assertFalse(expectException);
237  } catch (exception) {
238    assertTrue(expectException);
239    assertEquals(7, exception.stack.split('\n').length);
240  }
241  %DeoptimizeFunction(context);
242  %ClearFunctionTypeFeedback(context);
243  %ClearFunctionTypeFeedback(setter);
244}
245
246function TestSetterInAllContexts(setter, obj, expectException) {
247  TrySetter(value_context_for_setter, setter, obj, expectException, 111, 111);
248  TrySetter(test_context_for_setter, setter, obj, expectException, true, 333);
249  TrySetter(test_context_for_setter, setter, obj, expectException, false, 444);
250  TrySetter(effect_context_for_setter, setter, obj, expectException, 555, 666);
251}
252
253// -----------------------------------------------------------------------------
254// Test setter without a return in all contexts.
255
256function setter1(value) {
257  assertSame(obj, this);
258  accessorCallCount++;
259  forceDeopt.deopt;
260  setterValue = value;
261}
262
263function ConstrS1() { }
264obj = Object.defineProperty(new ConstrS1(), "setterProperty", { set: setter1 });
265TestSetterInAllContexts(setter1, obj, false);
266obj = Object.create(obj);
267TestSetterInAllContexts(setter1, obj, false);
268
269// -----------------------------------------------------------------------------
270// Test setter returning something different than the RHS in all contexts.
271
272function setter2(value) {
273  assertSame(obj, this);
274  accessorCallCount++;
275  forceDeopt.deopt;
276  setterValue = value;
277  return 1000000;
278}
279
280function ConstrS2() { }
281obj = Object.defineProperty(new ConstrS2(), "setterProperty", { set: setter2 });
282TestSetterInAllContexts(setter2, obj, false);
283obj = Object.create(obj);
284TestSetterInAllContexts(setter2, obj, false);
285
286// -----------------------------------------------------------------------------
287// Test setter with too few arguments without a return in all contexts.
288
289function setter3() {
290  assertSame(obj, this);
291  accessorCallCount++;
292  forceDeopt.deopt;
293  setterValue = setterArgument;
294}
295
296function ConstrS3() { }
297obj = Object.defineProperty(new ConstrS3(), "setterProperty", { set: setter3 });
298TestSetterInAllContexts(setter3, obj, false);
299obj = Object.create(obj);
300TestSetterInAllContexts(setter3, obj, false);
301
302// -----------------------------------------------------------------------------
303// Test setter with too few arguments with a return in all contexts.
304
305function setter4() {
306  assertSame(obj, this);
307  accessorCallCount++;
308  forceDeopt.deopt;
309  setterValue = setterArgument;
310  return 2000000;
311}
312
313function ConstrS4() { }
314obj = Object.defineProperty(new ConstrS4(), "setterProperty", { set: setter4 });
315TestSetterInAllContexts(setter4, obj, false);
316obj = Object.create(obj);
317TestSetterInAllContexts(setter4, obj, false);
318
319// -----------------------------------------------------------------------------
320// Test setter with too many arguments without a return in all contexts.
321
322function setter5(value, foo) {
323  assertSame(obj, this);
324  assertEquals(undefined, foo);
325  accessorCallCount++;
326  forceDeopt.deopt;
327  setterValue = value;
328}
329
330function ConstrS5() { }
331obj = Object.defineProperty(new ConstrS5(), "setterProperty", { set: setter5 });
332TestSetterInAllContexts(setter5, obj, false);
333obj = Object.create(obj);
334TestSetterInAllContexts(setter5, obj, false);
335
336// -----------------------------------------------------------------------------
337// Test setter with too many arguments with a return in all contexts.
338
339function setter6(value, foo) {
340  assertSame(obj, this);
341  assertEquals(undefined, foo);
342  accessorCallCount++;
343  forceDeopt.deopt;
344  setterValue = value;
345  return 3000000;
346}
347
348function ConstrS6() { }
349obj = Object.defineProperty(new ConstrS6(), "setterProperty", { set: setter6 });
350TestSetterInAllContexts(setter6, obj, false);
351obj = Object.create(obj);
352TestSetterInAllContexts(setter6, obj, false);
353
354// -----------------------------------------------------------------------------
355// Test setter which throws from optimized code.
356
357function setter7(value) {
358  accessorCallCount++;
359  forceDeopt.deopt;
360  if (accessorCallCount == 4) { 123 in null; }
361  setterValue = value;
362}
363
364function ConstrS7() { }
365obj = Object.defineProperty(new ConstrS7(), "setterProperty", { set: setter7 });
366TestSetterInAllContexts(setter7, obj, true);
367obj = Object.create(obj);
368TestSetterInAllContexts(setter7, obj, true);
369