blob: 563c2bc8c90b8f591549805d3631c2f493eb2b86 [file] [log] [blame]
/*
* Copyright 2008 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.javac;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JConstructor;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.JTypeParameter;
import com.google.gwt.core.ext.typeinfo.JWildcardType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.core.ext.typeinfo.TypeOracleException;
import com.google.gwt.dev.javac.impl.MockJavaResource;
import com.google.gwt.dev.javac.impl.StaticJavaResource;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import junit.framework.TestCase;
import org.apache.commons.collections.map.AbstractReferenceMap;
import org.apache.commons.collections.map.ReferenceMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Test TypeOracleMediator.
*/
public class TypeOracleMediatorTest extends TestCase {
private abstract class MutableJavaResource extends MockJavaResource {
private String extraSource = "";
public MutableJavaResource(String qualifiedTypeName) {
super(qualifiedTypeName);
}
@Override
protected CharSequence getContent() {
return getSource() + extraSource;
}
public abstract String getSource();
public void touch() {
super.touch();
extraSource += '\n';
}
}
private abstract class CheckedJavaResource extends MutableJavaResource {
public CheckedJavaResource(String packageName, String shortMainTypeName,
String... shortTypeNames) {
super(Shared.makeTypeName(packageName, shortMainTypeName));
register(getTypeName(), this);
for (String shortTypeName : shortTypeNames) {
register(Shared.makeTypeName(packageName, shortTypeName), this);
}
}
public abstract void check(JClassType type) throws NotFoundException;
}
private static void assertIsAssignable(JClassType from, JClassType to) {
assertTrue("'" + from + "' should be assignable to '" + to + "'",
from.isAssignableTo(to));
assertTrue("'" + to + "' should be assignable from '" + from + "'",
to.isAssignableFrom(from));
}
private static void assertIsNotAssignable(JClassType from, JClassType to) {
assertFalse(from + " should not be assignable to " + to,
from.isAssignableTo(to));
assertFalse(to + " should not be assignable to " + from,
to.isAssignableFrom(from));
}
private static void recordAssignability(
Map<JClassType, Set<JClassType>> assignabilityMap, JClassType from,
JClassType to) {
Set<JClassType> set = assignabilityMap.get(from);
if (set == null) {
set = new HashSet<JClassType>();
assignabilityMap.put(from, set);
}
set.add(to);
}
/**
* Public so that this will be initialized before the CUs.
*/
public final Map<String, CheckedJavaResource> publicTypeNameToTestCupMap = new HashMap<String, CheckedJavaResource>();
protected CheckedJavaResource CU_AfterAssimilate = new CheckedJavaResource(
"test.assim", "AfterAssimilate") {
@Override
public void check(JClassType type) {
assertEquals("test.assim.BeforeAssimilate",
type.getSuperclass().getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test.assim;\n");
sb.append("class AfterAssimilate extends BeforeAssimilate { }");
return sb.toString();
}
};
protected CheckedJavaResource CU_AnonymousClass = new CheckedJavaResource(
"test", "Enclosing") {
@Override
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
assertEquals("Enclosing", name);
checkEnclosing(type);
}
public void checkEnclosing(JClassType type) {
assertEquals("Enclosing", type.getSimpleSourceName());
assertEquals("test.Enclosing", type.getQualifiedSourceName());
// verify the anonymous class doesn't show up
JClassType[] nested = type.getNestedTypes();
assertEquals(0, nested.length);
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class Enclosing {\n");
sb.append(" public static Object getLocal() {");
sb.append(" return new Object() { };\n");
sb.append(" }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_Assignable = new CheckedJavaResource(
"test.sub", "Derived", "BaseInterface", "DerivedInterface",
"Derived.Nested") {
@Override
public void check(JClassType type) {
if ("Derived".equals(type.getSimpleSourceName())) {
checkDerived(type);
} else if ("Nested".equals(type.getSimpleSourceName())) {
checkNested(type);
}
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test.sub;\n");
sb.append("import test.Outer;");
sb.append("interface BaseInterface { }");
sb.append("interface DerivedInterface extends BaseInterface { }");
sb.append("public class Derived extends Outer.Inner {\n");
sb.append(" public static class Nested extends Outer.Inner implements DerivedInterface { }\n");
sb.append("}\n");
return sb.toString();
}
private void checkDerived(JClassType type) {
assertEquals("test.sub.Derived", type.getQualifiedSourceName());
}
private void checkNested(JClassType type) {
assertEquals("test.sub.Derived.Nested", type.getQualifiedSourceName());
}
};
protected CheckedJavaResource CU_BeforeAssimilate = new CheckedJavaResource(
"test.assim", "BeforeAssimilate") {
@Override
public void check(JClassType type) {
assertEquals("test.assim.BeforeAssimilate", type.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test.assim;\n");
sb.append("class BeforeAssimilate { }");
return sb.toString();
}
};
protected CheckedJavaResource CU_BindToTypeScope = new CheckedJavaResource(
"test", "BindToTypeScope", "BindToTypeScope.Object",
"BindToTypeScope.DerivedObject") {
@Override
public void check(JClassType type) throws NotFoundException {
if ("BindToTypeScope".equals(type.getSimpleSourceName())) {
checkBindToTypeScope(type);
} else if ("Object".equals(type.getSimpleSourceName())) {
checkObject(type);
} else {
checkDerivedObject(type);
}
}
public void checkBindToTypeScope(JClassType type) throws NotFoundException {
assertEquals("BindToTypeScope", type.getSimpleSourceName());
assertEquals("test.BindToTypeScope", type.getQualifiedSourceName());
JClassType object = type.getNestedType("Object");
assertNotNull(object);
JClassType derivedObject = type.getNestedType("DerivedObject");
assertNotNull(derivedObject);
}
public void checkObject(JClassType type) {
assertEquals("Object", type.getSimpleSourceName());
assertEquals("test.BindToTypeScope.Object", type.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class BindToTypeScope {\n");
sb.append(" public static class Object { }\n");
sb.append(" public static class DerivedObject extends Object { }\n");
sb.append("}\n");
return sb.toString();
}
private void checkDerivedObject(JClassType type) throws NotFoundException {
JClassType bindToTypeScope = type.getEnclosingType();
assertNotNull(bindToTypeScope);
JClassType object = bindToTypeScope.getNestedType("Object");
assertNotNull(object);
JClassType derivedObject = bindToTypeScope.getNestedType("DerivedObject");
assertNotNull(derivedObject);
assertEquals(object, derivedObject.getSuperclass());
}
};
protected CheckedJavaResource CU_ConstrainedList = new CheckedJavaResource(
"test", "ConstrainedList") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNotNull(type.isGenericType());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package test;\n");
sb.append("public interface ConstrainedList<E extends Throwable> {\n");
sb.append("}");
return sb.toString();
}
};
protected CheckedJavaResource CU_ConstrainedListAsField = new CheckedJavaResource(
"test", "ConstrainedListAsField") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNull(type.isGenericType());
assertNull(type.getEnclosingType());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package test;\n");
sb.append("public class ConstrainedListAsField {\n");
sb.append(" private ConstrainedList<?> field;");
sb.append("}");
return sb.toString();
}
};
protected CheckedJavaResource CU_DeclaresInnerGenericType = new CheckedJavaResource(
"parameterized.type.build.dependency", "Class1", "Class1.Inner") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNotNull(type.isGenericType());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package parameterized.type.build.dependency;\n");
sb.append("public class Class1<T> {\n");
sb.append(" public interface Inner<T> {}\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_DefaultClass = new CheckedJavaResource(
"test", "DefaultClass") {
@Override
public void check(JClassType type) {
assertEquals("DefaultClass", type.getSimpleSourceName());
assertEquals("test.DefaultClass", type.getQualifiedSourceName());
JClassType object = type.getOracle().findType("java.lang", "Object");
assertNotNull(object);
assertEquals(object, type.getSuperclass());
assertNull(type.isInterface());
assertEquals(0, type.getMethods().length);
assertEquals(0, type.getFields().length);
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class DefaultClass extends Object { }\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_ExtendsGenericList = new CheckedJavaResource(
"test.refresh", "ExtendsGenericList") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNotNull(type.getSuperclass().isParameterized());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package test.refresh;\n");
sb.append("class ExtendsGenericList extends GenericList<Object> {}");
return sb.toString();
}
};
protected CheckedJavaResource CU_ExtendsGenericOuterInner = new CheckedJavaResource(
"test", "ExtendsOuter", "ExtendsOuter.ExtendsInner") {
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
if ("ExtendsOuter".equals(name)) {
checkOuter(type);
} else {
checkInner(type);
}
}
public void checkInner(JClassType type) {
assertEquals("ExtendsInner", type.getSimpleSourceName());
assertEquals("test.ExtendsOuter.ExtendsInner",
type.getQualifiedSourceName());
assertEquals("test.ExtendsOuter",
type.getEnclosingType().getQualifiedSourceName());
}
public void checkOuter(JClassType type) {
assertEquals("ExtendsOuter", type.getSimpleSourceName());
assertEquals("test.ExtendsOuter", type.getQualifiedSourceName());
JClassType[] nested = type.getNestedTypes();
assertEquals(1, nested.length);
JClassType inner = nested[0];
assertEquals("test.ExtendsOuter.ExtendsInner",
inner.getQualifiedSourceName());
}
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class ExtendsOuter extends Outer<Object> {\n");
sb.append(" public class ExtendsInner extends Inner {\n");
sb.append(" }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_ExtendsParameterizedType = new CheckedJavaResource(
"parameterized.type.build.dependency", "Class2") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNotNull(type.getSuperclass().isParameterized());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package parameterized.type.build.dependency;\n");
sb.append("public class Class2 extends Class1<Object> {}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_FieldsAndTypes = new CheckedJavaResource(
"test", "Fields", "SomeType") {
@Override
public void check(JClassType type) throws NotFoundException {
if ("Fields".equals(type.getSimpleSourceName())) {
assertEquals("test.Fields", type.getQualifiedSourceName());
TypeOracle tio = type.getOracle();
JField[] fields = type.getFields();
assertEquals(12, fields.length);
JField field;
JType fieldType;
JArrayType arrayType;
JType componentType;
final JClassType someType = tio.getType("test", "SomeType");
final JArrayType intArrayType = tio.getArrayType(JPrimitiveType.INT);
final JArrayType someTypeArrayType = tio.getArrayType(someType);
final JArrayType intArrayArrayType = tio.getArrayType(intArrayType);
field = type.getField("privateInt");
assertTrue(field.isPrivate());
assertEquals(JPrimitiveType.INT, field.getType());
field = type.getField("privateSomeType");
assertTrue(field.isPrivate());
assertEquals(someType, field.getType());
field = type.getField("protectedInt");
assertTrue(field.isProtected());
field = type.getField("publicInt");
assertTrue(field.isPublic());
field = type.getField("packageInt");
assertTrue(field.isDefaultAccess());
field = type.getField("staticInt");
assertTrue(field.isStatic());
field = type.getField("transientInt");
assertTrue(field.isTransient());
field = type.getField("volatileInt");
assertTrue(field.isVolatile());
field = type.getField("multiInt");
assertTrue(field.isPublic());
assertTrue(field.isStatic());
assertTrue(field.isFinal());
assertTrue(field.isTransient());
field = type.getField("intArray");
fieldType = field.getType();
arrayType = fieldType.isArray();
assertNotNull(arrayType);
assertSame(intArrayType, arrayType);
componentType = arrayType.getComponentType();
assertNotNull(componentType);
assertSame(JPrimitiveType.INT, componentType);
assertEquals("int[]", fieldType.getQualifiedSourceName());
field = type.getField("someTypeArray");
fieldType = field.getType();
arrayType = fieldType.isArray();
assertNotNull(arrayType);
assertSame(someTypeArrayType, arrayType);
componentType = arrayType.getComponentType();
assertNotNull(componentType);
assertSame(someType, componentType);
assertEquals("test.SomeType[]", fieldType.getQualifiedSourceName());
field = type.getField("intArrayArray");
fieldType = field.getType();
arrayType = fieldType.isArray();
assertNotNull(arrayType);
assertSame(intArrayArrayType, arrayType);
componentType = arrayType.getComponentType();
assertNotNull(componentType);
assertSame(intArrayType, arrayType.getComponentType());
arrayType = (JArrayType) arrayType.getComponentType();
assertSame(JPrimitiveType.INT, arrayType.getComponentType());
assertEquals("int[][]", fieldType.getQualifiedSourceName());
} else {
// No need to check SomeType since
// there's already a DefaultClass
// test.
}
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("class SomeType { }");
sb.append("public class Fields {\n");
sb.append(" private int privateInt;\n");
sb.append(" private SomeType privateSomeType;\n");
sb.append(" protected int protectedInt;\n");
sb.append(" public int publicInt;\n");
sb.append(" int packageInt;\n");
sb.append(" private static int staticInt;\n");
sb.append(" private transient int transientInt;\n");
sb.append(" private volatile int volatileInt;\n");
sb.append(" public static final transient int multiInt = 0;\n");
sb.append(" private int[] intArray;\n");
sb.append(" private SomeType[] someTypeArray;\n");
sb.append(" private int[][] intArrayArray;\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_GenericList = new CheckedJavaResource(
"test.refresh", "GenericList") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNotNull(type.isGenericType());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package test.refresh;\n");
sb.append("class GenericList<T> {\n");
sb.append(" public static final int CONSTANT = 0;\n");
sb.append("}");
return sb.toString();
}
};
protected CheckedJavaResource CU_GenericOuterInner = new CheckedJavaResource(
"test", "Outer", "Outer.Inner") {
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
if ("GenericOuter".equals(name)) {
checkOuter(type);
} else {
checkInner(type);
}
}
public void checkInner(JClassType type) {
assertEquals("Inner", type.getSimpleSourceName());
assertEquals("test.Outer.Inner", type.getQualifiedSourceName());
assertEquals("test.Outer",
type.getEnclosingType().getQualifiedSourceName());
}
public void checkOuter(JClassType type) {
assertEquals("Outer", type.getSimpleSourceName());
assertEquals("test.Outer", type.getQualifiedSourceName());
JClassType[] nested = type.getNestedTypes();
assertEquals(1, nested.length);
JClassType inner = nested[0];
assertEquals("test.Outer.Inner", inner.getQualifiedSourceName());
}
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("import java.util.List;\n");
sb.append("public class Outer<V> {\n");
sb.append(" public class Inner {\n");
sb.append(" private V field;\n");
sb.append(" private List<V> list;\n");
sb.append(" }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_HasSyntaxErrors = new CheckedJavaResource(
"test", "HasSyntaxErrors", "NoSyntaxErrors") {
@Override
public void check(JClassType classInfo) {
fail("This class should have been removed");
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("class NoSyntaxErrors { }\n");
sb.append("public class HasSyntaxErrors { a syntax error }\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_HasUnresolvedSymbols = new CheckedJavaResource(
"test", "Invalid", "Valid") {
@Override
public void check(JClassType classInfo) {
fail("Both classes should have been removed");
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class Invalid extends NoSuchClass { }\n");
sb.append("class Valid extends Object { }\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_List = new CheckedJavaResource("java.util",
"List") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNotNull(type.isGenericType());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package java.util;\n");
sb.append("public interface List<E> {\n");
sb.append("}");
return sb.toString();
}
};
protected CheckedJavaResource CU_ListAsField = new CheckedJavaResource(
"test.refresh", "ListAsField") {
@Override
public void check(JClassType type) throws NotFoundException {
assertNull(type.isGenericType());
assertNull(type.getEnclosingType());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package test.refresh;\n");
sb.append("import java.util.List;\n");
sb.append("public class ListAsField {\n");
sb.append(" private List<Object> field;");
sb.append("}");
return sb.toString();
}
};
protected CheckedJavaResource CU_LocalClass = new CheckedJavaResource("test",
"Enclosing") {
@Override
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
assertEquals("Enclosing", name);
checkEnclosing(type);
}
public void checkEnclosing(JClassType type) {
assertEquals("Enclosing", type.getSimpleSourceName());
assertEquals("test.Enclosing", type.getQualifiedSourceName());
// verify the local class doesn't show up
JClassType[] nested = type.getNestedTypes();
assertEquals(0, nested.length);
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class Enclosing {\n");
sb.append(" public static Object getLocal() {");
sb.append(" class MyObject { }\n");
sb.append(" return new MyObject();\n");
sb.append(" }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_LocalClass2 = new CheckedJavaResource("test",
"Enclosing") {
@Override
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
assertEquals("Enclosing", name);
checkEnclosing(type);
}
public void checkEnclosing(JClassType type) {
assertEquals("Enclosing", type.getSimpleSourceName());
assertEquals("test.Enclosing", type.getQualifiedSourceName());
// verify the local class doesn't show up
JClassType[] nested = type.getNestedTypes();
assertEquals(0, nested.length);
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class Enclosing {\n");
sb.append(" public int foo;\n");
sb.append(" public Object getLocal() {\n");
sb.append(" class MyObject {\n");
sb.append(" int getFoo() { return foo; }\n");
sb.append(" }\n");
sb.append(" return new MyObject() {};\n");
sb.append(" }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_MethodsAndParams = new CheckedJavaResource(
"test", "Methods") {
@Override
public void check(JClassType type) throws NotFoundException {
TypeOracle tio = type.getOracle();
JMethod[] methods = type.getMethods();
assertEquals(6, methods.length);
JMethod method;
JType[] thrownTypes;
final JClassType javaLangObject = tio.findType("java.lang", "Object");
final JClassType javaLangThrowable = tio.findType("java.lang",
"Throwable");
final JType[] noParamTypes = new JType[0];
method = type.getMethod("returnsInt", noParamTypes);
assertSame(JPrimitiveType.INT, method.getReturnType());
assertEquals(0, method.getParameters().length);
method = type.getMethod("returnsSomeType", noParamTypes);
assertSame(javaLangObject, method.getReturnType());
assertEquals(0, method.getParameters().length);
method = type.getMethod("staticMethod", noParamTypes);
assertSame(JPrimitiveType.VOID, method.getReturnType());
assertEquals(0, method.getParameters().length);
assertTrue(method.isStatic());
method = type.getMethod("finalMethod", noParamTypes);
assertSame(JPrimitiveType.VOID, method.getReturnType());
assertEquals(0, method.getParameters().length);
assertTrue(method.isFinal());
try {
method = type.getMethod("overloaded", noParamTypes);
fail("expected throw");
} catch (NotFoundException e) {
}
methods = type.getOverloads("overloaded");
assertEquals(2, methods.length);
for (JMethod element : methods) {
assertEquals("overloaded", element.getName());
}
method = type.getMethod("overloaded", new JType[] {
JPrimitiveType.INT, javaLangObject});
assertSame(JPrimitiveType.VOID, method.getReturnType());
thrownTypes = method.getThrows();
assertEquals(1, thrownTypes.length);
assertSame(javaLangThrowable, thrownTypes[0]);
method = type.getMethod("overloaded", new JType[] {
JPrimitiveType.INT, JPrimitiveType.CHAR});
assertSame(javaLangObject, method.getReturnType());
thrownTypes = method.getThrows();
assertEquals(0, thrownTypes.length);
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class Methods {\n");
sb.append(" private int returnsInt() { return 0; };\n");
sb.append(" private Object returnsSomeType() { return null; }\n");
sb.append(" public static void staticMethod() { return; }\n");
sb.append(" public final void finalMethod() { return; }\n");
sb.append(" public void overloaded(int x, Object y) throws Throwable { return; }\n");
sb.append(" public Object overloaded(int x, char y) { return null; }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_NestedGenericInterfaces = new CheckedJavaResource(
"test", "OuterInt", "OuterInt.InnerInt") {
@Override
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
if ("OuterInt".equals(name)) {
checkOuter(type);
} else {
checkInner(type);
}
}
public void checkInner(JClassType type) {
assertEquals("InnerInt", type.getSimpleSourceName());
assertEquals("test.OuterInt.InnerInt", type.getQualifiedSourceName());
assertEquals("test.OuterInt",
type.getEnclosingType().getQualifiedSourceName());
}
public void checkOuter(JClassType type) {
assertEquals("OuterInt", type.getSimpleSourceName());
assertEquals("test.OuterInt", type.getQualifiedSourceName());
JClassType[] nested = type.getNestedTypes();
assertEquals(1, nested.length);
JClassType inner = nested[0];
assertEquals("test.OuterInt.InnerInt", inner.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public interface OuterInt<K,V> {\n");
sb.append(" public interface InnerInt<V> { }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_Object = new CheckedJavaResource(
"java.lang", "Object") {
@Override
public void check(JClassType type) {
assertEquals("Object", type.getSimpleSourceName());
assertEquals("java.lang.Object", type.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;");
sb.append("public class Object { }");
return sb.toString();
}
};
protected CheckedJavaResource CU_OuterInner = new CheckedJavaResource("test",
"Outer", "Outer.Inner") {
@Override
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
if ("Outer".equals(name)) {
checkOuter(type);
} else {
checkInner(type);
}
}
public void checkInner(JClassType type) {
assertEquals("Inner", type.getSimpleSourceName());
assertEquals("test.Outer.Inner", type.getQualifiedSourceName());
assertEquals("test.Outer",
type.getEnclosingType().getQualifiedSourceName());
}
public void checkOuter(JClassType type) {
assertEquals("Outer", type.getSimpleSourceName());
assertEquals("test.Outer", type.getQualifiedSourceName());
JClassType[] nested = type.getNestedTypes();
assertEquals(1, nested.length);
JClassType inner = nested[0];
assertEquals("test.Outer.Inner", inner.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class Outer {\n");
sb.append(" public static class Inner { }\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_ReferencesGenericListConstant = new CheckedJavaResource(
"test.refresh", "ReferencesGenericListConstant") {
@Override
public void check(JClassType type) throws NotFoundException {
assertEquals("test.refresh.ReferencesGenericListConstant",
type.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package test.refresh;\n");
sb.append("class ReferencesGenericListConstant {\n");
sb.append(" public static final int MY_CONSTANT = GenericList.CONSTANT;\n");
sb.append("}");
return sb.toString();
}
};
protected CheckedJavaResource CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed = new CheckedJavaResource(
"parameterized.type.build.dependency", "Class0") {
@Override
public void check(JClassType type) throws NotFoundException {
JClassType[] intfs = type.getImplementedInterfaces();
assertEquals(1, intfs.length);
assertNotNull(intfs[0].isParameterized());
}
@Override
public String getSource() {
StringBuilder sb = new StringBuilder();
sb.append("package parameterized.type.build.dependency;\n");
sb.append("public class Class0 implements Class2.Inner<Object> {\n");
sb.append("}\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_RefsInfectedCompilationUnit = new CheckedJavaResource(
"test", "RefsInfectedCompilationUnit") {
@Override
public void check(JClassType classInfo) {
fail("This class should should have been removed because it refers to a class in another compilation unit that had problems");
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
sb.append("public class RefsInfectedCompilationUnit extends Valid { }\n");
return sb.toString();
}
};
protected CheckedJavaResource CU_Throwable = new CheckedJavaResource(
"java.lang", "Throwable") {
@Override
public void check(JClassType type) {
assertEquals("Throwable", type.getSimpleSourceName());
assertEquals("java.lang.Throwable", type.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;");
sb.append("public class Throwable { }");
return sb.toString();
}
};
protected CheckedJavaResource CU_UnnestedImplementations = new CheckedJavaResource(
"test", "Implementations") {
@Override
public void check(JClassType type) {
assertEquals("Implementations", type.getSimpleSourceName());
assertEquals("test.Implementations", type.getQualifiedSourceName());
}
@Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;");
sb.append("public class Implementations {");
sb.append(" public static class OuterImpl<K,V> implements OuterInt<K,V> {}");
sb.append(" public static class InnerImpl<V> implements OuterInt.InnerInt<V> {}");
sb.append("}");
return sb.toString();
}
};
private TypeOracle typeOracle;
private final Set<Resource> resources = new HashSet<Resource>();
public void checkTypes(JClassType[] types) throws NotFoundException {
for (JClassType type : types) {
check(type);
JClassType[] nestedTypes = type.getNestedTypes();
checkTypes(nestedTypes);
}
}
/**
* Tests which variant of AbstractRefrenceMap we want to store the map for
* parameterizedTypes, arrayTypes, and wildCardTypes in TypeOracle. Note: this
* test is manual because gc can be unreliable.
*/
@SuppressWarnings("unchecked")
public void manualTestAbstractRefrenceMap() {
/*
* with a HARD -> WEAK map, verify that the entry remains if there is no
* reference to key, but is deleted when the reference to value is gone
*/
Map<Integer, Integer> simpleMap = new ReferenceMap(
AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK, true);
Integer bar = new Integer(42);
simpleMap.put(new Integer(32), bar);
Runtime.getRuntime().gc();
assertEquals(1, simpleMap.size());
bar = null;
Runtime.getRuntime().gc();
assertEquals(0, simpleMap.size());
/*
* with a WEAK -> WEAK map, verify that the entry is gone if there are no
* references to either the key or the value.
*/
simpleMap = new ReferenceMap(AbstractReferenceMap.WEAK,
AbstractReferenceMap.WEAK, true);
Map<Integer, Integer> reverseMap = new ReferenceMap(
AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true);
Integer foo = new Integer(32);
bar = new Integer(42);
simpleMap.put(foo, bar);
reverseMap.put(bar, foo);
Runtime.getRuntime().gc();
assertEquals(1, simpleMap.size());
assertEquals(1, reverseMap.size());
bar = null;
Runtime.getRuntime().gc();
assertEquals(0, simpleMap.size());
assertEquals(0, reverseMap.size());
}
public void testAssignable() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_Assignable);
resources.add(CU_OuterInner);
compileAndRefresh();
JClassType[] allTypes = typeOracle.getTypes();
assertEquals(7, allTypes.length);
Map<JClassType, Set<JClassType>> assignabilityMap = new HashMap<JClassType, Set<JClassType>>();
JClassType inner = typeOracle.findType("test.Outer.Inner");
JClassType baseIntf = typeOracle.findType("test.sub.BaseInterface");
JClassType derivedIntf = typeOracle.findType("test.sub.DerivedInterface");
recordAssignability(assignabilityMap, derivedIntf, baseIntf);
JClassType derived = typeOracle.findType("test.sub.Derived");
recordAssignability(assignabilityMap, derived, inner);
JClassType nested = typeOracle.findType("test.sub.Derived.Nested");
recordAssignability(assignabilityMap, nested, inner);
recordAssignability(assignabilityMap, nested, derivedIntf);
recordAssignability(assignabilityMap, nested, baseIntf);
for (JClassType fromType : allTypes) {
for (JClassType toType : allTypes) {
if (fromType == toType || toType == typeOracle.getJavaLangObject()) {
assertIsAssignable(fromType, toType);
} else {
Set<JClassType> set = assignabilityMap.get(fromType);
if (set != null && set.contains(toType)) {
assertIsAssignable(fromType, toType);
} else {
assertIsNotAssignable(fromType, toType);
}
}
}
}
}
public void testAssimilation() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_BeforeAssimilate);
compileAndRefresh();
assertEquals(2, typeOracle.getTypes().length);
// Build onto an existing type oracle.
resources.add(CU_AfterAssimilate);
compileAndRefresh();
assertEquals(3, typeOracle.getTypes().length);
}
public void testBindToTypeScope() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_BindToTypeScope);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(4, types.length);
}
public void testConstructors() throws TypeOracleException {
resources.add(CU_Object);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(1, types.length);
JClassType objectType = types[0];
assertEquals("Object", objectType.getSimpleSourceName());
JConstructor[] ctors = objectType.getConstructors();
assertEquals(1, ctors.length);
JConstructor defaultCtor = ctors[0];
assertEquals("Object", defaultCtor.getName());
assertEquals(0, defaultCtor.getParameters().length);
}
public void testConstrainedList() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_Throwable);
resources.add(CU_ConstrainedList);
compileAndRefresh();
JClassType type = typeOracle.getType("test.ConstrainedList");
JClassType throwable = typeOracle.getType("java.lang.Throwable");
assertNull(type.isParameterized());
JGenericType genericType = type.isGenericType();
assertNotNull(genericType);
JTypeParameter[] typeParams = genericType.getTypeParameters();
assertEquals(1, typeParams.length);
assertEquals(throwable, typeParams[0].getBaseType());
assertEquals(type, typeParams[0].getDeclaringClass());
JClassType[] bounds = typeParams[0].getBounds();
assertEquals(1, bounds.length);
assertEquals(throwable, bounds[0]);
}
public void testConstrainedField() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_Throwable);
resources.add(CU_ConstrainedList);
resources.add(CU_ConstrainedListAsField);
compileAndRefresh();
// Get the types produced by the TypeOracle
JClassType type = typeOracle.getType("test.ConstrainedListAsField");
assertNull(type.isParameterized());
JField field = type.getField("field");
assertNotNull(field);
JType fieldType = field.getType();
JParameterizedType fieldParamType = fieldType.isParameterized();
assertNotNull(fieldParamType);
assertNull(fieldParamType.getEnclosingType());
JGenericType baseType = fieldParamType.getBaseType();
assertNotNull(baseType);
assertEquals("test.ConstrainedList", baseType.getQualifiedSourceName());
JClassType[] typeArgs = fieldParamType.getTypeArgs();
assertEquals(1, typeArgs.length);
JWildcardType wildcard = typeArgs[0].isWildcard();
assertNotNull(wildcard);
JClassType upperBound = wildcard.getUpperBound();
assertEquals("Throwable", upperBound.getSimpleSourceName());
}
public void testDefaultClass() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_DefaultClass);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(2, types.length);
}
public void testEnclosingGenericType() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_List);
resources.add(CU_GenericOuterInner);
resources.add(CU_ExtendsGenericOuterInner);
compileAndRefresh();
// Get the types produced by the TypeOracle
JClassType outer = typeOracle.getType("test.Outer");
JClassType inner = typeOracle.getType("test.Outer.Inner");
assertNull(outer.getEnclosingType());
assertEquals(outer, inner.getEnclosingType());
assertNull(inner.isParameterized());
assertNotNull(outer.isGenericType());
assertNotNull(inner.isGenericType());
JField field = inner.getField("field");
assertNotNull(field);
JType fieldType = field.getType();
JTypeParameter typeParam = fieldType.isTypeParameter();
assertNotNull(typeParam);
assertEquals("V", typeParam.getName());
JClassType[] bounds = typeParam.getBounds();
assertEquals(1, bounds.length);
assertEquals(typeOracle.getJavaLangObject(), bounds[0]);
JClassType extendsOuter = typeOracle.getType("test.ExtendsOuter");
JClassType extendsInner = typeOracle.getType("test.ExtendsOuter.ExtendsInner");
assertNull(extendsOuter.getEnclosingType());
assertEquals(extendsOuter, extendsInner.getEnclosingType());
JClassType outerSuper = extendsOuter.getSuperclass();
JParameterizedType outerSuperParam = outerSuper.isParameterized();
assertNotNull(outerSuperParam);
assertEquals(outer, outerSuperParam.getBaseType());
JClassType innerSuper = extendsInner.getSuperclass();
JParameterizedType innerSuperParam = innerSuper.isParameterized();
assertNotNull(innerSuperParam);
assertEquals(inner, innerSuperParam.getBaseType());
}
public void testEnclosingType() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_List);
resources.add(CU_ListAsField);
compileAndRefresh();
// Get the types produced by the TypeOracle
JClassType listAsField = typeOracle.getType("test.refresh.ListAsField");
assertNull(listAsField.isParameterized());
JField field = listAsField.getField("field");
assertNotNull(field);
JType fieldType = field.getType();
JParameterizedType fieldParamType = fieldType.isParameterized();
assertNotNull(fieldParamType);
assertNull(fieldParamType.getEnclosingType());
JGenericType baseType = fieldParamType.getBaseType();
assertNotNull(baseType);
assertEquals("java.util.List", baseType.getQualifiedSourceName());
}
public void testFieldsAndTypes() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_FieldsAndTypes);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(3, types.length);
}
// Check that anonymous classes are not reflected in TypeOracle
public void testLocal() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_LocalClass);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(2, types.length);
}
public void testLocalWithSynthetic() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_LocalClass2);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(2, types.length);
}
public void testMethodsAndParams() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_Throwable);
resources.add(CU_MethodsAndParams);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(3, types.length);
}
public void testOuterInner() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_OuterInner);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(3, types.length);
JClassType outer = null;
for (JClassType type : types) {
if ("Outer".equals(type.getSimpleSourceName())) {
outer = type;
break;
}
}
assertNotNull(outer);
JClassType superclass = outer.getSuperclass();
assertEquals(typeOracle.getJavaLangObject(), superclass);
}
/**
* Tests that we can build nested parameterized types even if that happens
* while the type oracle is being built. This test assumes that
* CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed will
* cause a parameterized form of CU_DeclaresInnerGenericType to be created
* before the type oracle has had a chance to resolve
* CU_DeclaresInnerGenericType.
*/
public void testParameterizedTypeBuildDependencies()
throws TypeOracleException {
resources.add(CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed);
resources.add(CU_ExtendsParameterizedType);
resources.add(CU_DeclaresInnerGenericType);
resources.add(CU_Object);
compileAndRefresh();
assertNull(typeOracle.findType("test.parameterizedtype.build.dependencies.Class2"));
}
/**
* Test that modifying a type will cause any types that depend on it to be
* rebuilt by the TypeOracleBuilder during a refresh.
*
* @throws UnableToCompleteException
* @throws NotFoundException
* @throws IOException
*/
public void testRefresh() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_ExtendsGenericList);
resources.add(CU_GenericList);
resources.add(CU_ReferencesGenericListConstant);
compileAndRefresh();
// Get the types produced by the TypeOracle
JClassType extendsGenericListType = typeOracle.getType("test.refresh.ExtendsGenericList");
JClassType genericListType = typeOracle.getType("test.refresh.GenericList");
JClassType referencesGenericListConstantType = typeOracle.getType("test.refresh.ReferencesGenericListConstant");
/*
* Invalidate CU_GenericList and simulate a refresh. This should cause
* anything that depends on GenericList to be rebuilt by the type oracle.
*/
CU_GenericList.touch();
compileAndRefresh();
assertNotSame(genericListType.getQualifiedSourceName() + "; ",
typeOracle.getType("test.refresh.GenericList"), genericListType);
assertNotSame(extendsGenericListType.getQualifiedSourceName() + "; ",
typeOracle.getType("test.refresh.ExtendsGenericList"),
extendsGenericListType);
/*
* Make sure that referencing a constant field will cause a type to be
* rebuilt if the constant changes.
*/
assertNotSame(referencesGenericListConstantType.getQualifiedSourceName(),
typeOracle.getType("test.refresh.ReferencesGenericListConstant"),
referencesGenericListConstantType);
}
/**
* Tests that refreshing with a unit that has errors does not cause new units
* that reference unchanged units to be removed. The strategy is to add some
* good units that reference each other and build a {@link TypeOracle}. Then
* we add some new units that have errors as well as some units that reference
* old units which did not have errors. This ensures that the correct units
* are pruned from the type oracle in the case where we encounter units with
* errors.
*
* @throws UnableToCompleteException
* @throws IOException
*/
public void testRefreshWithErrors() throws TypeOracleException {
// Add Object
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;");
sb.append("public class Object { }");
addResource("java.lang.Object", sb);
// Add UnmodifiedClass that will never change.
sb = new StringBuffer();
sb.append("package test.refresh.with.errors;");
sb.append("public class UnmodifiedClass { }");
addResource("test.refresh.with.errors.UnmodifiedClass", sb);
// Add GoodClass that references a class that will go bad.
sb = new StringBuffer();
sb.append("package test.refresh.with.errors;\n");
sb.append("public class GoodClass {\n");
sb.append(" ClassThatWillGoBad ctwgb;\n");
sb.append("}\n");
addResource("test.refresh.with.errors.GoodClass", sb);
// Add ClassThatWillGoBad that goes bad on the next refresh.
MutableJavaResource unitThatWillGoBad = new MutableJavaResource(
"test.refresh.with.errors.ClassThatWillGoBad") {
private String source = "package test.refresh.with.errors;\n"
+ "public class ClassThatWillGoBad { }\n";
@Override
public String getSource() {
return source;
}
@Override
public void touch() {
super.touch();
source = "This will cause a syntax error.";
}
};
resources.add(unitThatWillGoBad);
compileAndRefresh();
assertNotNull(typeOracle.findType("test.refresh.with.errors.UnmodifiedClass"));
assertNotNull(typeOracle.findType("test.refresh.with.errors.GoodClass"));
assertNotNull(typeOracle.findType("test.refresh.with.errors.ClassThatWillGoBad"));
// Add AnotherGoodClass that references a
// class that was not recompiled.
sb = new StringBuffer();
sb.append("package test.refresh.with.errors;\n");
sb.append("public class AnotherGoodClass {\n");
sb.append(" UnmodifiedClass uc; // This will cause the runaway pruning.\n");
sb.append("}\n");
addResource("test.refresh.with.errors.AnotherGoodClass", sb);
// Add BadClass that has errors and originally
// forced issue 2238.
sb = new StringBuffer();
sb.append("package test.refresh.with.errors;\n");
sb.append("public class BadClass {\n");
sb.append(" This will trigger a syntax error.\n");
sb.append("}\n");
addResource("test.refresh.with.errors.BadClass", sb);
// Now this cup should cause errors.
unitThatWillGoBad.touch();
compileAndRefresh();
assertNotNull(typeOracle.findType("test.refresh.with.errors.UnmodifiedClass"));
assertNotNull(typeOracle.findType("test.refresh.with.errors.AnotherGoodClass"));
assertNull(typeOracle.findType("test.refresh.with.errors.BadClass"));
assertNull(typeOracle.findType("test.refresh.with.errors.ClassThatWillGoBad"));
assertNull(typeOracle.findType("test.refresh.with.errors.GoodClass"));
}
public void testSyntaxErrors() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_HasSyntaxErrors);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
// Only java.lang.Object should remain.
//
assertEquals(1, types.length);
assertEquals("java.lang.Object", types[0].getQualifiedSourceName());
}
public void testTypeParams() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_NestedGenericInterfaces);
resources.add(CU_UnnestedImplementations);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
assertEquals(6, types.length);
JClassType type = typeOracle.findType("test.Implementations.InnerImpl");
assertNotNull(type);
JClassType[] interfaces = type.getImplementedInterfaces();
assertEquals(1, interfaces.length);
JClassType intf = interfaces[0];
JParameterizedType intfParam = intf.isParameterized();
assertNotNull(intfParam);
JClassType intfEnclosing = intf.getEnclosingType();
assertNotNull(intfEnclosing.isRawType());
}
public void testUnresolvedSymbls() throws TypeOracleException {
resources.add(CU_Object);
resources.add(CU_HasUnresolvedSymbols);
resources.add(CU_RefsInfectedCompilationUnit);
compileAndRefresh();
JClassType[] types = typeOracle.getTypes();
// Only java.lang.Object should remain.
//
assertEquals(1, types.length);
assertEquals("java.lang.Object", types[0].getQualifiedSourceName());
}
/**
* Creates a {@link Resource} and adds it the set of resources.
*
* @throws UnableToCompleteException
*/
private void addResource(String qualifiedTypeName, CharSequence source) {
resources.add(new StaticJavaResource(qualifiedTypeName, source));
}
private void check(JClassType classInfo) throws NotFoundException {
final String qName = classInfo.getQualifiedSourceName();
CheckedJavaResource cup = publicTypeNameToTestCupMap.get(qName);
if (cup != null) {
cup.check(classInfo);
}
}
private void compileAndRefresh() throws TypeOracleException {
typeOracle = TypeOracleTestingUtils.buildTypeOracle(createTreeLogger(),
resources);
checkTypes(typeOracle.getTypes());
}
/**
* Tweak this if you want to see the log output.
*/
private TreeLogger createTreeLogger() {
boolean reallyLog = false;
if (reallyLog) {
AbstractTreeLogger logger = new PrintWriterTreeLogger();
logger.setMaxDetail(TreeLogger.ALL);
return logger;
} else {
return TreeLogger.NULL;
}
}
private void register(String qualifiedTypeName, CheckedJavaResource cup) {
publicTypeNameToTestCupMap.put(qualifiedTypeName, cup);
}
}