1// Copyright 2013 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 --use-escape-analysis --expose-gc
29
30
31// Test stores on a join path.
32(function testJoin() {
33  function constructor() {
34    this.a = 0;
35  }
36  function join(mode, expected) {
37    var object = new constructor();
38    if (mode) {
39      object.a = 1;
40    } else {
41      object.a = 2;
42    }
43    assertEquals(expected, object.a);
44  }
45  join(true, 1); join(true, 1);
46  join(false, 2); join(false, 2);
47  %OptimizeFunctionOnNextCall(join);
48  join(true, 1); join(false, 2);
49})();
50
51
52// Test loads and stores inside a loop.
53(function testLoop() {
54  function constructor() {
55    this.a = 0;
56    this.b = 23;
57  }
58  function loop() {
59    var object = new constructor();
60    for (var i = 1; i < 10; i++) {
61      object.a = object.a + i;
62      assertEquals(i*(i+1)/2, object.a);
63      assertEquals(23, object.b);
64    }
65    assertEquals(45, object.a);
66    assertEquals(23, object.b);
67  }
68  loop(); loop();
69  %OptimizeFunctionOnNextCall(loop);
70  loop(); loop();
71})();
72
73
74// Test loads and stores inside nested loop.
75(function testNested() {
76  function constructor() {
77    this.a = 0;
78    this.b = 0;
79    this.c = 23;
80  }
81  function nested() {
82    var object = new constructor();
83    for (var i = 1; i < 10; i++) {
84      object.a = object.a + i;
85      assertEquals(i*(i+1)/2, object.a);
86      assertEquals((i-1)*6, object.b);
87      assertEquals(23, object.c);
88      for (var j = 1; j < 4; j++) {
89        object.b = object.b + j;
90        assertEquals(i*(i+1)/2, object.a);
91        assertEquals((i-1)*6+j*(j+1)/2, object.b);
92        assertEquals(23, object.c);
93      }
94      assertEquals(i*(i+1)/2, object.a);
95      assertEquals(i*6, object.b);
96      assertEquals(23, object.c);
97    }
98    assertEquals(45, object.a);
99    assertEquals(54, object.b);
100    assertEquals(23, object.c);
101  }
102  nested(); nested();
103  %OptimizeFunctionOnNextCall(nested);
104  nested(); nested();
105})();
106
107
108// Test deoptimization with captured objects in local variables.
109(function testDeoptLocal() {
110  var deopt = { deopt:false };
111  function constructor1() {
112    this.a = 1.0;
113    this.b = 2.3;
114    this.c = 3.0;
115  }
116  function constructor2(o) {
117    this.d = o;
118    this.e = 4.5;
119  }
120  function func() {
121    var o1 = new constructor1();
122    var o2 = new constructor2(o1);
123    deopt.deopt;
124    assertEquals(1.0, o1.a);
125    assertEquals(2.3, o2.d.b);
126    assertEquals(3.0, o2.d.c);
127    assertEquals(4.5, o2.e);
128  }
129  func(); func();
130  %OptimizeFunctionOnNextCall(func);
131  func(); func();
132  delete deopt.deopt;
133  func(); func();
134})();
135
136
137// Test deoptimization with captured objects on operand stack.
138(function testDeoptOperand() {
139  var deopt = { deopt:false };
140  function constructor1() {
141    this.a = 1.0;
142    this.b = 2.3;
143    deopt.deopt;
144    assertEquals(1.0, this.a);
145    assertEquals(2.3, this.b);
146    this.b = 2.7;
147    this.c = 3.0;
148    this.d = 4.5;
149  }
150  function constructor2() {
151    this.e = 5.0;
152    this.f = new constructor1();
153    assertEquals(1.0, this.f.a);
154    assertEquals(2.7, this.f.b);
155    assertEquals(3.0, this.f.c);
156    assertEquals(4.5, this.f.d);
157    assertEquals(5.0, this.e);
158    this.e = 5.9;
159    this.g = 6.7;
160  }
161  function func() {
162    var o = new constructor2();
163    assertEquals(1.0, o.f.a);
164    assertEquals(2.7, o.f.b);
165    assertEquals(3.0, o.f.c);
166    assertEquals(4.5, o.f.d);
167    assertEquals(5.9, o.e);
168    assertEquals(6.7, o.g);
169  }
170  func(); func();
171  %OptimizeFunctionOnNextCall(func);
172  func(); func();
173  delete deopt.deopt;
174  func(); func();
175})();
176
177
178// Test map checks on captured objects.
179(function testMapCheck() {
180  var sum = 0;
181  function getter() { return 27; }
182  function setter(v) { sum += v; }
183  function constructor() {
184    this.x = 23;
185    this.y = 42;
186  }
187  function check(x, y) {
188    var o = new constructor();
189    assertEquals(x, o.x);
190    assertEquals(y, o.y);
191  }
192  var monkey = Object.create(null, {
193    x: { get:getter, set:setter },
194    y: { get:getter, set:setter }
195  });
196  check(23, 42); check(23, 42);
197  %OptimizeFunctionOnNextCall(check);
198  check(23, 42); check(23, 42);
199  constructor.prototype = monkey;
200  check(27, 27); check(27, 27);
201  assertEquals(130, sum);
202})();
203
204
205// Test OSR into a loop with captured objects.
206(function testOSR() {
207  function constructor() {
208    this.a = 23;
209  }
210  function osr1(length) {
211    assertEquals(23, (new constructor()).a);
212    var result = 0;
213    for (var i = 0; i < length; i++) {
214      result = (result + i) % 99;
215    }
216    return result;
217  }
218  function osr2(length) {
219    var result = 0;
220    for (var i = 0; i < length; i++) {
221      result = (result + i) % 99;
222    }
223    assertEquals(23, (new constructor()).a);
224    return result;
225  }
226  function osr3(length) {
227    var result = 0;
228    var o = new constructor();
229    for (var i = 0; i < length; i++) {
230      result = (result + i) % 99;
231    }
232    assertEquals(23, o.a);
233    return result;
234  }
235  function test(closure) {
236    assertEquals(45, closure(10));
237    assertEquals(45, closure(10));
238    assertEquals(10, closure(50000));
239  }
240  test(osr1);
241  test(osr2);
242  test(osr3);
243})();
244
245
246// Test out-of-bounds access on captured objects.
247(function testOOB() {
248  function cons1() {
249    this.x = 1;
250    this.y = 2;
251    this.z = 3;
252  }
253  function cons2() {
254    this.a = 7;
255  }
256  function oob(constructor, branch) {
257    var o = new constructor();
258    if (branch) {
259      return o.a;
260    } else {
261      return o.z;
262    }
263  }
264  assertEquals(3, oob(cons1, false));
265  assertEquals(3, oob(cons1, false));
266  assertEquals(7, oob(cons2, true));
267  assertEquals(7, oob(cons2, true));
268  gc();  // Clears type feedback of constructor call.
269  assertEquals(7, oob(cons2, true));
270  assertEquals(7, oob(cons2, true));
271  %OptimizeFunctionOnNextCall(oob);
272  assertEquals(7, oob(cons2, true));
273})();
274
275
276// Test non-shallow nested graph of captured objects.
277(function testDeep() {
278  var deopt = { deopt:false };
279  function constructor1() {
280    this.x = 23;
281  }
282  function constructor2(nested) {
283    this.a = 17;
284    this.b = nested;
285    this.c = 42;
286  }
287  function deep() {
288    var o1 = new constructor1();
289    var o2 = new constructor2(o1);
290    assertEquals(17, o2.a);
291    assertEquals(23, o2.b.x);
292    assertEquals(42, o2.c);
293    o1.x = 99;
294    deopt.deopt;
295    assertEquals(99, o1.x);
296    assertEquals(99, o2.b.x);
297  }
298  deep(); deep();
299  %OptimizeFunctionOnNextCall(deep);
300  deep(); deep();
301  delete deopt.deopt;
302  deep(); deep();
303})();
304
305
306// Test non-shallow nested graph of captured objects with duplicates
307(function testDeepDuplicate() {
308  function constructor1() {
309    this.x = 23;
310  }
311  function constructor2(nested) {
312    this.a = 17;
313    this.b = nested;
314    this.c = 42;
315  }
316  function deep(shouldDeopt) {
317    var o1 = new constructor1();
318    var o2 = new constructor2(o1);
319    var o3 = new constructor2(o1);
320    assertEquals(17, o2.a);
321    assertEquals(23, o2.b.x);
322    assertEquals(42, o2.c);
323    o3.c = 54;
324    o1.x = 99;
325    if (shouldDeopt) %DeoptimizeFunction(deep);
326    assertEquals(99, o1.x);
327    assertEquals(99, o2.b.x);
328    assertEquals(99, o3.b.x);
329    assertEquals(54, o3.c);
330    assertEquals(17, o3.a);
331    assertEquals(42, o2.c);
332    assertEquals(17, o2.a);
333    o3.b.x = 1;
334    assertEquals(1, o1.x);
335  }
336  deep(false); deep(false);
337  %OptimizeFunctionOnNextCall(deep);
338  deep(false); deep(false);
339  deep(true); deep(true);
340})();
341
342
343// Test non-shallow nested graph of captured objects with inline
344(function testDeepInline() {
345  function h() {
346    return { y : 3 };
347  }
348
349  function g(x) {
350    var u = { x : h() };
351    %DeoptimizeFunction(f);
352    return u;
353  }
354
355  function f() {
356    var l = { dummy : { } };
357    var r = g(l);
358    assertEquals(3, r.x.y);
359  }
360
361  f(); f(); f();
362  %OptimizeFunctionOnNextCall(f);
363  f();
364})();
365
366
367// Test two nested objects
368(function testTwoNestedObjects() {
369  function f() {
370    var l = { x : { y : 111 } };
371    var l2 = { x : { y : 111 } };
372    %DeoptimizeFunction(f);
373    assertEquals(111, l.x.y);
374    assertEquals(111, l2.x.y);
375  }
376
377  f(); f(); f();
378  %OptimizeFunctionOnNextCall(f);
379  f();
380})();
381
382
383// Test a nested object and a duplicate
384(function testTwoObjectsWithDuplicate() {
385  function f() {
386    var l = { x : { y : 111 } };
387    var dummy = { d : 0 };
388    var l2 = l.x;
389    %DeoptimizeFunction(f);
390    assertEquals(111, l.x.y);
391    assertEquals(111, l2.y);
392    assertEquals(0, dummy.d);
393  }
394
395  f(); f(); f();
396  %OptimizeFunctionOnNextCall(f);
397  f();
398})();
399
400
401// Test materialization of a field that requires a Smi value.
402(function testSmiField() {
403  var deopt = { deopt:false };
404  function constructor() {
405    this.x = 1;
406  }
407  function field(x) {
408    var o = new constructor();
409    o.x = x;
410    deopt.deopt
411    assertEquals(x, o.x);
412  }
413  field(1); field(2);
414  %OptimizeFunctionOnNextCall(field);
415  field(3); field(4);
416  delete deopt.deopt;
417  field(5.5); field(6.5);
418})();
419
420
421// Test materialization of a field that requires a heap object value.
422(function testHeapObjectField() {
423  var deopt = { deopt:false };
424  function constructor() {
425    this.x = {};
426  }
427  function field(x) {
428    var o = new constructor();
429    o.x = x;
430    deopt.deopt
431    assertEquals(x, o.x);
432  }
433  field({}); field({});
434  %OptimizeFunctionOnNextCall(field);
435  field({}); field({});
436  delete deopt.deopt;
437  field(1); field(2);
438})();
439