| // Copyright 2013 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 --expose-gc | |
| // Test stores on a join path. | |
| (function testJoin() { | |
| function constructor() { | |
| this.a = 0; | |
| } | |
| function join(mode, expected) { | |
| var object = new constructor(); | |
| if (mode) { | |
| object.a = 1; | |
| } else { | |
| object.a = 2; | |
| } | |
| assertEquals(expected, object.a); | |
| } | |
| %PrepareFunctionForOptimization(join); | |
| join(true, 1); join(true, 1); | |
| join(false, 2); join(false, 2); | |
| %OptimizeFunctionOnNextCall(join); | |
| join(true, 1); join(false, 2); | |
| })(); | |
| // Test loads and stores inside a loop. | |
| (function testLoop() { | |
| function constructor() { | |
| this.a = 0; | |
| this.b = 23; | |
| } | |
| function loop() { | |
| var object = new constructor(); | |
| for (var i = 1; i < 10; i++) { | |
| object.a = object.a + i; | |
| assertEquals(i*(i+1)/2, object.a); | |
| assertEquals(23, object.b); | |
| } | |
| assertEquals(45, object.a); | |
| assertEquals(23, object.b); | |
| } | |
| %PrepareFunctionForOptimization(loop); | |
| loop(); loop(); | |
| %OptimizeFunctionOnNextCall(loop); | |
| loop(); loop(); | |
| })(); | |
| // Test loads and stores inside nested loop. | |
| (function testNested() { | |
| function constructor() { | |
| this.a = 0; | |
| this.b = 0; | |
| this.c = 23; | |
| } | |
| function nested() { | |
| var object = new constructor(); | |
| for (var i = 1; i < 10; i++) { | |
| object.a = object.a + i; | |
| assertEquals(i*(i+1)/2, object.a); | |
| assertEquals((i-1)*6, object.b); | |
| assertEquals(23, object.c); | |
| for (var j = 1; j < 4; j++) { | |
| object.b = object.b + j; | |
| assertEquals(i*(i+1)/2, object.a); | |
| assertEquals((i-1)*6+j*(j+1)/2, object.b); | |
| assertEquals(23, object.c); | |
| } | |
| assertEquals(i*(i+1)/2, object.a); | |
| assertEquals(i*6, object.b); | |
| assertEquals(23, object.c); | |
| } | |
| assertEquals(45, object.a); | |
| assertEquals(54, object.b); | |
| assertEquals(23, object.c); | |
| } | |
| %PrepareFunctionForOptimization(nested); | |
| nested(); nested(); | |
| %OptimizeFunctionOnNextCall(nested); | |
| nested(); nested(); | |
| })(); | |
| // Test deoptimization with captured objects in local variables. | |
| (function testDeoptLocal() { | |
| var deopt = { deopt:false }; | |
| function constructor1() { | |
| this.a = 1.0; | |
| this.b = 2.3; | |
| this.c = 3.0; | |
| } | |
| function constructor2(o) { | |
| this.d = o; | |
| this.e = 4.5; | |
| } | |
| function func() { | |
| var o1 = new constructor1(); | |
| var o2 = new constructor2(o1); | |
| deopt.deopt; | |
| assertEquals(1.0, o1.a); | |
| assertEquals(2.3, o2.d.b); | |
| assertEquals(3.0, o2.d.c); | |
| assertEquals(4.5, o2.e); | |
| } | |
| %PrepareFunctionForOptimization(func); | |
| func(); func(); | |
| %OptimizeFunctionOnNextCall(func); | |
| func(); func(); | |
| delete deopt.deopt; | |
| func(); func(); | |
| })(); | |
| // Test deoptimization with captured objects on operand stack. | |
| (function testDeoptOperand() { | |
| var deopt = { deopt:false }; | |
| function constructor1() { | |
| this.a = 1.0; | |
| this.b = 2.3; | |
| deopt.deopt; | |
| assertEquals(1.0, this.a); | |
| assertEquals(2.3, this.b); | |
| this.b = 2.7; | |
| this.c = 3.0; | |
| this.d = 4.5; | |
| } | |
| function constructor2() { | |
| this.e = 5.0; | |
| this.f = new constructor1(); | |
| assertEquals(1.0, this.f.a); | |
| assertEquals(2.7, this.f.b); | |
| assertEquals(3.0, this.f.c); | |
| assertEquals(4.5, this.f.d); | |
| assertEquals(5.0, this.e); | |
| this.e = 5.9; | |
| this.g = 6.7; | |
| } | |
| function func() { | |
| var o = new constructor2(); | |
| assertEquals(1.0, o.f.a); | |
| assertEquals(2.7, o.f.b); | |
| assertEquals(3.0, o.f.c); | |
| assertEquals(4.5, o.f.d); | |
| assertEquals(5.9, o.e); | |
| assertEquals(6.7, o.g); | |
| } | |
| %PrepareFunctionForOptimization(func); | |
| func(); func(); | |
| %OptimizeFunctionOnNextCall(func); | |
| func(); func(); | |
| delete deopt.deopt; | |
| func(); func(); | |
| })(); | |
| // Test map checks on captured objects. | |
| (function testMapCheck() { | |
| var sum = 0; | |
| function getter() { return 27; } | |
| function setter(v) { sum += v; } | |
| function constructor() { | |
| this.x = 23; | |
| this.y = 42; | |
| } | |
| function check(x, y) { | |
| var o = new constructor(); | |
| assertEquals(x, o.x); | |
| assertEquals(y, o.y); | |
| } | |
| var monkey = Object.create(null, { | |
| x: { get:getter, set:setter }, | |
| y: { get:getter, set:setter } | |
| }); | |
| %PrepareFunctionForOptimization(check); | |
| check(23, 42); check(23, 42); | |
| %OptimizeFunctionOnNextCall(check); | |
| check(23, 42); check(23, 42); | |
| constructor.prototype = monkey; | |
| check(27, 27); check(27, 27); | |
| assertEquals(130, sum); | |
| })(); | |
| // Test OSR into a loop with captured objects. | |
| (function testOSR() { | |
| function constructor() { | |
| this.a = 23; | |
| } | |
| function osr1(length) { | |
| assertEquals(23, (new constructor()).a); | |
| var result = 0; | |
| for (var i = 0; i < length; i++) { | |
| result = (result + i) % 99; | |
| } | |
| return result; | |
| } | |
| function osr2(length) { | |
| var result = 0; | |
| for (var i = 0; i < length; i++) { | |
| result = (result + i) % 99; | |
| } | |
| assertEquals(23, (new constructor()).a); | |
| return result; | |
| } | |
| function osr3(length) { | |
| var result = 0; | |
| var o = new constructor(); | |
| for (var i = 0; i < length; i++) { | |
| result = (result + i) % 99; | |
| } | |
| assertEquals(23, o.a); | |
| return result; | |
| } | |
| function test(closure) { | |
| assertEquals(45, closure(10)); | |
| assertEquals(45, closure(10)); | |
| assertEquals(10, closure(50000)); | |
| } | |
| test(osr1); | |
| test(osr2); | |
| test(osr3); | |
| })(); | |
| // Test out-of-bounds access on captured objects. | |
| (function testOOB() { | |
| function cons1() { | |
| this.x = 1; | |
| this.y = 2; | |
| this.z = 3; | |
| } | |
| function cons2() { | |
| this.a = 7; | |
| } | |
| function oob(constructor, branch) { | |
| var o = new constructor(); | |
| if (branch) { | |
| return o.a; | |
| } else { | |
| return o.z; | |
| } | |
| } | |
| %PrepareFunctionForOptimization(oob); | |
| assertEquals(3, oob(cons1, false)); | |
| assertEquals(3, oob(cons1, false)); | |
| assertEquals(7, oob(cons2, true)); | |
| assertEquals(7, oob(cons2, true)); | |
| gc(); // Clears type feedback of constructor call. | |
| assertEquals(7, oob(cons2, true)); | |
| assertEquals(7, oob(cons2, true)); | |
| %OptimizeFunctionOnNextCall(oob); | |
| assertEquals(7, oob(cons2, true)); | |
| })(); | |
| // Test non-shallow nested graph of captured objects. | |
| (function testDeep() { | |
| var deopt = { deopt:false }; | |
| function constructor1() { | |
| this.x = 23; | |
| } | |
| function constructor2(nested) { | |
| this.a = 17; | |
| this.b = nested; | |
| this.c = 42; | |
| } | |
| function deep() { | |
| var o1 = new constructor1(); | |
| var o2 = new constructor2(o1); | |
| assertEquals(17, o2.a); | |
| assertEquals(23, o2.b.x); | |
| assertEquals(42, o2.c); | |
| o1.x = 99; | |
| deopt.deopt; | |
| assertEquals(99, o1.x); | |
| assertEquals(99, o2.b.x); | |
| } | |
| %PrepareFunctionForOptimization(deep); | |
| deep(); deep(); | |
| %OptimizeFunctionOnNextCall(deep); | |
| deep(); deep(); | |
| delete deopt.deopt; | |
| deep(); deep(); | |
| })(); | |
| // Test non-shallow nested graph of captured objects with duplicates | |
| (function testDeepDuplicate() { | |
| function constructor1() { | |
| this.x = 23; | |
| } | |
| function constructor2(nested) { | |
| this.a = 17; | |
| this.b = nested; | |
| this.c = 42; | |
| } | |
| function deep(shouldDeopt) { | |
| var o1 = new constructor1(); | |
| var o2 = new constructor2(o1); | |
| var o3 = new constructor2(o1); | |
| assertEquals(17, o2.a); | |
| assertEquals(23, o2.b.x); | |
| assertEquals(42, o2.c); | |
| o3.c = 54; | |
| o1.x = 99; | |
| if (shouldDeopt) %DeoptimizeFunction(deep); | |
| assertEquals(99, o1.x); | |
| assertEquals(99, o2.b.x); | |
| assertEquals(99, o3.b.x); | |
| assertEquals(54, o3.c); | |
| assertEquals(17, o3.a); | |
| assertEquals(42, o2.c); | |
| assertEquals(17, o2.a); | |
| o3.b.x = 1; | |
| assertEquals(1, o1.x); | |
| } | |
| %PrepareFunctionForOptimization(deep); | |
| deep(false); deep(false); | |
| %OptimizeFunctionOnNextCall(deep); | |
| deep(false); deep(false); | |
| deep(true); deep(true); | |
| })(); | |
| // Test non-shallow nested graph of captured objects with inline | |
| (function testDeepInline() { | |
| function h() { | |
| return { y : 3 }; | |
| } | |
| function g(x) { | |
| var u = { x : h() }; | |
| %DeoptimizeFunction(f); | |
| return u; | |
| } | |
| function f() { | |
| var l = { dummy : { } }; | |
| var r = g(l); | |
| assertEquals(3, r.x.y); | |
| } | |
| %PrepareFunctionForOptimization(f); | |
| f(); f(); f(); | |
| %OptimizeFunctionOnNextCall(f); | |
| f(); | |
| })(); | |
| // Test two nested objects | |
| (function testTwoNestedObjects() { | |
| function f() { | |
| var l = { x : { y : 111 } }; | |
| var l2 = { x : { y : 111 } }; | |
| %DeoptimizeFunction(f); | |
| assertEquals(111, l.x.y); | |
| assertEquals(111, l2.x.y); | |
| } | |
| %PrepareFunctionForOptimization(f); | |
| f(); f(); f(); | |
| %OptimizeFunctionOnNextCall(f); | |
| f(); | |
| })(); | |
| // Test a nested object and a duplicate | |
| (function testTwoObjectsWithDuplicate() { | |
| function f() { | |
| var l = { x : { y : 111 } }; | |
| var dummy = { d : 0 }; | |
| var l2 = l.x; | |
| %DeoptimizeFunction(f); | |
| assertEquals(111, l.x.y); | |
| assertEquals(111, l2.y); | |
| assertEquals(0, dummy.d); | |
| } | |
| %PrepareFunctionForOptimization(f); | |
| f(); f(); f(); | |
| %OptimizeFunctionOnNextCall(f); | |
| f(); | |
| })(); | |
| // Test materialization of a field that requires a Smi value. | |
| (function testSmiField() { | |
| var deopt = { deopt:false }; | |
| function constructor() { | |
| this.x = 1; | |
| } | |
| function field(x) { | |
| var o = new constructor(); | |
| o.x = x; | |
| deopt.deopt | |
| assertEquals(x, o.x); | |
| } | |
| %PrepareFunctionForOptimization(field); | |
| field(1); field(2); | |
| %OptimizeFunctionOnNextCall(field); | |
| field(3); field(4); | |
| delete deopt.deopt; | |
| field(5.5); field(6.5); | |
| })(); | |
| // Test materialization of a field that requires a heap object value. | |
| (function testHeapObjectField() { | |
| var deopt = { deopt:false }; | |
| function constructor() { | |
| this.x = {}; | |
| } | |
| function field(x) { | |
| var o = new constructor(); | |
| o.x = x; | |
| deopt.deopt | |
| assertEquals(x, o.x); | |
| } | |
| %PrepareFunctionForOptimization(field); | |
| field({}); field({}); | |
| %OptimizeFunctionOnNextCall(field); | |
| field({}); field({}); | |
| delete deopt.deopt; | |
| field(1); field(2); | |
| })(); |