blob: ce9cf6615c3b82c586db81cc283815be8711683d [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
/**
* Test for {@link TypeTightener}.
*/
public class TypeTightenerTest extends OptimizerTestBase {
@Override
protected void setUp() throws Exception {
super.setUp();
}
public void testCastOperation() throws Exception {
addSnippetClassDecl("static class A { " + "String name; public void set() { name = \"A\";} }");
addSnippetClassDecl("static class B extends A {" + "public void set() { name = \"B\";} }");
addSnippetClassDecl("static class C extends A {" + "public void set() { name = \"C\";} }");
Result result =
optimize("void", "A b = new B();", "((A)b).set();", "((B)b).set();", "((C)b).set();");
result.intoString("EntryPoint$B b = new EntryPoint$B();\n" + "b.set();\n" + "b.set();\n"
+ "((null) b).nullMethod();");
}
public void testConditional() throws Exception {
addSnippetClassDecl("static class D {}");
addSnippetClassDecl("static class C extends D {}");
addSnippetClassDecl("static class B extends C {}");
addSnippetClassDecl("static class A extends C {}");
Result result = optimize("void", "int x = 0;", "A a1 = new A();", "A a2 = new A();",
"B b = new B();", "C c = new C();", "C c1 = (x > 0) ? a1 : b;", "C c2 = (x > 0) ? a1 : a2;",
"D d = (x > 0) ? b : c;");
result.intoString("int x = 0;\n" + "EntryPoint$A a1 = new EntryPoint$A();\n"
+ "EntryPoint$A a2 = new EntryPoint$A();\n" + "EntryPoint$B b = new EntryPoint$B();\n"
+ "EntryPoint$C c = new EntryPoint$C();\n" + "EntryPoint$C c1 = x > 0 ? a1 : b;\n"
+ "EntryPoint$A c2 = x > 0 ? a1 : a2;\n" + "EntryPoint$C d = x > 0 ? b : c;");
}
public void testTightenLocalVariable() throws Exception {
addSnippetClassDecl("static abstract class B {};");
addSnippetClassDecl("static class C extends B{};");
addSnippetClassDecl("static class D extends C{};");
addSnippetClassDecl("static class E extends C{};");
Result result = optimize("void",
"B b;",
"b = new C();", "b = new D();", "b = new E();");
result.intoString("EntryPoint$C b;\n" + "b = new EntryPoint$C();\n"
+ "b = new EntryPoint$D();\n" + "b = new EntryPoint$E();");
}
public void testTightenField() throws Exception {
addSnippetClassDecl("static class A {};");
addSnippetClassDecl("static class B extends A{};");
addSnippetClassDecl("static class C extends B{};");
addSnippetClassDecl("static class D extends B{};");
addSnippetClassDecl("static class Test {private A a;"
+ "public void fun1() {a = new C();}"
+ "public void fun2() {a = new D();}"
+ "}");
Result result = optimize("void", "");
assertEquals("EntryPoint$B a",
OptimizerTestBase.findField(result.findClass("EntryPoint$Test"), "a").toString());
}
public void testTightenParameterBasedOnAssignment() throws Exception {
addSnippetClassDecl("static class A { }");
addSnippetClassDecl("static class B extends A { }");
addSnippetClassDecl("static class C extends B { }");
addSnippetClassDecl("static class D extends B { }");
addSnippetClassDecl("static void test(A a) { }");
Result result = optimize("void", "C c = new C(); D d = new D(); test(c); test(d);");
assertParameterTypes(result, "test", "EntryPoint$B");
}
public void testTightenParameterBasedOnLeafType() throws Exception {
addSnippetClassDecl("abstract static class A { void makeSureFunIsCalled() {fun(this);} }");
addSnippetClassDecl("static class B extends A {}");
addSnippetClassDecl("static void fun(A a) {if (a == null) return;}");
Result result = optimize("void", "");
assertParameterTypes(result, "fun", "EntryPoint$B");
}
public void testTightenParameterBasedOnOverriddens() throws Exception {
addSnippetClassDecl("static class A {}");
addSnippetClassDecl("static class B extends A {}");
addSnippetClassDecl("static class Test1 { public void fun(A a) {} }");
addSnippetClassDecl("static class Test2 extends Test1 {public void fun (A a) {} }");
Result result = optimize("void", "B b = new B();"
+ "Test1 test1 = new Test1();"
+ "test1.fun(b);");
assertParameterTypes(result, "EntryPoint$Test1.fun(Ltest/EntryPoint$A;)V", "EntryPoint$B");
assertParameterTypes(result, "EntryPoint$Test2.fun(Ltest/EntryPoint$A;)V", "EntryPoint$B");
}
public void testInstanceOf() throws Exception {
addSnippetClassDecl("static class A {};");
addSnippetClassDecl("static class B extends A {};");
Result result = optimize("void", "A a = new A();", "B b = new B();", "boolean test;",
"test = a instanceof A;", "test = b instanceof B;", "test = a instanceof B;",
"test = b instanceof A;");
result.intoString("EntryPoint$A a = new EntryPoint$A();\n"
+ "EntryPoint$B b = new EntryPoint$B();\n" + "boolean test;\n" + "test = true;\n"
+ "test = true;\n" + "test = a instanceof EntryPoint$B;\n" + "test = true;");
}
public void testMethodBasedOnLeafType() throws Exception {
addSnippetClassDecl("abstract static class A { void makeSureFunIsCalled() {fun(this);} }");
addSnippetClassDecl("static class B extends A {}");
addSnippetClassDecl("static A fun(A a) {return a;}");
Result result = optimize("void", "");
assertParameterTypes(result, "fun", "EntryPoint$B");
assertReturnType(result, "fun", "EntryPoint$B");
}
public void testMethodBasedOnReturns() throws Exception {
addSnippetClassDecl("static class A {}");
addSnippetClassDecl("static class B extends A {}");
addSnippetClassDecl("static class C extends B {}");
addSnippetClassDecl("static class D extends B {}");
addSnippetClassDecl(
"A fun(int a) { if(a<0) return new B(); else if(a==0) return new C(); return new D();}");
Result result = optimize("void", "");
assertReturnType(result, "fun", "EntryPoint$B");
}
public void testMethodBasedOnOverriders() throws Exception {
addSnippetClassDecl("abstract static class A { abstract public A fun(); }");
addSnippetClassDecl("static class B extends A { public B fun() {return new B();} }");
addSnippetClassDecl("static class C extends A { public B fun() {return new B();} }");
addSnippetClassDecl("static class D extends B { public D fun() {return new D();} }");
Result result = optimize("void", "");
assertReturnType(result, "EntryPoint$A.fun()Ltest/EntryPoint$A;", "EntryPoint$B");;
}
public void testTightenDependsOnTightenedMethods() throws Exception {
addSnippetClassDecl("abstract static class A {}");
addSnippetClassDecl("static class B extends A {}");
addSnippetClassDecl("static A test() { return new B(); }");
// test should be tightened to be static EntryPoint$B test();
addSnippetClassDecl("static class C {A a;}");
addSnippetClassDecl("static void fun1(A a) {if (a == null) return;}");
addSnippetClassDecl("static A fun2() { return test(); }"); // tighten method return type
Result result = optimize("void",
"A a = test();" // tighten local varialbe.
+ "C c = new C(); c.a = test();" // tighten field
+ "fun1(test());" // tighten parameter
);
assertReturnType(result, "fun2()Ltest/EntryPoint$A;", "EntryPoint$B");
assertParameterTypes(result, "fun1", "EntryPoint$B");;
assertEquals("EntryPoint$B a",
OptimizerTestBase.findLocal(result.findMethod(MAIN_METHOD_NAME), "a").toString());
assertEquals("EntryPoint$B a",
OptimizerTestBase.findField(result.findClass("EntryPoint$C"), "a").toString());
}
public void testDoNotTighten_staticDispatch() throws Exception {
addSnippetClassDecl("abstract static class A { public void m() {} }");
addSnippetClassDecl("static class B extends A { public void m() { super.m(); }}");
Result result = optimize("void",
"new B().m();");
assertForwardsTo(result.findMethod("test.EntryPoint$B.m()V"),
result.findMethod("test.EntryPoint$A.m()V"));
}
@Override
protected boolean optimizeMethod(JProgram program, JMethod method) {
program.addEntryMethod(findMainMethod(program));
boolean didChange = false;
// TODO(jbrosenberg): remove loop when Pruner/CFA interaction is perfect.
while (TypeTightener.exec(program).didChange()) {
didChange = true;
}
return didChange;
}
}