blob: 2b27c4d9a11c07943f3b0368525b597f5b0ee881 [file] [log] [blame]
/*
* Copyright 2015 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.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.javac.testing.impl.JavaResourceBase;
import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
/**
* Test for {@link UnifyAst}.
*/
public class UnifyAstTest extends OptimizerTestBase {
public void testPackageInfo_defaultPackagePresent() throws Exception {
final MockJavaResource packageInfo =
JavaResourceBase.createMockJavaResource("package-info");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public class A {",
"}");
addAll(packageInfo, A);
Result result = optimize("void", "");
assertNotNull(result.findClass("package-info"));
}
public void testPackageInfo_defaultPackageAbsent() throws Exception {
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public class A {",
"}");
addAll(A);
Result result = optimize("void", "");
assertNull(result.findClass("package-info"));
}
public void testOverrides_base() throws Exception {
addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
Result result = optimize("void", "");
assertOverrides(result, "a.B.m()V", "a.I.m()V", "a.A.m()V");
assertOverrides(result, "a.B.m1()La/A;", "a.I.m1()La/A;", "a.A.m1()La/A;");
assertOverrides(result, "a.B.pp()V", "a.I.pp()V", "a.A.pp()V");
assertOverrides(result, "b.C.m1()La/A;", "a.A.m1()La/A;");
// The method dispatched for b.C.m()V is actually a.A,m()V.
// no artificial forwarding method was inserted.
assertEquals("a.A.m()V", findMethod(result, "b.C.m()V").toString());
// and for and b.D.m()V is actually a.B,m()V.
assertEquals("a.B.m()V", findMethod(result, "b.D.m()V").toString());
}
public void testOverrides_differentReturnTypes() throws Exception {
addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
Result result = optimize("void", "");
assertOverrides(result, "a.B.m1()La/A;", "a.I.m1()La/A;" ,"a.A.m1()La/A;");
assertOverrides(result, "a.A.m1()La/A;");
assertOverrides(result, "b.C.m1()La/A;", "a.A.m1()La/A;");
assertOverrides(result, "b.D.m1()La/A;", "a.I.m1()La/A;", "a.A.m1()La/A;", "a.B.m1()La/A;");
assertOverrides(result, "b.C.m1()Lb/C;");
assertOverrides(result, "b.D.m1()Lb/D;");
assertOverrides(result, "b.E.m1()Lb/C;", "b.C.m1()Lb/C;");
assertOverrides(result, "b.E.m1()Lb/E;");
}
public void testOverrides_orderInOverriddenSet() throws Exception {
addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
Result result = optimize("void", "");
for (JDeclaredType type : result.getOptimizedProgram().getDeclaredTypes()) {
for (JMethod method : type.getMethods()) {
JavaAstVerifier.assertCorrectOverriddenOrder(result.getOptimizedProgram(), method);
}
}
}
public void testOverrides_orderInOverridingnSet() throws Exception {
addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
Result result = optimize("void", "");
for (JDeclaredType type : result.getOptimizedProgram().getDeclaredTypes()) {
for (JMethod method : type.getMethods()) {
JavaAstVerifier.assertCorrectOverridingOrder(result.getOptimizedProgram(), method);
}
}
}
public void testOverrides_packagePrivate() throws Exception {
addAll(A_A, A_I, A_J, A_B, B_C, B_D, B_E);
Result result = optimize("void", "");
assertOverrides(result, "a.B.pp()V", "a.A.pp()V", "a.I.pp()V");
assertOverrides(result, "a.A.pp()V");
assertOverrides(result, "a.I.pp()V");
assertOverrides(result, "a.J.pp()V", "a.I.pp()V");
assertOverrides(result, "b.C.pp()V");
assertEquals("a.B.pp()V", findMethod(result, "b.D.pp()V").toString());
}
/**
* There are three main scenarios in accidental overrides:
* <ul>
* <li>
* (1) an interface that extends two or more different interfaces that declare or inherit a
* method with the same signature,
* </li>
* <li>
* (2) an a class that accidentally implements a method of an interface, and
* </li>
* <li>
* (3) an a abstract class that accidentally declares a method of an interface.
* </li>
* </ul>
*/
public void testAccidentalOverrides_interfaceCase() throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" void m();",
"}");
final MockJavaResource I2 =
JavaResourceBase.createMockJavaResource("I2",
"public interface I2 {",
" void m();",
"}");
final MockJavaResource I3 =
JavaResourceBase.createMockJavaResource("I3",
"public interface I3 extends I1, I2 {",
"}");
addAll(I1, I2, I3);
Result result = optimize("void", "");
JMethod I3_m = findMethod(result, "I3.m()V");
assertTrue(I3_m.isAbstract());
assertOverrides(result, "I3.m()V", "I1.m()V", "I2.m()V");
}
public void testAccidentalOverrides_abstractClassCase() throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" void m();",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public abstract class A {",
" public abstract void m();",
"}");
final MockJavaResource B =
JavaResourceBase.createMockJavaResource("B",
"public abstract class B extends A {",
"}");
final MockJavaResource C1 =
JavaResourceBase.createMockJavaResource("C1",
"public abstract class C1 implements I11 {",
"}");
final MockJavaResource C2 =
JavaResourceBase.createMockJavaResource("C2",
"public abstract class C2 extends B implements I11 {",
"}");
addAll(I1, I11, A, B, C1, C2);
Result result = optimize("void", "");
JMethod C1_m = findMethod(result, "C1.m()V");
assertTrue(C1_m.isAbstract());
assertOverrides(result, "C1.m()V", "I1.m()V");
JMethod C2_m = findMethod(result, "C2.m()V");
assertTrue(C2_m.isAbstract());
assertOverrides(result, "C2.m()V", "A.m()V", "I1.m()V");
}
/**
* If the method in the superclass is abstract and package private, the stub needs to be marked
* as an override. (If the method was concrete and package private then a compiler error will
* be emitted by JDT).
*/
public void testAccidentalOverrides_abstractClassPackagePrivateMethod()
throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" void m();",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public abstract class A {",
" abstract void m(); // m() is package private.",
"}");
final MockJavaResource B =
JavaResourceBase.createMockJavaResource("B",
"public abstract class B extends A {",
"}");
// C exposes package private method A.m()V.
final MockJavaResource C =
JavaResourceBase.createMockJavaResource("C",
"public abstract class C extends B implements I11 {",
"}");
addAll(I1, I11, A, B, C);
Result result = optimize("void", "");
JMethod C_m = findMethod(result, "C.m()V");
assertTrue(C_m.isAbstract());
assertOverrides(result, "C.m()V", "A.m()V", "I1.m()V");
}
public void testAccidentalOverrides_concreteImplementationCase()
throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" void m();",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public class A {",
" public void m() {};",
"}");
final MockJavaResource B =
JavaResourceBase.createMockJavaResource("B",
"public abstract class B extends A {",
"}");
final MockJavaResource C =
JavaResourceBase.createMockJavaResource("C",
"public abstract class C extends B implements I11 {",
"}");
addAll(I1, I11, A, B, C);
Result result = optimize("void", "");
JMethod C_m = findMethod(result, "C.m()V");
assertFalse(C_m.isAbstract());
assertForwardsTo(C_m, findMethod(result, "A.m()V"));
assertOverrides(result, "C.m()V", "A.m()V", "I1.m()V");
}
public void testAccidentalOverrides_concreteImplementationInterfaceMethodDefault()
throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" default void m() {}",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public class A {",
" public void m() {};",
"}");
final MockJavaResource B =
JavaResourceBase.createMockJavaResource("B",
"public abstract class B extends A {",
"}");
final MockJavaResource C =
JavaResourceBase.createMockJavaResource("C",
"public abstract class C extends B implements I11 {",
"}");
addAll(I1, I11, A, B, C);
Result result = optimize("void", "");
JMethod C_m = findMethod(result, "C.m()V");
assertFalse(C_m.isAbstract());
assertForwardsTo(C_m, findMethod(result, "A.m()V"));
assertOverrides(result, "C.m()V", "A.m()V", "I1.m()V");
}
public void testDefaults_simpleCase()
throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" default void m() {}",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public class A implements I11 {",
"}");
addAll(I1, I11, A);
Result result = optimize("void", "");
JMethod A_m = findMethod(result, "A.m()V");
assertFalse(A_m.isAbstract());
assertForwardsTo(A_m, findMethod(result, "I1.m()V"));
assertOverrides(result, "A.m()V", "I1.m()V");
}
public void testDefaults_diamond()
throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" default void m() {}",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource I12 =
JavaResourceBase.createMockJavaResource("I12",
"public interface I12 extends I1 {",
" default void m() {}",
"}");
final MockJavaResource A1 =
JavaResourceBase.createMockJavaResource("A1",
"public abstract class A1 implements I11, I12 {",
"}");
final MockJavaResource A2 =
JavaResourceBase.createMockJavaResource("A2",
"public abstract class A2 implements I12, I11 {",
"}");
addAll(I1, I11, I12, A1, A2);
Result result = optimize("void", "");
JMethod A1_m = findMethod(result, "A1.m()V");
assertFalse(A1_m.isAbstract());
assertForwardsTo(A1_m, findMethod(result, "I12.m()V"));
assertOverrides(result, "A1.m()V", "I1.m()V", "I12.m()V");
JMethod A2_m = findMethod(result, "A2.m()V");
assertFalse(A2_m.isAbstract());
assertForwardsTo(A2_m, findMethod(result, "I12.m()V"));
assertOverrides(result, "A2.m()V", "I1.m()V", "I12.m()V");
}
public void testDefaults_diamondAbstractBaseClass()
throws UnableToCompleteException {
final MockJavaResource I1 =
JavaResourceBase.createMockJavaResource("I1",
"public interface I1 {",
" default void m() {}",
"}");
final MockJavaResource I11 =
JavaResourceBase.createMockJavaResource("I11",
"public interface I11 extends I1 {",
"}");
final MockJavaResource I12 =
JavaResourceBase.createMockJavaResource("I12",
"public interface I12 extends I1 {",
" default void m() {}",
"}");
final MockJavaResource A =
JavaResourceBase.createMockJavaResource("A",
"public abstract class A implements I1 {",
"}");
final MockJavaResource A1 =
JavaResourceBase.createMockJavaResource("A1",
"public abstract class A1 extends A implements I11, I12 {",
"}");
final MockJavaResource A2 =
JavaResourceBase.createMockJavaResource("A2",
"public abstract class A2 extends A implements I12, I11 {",
"}");
addAll(I1, I11, I12, A, A1, A2);
Result result = optimize("void", "");
JMethod A_m = findMethod(result, "A.m()V");
assertFalse(A_m.isAbstract());
assertForwardsTo(A_m, findMethod(result, "I1.m()V"));
assertOverrides(result, "A.m()V", "I1.m()V");
JMethod A1_m = findMethod(result, "A1.m()V");
assertFalse(A1_m.isAbstract());
assertForwardsTo(A1_m, findMethod(result, "I12.m()V"));
assertOverrides(result, "A1.m()V", "A.m()V", "I1.m()V", "I12.m()V");
JMethod A2_m = findMethod(result, "A2.m()V");
assertFalse(A2_m.isAbstract());
assertForwardsTo(A2_m, findMethod(result, "I12.m()V"));
assertOverrides(result, "A2.m()V", "A.m()V", "I1.m()V", "I12.m()V");
}
/**
* Regression test for specialization resolution.
*/
public void testOverrides_specializations()
throws UnableToCompleteException {
final MockJavaResource SpecializedImpl =
JavaResourceBase.createMockJavaResource("test.SpecializedImpl",
"package test;",
"import javaemul.internal.annotations.SpecializeMethod;",
"public class SpecializedImpl<K> extends Impl<K> implements I {",
" @SpecializeMethod(params = {String.class}, target = \"putString\")",
" public void put(K k) { }",
" public void putString(String k) { }",
"}");
final MockJavaResource Impl =
JavaResourceBase.createMockJavaResource("test.Impl",
"package test;",
"public class Impl<K> {",
" public void put(K k) { }",
" public void m() { }",
"}");
final MockJavaResource I =
JavaResourceBase.createMockJavaResource("test.I",
"package test;",
"public interface I {",
" public void m();",
"}");
addAll(I, SpecializedImpl, Impl);
Result result =
optimize("void", "new SpecializedImpl<String>().put(\"2\");");
JClassType testImplClass = (JClassType) result.findClass("test.SpecializedImpl");
String mSignature = "m()V";
JMethod testImplChild_putString = findMethod(result, "test.SpecializedImpl." + mSignature);
assertSame(testImplChild_putString,
result.getOptimizedProgram()
.typeOracle.getInstanceMethodBySignature(testImplClass, mSignature));
}
public void testGetProperty_error_undefinedProperty()
throws UnableToCompleteException {
final MockJavaResource someClass =
JavaResourceBase.createMockJavaResource("test.SomeClass",
"package test;",
"public class SomeClass {",
" public void m() { String a = System.getProperty(\"undefined\"); }",
"}");
shouldGenerateError(someClass, 3, "Property 'undefined' is not defined.");
}
public void testGetProperty_error_multivalued()
throws UnableToCompleteException {
final MockJavaResource someClass =
JavaResourceBase.createMockJavaResource("test.SomeClass",
"package test;",
"public class SomeClass {",
" public void m() { String a = System.getProperty(\"multivalued\"); }",
"}");
shouldGenerateError(someClass, 3, "Property 'multivalued' is multivalued." +
" Multivalued properties are not supported by System.getProperty().");
}
public void testGetPropertyWithDefault_error_multivalued()
throws UnableToCompleteException {
final MockJavaResource someClass =
JavaResourceBase.createMockJavaResource("test.SomeClass",
"package test;",
"public class SomeClass {",
" public void m() { String a = System.getProperty(\"multivalued\", \"somevalue\"); }",
"}");
shouldGenerateError(someClass, 3, "Property 'multivalued' is multivalued." +
" Multivalued properties are not supported by System.getProperty().");
}
public void testGetPropertyWithDefault_success_undefined()
throws UnableToCompleteException {
final MockJavaResource someClass =
JavaResourceBase.createMockJavaResource("test.SomeClass",
"package test;",
"public class SomeClass {",
" public void m() { String a = System.getProperty(\"undefined\", \"somevalue\"); }",
"}");
shouldGenerateNoError(someClass);
}
@Override
protected boolean doOptimizeMethod(TreeLogger logger, JProgram program, JMethod method) {
program.addEntryMethod(findMainMethod(program));
return false;
}
@Override
protected CompilerContext provideCompilerContext() {
CompilerContext context = super.provideCompilerContext();
context.getModule().getProperties().createConfiguration("multivalued", true);
return context;
}
@Override
protected Pass providePass() {
return new Pass() {
private Result result = null;
@Override
public boolean run(TreeLogger logger, MockJavaResource buggyResource,
MockJavaResource extraResource) {
addAll(buggyResource, extraResource);
try {
result = optimize(logger, "void", "new SomeClass().m();");
} catch (UnableToCompleteException e) {
return false;
}
return true;
}
@Override
public boolean classAvailable(String className) {
return result != null && result.findClass(className) != null;
}
@Override
public String getTopErrorMessage(Type logLevel, MockJavaResource resource) {
return (logLevel == Type.WARN ? "Warnings" : "Errors") +
" in '" + resource.getPath() + "'";
}
};
}
private static final MockJavaResource A_A =
JavaResourceBase.createMockJavaResource("a.A",
"package a;",
"public class A {",
" public void m() { }",
" public A m1() { return null; }",
" void pp() {}",
"}");
private static final MockJavaResource A_I =
JavaResourceBase.createMockJavaResource("a.I",
"package a;",
"public interface I {",
" void m();",
" A m1();",
" void pp();",
"}");
private static final MockJavaResource A_J =
JavaResourceBase.createMockJavaResource("a.J",
"package a;",
"public interface J extends I {",
" void pp();",
"}");
/**
* a.B accidentally implements a.I.m() and a.I.m1() and
* explicitly implements a.I.pp()
*
* a.B also overrides package private a.A.pp()V and makes it public.
*/
private static final MockJavaResource A_B =
JavaResourceBase.createMockJavaResource("a.B",
"package a;",
"public class B extends A implements a.I {",
" public void pp() {}",
"}");
private static final MockJavaResource B_C =
JavaResourceBase.createMockJavaResource("b.C",
"package b;",
"public class C extends a.A {",
" public C m1() { return null; }",
" void pp() {}",
"}");
private static final MockJavaResource B_D =
JavaResourceBase.createMockJavaResource("b.D",
"package b;",
"public class D extends a.B {",
" public D m1() { return null; }",
"}");
private static final MockJavaResource B_E =
JavaResourceBase.createMockJavaResource("b.E",
"package b;",
"public class E extends b.C {",
" public E m1() { return null; }",
"}");
}