TypeOracle now builds from bytecode instead of JDT structures (part of Instant Hosted Mode work).
Patch by: jat, me
Review by: me, amitmanjhi, jat
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6784 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
index 0a80f52..2e46788 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
@@ -42,6 +42,8 @@
private List<JTypeParameter> typeParams = Lists.create();
+ private String[] realParameterNames = null;
+
JAbstractMethod(JAbstractMethod srcMethod) {
this.annotations = new Annotations(srcMethod.annotations);
this.isVarArgs = srcMethod.isVarArgs;
@@ -114,6 +116,7 @@
}
public JParameter[] getParameters() {
+ // TODO(jat): where do we handle fake arg names?
return params.toArray(TypeOracle.NO_JPARAMS);
}
@@ -236,6 +239,22 @@
return annotations.getDeclaredAnnotations();
}
+ // Called only by a JParameter, passing itself as a reference for lookup.
+ String getRealParameterName(JParameter parameter) {
+ if (realParameterNames == null) {
+ fetchRealParameterNames();
+ }
+ int n = params.size();
+ for (int i = 0; i < n; ++i) {
+ // Identity tests are ok since identity is durable within an oracle.
+ if (params.get(i) == parameter) {
+ return realParameterNames == null ? "arg" + i : realParameterNames[i];
+ }
+ }
+ // TODO: report error if we are asked for an unknown JParameter?
+ return null;
+ }
+
boolean hasParamTypes(JType[] paramTypes) {
if (params.size() != paramTypes.length) {
return false;
@@ -251,4 +270,8 @@
}
return true;
}
+
+ private void fetchRealParameterNames() {
+ realParameterNames = getEnclosingType().getOracle().getJavaSourceParser().getArguments(this);
+ }
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
index d61f061..2063d58 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAnnotationType.java
@@ -23,9 +23,9 @@
public class JAnnotationType extends JRealClassType {
public JAnnotationType(TypeOracle oracle, JPackage declaringPackage,
- JClassType enclosingType, boolean isLocalType, String name,
+ String enclosingTypeName, boolean isLocalType, String name,
boolean isInterface) {
- super(oracle, declaringPackage, enclosingType, isLocalType, name,
+ super(oracle, declaringPackage, enclosingTypeName, isLocalType, name,
isInterface);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
index 6972d61..aca74a7 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
@@ -26,6 +26,8 @@
private JType componentType;
+ private String lazyQualifiedBinaryName;
+
private String lazyQualifiedName;
private String lazySimpleName;
@@ -183,6 +185,14 @@
}
@Override
+ public String getQualifiedBinaryName() {
+ if (lazyQualifiedBinaryName == null) {
+ lazyQualifiedBinaryName = "[" + getComponentType().getQualifiedBinaryName();
+ }
+ return lazyQualifiedBinaryName;
+ }
+
+ @Override
public String getQualifiedSourceName() {
if (lazyQualifiedName == null) {
lazyQualifiedName = getComponentType().getQualifiedSourceName() + "[]";
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
index c8b112e..5090d92 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JClassType.java
@@ -101,7 +101,7 @@
*
* @param lhsType
* @param rhsType
- * @return
+ * @return true if rhsType can be assigned to lhsType
*/
private static boolean areClassTypesAssignableNoSupers(JClassType lhsType,
JClassType rhsType) {
@@ -620,15 +620,6 @@
return null;
}
- protected final String makeCompoundName(JClassType type) {
- if (type.getEnclosingType() == null) {
- return type.getSimpleSourceName();
- } else {
- return makeCompoundName(type.getEnclosingType()) + "."
- + type.getSimpleSourceName();
- }
- }
-
/**
* Tells this type's superclasses and superinterfaces about it.
*/
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
index 76f7fc4..347b534 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JEnumType.java
@@ -25,9 +25,9 @@
private JEnumConstant[] lazyEnumConstants;
public JEnumType(TypeOracle oracle, JPackage declaringPackage,
- JClassType enclosingType, boolean isLocalType, String name,
+ String enclosingTypeName, boolean isLocalType, String name,
boolean isInterface) {
- super(oracle, declaringPackage, enclosingType, isLocalType, name,
+ super(oracle, declaringPackage, enclosingTypeName, isLocalType, name,
isInterface);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
index 18f6681..c08cc93 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JGenericType.java
@@ -30,9 +30,9 @@
private List<JTypeParameter> typeParams = Lists.create();
public JGenericType(TypeOracle oracle, JPackage declaringPackage,
- JClassType enclosingType, boolean isLocalType, String name,
+ String enclosingTypeName, boolean isLocalType, String name,
boolean isInterface, JTypeParameter[] jtypeParameters) {
- super(oracle, declaringPackage, enclosingType, isLocalType, name,
+ super(oracle, declaringPackage, enclosingTypeName, isLocalType, name,
isInterface);
if (jtypeParameters != null) {
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
index b13db90..df15ce1 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
@@ -26,11 +26,13 @@
private final Annotations annotations;
- private final String name;
+ private String name;
private JType type;
private final JAbstractMethod enclosingMethod;
+
+ private boolean argNameIsReal;
public JParameter(JAbstractMethod enclosingMethod, JType type,
String name) {
@@ -39,9 +41,16 @@
public JParameter(JAbstractMethod enclosingMethod, JType type, String name,
Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+ this(enclosingMethod, type, name, declaredAnnotations, true);
+ }
+
+ public JParameter(JAbstractMethod enclosingMethod, JType type, String name,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
+ boolean argNameIsReal) {
this.enclosingMethod = enclosingMethod;
this.type = type;
this.name = name;
+ this.argNameIsReal = argNameIsReal;
enclosingMethod.addParameter(this);
@@ -79,6 +88,10 @@
}
public String getName() {
+ if (!argNameIsReal) {
+ name = enclosingMethod.getRealParameterName(this);
+ argNameIsReal = true;
+ }
return name;
}
@@ -90,6 +103,7 @@
return annotations.isAnnotationPresent(annotationClass);
}
+ @Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(type.getParameterizedQualifiedSourceName());
@@ -112,6 +126,11 @@
return annotations.getDeclaredAnnotations();
}
+ // Only called by JAbstractMethod after real parameter names are fetched.
+ void setName(String name) {
+ this.name = name;
+ }
+
// Called when parameter types are found to be parameterized
void setType(JType type) {
this.type = type;
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
index b823d7a..1eaf518 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameterizedType.java
@@ -99,7 +99,8 @@
});
this.typeArgs = Lists.create(typeArgs);
- assert (this.typeArgs.indexOf(null) == -1);
+ assert (this.typeArgs.indexOf(null) == -1)
+ : "Unresolved typeArg creating JParameterizedType from " + baseType;
// NOTE: Can't perform substitutions until we are done building
}
@@ -238,6 +239,11 @@
return sb.toString();
}
+ @Override
+ public String getQualifiedBinaryName() {
+ return getBaseType().getQualifiedBinaryName();
+ }
+
/**
* Everything is fully qualified and includes the < and > in the
* signature.
@@ -427,7 +433,7 @@
/**
* Initialize a map of substitutions for {@link JTypeParameter}s to
* corresponding {@link JClassType}s. This can only be initialized after the
- * {@link com.google.gwt.dev.jdt.TypeOracleBuilder TypeOracleBuilder} has
+ * {@link com.google.gwt.dev.javac.TypeOracleMediator TypeOracleMediator} has
* fully resolved all of the {@link JClassType}s.
*/
void maybeInitializeTypeParameterSubstitutionMap() {
@@ -531,4 +537,4 @@
// Legal substitution can be made and is record in substitutions.
return substitutions;
}
-}
\ No newline at end of file
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
index 34582b5..38fed10 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JPrimitiveType.java
@@ -82,6 +82,11 @@
return jni;
}
+ @Override
+ public String getQualifiedBinaryName() {
+ return name;
+ }
+
public String getQualifiedBoxedSourceName() {
return "java.lang." + boxedName;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
index 9b6ea87..b0d40ac 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRawType.java
@@ -136,6 +136,11 @@
}
@Override
+ public String getQualifiedBinaryName() {
+ return getBaseType().getQualifiedBinaryName();
+ }
+
+ @Override
public String getQualifiedSourceName() {
return getBaseType().getQualifiedSourceName();
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index b492b98..2a7e990 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -34,7 +34,10 @@
private final JPackage declaringPackage;
- private final JClassType enclosingType;
+ /**
+ * Set when this class is resolved, then never modified.
+ */
+ private JClassType enclosingType;
private List<JClassType> interfaces = Lists.create();
@@ -42,6 +45,8 @@
private final boolean isLocalType;
+ private String lazyQualifiedBinaryName;
+
private String lazyQualifiedName;
private final Members members = new Members(this);
@@ -56,16 +61,27 @@
private JClassType superclass;
+ /**
+ * Create a class type that reflects an actual type.
+ *
+ * @param oracle
+ * @param declaringPackage
+ * @param enclosingTypeName the fully qualified source name of the enclosing
+ * class or null if a top-level class - setEnclosingType must be
+ * called later with the proper enclosing type if this is non-null
+ * @param isLocalType
+ * @param name
+ * @param isInterface
+ */
public JRealClassType(TypeOracle oracle, JPackage declaringPackage,
- JClassType enclosingType, boolean isLocalType, String name,
+ String enclosingTypeName, boolean isLocalType, String name,
boolean isInterface) {
this.oracle = oracle;
this.declaringPackage = declaringPackage;
- this.enclosingType = enclosingType;
this.isLocalType = isLocalType;
this.name = name;
this.isInterface = isInterface;
- if (enclosingType == null) {
+ if (enclosingTypeName == null) {
// Add myself to my package.
//
declaringPackage.addType(this);
@@ -73,18 +89,12 @@
//
nestedName = name;
} else {
- // Add myself to my enclosing type.
- //
- enclosingType.addNestedType(this);
// Compute my "nested name".
//
- JClassType enclosing = enclosingType;
- String nn = name;
- do {
- nn = enclosing.getSimpleSourceName() + "." + nn;
- enclosing = enclosing.getEnclosingType();
- } while (enclosing != null);
- nestedName = nn;
+ nestedName = enclosingTypeName + "." + name;
+
+ // We will add ourselves to the enclosing class when it is set in
+ // setEnclosingType().
}
oracle.addNewType(this);
}
@@ -94,11 +104,13 @@
annotations.addAnnotations(declaredAnnotations);
}
+ @Override
public void addImplementedInterface(JClassType intf) {
assert (intf != null);
interfaces = Lists.add(interfaces, intf);
}
+ @Override
public void addModifierBits(int bits) {
modifierBits |= bits;
}
@@ -123,6 +135,7 @@
return members.findNestedType(typeName);
}
+ @Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return annotations.getAnnotation(annotationClass);
}
@@ -138,6 +151,7 @@
return members.getConstructors();
}
+ @Override
public JClassType getEnclosingType() {
return enclosingType;
}
@@ -157,6 +171,7 @@
return members.getFields();
}
+ @Override
public JClassType[] getImplementedInterfaces() {
return interfaces.toArray(TypeOracle.NO_JCLASSES);
}
@@ -182,6 +197,7 @@
return members.getMethods();
}
+ @Override
public String getName() {
return nestedName;
}
@@ -196,6 +212,7 @@
return members.getNestedTypes();
}
+ @Override
public TypeOracle getOracle() {
return oracle;
}
@@ -210,18 +227,32 @@
return members.getOverridableMethods();
}
+ @Override
public JPackage getPackage() {
return declaringPackage;
}
@Override
+ public String getQualifiedBinaryName() {
+ if (lazyQualifiedBinaryName == null) {
+ lazyQualifiedBinaryName = "";
+ JPackage pkg = getPackage();
+ if (!pkg.isDefault()) {
+ lazyQualifiedBinaryName = pkg.getName() + ".";
+ }
+ lazyQualifiedBinaryName += nestedName.replace('.', '$');
+ }
+ return lazyQualifiedBinaryName;
+ }
+
+ @Override
public String getQualifiedSourceName() {
if (lazyQualifiedName == null) {
JPackage pkg = getPackage();
if (!pkg.isDefault()) {
- lazyQualifiedName = pkg.getName() + "." + makeCompoundName(this);
+ lazyQualifiedName = pkg.getName() + "." + nestedName;
} else {
- lazyQualifiedName = makeCompoundName(this);
+ lazyQualifiedName = nestedName;
}
}
return lazyQualifiedName;
@@ -232,10 +263,12 @@
return name;
}
+ @Override
public JClassType[] getSubtypes() {
return allSubtypes.toArray(TypeOracle.NO_JCLASSES);
}
+ @Override
public JClassType getSuperclass() {
return superclass;
}
@@ -247,10 +280,12 @@
oracle.invalidate(this);
}
+ @Override
public boolean isAbstract() {
return 0 != (modifierBits & TypeOracle.MOD_ABSTRACT);
}
+ @Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return annotations.isAnnotationPresent(annotationClass);
}
@@ -278,6 +313,7 @@
* @return <code>true</code> if the type is default instantiable, or
* <code>false</code> otherwise
*/
+ @Override
public boolean isDefaultInstantiable() {
if (isInterface() != null || isAbstract()) {
return false;
@@ -324,6 +360,7 @@
* @return true if this type is a local type, whether it is named or
* anonymous.
*/
+ @Override
public boolean isLocalType() {
return isLocalType;
}
@@ -334,6 +371,7 @@
* @return true if this type has an enclosing type, false if this type is a
* top-level type
*/
+ @Override
public boolean isMemberType() {
return enclosingType != null;
}
@@ -350,14 +388,17 @@
return null;
}
+ @Override
public boolean isPrivate() {
return 0 != (modifierBits & TypeOracle.MOD_PRIVATE);
}
+ @Override
public boolean isProtected() {
return 0 != (modifierBits & TypeOracle.MOD_PROTECTED);
}
+ @Override
public boolean isPublic() {
return 0 != (modifierBits & TypeOracle.MOD_PUBLIC);
}
@@ -368,6 +409,7 @@
return null;
}
+ @Override
public boolean isStatic() {
return 0 != (modifierBits & TypeOracle.MOD_STATIC);
}
@@ -384,6 +426,27 @@
oracle.resurrect(this);
}
+ /**
+ * INTERNAL METHOD -- this should only be called by TypeOracleMediator.
+ *
+ * TODO: reduce visibility.
+ *
+ * @param enclosingType
+ */
+ public void setEnclosingType(JClassType enclosingType) {
+ assert this.enclosingType == null;
+ assert enclosingType != null;
+
+ this.enclosingType = enclosingType;
+
+ // Add myself to my enclosing type.
+ JRawType rawType = enclosingType.isRawType();
+ if (rawType != null) {
+ enclosingType = rawType.getGenericType();
+ }
+ enclosingType.addNestedType(this);
+ }
+
@Override
public void setSuperclass(JClassType type) {
assert (type != null);
@@ -411,6 +474,7 @@
}
}
+ @Override
protected void acceptSubtype(JClassType me) {
allSubtypes = IdentitySets.add(allSubtypes, me);
notifySuperTypesOf(me);
@@ -441,10 +505,12 @@
return members.findNestedTypeImpl(typeName, index);
}
+ @Override
protected int getModifierBits() {
return modifierBits;
}
+ @Override
protected void getOverridableMethodsOnSuperclassesAndThisClass(
Map<String, JMethod> methodsBySignature) {
members.getOverridableMethodsOnSuperclassesAndThisClass(methodsBySignature);
@@ -458,6 +524,7 @@
*
* @param methodsBySignature
*/
+ @Override
protected void getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(
Map<String, JMethod> methodsBySignature) {
members.getOverridableMethodsOnSuperinterfacesAndMaybeThisInterface(methodsBySignature);
@@ -466,6 +533,7 @@
/**
* Tells this type's superclasses and superinterfaces about it.
*/
+ @Override
protected void notifySuperTypesOf(JClassType me) {
// TODO(scottb): revisit
if (superclass != null) {
@@ -477,6 +545,7 @@
}
}
+ @Override
protected void removeSubtype(JClassType me) {
allSubtypes = IdentitySets.remove(allSubtypes, me);
@@ -494,6 +563,7 @@
/**
* NOTE: This method is for testing purposes only.
*/
+ @Override
Annotation[] getAnnotations() {
return annotations.getAnnotations();
}
@@ -501,6 +571,7 @@
/**
* NOTE: This method is for testing purposes only.
*/
+ @Override
Annotation[] getDeclaredAnnotations() {
return annotations.getDeclaredAnnotations();
}
@@ -510,6 +581,7 @@
return this;
}
+ @Override
void notifySuperTypes() {
notifySuperTypesOf(this);
}
@@ -517,6 +589,7 @@
/**
* Removes references to this instance from all of its super types.
*/
+ @Override
void removeFromSupertypes() {
removeSubtype(this);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
index 15bc0bd..6c4a34f 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JType.java
@@ -40,6 +40,11 @@
return getQualifiedSourceName();
}
+ /**
+ * TODO(scottb): remove if we can resolve param names differently.
+ */
+ public abstract String getQualifiedBinaryName();
+
public abstract String getQualifiedSourceName();
public abstract String getSimpleSourceName();
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
index ecb9c41..74de83a 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
@@ -96,6 +96,12 @@
}
@Override
+ public String getQualifiedBinaryName() {
+ // TODO(jat): !! does a binary name have meaning for a type parameter?
+ return toString(true);
+ }
+
+ @Override
public String getQualifiedSourceName() {
return toString(false);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
index 7681004..859fde1 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JWildcardType.java
@@ -110,6 +110,12 @@
}
@Override
+ public String getQualifiedBinaryName() {
+ // TODO(jat): !! does a binary name have meaning for a wildcard?
+ return toString(true);
+ }
+
+ @Override
public String getQualifiedSourceName() {
return toString(false);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index a6138a6..5ad372a 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -16,7 +16,9 @@
package com.google.gwt.core.ext.typeinfo;
import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
+import com.google.gwt.dev.javac.JavaSourceParser;
import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.Name;
import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.dev.util.collect.IdentityHashMap;
@@ -262,6 +264,8 @@
private JClassType javaLangObject;
+ private JavaSourceParser javaSourceParser = new JavaSourceParser();
+
/**
* Maps SingleJsoImpl interfaces to the implementing JSO subtype.
*/
@@ -275,7 +279,7 @@
/**
* A list of recently-added types that will be fully initialized on the next
- * call to {@link #finish(TreeLogger)}.
+ * call to {@link #finish}.
*/
private final List<JRealClassType> recentTypes = new ArrayList<JRealClassType>();
@@ -294,6 +298,17 @@
}
/**
+ * Called to add a source reference for a top-level class type.
+ *
+ * SHOULD ONLY BE CALLED FROM TypeOracleMediator.
+ *
+ * TODO: make not public?
+ */
+ public void addSourceReference(JRealClassType type, Resource sourceFile) {
+ javaSourceParser.addSourceForType(type, sourceFile);
+ }
+
+ /**
* Attempts to find a package by name. All requests for the same package
* return the same package object.
*
@@ -342,7 +357,7 @@
* TODO: make not public?
*/
public void finish() {
- JClassType[] newTypes = recentTypes.toArray(NO_JCLASSES);
+ JClassType[] newTypes = recentTypes.toArray(new JClassType[recentTypes.size()]);
computeHierarchyRelationships(newTypes);
computeSingleJsoImplData(newTypes);
recentTypes.clear();
@@ -378,6 +393,10 @@
return javaLangObject;
}
+ public JavaSourceParser getJavaSourceParser() {
+ return javaSourceParser;
+ }
+
/**
* Ensure that a package with the specified name exists as well as its parent
* packages.
@@ -852,12 +871,13 @@
}
}
}
-
+
/**
* Removes the specified type from the type oracle.
*/
private void removeType(JRealClassType invalidType) {
allTypes.remove(invalidType.getQualifiedSourceName());
+ recentTypes.remove(invalidType);
JPackage pkg = invalidType.getPackage();
if (pkg != null) {
pkg.remove(invalidType);
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
index d356681..1f14afe 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -172,7 +172,9 @@
}
void graveyard() {
- realClassType.invalidate();
+ if (realClassType != null) {
+ realClassType.invalidate();
+ }
}
void invalidate() {
diff --git a/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java b/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java
new file mode 100644
index 0000000..2444352
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/JavaSourceParser.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2009 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.typeinfo.JAbstractMethod;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.Name.BinaryName;
+
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil;
+
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.WeakHashMap;
+
+/**
+ * Methods to do direct parsing of Java source -- currently the only uses are
+ * for finding actual method parameter names on request.
+ */
+public class JavaSourceParser {
+
+ public static JClassType getTopmostType(JClassType type) {
+ while (type.getEnclosingType() != null) {
+ type = type.getEnclosingType();
+ }
+ return type;
+ }
+
+ /**
+ * Spits a binary name into a series of char arrays, corresponding to
+ * enclosing classes.
+ *
+ * <p>For example, {@code test.Foo$Bar} gets expanded to [[Foo],[Bar]].
+ * Note that the package is not included.
+ *
+ * @param binaryName class name in binary form (ie, test.Foo$Bar)
+ * @return list of char arrays of class names, from outer to inner
+ */
+ // @VisibleForTesting
+ static List<char[]> getClassChain(String binaryName) {
+ ArrayList<char[]> result = new ArrayList<char[]>();
+ String className = BinaryName.getClassName(binaryName);
+ int idx;
+ while ((idx = className.indexOf('$')) >= 0) {
+ result.add(className.substring(0, idx).toCharArray());
+ className = className.substring(idx + 1);
+ }
+ result.add(className.toCharArray());
+ return result;
+ }
+
+ /**
+ * Find a matching method in a type.
+ *
+ * @param type JDT method
+ * @param jMethod TypeOracle method object to find
+ * @return method declaration or null if not found
+ */
+ private static AbstractMethodDeclaration findMethod(TypeDeclaration type,
+ JAbstractMethod jMethod) {
+ List<AbstractMethodDeclaration> candidates = findNamedMethods(type,
+ jMethod.getName());
+ if (candidates.size() == 0) {
+ return null;
+ }
+ if (candidates.size() == 1) {
+ return candidates.get(0);
+ }
+ nextCandidate: for (AbstractMethodDeclaration candidate : candidates) {
+ int n = candidate.arguments == null ? 0 : candidate.arguments.length;
+ JParameter[] params = jMethod.getParameters();
+ if (n != params.length) {
+ continue;
+ }
+ for (int i = 0; i < n; ++i) {
+ if (!typeMatches(candidate.arguments[i].type,
+ params[i].getType())) {
+ continue nextCandidate;
+ }
+ }
+ return candidate;
+ }
+ return null;
+ }
+
+ /**
+ * Find all methods which have the requested name.
+ *
+ * <p>{@code <clinit>} is not supported.
+ * @param type JDT type declaration
+ * @param name name of methods to find
+ * @return list of matching methods
+ */
+ private static List<AbstractMethodDeclaration> findNamedMethods(
+ TypeDeclaration type, String name) {
+ List<AbstractMethodDeclaration> matching = new ArrayList<AbstractMethodDeclaration>();
+ boolean isCtor = "<init>".equals(name);
+ char[] nameArray = name.toCharArray();
+ for (AbstractMethodDeclaration method : type.methods) {
+ if ((isCtor && method.isConstructor()) ||
+ (!isCtor && !method.isConstructor() && !method.isClinit()
+ && Arrays.equals(method.selector, nameArray))) {
+ matching.add(method);
+ }
+ }
+ return matching;
+ }
+
+ /**
+ * Find a particular type in a compilation unit.
+ *
+ * @param unit JDT cud
+ * @param binaryName binary name of the type to find (ie, test.Foo$Bar)
+ * @return type declaration or null if not found
+ */
+ private static TypeDeclaration findType(CompilationUnitDeclaration unit,
+ String binaryName) {
+ List<char[]> classChain = getClassChain(binaryName);
+ TypeDeclaration curType = findType(unit.types, classChain.get(0));
+ for (int i = 1; i < classChain.size(); ++i) {
+ if (curType == null) {
+ return null;
+ }
+ curType = findType(curType.memberTypes, classChain.get(i));
+ }
+ return curType;
+ }
+
+ /**
+ * Find one type by name in a array of types.
+ *
+ * @param types array of types
+ * @param name name of type to find
+ * @return matching type or null if not found
+ */
+ private static TypeDeclaration findType(TypeDeclaration[] types, char[] name) {
+ for (TypeDeclaration type : types) {
+ if (Arrays.equals(name, type.name)) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parse Java source.
+ *
+ * @param javaSource String containing Java source to parse
+ * @return a CompilationUnitDeclaration or null if parsing failed
+ */
+ private static CompilationUnitDeclaration parseJava(String javaSource) {
+ CodeSnippetParsingUtil parsingUtil = new CodeSnippetParsingUtil();
+ CompilerOptions options = new CompilerOptions();
+ options.complianceLevel = ClassFileConstants.JDK1_5;
+ options.sourceLevel = ClassFileConstants.JDK1_5;
+ CompilationUnitDeclaration unit = parsingUtil.parseCompilationUnit(
+ javaSource.toString().toCharArray(), options.getMap(), true);
+ if (unit.compilationResult().hasProblems()) {
+ return null;
+ }
+ return unit;
+ }
+
+ /**
+ * Compares an unresolved JDT type to a TypeOracle type to see if they match.
+ *
+ * @param jdtType
+ * @param toType
+ * @return true if the two type objects resolve to the same
+ */
+ private static boolean typeMatches(TypeReference jdtType, JType toType) {
+ List<char[]> toNameComponents = getClassChain(
+ toType.getQualifiedBinaryName());
+ int toLen = toNameComponents.size();
+ char[][] jdtNameComponents = jdtType.getTypeName();
+ int jdtLen = jdtNameComponents.length;
+ int maxToCompare = Math.min(toLen, jdtLen);
+
+ // compare from the end
+ for (int i = 1; i <= maxToCompare; ++i) {
+ if (!Arrays.equals(jdtNameComponents[jdtLen - i],
+ toNameComponents.get(toLen - i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Map of top-level classes to the source file associated with it.
+ */
+ private WeakHashMap<JClassType, Resource> classSources = new WeakHashMap<JClassType, Resource>();
+
+ /**
+ * Cache of top-level classes to JDT CUDs associated with them.
+ *
+ * <p>CUDs may be discarded at any time (with a performance cost if they are
+ * needed again), and are held in SoftReferences to allow GC to dump them.
+ */
+ private WeakHashMap<JClassType, SoftReference<CompilationUnitDeclaration>> cudCache = new WeakHashMap<JClassType, SoftReference<CompilationUnitDeclaration>>();
+
+ /**
+ * Add a source file associated with the outermost enclosing class.
+ *
+ * @param topType
+ * @param source
+ *
+ * TODO: reduce visibility
+ */
+ public synchronized void addSourceForType(JClassType topType, Resource source) {
+ classSources.put(topType, source);
+ }
+
+ /**
+ * Return the real argument names for a given method from the source.
+ *
+ * @param method method to lookup parameter names for
+ * @return array of argument names or null if no source is available
+ */
+ public synchronized String[] getArguments(JAbstractMethod method) {
+ JClassType type = method.getEnclosingType();
+ JClassType topType = getTopmostType(type);
+ CompilationUnitDeclaration cud = getCudForTopLevelType(topType);
+ if (cud == null) {
+ return null;
+ }
+ TypeDeclaration jdtType = findType(cud, type.getQualifiedBinaryName());
+ if (jdtType == null) {
+ // TODO(jat): any thing else to do here?
+ return null;
+ }
+ AbstractMethodDeclaration jdtMethod = findMethod(jdtType, method);
+ if (jdtMethod == null) {
+ // TODO(jat): any thing else to do here?
+ return null;
+ }
+ int n = jdtMethod.arguments.length;
+ String[] argNames = new String[n];
+ for (int i = 0; i < n; ++i) {
+ argNames[i] = String.valueOf(jdtMethod.arguments[i].name);
+ }
+ return argNames;
+ }
+
+ /**
+ * Finds a JDT CUD for a given top-level type, generating it if needed.
+ *
+ * @param topType top-level JClassType
+ * @return CUD instance or null if no source found
+ */
+ private synchronized CompilationUnitDeclaration getCudForTopLevelType(
+ JClassType topType) {
+ CompilationUnitDeclaration cud = null;
+ if (cudCache.containsKey(topType)) {
+ SoftReference<CompilationUnitDeclaration> cudRef = cudCache.get(topType);
+ if (cudRef != null) {
+ cud = cudRef.get();
+ }
+ }
+ if (cud == null) {
+ Resource classSource = classSources.get(topType);
+ String source = null;
+ if (classSource != null) {
+ InputStream stream = classSource.openContents();
+ source = Util.readStreamAsString(stream);
+ }
+ if (source == null) {
+ // cache negative result so we don't try again
+ cudCache.put(topType, null);
+ } else {
+ cud = parseJava(source);
+ cudCache.put(topType,
+ new SoftReference<CompilationUnitDeclaration>(cud));
+ }
+ }
+ return cud;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/Resolver.java b/dev/core/src/com/google/gwt/dev/javac/Resolver.java
new file mode 100644
index 0000000..e62b129
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/Resolver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for resolving various aspects of a class.
+ */
+public interface Resolver {
+
+ Map<String, JRealClassType> getBinaryMapper();
+
+ TypeOracle getTypeOracle();
+
+ boolean resolveAnnotation(TreeLogger logger,
+ CollectAnnotationData annotVisitor,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations);
+
+ boolean resolveAnnotations(TreeLogger logger,
+ List<CollectAnnotationData> annotations,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations);
+
+ boolean resolveClass(TreeLogger logger, JRealClassType type);
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
index fee85bc..94bbddd 100644
--- a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
@@ -16,7 +16,6 @@
package com.google.gwt.dev.javac;
import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.typeinfo.HasTypeParameters;
import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
import com.google.gwt.core.ext.typeinfo.JAnnotationType;
@@ -32,58 +31,43 @@
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
import com.google.gwt.core.ext.typeinfo.JRealClassType;
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.TypeOracle;
-import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
-import com.google.gwt.dev.javac.CompilationUnit.State;
+import com.google.gwt.dev.asm.ClassReader;
+import com.google.gwt.dev.asm.ClassVisitor;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.signature.SignatureReader;
+import com.google.gwt.dev.asm.util.TraceClassVisitor;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData;
+import com.google.gwt.dev.javac.asm.CollectClassData;
+import com.google.gwt.dev.javac.asm.CollectFieldData;
+import com.google.gwt.dev.javac.asm.CollectMethodData;
+import com.google.gwt.dev.javac.asm.CollectTypeParams;
+import com.google.gwt.dev.javac.asm.ResolveClassSignature;
+import com.google.gwt.dev.javac.asm.ResolveMethodSignature;
+import com.google.gwt.dev.javac.asm.ResolveTypeSignature;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData.AnnotationData;
+import com.google.gwt.dev.javac.asm.CollectClassData.AnnotationEnum;
import com.google.gwt.dev.javac.impl.Shared;
-import com.google.gwt.dev.util.collect.HashMap;
-import com.google.gwt.dev.util.collect.HashSet;
-import com.google.gwt.dev.util.collect.IdentityHashMap;
-import com.google.gwt.dev.util.collect.Maps;
+import com.google.gwt.dev.javac.impl.SourceFileCompilationUnit;
+import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.util.Name;
+import com.google.gwt.dev.util.Name.InternalName;
-import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Annotation;
-import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Argument;
-import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
-import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
-import org.eclipse.jdt.internal.compiler.ast.Clinit;
-import org.eclipse.jdt.internal.compiler.ast.Expression;
-import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.Initializer;
-import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
-import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.NameReference;
-import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
-import org.eclipse.jdt.internal.compiler.ast.TypeReference;
-import org.eclipse.jdt.internal.compiler.ast.Wildcard;
-import org.eclipse.jdt.internal.compiler.impl.Constant;
-import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
-import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
-import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
-import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
-import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
-import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
-import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -94,7 +78,18 @@
*/
public class TypeOracleMediator {
- private static final JClassType[] NO_JCLASSES = new JClassType[0];
+ /**
+ * Pairs of bits to convert from ASM Opcodes.* to Shared.* bitfields.
+ */
+ private static final int[] ASM_TO_SHARED_MODIFIERS = new int[] {
+ Opcodes.ACC_PUBLIC, Shared.MOD_PUBLIC, Opcodes.ACC_PRIVATE,
+ Shared.MOD_PRIVATE, Opcodes.ACC_PROTECTED, Shared.MOD_PROTECTED,
+ Opcodes.ACC_STATIC, Shared.MOD_STATIC, Opcodes.ACC_FINAL,
+ Shared.MOD_FINAL, Opcodes.ACC_ABSTRACT, Shared.MOD_ABSTRACT,
+ Opcodes.ACC_VOLATILE, Shared.MOD_VOLATILE, Opcodes.ACC_TRANSIENT,
+ Shared.MOD_TRANSIENT,};
+
+ private static final JTypeParameter[] NO_TYPE_PARAMETERS = new JTypeParameter[0];
/**
* Returns the binary name of a type. This is the same name that would be
@@ -134,192 +129,308 @@
return classType.getQualifiedSourceName();
}
- /**
- * Returns the value associated with a JDT constant.
- */
- private static Object getConstantValue(Constant constant) {
- switch (constant.typeID()) {
- case TypeIds.T_char:
- return constant.charValue();
- case TypeIds.T_byte:
- return constant.byteValue();
- case TypeIds.T_short:
- return constant.shortValue();
- case TypeIds.T_boolean:
- return constant.booleanValue();
- case TypeIds.T_long:
- return constant.longValue();
- case TypeIds.T_double:
- return constant.doubleValue();
- case TypeIds.T_float:
- return constant.floatValue();
- case TypeIds.T_int:
- return constant.intValue();
- case TypeIds.T_JavaLangString:
- return constant.stringValue();
- case TypeIds.T_null:
- return null;
- default:
- break;
+ private static JTypeParameter[] collectTypeParams(String signature) {
+ if (signature != null) {
+ List<JTypeParameter> params = new ArrayList<JTypeParameter>();
+ SignatureReader reader = new SignatureReader(signature);
+ reader.accept(new CollectTypeParams(params));
+ return params.toArray(new JTypeParameter[params.size()]);
}
+ return NO_TYPE_PARAMETERS;
+ }
- assert false : "Unknown constant type";
+ /**
+ * @param resolvedType a resolved type, which is either a primitive, an array,
+ * or a JRealClassType.
+ */
+ private static <T> Class<? extends T> getClassLiteral(TreeLogger logger,
+ Class<T> requestedClass, JType resolvedType) {
+ Class<?> clazz = null;
+ if (resolvedType instanceof JPrimitiveType) {
+ clazz = getClassLiteralForPrimitive((JPrimitiveType) resolvedType);
+ } else {
+ String binaryName;
+ if (resolvedType instanceof JArrayType) {
+ binaryName = ((JArrayType) resolvedType).getQualifiedBinaryName();
+ } else if (resolvedType instanceof JRealClassType) {
+ binaryName = ((JRealClassType) resolvedType).getQualifiedBinaryName();
+ } else {
+ throw new IllegalArgumentException(
+ "Unexpected type for class literal '"
+ + resolvedType.getQualifiedSourceName() + "'");
+ }
+ try {
+ clazz = Class.forName(binaryName, false,
+ Thread.currentThread().getContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ logger.log(TreeLogger.ERROR,
+ "Unable to get class object for annotation " + resolvedType, e);
+ return null;
+ }
+ }
+ if (requestedClass.isAssignableFrom(clazz)) {
+ return clazz.asSubclass(requestedClass);
+ }
return null;
}
- private static String getMethodName(JClassType enclosingType,
- AbstractMethodDeclaration jmethod) {
- if (jmethod.isConstructor()) {
- return String.valueOf(enclosingType.getSimpleSourceName());
+ private static Class<?> getClassLiteralForPrimitive(JPrimitiveType type) {
+ if (type.equals(JPrimitiveType.INT)) {
+ return Integer.TYPE;
+ } else if (type.equals(JPrimitiveType.BOOLEAN)) {
+ return Boolean.TYPE;
+ } else if (type.equals(JPrimitiveType.DOUBLE)) {
+ return Double.TYPE;
+ } else if (type.equals(JPrimitiveType.FLOAT)) {
+ return Float.TYPE;
+ } else if (type.equals(JPrimitiveType.LONG)) {
+ return Long.TYPE;
+ } else if (type.equals(JPrimitiveType.VOID)) {
+ return Void.TYPE;
+ } else if (type.equals(JPrimitiveType.CHAR)) {
+ return Character.TYPE;
+ } else if (type.equals(JPrimitiveType.SHORT)) {
+ return Short.TYPE;
+ } else if (type.equals(JPrimitiveType.BYTE)) {
+ return Byte.TYPE;
} else {
- return String.valueOf(jmethod.binding.selector);
+ assert false : "Unexpected type " + type;
+ return null;
}
}
- private static RetentionPolicy getRetentionPolicy(
- Class<? extends java.lang.annotation.Annotation> clazz) {
- // Default retention policy is CLASS, see @Retention.
- RetentionPolicy retentionPolicy = RetentionPolicy.CLASS;
- Retention retentionAnnotation = clazz.getAnnotation(Retention.class);
- if (retentionAnnotation != null) {
- retentionPolicy = retentionAnnotation.value();
+ private static JTypeParameter[] getTypeParametersForClass(
+ CollectClassData classData) {
+ JTypeParameter[] typeParams = null;
+ if (classData.getSignature() != null) {
+ // TODO(jat): do we need to consider generic types w/ method type
+ // params for local classes?
+ typeParams = collectTypeParams(classData.getSignature());
}
- return retentionPolicy;
+ return typeParams;
+ }
+
+ private static Class<?> getWrapperClass(Class<?> primitiveClass) {
+ assert primitiveClass.isPrimitive();
+ if (primitiveClass.equals(Integer.TYPE)) {
+ return Integer.class;
+ } else if (primitiveClass.equals(Boolean.TYPE)) {
+ return Boolean.class;
+ } else if (primitiveClass.equals(Byte.TYPE)) {
+ return Byte.class;
+ } else if (primitiveClass.equals(Character.TYPE)) {
+ return Character.class;
+ } else if (primitiveClass.equals(Short.TYPE)) {
+ return Short.class;
+ } else if (primitiveClass.equals(Long.TYPE)) {
+ return Long.class;
+ } else if (primitiveClass.equals(Float.TYPE)) {
+ return Float.class;
+ } else if (primitiveClass.equals(Double.TYPE)) {
+ return Double.class;
+ } else {
+ throw new IllegalArgumentException(primitiveClass.toString()
+ + " not a primitive class");
+ }
}
/**
- * Returns <code>true</code> if this name is the special package-info type
- * name.
- *
* @return <code>true</code> if this name is the special package-info type
- * name
+ * name.
*/
private static boolean isPackageInfoTypeName(String qname) {
return "package-info".equals(qname);
}
- private static boolean maybeGeneric(TypeDeclaration typeDecl,
- JClassType enclosingType) {
-
- if (typeDecl.typeParameters != null) {
- // Definitely generic since it has type parameters.
- return true;
+ /**
+ * Returns true if this class is a non-static class inside a generic class.
+ *
+ * TODO(jat): do we need to consider the entire hierarchy?
+ *
+ * @param classData
+ * @param enclosingClassData
+ * @return true if this class is a non-static class inside a generic class
+ */
+ private static boolean nonStaticInsideGeneric(CollectClassData classData,
+ CollectClassData enclosingClassData) {
+ if (enclosingClassData == null
+ || (classData.getAccess() & Opcodes.ACC_STATIC) != 0) {
+ return false;
}
-
- if (enclosingType != null && enclosingType.isGenericType() != null) {
- if (!typeDecl.binding.isStatic()) {
- /*
- * This non-static, inner class can reference the type parameters of its
- * enclosing generic type, so we will treat it as a generic type.
- */
- return true;
- }
- }
-
- if (typeDecl.binding.isLocalType()) {
- LocalTypeBinding localTypeBinding = (LocalTypeBinding) typeDecl.binding;
- MethodBinding enclosingMethod = localTypeBinding.enclosingMethod;
- if (enclosingMethod != null) {
- if (enclosingMethod.typeVariables != null
- && enclosingMethod.typeVariables.length != 0) {
- /*
- * This local type can reference the type parameters of its enclosing
- * method, so we will treat is as a generic type.
- */
- return true;
- }
- }
- }
-
- return false;
+ return getTypeParametersForClass(enclosingClassData) != null;
}
- // TODO: move into the Annotations class or create an AnnotationsUtil class?
- private static HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> newAnnotationMap() {
- return new HashMap<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation>();
+ /**
+ * Substitute the raw type if the supplied type is generic.
+ *
+ * @param type
+ * @return original type or its raw type if it is generic
+ */
+ private static JType possiblySubstituteRawType(JType type) {
+ if (type != null) {
+ JGenericType genericType = type.isGenericType();
+ if (genericType != null) {
+ type = genericType.getRawType();
+ }
+ }
+ return type;
}
- private final Map<String, JRealClassType> binaryMapper = new HashMap<String, JRealClassType>();
+ // map of internal names to classes
+ final Map<String, JRealClassType> binaryMapper = new HashMap<String, JRealClassType>();
- /**
- * Mapping of source type bindings; transient because freshly-compiled units
- * are processed in chunks, so only references between types in the same chunk
- * are source bindings; the rest are going to be binary type bindings.
- */
- private final transient Map<SourceTypeBinding, JRealClassType> sourceMapper = new IdentityHashMap<SourceTypeBinding, JRealClassType>();
+ final TypeOracle typeOracle;
- /**
- * Mapping of type variable bindings; transient because compilation units are
- * processed monolithically, and a tv binding is only valid within a single
- * unit.
- */
- private final transient Map<TypeVariableBinding, JTypeParameter> tvMapper = new IdentityHashMap<TypeVariableBinding, JTypeParameter>();
+ // map of internal names to class visitors
+ // transient since it is not retained across calls to addNewUnits
+ private transient Map<String, CollectClassData> classMap;
- private final TypeOracle typeOracle = new TypeOracle();
+ // transient since it is not retained across calls to addNewUnits
+ private transient HashMap<JRealClassType, CollectClassData> classMapType;
- /**
- * Set of unresolved types to process, generated from the first phase of type
- * oracle resolution. Transient because they all get resolved in the second
- * phase.
- */
- private final transient Set<JRealClassType> unresolvedTypes = new HashSet<JRealClassType>();
+ private final Set<JRealClassType> resolved = new HashSet<JRealClassType>();
+
+ private Resolver resolver;
+
+ public TypeOracleMediator() {
+ this(null);
+ }
+
+ // @VisibleForTesting
+ public TypeOracleMediator(TypeOracle typeOracle) {
+ if (typeOracle == null) {
+ typeOracle = new TypeOracle();
+ }
+ this.typeOracle = typeOracle;
+ resolver = new Resolver() {
+ public Map<String, JRealClassType> getBinaryMapper() {
+ return TypeOracleMediator.this.binaryMapper;
+ }
+
+ public TypeOracle getTypeOracle() {
+ return TypeOracleMediator.this.typeOracle;
+ }
+
+ public boolean resolveAnnotation(TreeLogger logger,
+ CollectAnnotationData annotVisitor,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+ return TypeOracleMediator.this.resolveAnnotation(logger, annotVisitor,
+ declaredAnnotations);
+ }
+
+ public boolean resolveAnnotations(TreeLogger logger,
+ List<CollectAnnotationData> annotations,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+ return TypeOracleMediator.this.resolveAnnotations(logger, annotations,
+ declaredAnnotations);
+ }
+
+ public boolean resolveClass(TreeLogger logger, JRealClassType type) {
+ return TypeOracleMediator.this.resolveClass(logger, type);
+ }
+ };
+ }
/**
* Adds new units to an existing TypeOracle.
*/
public void addNewUnits(TreeLogger logger, Collection<CompilationUnit> units) {
- // Perform a shallow pass to establish identity for new and old types.
+ // First collect all class data and resurrect graveyard types.
+ classMap = new HashMap<String, CollectClassData>();
for (CompilationUnit unit : units) {
- switch (unit.getState()) {
- case GRAVEYARD:
- for (CompiledClass compiledClass : unit.getCompiledClasses()) {
- JRealClassType type = compiledClass.getRealClassType();
- assert (type != null);
+ if (unit.getState() == CompilationUnit.State.GRAVEYARD) {
+ for (CompiledClass compiledClass : unit.getCompiledClasses()) {
+ JRealClassType type = compiledClass.getRealClassType();
+ if (type != null) {
type.resurrect();
- binaryMapper.put(compiledClass.getInternalName(), type);
}
- break;
- case COMPILED:
- for (CompiledClass compiledClass : unit.getCompiledClasses()) {
- JRealClassType type = createType(compiledClass);
- binaryMapper.put(compiledClass.getInternalName(), type);
- }
- break;
- case CHECKED:
- for (CompiledClass compiledClass : unit.getCompiledClasses()) {
- JRealClassType type = compiledClass.getRealClassType();
- assert (type != null);
- binaryMapper.put(compiledClass.getInternalName(), type);
- }
- break;
- }
- }
-
- // Perform a deep pass to resolve all new types in terms of our types.
- for (CompilationUnit unit : units) {
- if (unit.getState() != State.COMPILED) {
+ }
+ continue;
+ } else if (!unit.isCompiled()) {
continue;
}
- TreeLogger cudLogger = logger.branch(TreeLogger.SPAM,
- "Processing types in compilation unit: " + unit.getDisplayLocation());
Set<CompiledClass> compiledClasses = unit.getCompiledClasses();
for (CompiledClass compiledClass : compiledClasses) {
- if (unresolvedTypes.remove(compiledClass.getRealClassType())) {
- TypeDeclaration typeDeclaration = compiledClass.getTypeDeclaration();
- if (!resolveTypeDeclaration(cudLogger, typeDeclaration)) {
- logger.log(TreeLogger.WARN,
- "Unexpectedly unable to fully resolve type "
- + compiledClass.getSourceName());
- }
+ CollectClassData cv = processClass(compiledClass);
+ // skip any classes that can't be referenced by name outside of
+ // their local scope, such as anonymous classes and method-local classes
+ if (!cv.hasNoExternalName()) {
+ classMap.put(compiledClass.getInternalName(), cv);
}
}
}
- // Clean transient state.
- assert unresolvedTypes.size() == 0;
- sourceMapper.clear();
- tvMapper.clear();
+
+ // Perform a shallow pass to establish identity for new and old types.
+ classMapType = new HashMap<JRealClassType, CollectClassData>();
+ Set<JRealClassType> unresolvedTypes = new HashSet<JRealClassType>();
+ for (CompilationUnit unit : units) {
+ if (!unit.isCompiled()) {
+ continue;
+ }
+ Set<CompiledClass> compiledClasses = unit.getCompiledClasses();
+ for (CompiledClass compiledClass : compiledClasses) {
+ String internalName = compiledClass.getInternalName();
+ CollectClassData cv = classMap.get(internalName);
+ if (cv == null) {
+ // ignore classes that were skipped earlier
+ continue;
+ }
+ JRealClassType type = compiledClass.getRealClassType();
+ if (type == null) {
+ type = createType(compiledClass, unresolvedTypes);
+ } else {
+ resolved.add(type);
+ }
+ if (type != null) {
+ binaryMapper.put(internalName, type);
+ classMapType.put(type, cv);
+ }
+ }
+ }
+
+ // Hook up enclosing types
+ TreeLogger branch = logger.branch(TreeLogger.SPAM,
+ "Resolving enclosing classes");
+ for (Iterator<JRealClassType> it = unresolvedTypes.iterator(); it.hasNext();) {
+ JRealClassType type = it.next();
+ if (!resolveEnclosingClass(branch, type)) {
+ // already logged why it failed, don't try and use it further
+ it.remove();
+ }
+ }
+
+ // Resolve unresolved types.
+ for (JRealClassType type : unresolvedTypes) {
+ branch = logger.branch(TreeLogger.SPAM, "Resolving "
+ + type.getQualifiedSourceName());
+ if (!resolveClass(branch, type)) {
+ // already logged why it failed
+ // TODO: should we do anything else here?
+ }
+ }
typeOracle.finish();
+
+ // save source references
+ for (CompilationUnit unit : units) {
+ if (unit.isCompiled() && unit instanceof SourceFileCompilationUnit) {
+ SourceFileCompilationUnit sourceUnit = (SourceFileCompilationUnit) unit;
+ Resource sourceFile = sourceUnit.getSourceFile();
+ Set<CompiledClass> compiledClasses = unit.getCompiledClasses();
+ for (CompiledClass compiledClass : compiledClasses) {
+ JRealClassType type = compiledClass.getRealClassType();
+ typeOracle.addSourceReference(type, sourceFile);
+ }
+ }
+ }
+
+ // no longer needed
+ classMap = null;
+ classMapType = null;
+ }
+
+ public Map<String, JRealClassType> getBinaryMapper() {
+ return binaryMapper;
}
public TypeOracle getTypeOracle() {
@@ -330,1050 +441,752 @@
* Full refresh based on new units.
*/
public void refresh(TreeLogger logger, Collection<CompilationUnit> units) {
- logger = logger.branch(TreeLogger.DEBUG, "Refreshing TypeOracle");
binaryMapper.clear();
typeOracle.reset();
+ resolved.clear();
addNewUnits(logger, units);
}
- private Object createAnnotationInstance(TreeLogger logger,
- Expression expression) {
- Annotation annotation = (Annotation) expression;
-
- // Determine the annotation class
- TypeBinding resolvedType = annotation.resolvedType;
- Class<?> classLiteral = getClassLiteral(logger, resolvedType);
- if (classLiteral == null) {
- return null;
- }
-
- Class<? extends java.lang.annotation.Annotation> clazz = classLiteral.asSubclass(java.lang.annotation.Annotation.class);
-
- // Build the map of identifiers to values.
- Map<String, Object> identifierToValue = new HashMap<String, Object>();
- for (MemberValuePair mvp : annotation.memberValuePairs()) {
- // Method name
- String identifier = String.valueOf(mvp.name);
-
- // Value
- Expression expressionValue = mvp.value;
- TypeBinding expectedElementValueType = mvp.binding.returnType;
- Object elementValue = getAnnotationElementValue(logger,
- expectedElementValueType, expressionValue);
- if (elementValue == null) {
+ private Annotation createAnnotation(TreeLogger logger,
+ Class<? extends Annotation> annotationClass, AnnotationData annotData) {
+ Map<String, Object> values = annotData.getValues();
+ for (Map.Entry<String, Object> entry : values.entrySet()) {
+ Method method = null;
+ Throwable caught = null;
+ try {
+ method = annotationClass.getMethod(entry.getKey());
+ } catch (SecurityException e) {
+ caught = e;
+ } catch (NoSuchMethodException e) {
+ caught = e;
+ }
+ if (caught != null) {
+ logger.log(TreeLogger.WARN, "Exception resolving "
+ + annotationClass.getCanonicalName() + "." + entry.getKey(), caught);
return null;
}
-
- /*
- * If the expected value is supposed to be an array then the element value
- * had better be an array.
- */
- assert (expectedElementValueType.isArrayType() == false || expectedElementValueType.isArrayType()
- && elementValue.getClass().isArray());
-
- identifierToValue.put(identifier, elementValue);
+ entry.setValue(resolveAnnotationValue(logger, method.getReturnType(),
+ entry.getValue()));
}
-
- return AnnotationProxyFactory.create(clazz,
- Maps.normalize(identifierToValue));
+ return AnnotationProxyFactory.create(annotationClass, values);
}
- private JRealClassType createType(CompiledClass compiledClass) {
- JRealClassType realClassType = compiledClass.getRealClassType();
- if (realClassType == null) {
- JRealClassType enclosingType = null;
- CompiledClass enclosingClass = compiledClass.getEnclosingClass();
- if (enclosingClass != null) {
- enclosingType = enclosingClass.getRealClassType();
- if (enclosingType == null) {
- enclosingType = createType(enclosingClass);
- }
- }
- realClassType = createType(compiledClass, enclosingType);
- if (realClassType != null) {
- unresolvedTypes.add(realClassType);
- sourceMapper.put(compiledClass.getTypeDeclaration().binding,
- realClassType);
- compiledClass.setRealClassType(realClassType);
- }
- }
- return realClassType;
- }
-
- /**
- * Maps a TypeDeclaration into a JRealClassType. If the TypeDeclaration has
- * TypeParameters (i.e, it is a generic type or method), then the
- * TypeParameters are mapped into JTypeParameters.
- */
private JRealClassType createType(CompiledClass compiledClass,
- JRealClassType enclosingType) {
- TypeDeclaration typeDecl = compiledClass.getTypeDeclaration();
- SourceTypeBinding binding = typeDecl.binding;
- assert (binding.constantPoolName() != null);
-
+ CollectClassData classData, CollectClassData enclosingClassData) {
+ int access = classData.getAccess();
String qname = compiledClass.getSourceName();
String className = Shared.getShortName(qname);
+ JRealClassType resultType = null;
String jpkgName = compiledClass.getPackageName();
JPackage pkg = typeOracle.getOrCreatePackage(jpkgName);
- boolean isLocalType = binding instanceof LocalTypeBinding;
- boolean isIntf = TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.INTERFACE_DECL;
- boolean isAnnotation = TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL;
-
- JRealClassType resultType;
- if (isAnnotation) {
- resultType = new JAnnotationType(typeOracle, pkg, enclosingType,
+ boolean isIntf = (access & Opcodes.ACC_INTERFACE) != 0;
+ boolean isLocalType = classData.isLocal();
+ String enclosingTypeName = null;
+ if (enclosingClassData != null) {
+ enclosingTypeName = InternalName.toSourceName(InternalName.getClassName(enclosingClassData.getName()));
+ }
+ if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+ resultType = new JAnnotationType(typeOracle, pkg, enclosingTypeName,
+ false, className, true);
+ } else if ((access & Opcodes.ACC_ENUM) != 0) {
+ resultType = new JEnumType(typeOracle, pkg, enclosingTypeName,
isLocalType, className, isIntf);
- } else if (maybeGeneric(typeDecl, enclosingType)) {
- // Go through and create declarations for each of the type parameters on
- // the generic class or method
- JTypeParameter[] jtypeParameters = declareTypeParameters(typeDecl.typeParameters);
-
- JGenericType jgenericType = new JGenericType(typeOracle, pkg,
- enclosingType, isLocalType, className, isIntf, jtypeParameters);
-
- resultType = jgenericType;
- } else if (binding.isEnum()) {
- resultType = new JEnumType(typeOracle, pkg, enclosingType, isLocalType,
- className, isIntf);
} else {
- resultType = new JRealClassType(typeOracle, pkg, enclosingType,
- isLocalType, className, isIntf);
+ JTypeParameter[] typeParams = getTypeParametersForClass(classData);
+ if ((typeParams != null && typeParams.length > 0)
+ || nonStaticInsideGeneric(classData, enclosingClassData)) {
+ resultType = new JGenericType(typeOracle, pkg, enclosingTypeName,
+ isLocalType, className, isIntf, typeParams);
+ } else {
+ resultType = new JRealClassType(typeOracle, pkg, enclosingTypeName,
+ isLocalType, className, isIntf);
+ }
}
/*
* Declare type parameters for all methods; we must do this during the first
* pass.
*/
- if (typeDecl.methods != null) {
- for (AbstractMethodDeclaration method : typeDecl.methods) {
- declareTypeParameters(method.typeParameters());
- }
- }
-
+ // if (typeDecl.methods != null) {
+ // for (AbstractMethodDeclaration method : typeDecl.methods) {
+ // declareTypeParameters(method.typeParameters());
+ // }
+ // }
/*
* Add modifiers since these are needed for
* TypeOracle.getParameterizedType's error checking code.
*/
- resultType.addModifierBits(Shared.bindingToModifierBits(binding));
+ resultType.addModifierBits(mapBits(ASM_TO_SHARED_MODIFIERS, access));
+ if (isIntf) {
+ // Always add implicit modifiers on interfaces.
+ resultType.addModifierBits(Shared.MOD_STATIC | Shared.MOD_ABSTRACT);
+ }
+
return resultType;
}
- private JClassType[] createTypeParameterBounds(TreeLogger logger,
- TypeVariableBinding tvBinding) {
- TypeBinding firstBound = tvBinding.firstBound;
- if (firstBound == null) {
- // No bounds were specified, so we default to Object. We assume that the
- // superclass field of a TypeVariableBinding object is a Binding
- // for a java.lang.Object, and we use this binding to find the
- // JClassType for java.lang.Object. To be sure that our assumption
- // about the superclass field is valid, we perform a runtime check
- // against the name of the resolved type.
-
- // You may wonder why we have to go this roundabout way to find a
- // JClassType for java.lang.Object. The reason is that the TypeOracle
- // has not been constructed as yet, so we cannot simply call
- // TypeOracle.getJavaLangObject().
- JClassType jimplicitUpperBound = (JClassType) resolveType(logger,
- tvBinding.superclass);
- if (jimplicitUpperBound != null) {
- assert (Object.class.getName().equals(jimplicitUpperBound.getQualifiedSourceName()));
- return new JClassType[] {jimplicitUpperBound};
- }
-
- // Failed to resolve the implicit upper bound.
- return null;
- }
-
- List<JClassType> bounds = new ArrayList<JClassType>();
- JClassType jfirstBound = (JClassType) resolveType(logger, firstBound);
- if (jfirstBound == null) {
- return null;
- }
-
- bounds.add(jfirstBound);
-
- ReferenceBinding[] superInterfaces = tvBinding.superInterfaces();
- for (ReferenceBinding superInterface : superInterfaces) {
- if (superInterface != firstBound) {
- JClassType jsuperInterface = (JClassType) resolveType(logger,
- superInterface);
- if (jsuperInterface == null) {
+ private JRealClassType createType(CompiledClass compiledClass,
+ Set<JRealClassType> unresolvedTypes) {
+ JRealClassType realClassType = compiledClass.getRealClassType();
+ if (realClassType == null) {
+ CollectClassData classData = classMap.get(compiledClass.getInternalName());
+ String outerClassName = classData.getOuterClass();
+ CollectClassData enclosingClassData = null;
+ if (outerClassName != null) {
+ enclosingClassData = classMap.get(outerClassName);
+ if (enclosingClassData == null) {
+ // if our enclosing class was skipped, skip this one too
return null;
}
- bounds.add(jsuperInterface);
- } else {
- /*
- * If the first bound was an interface JDT will still include it in the
- * set of superinterfaces. So, we ignore it since we have already added
- * it to the bounds.
- */
}
+ realClassType = createType(compiledClass, classData, enclosingClassData);
+ unresolvedTypes.add(realClassType);
+ compiledClass.setRealClassType(realClassType);
}
-
- return bounds.toArray(NO_JCLASSES);
+ return realClassType;
}
- /**
- * Declares TypeParameters declared on a JGenericType or a JAbstractMethod by
- * mapping the TypeParameters into JTypeParameters. <p/> This mapping has to
- * be done on the first pass through the AST in order to handle declarations
- * of the form: <<C exends GenericClass<T>, T extends SimpleClass> <p/> JDT
- * already knows that GenericClass<T> is a parameterized type with a type
- * argument of <T extends SimpleClass>. Therefore, in order to resolve
- * GenericClass<T>, we must have knowledge of <T extends SimpleClass>. <p/>
- * By calling this method on the first pass through the AST, a JTypeParameter
- * for <T extends SimpleClass> will be created.
- */
- private JTypeParameter[] declareTypeParameters(TypeParameter[] typeParameters) {
- if (typeParameters == null || typeParameters.length == 0) {
- return null;
+ private Class<? extends Annotation> getAnnotationClass(TreeLogger logger,
+ AnnotationData annotData) {
+ Type type = Type.getType(annotData.getDesc());
+ JType resolvedType = resolveType(type);
+ if (resolvedType != null) {
+ return getClassLiteral(logger, Annotation.class, resolvedType);
}
-
- JTypeParameter[] jtypeParamArray = new JTypeParameter[typeParameters.length];
- for (int i = 0; i < typeParameters.length; ++i) {
- TypeParameter typeParam = typeParameters[i];
- jtypeParamArray[i] = new JTypeParameter(String.valueOf(typeParam.name), i);
- tvMapper.put(typeParam.binding, jtypeParamArray[i]);
- }
-
- return jtypeParamArray;
- }
-
- /**
- * Returns an annotation element value as defined in JLS 3.0 section 9.7.
- *
- * @param logger
- * @param expectedElementValueType the expected element value type
- * @param elementValueExpression the expression that defines the element value
- *
- * @return annotation element value as defined in JLS 3.0 section 9.7
- */
- @SuppressWarnings("unchecked")
- private Object getAnnotationElementValue(TreeLogger logger,
- TypeBinding expectedElementValueType, Expression elementValueExpression) {
-
- Object elementValue = null;
-
- if (elementValueExpression.constant != null
- && elementValueExpression.constant != Constant.NotAConstant) {
- /*
- * Rely on JDT's computed constant value to deal with an
- * AnnotationElementValue expression whose resolved type is a primitive or
- * a string.
- */
- Constant constant = elementValueExpression.constant;
- int expectedTypeId = expectedElementValueType.id;
-
- if (expectedElementValueType.isArrayType()) {
- /*
- * This can happen when an element value is an array with a single
- * element. In this case JLS 3.0 section 9.7 allows for the
- * ArrayInitializer expression to be implicit. Since, element values can
- * only be single dimensional arrays, we take the leaf type of the
- * expected array type as our resultant element value type.
- */
- assert (!elementValueExpression.resolvedType.isArrayType() && expectedElementValueType.dimensions() == 1);
-
- expectedTypeId = expectedElementValueType.leafComponentType().id;
- }
-
- if (elementValueExpression.resolvedType.id != expectedTypeId) {
- /*
- * Narrowing and widening conversions are handled by the following
- * Constant.castTo call. JDT wants the upper four bits of this mask to
- * be the target type id and the lower four bits to be the source type
- * id. See Constant.castTo for more details.
- */
- constant = constant.castTo((expectedTypeId << 4)
- + elementValueExpression.resolvedType.id);
- }
-
- elementValue = getConstantValue(constant);
- } else if (elementValueExpression instanceof ClassLiteralAccess) {
- ClassLiteralAccess classLiteral = (ClassLiteralAccess) elementValueExpression;
- elementValue = getClassLiteral(logger, classLiteral.targetType);
- } else if (elementValueExpression instanceof ArrayInitializer) {
- elementValue = getAnnotationElementValueArray(logger,
- (ArrayInitializer) elementValueExpression);
- } else if (elementValueExpression instanceof NameReference) {
- /*
- * Any primitive types, conditionals, strings, arrays and name references
- * to constant fields will have all been handled by the constant
- * expression block above. This name reference can only be for an
- * enumerated type.
- */
- NameReference nameRef = (NameReference) elementValueExpression;
-
- assert (nameRef.constant == null || nameRef.constant == Constant.NotAConstant);
- assert (nameRef.actualReceiverType.isEnum());
-
- Class<?> clazz = getClassLiteral(logger, nameRef.actualReceiverType);
- Class<? extends Enum> enumClass = clazz.asSubclass(Enum.class);
-
- String enumName = String.valueOf(nameRef.fieldBinding().name);
- elementValue = Enum.valueOf(enumClass, enumName);
- } else if (elementValueExpression instanceof Annotation) {
- elementValue = createAnnotationInstance(logger, elementValueExpression);
- } else {
- assert (false);
- return null;
- }
-
- assert (elementValue != null);
-
- if (expectedElementValueType.isArrayType()
- && !elementValue.getClass().isArray()) {
- /*
- * Handle single element arrays where no explicit array initializer was
- * given.
- */
- Object array = Array.newInstance(elementValue.getClass(), 1);
- Array.set(array, 0, elementValue);
- elementValue = array;
- }
-
- return elementValue;
- }
-
- /**
- * Returns an annotation element value array. These arrays can only have a
- * single dimension.
- */
- private Object getAnnotationElementValueArray(TreeLogger logger,
- ArrayInitializer arrayInitializer) {
- assert (arrayInitializer.binding.dimensions == 1);
-
- Class<?> leafComponentClass = getClassLiteral(logger,
- arrayInitializer.binding.leafComponentType);
- if (leafComponentClass == null) {
- return null;
- }
-
- Expression[] initExpressions = arrayInitializer.expressions;
- int arrayLength = initExpressions != null ? initExpressions.length : 0;
-
- Object array = Array.newInstance(leafComponentClass, arrayLength);
- boolean failed = false;
- for (int i = 0; i < arrayLength; ++i) {
- Expression arrayInitExp = initExpressions[i];
- Object value = getAnnotationElementValue(logger,
- arrayInitializer.binding.leafComponentType, arrayInitExp);
- if (value != null) {
- Array.set(array, i, value);
- } else {
- failed = true;
- break;
- }
- }
-
- if (!failed) {
- return array;
- }
-
- return null;
- }
-
- private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) {
- if (resolvedType instanceof BaseTypeBinding) {
- return getClassLiteralForPrimitive((BaseTypeBinding) resolvedType);
- } else {
- try {
- String className = String.valueOf(resolvedType.constantPoolName());
- className = className.replace('/', '.');
- return Class.forName(className, false,
- Thread.currentThread().getContextClassLoader());
- } catch (ClassNotFoundException e) {
- logger.log(TreeLogger.ERROR, "", e);
+ try {
+ Class<?> clazz = Class.forName(type.getClassName(), false,
+ getClass().getClassLoader());
+ if (!Annotation.class.isAssignableFrom(clazz)) {
+ logger.log(TreeLogger.ERROR, "Binary-only type " + type.getClassName()
+ + " is not an annotation");
return null;
}
+ return clazz.asSubclass(Annotation.class);
+ } catch (ClassNotFoundException e) {
+ logger.log(TreeLogger.WARN, "Ignoring unresolvable annotation type "
+ + type.getClassName(), e);
+ return null;
}
}
- private Class<?> getClassLiteralForPrimitive(BaseTypeBinding type) {
- switch (type.id) {
- case TypeIds.T_boolean:
+ @SuppressWarnings("unused")
+ private Class<?> getClassLiteralForPrimitive(Type type) {
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
return Boolean.TYPE;
- case TypeIds.T_byte:
+ case Type.BYTE:
return Byte.TYPE;
- case TypeIds.T_char:
+ case Type.CHAR:
return Character.TYPE;
- case TypeIds.T_short:
+ case Type.SHORT:
return Short.TYPE;
- case TypeIds.T_int:
+ case Type.INT:
return Integer.TYPE;
- case TypeIds.T_long:
+ case Type.LONG:
return Long.TYPE;
- case TypeIds.T_float:
+ case Type.FLOAT:
return Float.TYPE;
- case TypeIds.T_double:
+ case Type.DOUBLE:
return Double.TYPE;
- case TypeIds.T_void:
+ case Type.VOID:
return Void.TYPE;
default:
- assert false : "Unexpected base type id " + type.id;
+ assert false : "Unexpected primitive type " + type;
return null;
}
}
/**
- * Returns the qualified name of the binding, excluding any type parameter
- * information.
+ * Map a bitset onto a different bitset.
+ *
+ * @param mapping int array containing a sequence of from/to pairs, each from
+ * entry should have exactly one bit set
+ * @param input bitset to map
+ * @return mapped bitset
*/
- private String getQualifiedName(ReferenceBinding binding) {
- String qualifiedName = CharOperation.toString(binding.compoundName);
- if (binding instanceof LocalTypeBinding) {
- // The real name of a local type is its constant pool name.
- qualifiedName = CharOperation.charToString(binding.constantPoolName());
- qualifiedName = qualifiedName.replace('/', '.');
- } else {
- /*
- * All other types have their fully qualified name as part of its compound
- * name.
- */
- qualifiedName = CharOperation.toString(binding.compoundName);
+ private int mapBits(int[] mapping, int input) {
+ int output = 0;
+ for (int i = 0; i < mapping.length; i += 2) {
+ if ((input & mapping[i]) != 0) {
+ output |= mapping[i + 1];
+ }
}
-
- qualifiedName = qualifiedName.replace('$', '.');
- return qualifiedName;
+ return output;
}
- private boolean resolveAnnotation(
- TreeLogger logger,
- Annotation jannotation,
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
- logger = logger.branch(TreeLogger.SPAM, "Resolving annotation '"
- + jannotation.printExpression(0, new StringBuffer()).toString() + "'",
- null);
+ /**
+ * Collects data about a class which only needs the bytecode and no TypeOracle
+ * data structures. This is used to make the initial shallow identity pass for
+ * creating JRealClassType/JGenericType objects.
+ */
+ private CollectClassData processClass(CompiledClass compiledClass) {
+ byte[] classBytes = compiledClass.getBytes();
+ ClassReader reader = new ClassReader(classBytes);
+ CollectClassData mcv = new CollectClassData(classBytes);
+ ClassVisitor cv = mcv;
+ if (false) {
+ cv = new TraceClassVisitor(cv, new PrintWriter(System.out));
+ }
+ reader.accept(cv, 0);
+ return mcv;
+ }
- // Determine the annotation class
- TypeBinding resolvedType = jannotation.resolvedType;
- Class<?> classLiteral = getClassLiteral(logger, resolvedType);
- if (classLiteral == null) {
+ private boolean resolveAnnotation(TreeLogger logger,
+ CollectAnnotationData annotVisitor,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+ AnnotationData annotData = annotVisitor.getAnnotation();
+ Class<? extends Annotation> annotationClass = getAnnotationClass(logger,
+ annotData);
+ if (annotationClass == null) {
return false;
}
-
- java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) createAnnotationInstance(
- logger, jannotation);
- if (annotation == null) {
+ Annotation annotInstance = createAnnotation(logger, annotationClass,
+ annotData);
+ if (annotInstance == null) {
return false;
}
-
- Class<? extends java.lang.annotation.Annotation> clazz = classLiteral.asSubclass(java.lang.annotation.Annotation.class);
- // Do not reflect source-only annotations.
- if (getRetentionPolicy(clazz) != RetentionPolicy.SOURCE) {
- declaredAnnotations.put(clazz, annotation);
- }
+ declaredAnnotations.put(annotationClass, annotInstance);
return true;
}
- private boolean resolveAnnotations(
- TreeLogger logger,
- Annotation[] annotations,
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations) {
+ private boolean resolveAnnotations(TreeLogger logger,
+ List<CollectAnnotationData> annotations,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
boolean succeeded = true;
if (annotations != null) {
- for (Annotation annotation : annotations) {
+ for (CollectAnnotationData annotation : annotations) {
succeeded &= resolveAnnotation(logger, annotation, declaredAnnotations);
}
}
return succeeded;
}
- private boolean resolveBoundForTypeParameter(TreeLogger logger,
- HasTypeParameters genericElement, TypeParameter typeParameter, int ordinal) {
- JClassType[] jbounds = createTypeParameterBounds(logger,
- typeParameter.binding);
- if (jbounds == null) {
+ @SuppressWarnings("unchecked")
+ private Object resolveAnnotationValue(TreeLogger logger,
+ Class<?> expectedType, Object value) {
+ if (expectedType.isArray()) {
+ Class<?> componentType = expectedType.getComponentType();
+ if (!value.getClass().isArray()) {
+ logger.log(TreeLogger.WARN, "Annotation error: expected array of "
+ + componentType.getCanonicalName() + ", got "
+ + value.getClass().getCanonicalName());
+ return null;
+ }
+ // resolve each element in the array
+ int n = Array.getLength(value);
+ Object newArray = Array.newInstance(componentType, n);
+ for (int i = 0; i < n; ++i) {
+ Object valueElement = Array.get(value, i);
+ Object resolvedValue = resolveAnnotationValue(logger, componentType,
+ valueElement);
+ if (resolvedValue == null
+ || !componentType.isAssignableFrom(resolvedValue.getClass())) {
+ logger.log(TreeLogger.ERROR, "Annotation error: expected "
+ + componentType + ", got " + resolvedValue);
+ } else {
+ Array.set(newArray, i, resolvedValue);
+ }
+ }
+ return newArray;
+ } else if (expectedType.isEnum()) {
+ if (!(value instanceof AnnotationEnum)) {
+ logger.log(TreeLogger.ERROR,
+ "Annotation error: expected an enum value," + " but got " + value);
+ return null;
+ }
+ AnnotationEnum annotEnum = (AnnotationEnum) value;
+ Type type = Type.getType(annotEnum.getDesc());
+ JType resolvedType = resolveType(type);
+ if (resolvedType == null) {
+ logger.log(TreeLogger.WARN,
+ "Unable to resolve annotation enum type of " + type.getClassName());
+ return null;
+ }
+ String enumValue = annotEnum.getValue();
+ // TODO: is there a way to avoid the SuppressWarning(unchecked) here?
+ Class<? extends Enum> enumType = getClassLiteral(logger, Enum.class,
+ resolvedType);
+ if (enumType == null) {
+ logger.log(TreeLogger.WARN,
+ "Unable to resolve annotation enum type of "
+ + resolvedType.getQualifiedSourceName());
+ return null;
+ }
+ return Enum.valueOf(enumType, enumValue);
+ } else if (Annotation.class.isAssignableFrom(expectedType)) {
+ if (!(value instanceof AnnotationData)) {
+ logger.log(TreeLogger.WARN,
+ "Annotation error: expected annotation type "
+ + expectedType.getCanonicalName() + ", got "
+ + value.getClass().getCanonicalName());
+ return null;
+ }
+ AnnotationData annotData = (AnnotationData) value;
+ Class<? extends Annotation> annotationClass = getAnnotationClass(logger,
+ annotData);
+ if (!expectedType.isAssignableFrom(annotationClass)) {
+ logger.log(TreeLogger.WARN, "Annotation error: expected "
+ + expectedType.getCanonicalName() + ", got "
+ + annotationClass.getCanonicalName());
+ return null;
+ }
+ return createAnnotation(logger, annotationClass, annotData);
+ } else if (expectedType.isPrimitive()) {
+ Class<?> wrapper = getWrapperClass(expectedType);
+ return wrapper.cast(value);
+ } else {
+ if (expectedType.isAssignableFrom(value.getClass())) {
+ return value;
+ }
+ if (Class.class.equals(expectedType)) {
+ if (!(value instanceof Type)) {
+ logger.log(TreeLogger.WARN, "Annotation error: expected a class "
+ + "literal, but received " + value);
+ return null;
+ }
+ Type valueType = (Type) value;
+ JRealClassType objType = resolveObject(valueType);
+ if (objType == null) {
+ // See if we can use a binary only class here
+ try {
+ return Class.forName(valueType.getClassName(), false,
+ getClass().getClassLoader());
+ } catch (ClassNotFoundException e) {
+ logger.log(TreeLogger.ERROR, "Annotation error: cannot resolve "
+ + valueType.getClassName(), e);
+ return null;
+ }
+ }
+ // TODO(jat): other checks?
+ try {
+ // TODO(jat): is it safe to use Class.forName here?
+ return Class.forName(objType.getQualifiedBinaryName(), false,
+ TypeOracleMediator.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ logger.log(TreeLogger.WARN, "Unable to load class literal for "
+ + objType, e);
+ return null;
+ }
+ }
+ // TODO(jat) asserts about other acceptable types
+ return value;
+ }
+ }
+
+ private JType resolveArray(Type type) {
+ assert type.getSort() == Type.ARRAY;
+ JType resolvedType = resolveType(type.getElementType());
+ int dims = type.getDimensions();
+ for (int i = 0; i < dims; ++i) {
+ resolvedType = typeOracle.getArrayType(resolvedType);
+ }
+ return resolvedType;
+ }
+
+ private boolean resolveClass(TreeLogger logger, JRealClassType type) {
+ assert type != null;
+ // Avoid cycles and useless computation.
+ if (resolved.contains(type)) {
+ return true;
+ }
+ resolved.add(type);
+
+ // Make sure our enclosing type is resolved first.
+ if (type.getEnclosingType() != null
+ && !resolveClass(logger, type.getEnclosingType())) {
return false;
}
- genericElement.getTypeParameters()[ordinal].setBounds(jbounds);
+ // Build a search list for type parameters to find their definition,
+ // resolving enclosing classes as we go up.
+ TypeParameterLookup typeParamLookup = new TypeParameterLookup();
+ typeParamLookup.pushEnclosingScopes(type);
+
+ CollectClassData classData = classMapType.get(type);
+ assert classData != null;
+ int access = classData.getAccess();
+
+ assert (!classData.getClassType().isLocal());
+
+ logger = logger.branch(TreeLogger.SPAM, "Found type '"
+ + type.getQualifiedSourceName() + "'", null);
+
+ // Handle package-info classes.
+ if (isPackageInfoTypeName(type.getSimpleSourceName())) {
+ return resolvePackage(logger, type, classData.getAnnotations());
+ }
+
+ // Resolve annotations
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+ resolveAnnotations(logger, classData.getAnnotations(), declaredAnnotations);
+ type.addAnnotations(declaredAnnotations);
+
+ String signature = classData.getSignature();
+ if (signature != null) {
+ // If we have a signature, use it for superclass and interfaces
+ SignatureReader reader = new SignatureReader(signature);
+ ResolveClassSignature classResolver = new ResolveClassSignature(
+ resolver, binaryMapper, logger, type, typeParamLookup);
+ reader.accept(classResolver);
+ classResolver.finish();
+ } else {
+ // Set the super type for non-interfaces
+ if ((access & Opcodes.ACC_INTERFACE) == 0) {
+ String superName = classData.getSuperName();
+ if (superName != null) {
+ JClassType superType = binaryMapper.get(superName);
+ if (superType == null || !resolveClass(logger, superType)) {
+ logger.log(TreeLogger.WARN, "Unable to resolve supertype "
+ + superName);
+ return false;
+ }
+ type.setSuperclass((JClassType) possiblySubstituteRawType(superType));
+ }
+ }
+
+ // Set interfaces
+ for (String intfName : classData.getInterfaces()) {
+ JClassType intf = binaryMapper.get(intfName);
+ if (intf == null || !resolveClass(logger, intf)) {
+ logger.log(TreeLogger.WARN, "Unable to resolve interface " + intfName);
+ return false;
+ }
+ type.addImplementedInterface((JClassType) possiblySubstituteRawType(intf));
+ }
+ }
+ if (((access & Opcodes.ACC_INTERFACE) == 0) && type.getSuperclass() == null) {
+ // Only Object or interfaces should not have a superclass
+ assert "java/lang/Object".equals(classData.getName());
+ }
+
+ // Process methods
+ for (CollectMethodData method : classData.getMethods()) {
+ if (!resolveMethod(logger, type, method, typeParamLookup)) {
+ logger.log(TreeLogger.WARN, "Unable to resolve method " + method);
+ return false;
+ }
+ }
+
+ // Process fields
+ // Track the next enum ordinal across resolveField calls.
+ int[] nextEnumOrdinal = new int[] {0};
+ for (CollectFieldData field : classData.getFields()) {
+ if (!resolveField(logger, type, field, typeParamLookup, nextEnumOrdinal)) {
+ logger.log(TreeLogger.WARN, "Unable to resolve field " + field);
+ return false;
+ }
+ }
+
return true;
}
- private boolean resolveBoundsForTypeParameters(TreeLogger logger,
- HasTypeParameters genericElement, TypeParameter[] typeParameters) {
- if (typeParameters != null) {
- for (int i = 0; i < typeParameters.length; ++i) {
- if (!resolveBoundForTypeParameter(logger, genericElement,
- typeParameters[i], i)) {
+ private boolean resolveClass(TreeLogger logger, JType type) {
+ if (!(type instanceof JClassType)) {
+ // non-classes are already resolved
+ return true;
+ }
+ if (type instanceof JRealClassType) {
+ return resolveClass(logger, (JRealClassType) type);
+ }
+ if (type instanceof JArrayType) {
+ return resolveClass(logger, ((JArrayType) type).getComponentType());
+ }
+ if (type instanceof JParameterizedType) {
+ return resolveClass(logger, ((JParameterizedType) type).getBaseType());
+ }
+ if (type instanceof JRawType) {
+ return resolveClass(logger, ((JRawType) type).getBaseType());
+ }
+ if (type instanceof JTypeParameter) {
+ JTypeParameter typeParam = (JTypeParameter) type;
+ if (!resolveClass(logger, typeParam.getDeclaringClass())) {
+ return false;
+ }
+ for (JClassType bound : typeParam.getBounds()) {
+ if (!resolveClass(logger, bound)) {
return false;
}
}
+ return true;
+ }
+ if (type instanceof JWildcardType) {
+ JWildcardType wildcard = (JWildcardType) type;
+ for (JClassType bound : wildcard.getUpperBounds()) {
+ if (!resolveClass(logger, bound)) {
+ return false;
+ }
+ }
+ for (JClassType bound : wildcard.getLowerBounds()) {
+ if (!resolveClass(logger, bound)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean resolveEnclosingClass(TreeLogger logger, JRealClassType type) {
+ assert type != null;
+ if (type.getEnclosingType() != null) {
+ return true;
+ }
+ // Find our enclosing class and set it
+ CollectClassData classData = classMapType.get(type);
+ assert classData != null;
+ String outerClass = classData.getOuterClass();
+ JRealClassType enclosingType = null;
+ if (outerClass != null) {
+ enclosingType = binaryMapper.get(outerClass);
+ // Ensure enclosing classes are resolved
+ if (enclosingType != null) {
+ if (!resolveEnclosingClass(logger, enclosingType)) {
+ return false;
+ }
+ if (enclosingType.isGenericType() != null
+ && (classData.getAccess() & (Opcodes.ACC_STATIC | Opcodes.ACC_INTERFACE)) != 0) {
+ // If the inner class doesn't have access to it's enclosing type's
+ // type variables, the enclosign type must be the raw type instead
+ // of the generic type.
+ JGenericType genericType = enclosingType.isGenericType();
+ type.setEnclosingType(genericType.getRawType());
+ } else {
+ type.setEnclosingType(enclosingType);
+ }
+ }
}
return true;
}
- private boolean resolveField(TreeLogger logger, JClassType enclosingType,
- FieldDeclaration jfield) {
-
- if (jfield instanceof Initializer) {
- // Pretend we didn't see this.
- //
- return true;
- }
-
- // Try to resolve annotations, ignore any that fail.
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
- resolveAnnotations(logger, jfield.annotations, declaredAnnotations);
-
- String name = String.valueOf(jfield.name);
- JField field;
- if (jfield.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
- assert (enclosingType.isEnum() != null);
- field = new JEnumConstant(enclosingType, name, declaredAnnotations,
- jfield.binding.original().id);
+ private boolean resolveField(TreeLogger logger, JRealClassType type,
+ CollectFieldData field, TypeParameterLookup typeParamLookup,
+ int[] nextEnumOrdinal) {
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+ resolveAnnotations(logger, field.getAnnotations(), declaredAnnotations);
+ String name = field.getName();
+ JField jfield;
+ if ((field.getAccess() & Opcodes.ACC_ENUM) != 0) {
+ assert (type.isEnum() != null);
+ jfield = new JEnumConstant(type, name, declaredAnnotations,
+ nextEnumOrdinal[0]++);
} else {
- field = new JField(enclosingType, name, declaredAnnotations);
+ jfield = new JField(type, name, declaredAnnotations);
}
// Get modifiers.
//
- field.addModifierBits(Shared.bindingToModifierBits(jfield.binding));
+ jfield.addModifierBits(mapBits(ASM_TO_SHARED_MODIFIERS, field.getAccess()));
- // Set the field type.
- //
- TypeBinding jfieldType = jfield.binding.type;
+ String signature = field.getSignature();
+ JType fieldType;
+ if (signature != null) {
+ SignatureReader reader = new SignatureReader(signature);
+ JType[] fieldTypeRef = new JType[1];
+ reader.acceptType(new ResolveTypeSignature(resolver, binaryMapper,
+ logger, fieldTypeRef, typeParamLookup, null));
+ fieldType = fieldTypeRef[0];
+ // TraceSignatureVisitor trace = new TraceSignatureVisitor(
+ // methodData.getAccess());
+ // reader.acceptType(trace);
+ // System.err.println("Field " + name + ": " + trace.getDeclaration());
- JType fieldType = resolveType(logger, jfieldType);
+ } else {
+ fieldType = resolveType(Type.getType(field.getDesc()));
+ }
if (fieldType == null) {
- // Unresolved type.
- //
return false;
}
- field.setType(fieldType);
-
+ jfield.setType(fieldType);
return true;
}
- private boolean resolveFields(TreeLogger logger, JClassType type,
- FieldDeclaration[] jfields) {
- if (jfields != null) {
- for (int i = 0; i < jfields.length; i++) {
- FieldDeclaration jfield = jfields[i];
- if (!resolveField(logger, type, jfield)) {
- return false;
- }
- }
- }
- return true;
- }
+ private boolean resolveMethod(TreeLogger logger, JRealClassType type,
+ CollectMethodData methodData, TypeParameterLookup typeParamLookup) {
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+ resolveAnnotations(logger, methodData.getAnnotations(), declaredAnnotations);
+ String name = methodData.getName();
- private boolean resolveMethod(TreeLogger logger, JClassType enclosingType,
- AbstractMethodDeclaration jmethod) {
-
- if (jmethod instanceof Clinit) {
- // Pretend we didn't see this.
- //
+ if ("<clinit>".equals(name)
+ || (methodData.getAccess() & Opcodes.ACC_SYNTHETIC) != 0) {
+ // Ignore the following and leave them out of TypeOracle:
+ // - static initializers
+ // - synthetic methods
return true;
}
- String name = getMethodName(enclosingType, jmethod);
-
- // Try to resolve annotations, ignore any that fail.
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
- resolveAnnotations(logger, jmethod.annotations, declaredAnnotations);
+ if (type.isEnum() != null && "<init>".equals(name)) {
+ // Leave enum constructors out of TypeOracle
+ return true;
+ }
+ if (type.isEnum() != null
+ && (methodData.getAccess() & Opcodes.ACC_STATIC) != 0) {
+ // Special-case synthetic static methods that aren't marked synthetic
+ if ("values".equals(name) || "valueOf".equals(name)) {
+ return true;
+ }
+ }
JAbstractMethod method;
// Declare the type parameters. We will pass them into the constructors for
// JConstructor/JMethod/JAnnotatedMethod. Then, we'll do a second pass to
// resolve the bounds on each JTypeParameter object.
- JTypeParameter[] jtypeParameters = resolveTypeParameters(jmethod.typeParameters());
+ JTypeParameter[] typeParams = collectTypeParams(methodData.getSignature());
- if (jmethod.isConstructor()) {
- method = new JConstructor(enclosingType, name, declaredAnnotations,
- jtypeParameters);
- // Do a second pass to resolve the bounds on each JTypeParameter.
- if (!resolveBoundsForTypeParameters(logger, method,
- jmethod.typeParameters())) {
- return false;
- }
+ typeParamLookup.pushScope(typeParams);
+ boolean hasReturnType = true;
+ if ("<init>".equals(name)) {
+ name = type.getSimpleSourceName();
+ method = new JConstructor(type, name, declaredAnnotations, typeParams);
+ hasReturnType = false;
} else {
- if (jmethod.isAnnotationMethod()) {
- AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod;
- Object defaultValue = null;
- if (annotationMethod.defaultValue != null) {
- defaultValue = getAnnotationElementValue(logger,
- annotationMethod.returnType.resolvedType,
- annotationMethod.defaultValue);
- }
- method = new JAnnotationMethod(enclosingType, name, defaultValue,
+ if (type.isAnnotation() != null) {
+ // TODO(jat): !! anything else to do here?
+ method = new JAnnotationMethod(type, name, typeParams,
declaredAnnotations);
} else {
- method = new JMethod(enclosingType, name, declaredAnnotations,
- jtypeParameters);
+ method = new JMethod(type, name, declaredAnnotations, typeParams);
}
-
- // Do a second pass to resolve the bounds on each JTypeParameter.
- // The type parameters must be resolved at this point, because they may
- // be used in the resolution of the method's return type.
- if (!resolveBoundsForTypeParameters(logger, method,
- jmethod.typeParameters())) {
- return false;
- }
-
- TypeBinding jreturnType = ((MethodDeclaration) jmethod).returnType.resolvedType;
- JType returnType = resolveType(logger, jreturnType);
- if (returnType == null) {
- // Unresolved type.
- //
- return false;
- }
- ((JMethod) method).setReturnType(returnType);
}
- // Parse modifiers.
- //
- method.addModifierBits(Shared.bindingToModifierBits(jmethod.binding));
- if (enclosingType.isInterface() != null) {
+ // Copy modifier flags
+ method.addModifierBits(mapBits(ASM_TO_SHARED_MODIFIERS,
+ methodData.getAccess()));
+ if (type.isInterface() != null) {
// Always add implicit modifiers on interface methods.
- //
method.addModifierBits(Shared.MOD_PUBLIC | Shared.MOD_ABSTRACT);
}
- // Add the parameters.
- //
- Argument[] jparams = jmethod.arguments;
- if (!resolveParameters(logger, method, jparams)) {
- return false;
- }
-
- // Add throws.
- //
- TypeReference[] jthrows = jmethod.thrownExceptions;
- if (!resolveThrownTypes(logger, method, jthrows)) {
- return false;
- }
-
- return true;
- }
-
- private boolean resolveMethods(TreeLogger logger, JClassType type,
- AbstractMethodDeclaration[] jmethods) {
- if (jmethods != null) {
- for (int i = 0; i < jmethods.length; i++) {
- AbstractMethodDeclaration jmethod = jmethods[i];
- if (!resolveMethod(logger, type, jmethod)) {
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean resolvePackage(TreeLogger logger, TypeDeclaration jclass) {
- SourceTypeBinding binding = jclass.binding;
-
- String packageName = String.valueOf(binding.fPackage.readableName());
- JPackage pkg = typeOracle.getOrCreatePackage(packageName);
- assert (pkg != null);
-
- CompilationUnitScope cus = (CompilationUnitScope) jclass.scope.parent;
- assert (cus != null);
-
- // Try to resolve annotations, ignore any that fail.
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
- resolveAnnotations(logger, cus.referenceContext.currentPackage.annotations,
- declaredAnnotations);
-
- pkg.addAnnotations(declaredAnnotations);
- return true;
- }
-
- private boolean resolveParameter(TreeLogger logger, JAbstractMethod method,
- Argument jparam) {
- TypeBinding jtype = jparam.binding.type;
- JType type = resolveType(logger, jtype);
- if (type == null) {
- // Unresolved.
- //
- return false;
- }
-
- // Try to resolve annotations, ignore any that fail.
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
- resolveAnnotations(logger, jparam.annotations, declaredAnnotations);
-
- String name = String.valueOf(jparam.name);
- new JParameter(method, type, name, declaredAnnotations);
- if (jparam.isVarArgs()) {
+ if ((methodData.getAccess() & Opcodes.ACC_VARARGS) != 0) {
method.setVarArgs();
}
+
+ String signature = methodData.getSignature();
+ Type[] argTypes = methodData.getArgTypes();
+ String[] argNames = methodData.getArgNames();
+ if (signature != null) {
+ // If we have a signature, use it for superclass and interfaces
+ SignatureReader reader = new SignatureReader(signature);
+ ResolveMethodSignature methodResolver = new ResolveMethodSignature(
+ resolver, logger, method, typeParamLookup, hasReturnType, methodData,
+ argTypes, argNames);
+ // TraceSignatureVisitor trace = new TraceSignatureVisitor(
+ // methodData.getAccess());
+ // reader.accept(trace);
+ // System.err.println("Method " + name + ": " + trace.getDeclaration());
+ reader.accept(methodResolver);
+ if (!methodResolver.finish()) {
+ return false;
+ }
+ } else {
+ if (hasReturnType) {
+ Type returnType = Type.getReturnType(methodData.getDesc());
+ JType returnJType = resolveType(returnType);
+ if (returnJType == null) {
+ return false;
+ }
+ ((JMethod) method).setReturnType(returnJType);
+ }
+
+ if (!resolveParameters(logger, method, methodData)) {
+ return false;
+ }
+ }
+ // The signature might not actually include the exceptions if they don't
+ // include a type variable, so resolveThrows is always used (it does
+ // nothing if there are already exceptions defined)
+ if (!resolveThrows(method, methodData)) {
+ return false;
+ }
+ typeParamLookup.popScope();
+ return true;
+ }
+
+ private JRealClassType resolveObject(Type type) {
+ assert type.getSort() == Type.OBJECT;
+ String className = type.getInternalName();
+ assert Name.isInternalName(className);
+ JRealClassType classType = binaryMapper.get(className);
+ return classType;
+ }
+
+ private boolean resolvePackage(TreeLogger logger, JRealClassType type,
+ List<CollectAnnotationData> annotations) {
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+ resolveAnnotations(logger, annotations, declaredAnnotations);
+ type.getPackage().addAnnotations(declaredAnnotations);
return true;
}
private boolean resolveParameters(TreeLogger logger, JAbstractMethod method,
- Argument[] jparams) {
- if (jparams != null) {
- for (int i = 0; i < jparams.length; i++) {
- Argument jparam = jparams[i];
- if (!resolveParameter(logger, method, jparam)) {
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean resolveThrownType(TreeLogger logger, JAbstractMethod method,
- TypeReference jthrown) {
-
- JType type = resolveType(logger, jthrown.resolvedType);
- if (type == null) {
- // Not resolved.
- //
- return false;
- }
-
- method.addThrows(type);
-
- return true;
- }
-
- private boolean resolveThrownTypes(TreeLogger logger, JAbstractMethod method,
- TypeReference[] jthrows) {
- if (jthrows != null) {
- for (int i = 0; i < jthrows.length; i++) {
- TypeReference jthrown = jthrows[i];
- if (!resolveThrownType(logger, method, jthrown)) {
- return false;
- }
- }
- }
- return true;
- }
-
- private JType resolveType(TreeLogger logger, TypeBinding binding) {
- // Check for primitives.
- if (binding instanceof BaseTypeBinding) {
- switch (binding.id) {
- case TypeIds.T_boolean:
- return JPrimitiveType.BOOLEAN;
- case TypeIds.T_byte:
- return JPrimitiveType.BYTE;
- case TypeIds.T_char:
- return JPrimitiveType.CHAR;
- case TypeIds.T_short:
- return JPrimitiveType.SHORT;
- case TypeIds.T_int:
- return JPrimitiveType.INT;
- case TypeIds.T_long:
- return JPrimitiveType.LONG;
- case TypeIds.T_float:
- return JPrimitiveType.FLOAT;
- case TypeIds.T_double:
- return JPrimitiveType.DOUBLE;
- case TypeIds.T_void:
- return JPrimitiveType.VOID;
- default:
- assert false : "Unexpected base type id " + binding.id;
- }
- }
-
- /*
- * Check for a user-defined type, which may be either a SourceTypeBinding or
- * a RawTypeBinding. Both of these are subclasses of ReferenceBinding and
- * all the functionality we need is on ReferenceBinding, so we cast it to
- * that and deal with them in common code.
- *
- * TODO: do we need to do anything more with raw types?
- */
- if (binding instanceof SourceTypeBinding
- || binding instanceof RawTypeBinding) {
- ReferenceBinding referenceBinding = (ReferenceBinding) binding;
-
- // First check the type oracle to prefer type identity with the type
- // oracle we're assimilating into.
- //
- String typeName = getQualifiedName(referenceBinding);
- JType resolvedType = typeOracle.findType(typeName);
- if (resolvedType == null) {
- // Otherwise, it should be something we've mapped during this build.
- resolvedType = sourceMapper.get(referenceBinding);
- }
-
- if (resolvedType != null) {
- if (binding instanceof RawTypeBinding) {
- // Use the raw type instead of the generic type.
- JGenericType genericType = (JGenericType) resolvedType;
- resolvedType = genericType.getRawType();
- }
- return resolvedType;
- }
- }
-
- if (binding instanceof BinaryTypeBinding) {
- // Try a binary lookup.
- String binaryName = String.valueOf(binding.constantPoolName());
- JRealClassType realClassType = binaryMapper.get(binaryName);
- if (realClassType != null) {
- return realClassType;
- }
- }
-
- // Check for an array.
- //
- if (binding instanceof ArrayBinding) {
- ArrayBinding arrayBinding = (ArrayBinding) binding;
-
- // Start by resolving the leaf type.
- //
- TypeBinding leafBinding = arrayBinding.leafComponentType;
- JType resolvedType = resolveType(logger, leafBinding);
- if (resolvedType != null) {
- int dims = arrayBinding.dimensions;
- for (int i = 0; i < dims; ++i) {
- // By using the oracle to intern, we guarantee correct identity
- // mapping of lazily-created array types.
- //
- resolvedType = typeOracle.getArrayType(resolvedType);
- }
- return resolvedType;
- } else {
- // Fall-through to failure.
- //
- }
- }
-
- // Check for parameterized.
- if (binding instanceof ParameterizedTypeBinding) {
- ParameterizedTypeBinding ptBinding = (ParameterizedTypeBinding) binding;
-
- /*
- * NOTE: it is possible for ParameterizedTypeBinding.arguments to be null.
- * This can happen if a generic class has a non-static, non-generic, inner
- * class that references a TypeParameter from its enclosing generic type.
- * You would think that typeVariables() would do the right thing but it
- * does not.
- */
- TypeBinding[] arguments = ptBinding.arguments;
- int nArguments = arguments != null ? arguments.length : 0;
- JClassType[] typeArguments = new JClassType[nArguments];
- boolean failed = false;
- for (int i = 0; i < typeArguments.length; ++i) {
- typeArguments[i] = (JClassType) resolveType(logger, arguments[i]);
- if (typeArguments[i] == null) {
- failed = true;
- }
- }
-
- JClassType enclosingType = null;
- if (ptBinding.enclosingType() != null) {
- enclosingType = (JClassType) resolveType(logger,
- ptBinding.enclosingType());
- if (enclosingType == null) {
- failed = true;
- }
- }
-
- /*
- * NOTE: In the case where a generic type has a nested, non-static,
- * non-generic type. The type for the binding will not be a generic type.
- */
- JType resolveType = resolveType(logger, ptBinding.genericType());
- if (resolveType == null) {
- failed = true;
- }
-
- if (!failed) {
- if (resolveType.isGenericType() != null) {
- return typeOracle.getParameterizedType(resolveType.isGenericType(),
- enclosingType, typeArguments);
- } else {
- /*
- * A static type (enum or class) that does not declare any type
- * parameters that is nested within a generic type might be referenced
- * via a parameterized type by JDT. In this case we just return the
- * type and don't treat it as a parameterized.
- */
- return resolveType;
- }
- } else {
- // Fall-through to failure
- }
- }
-
- if (binding instanceof TypeVariableBinding) {
- TypeVariableBinding tvBinding = (TypeVariableBinding) binding;
- JTypeParameter typeParameter = tvMapper.get(tvBinding);
- if (typeParameter != null) {
- return typeParameter;
- }
-
- // Fall-through to failure
- }
-
- if (binding instanceof WildcardBinding) {
- WildcardBinding wcBinding = (WildcardBinding) binding;
-
- assert (wcBinding.otherBounds == null);
-
- BoundType boundType;
- JClassType typeBound;
-
- switch (wcBinding.boundKind) {
- case Wildcard.EXTENDS: {
- assert (wcBinding.bound != null);
- boundType = BoundType.EXTENDS;
- typeBound = (JClassType) resolveType(logger, wcBinding.bound);
- }
- break;
- case Wildcard.SUPER: {
- assert (wcBinding.bound != null);
- boundType = BoundType.SUPER;
- typeBound = (JClassType) resolveType(logger, wcBinding.bound);
- }
- break;
- case Wildcard.UNBOUND: {
- boundType = BoundType.UNBOUND;
- typeBound = (JClassType) resolveType(logger, wcBinding.erasure());
- }
- break;
- default:
- assert false : "WildcardBinding of unknown boundKind???";
- return null;
- }
-
- if (boundType != null) {
- return typeOracle.getWildcardType(boundType, typeBound);
- }
-
- // Fall-through to failure
- }
-
- // Log other cases we know about that don't make sense.
- //
- String name = String.valueOf(binding.readableName());
- logger = logger.branch(TreeLogger.WARN, "Unable to resolve type: " + name
- + " binding: " + binding.getClass().getCanonicalName(), null);
-
- if (binding instanceof BinaryTypeBinding) {
- logger.log(TreeLogger.WARN,
- "Source not available for this type, so it cannot be resolved", null);
- }
-
- return null;
- }
-
- private boolean resolveTypeDeclaration(TreeLogger logger,
- TypeDeclaration clazz) {
- SourceTypeBinding binding = clazz.binding;
- assert (binding.constantPoolName() != null);
-
- String qname = String.valueOf(binding.qualifiedSourceName());
- logger = logger.branch(TreeLogger.SPAM, "Found type '" + qname + "'", null);
-
- // Handle package-info classes.
- if (isPackageInfoTypeName(qname)) {
- return resolvePackage(logger, clazz);
- }
-
- // Just resolve the type.
- JRealClassType jtype = (JRealClassType) resolveType(logger, binding);
- if (jtype == null) {
- // Failed to resolve.
- //
- return false;
- }
-
- /*
- * Modifiers were added during processType since getParameterizedType
- * depends on them being set.
- */
-
- // Try to resolve annotations, ignore any that fail.
- Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
- resolveAnnotations(logger, clazz.annotations, declaredAnnotations);
- jtype.addAnnotations(declaredAnnotations);
-
- // Resolve bounds for type parameters on generic types. Note that this
- // step does not apply to type parameters on generic methods; that
- // occurs during the method resolution stage.
- JGenericType jGenericType = jtype.isGenericType();
- if (jGenericType != null
- && !resolveBoundsForTypeParameters(logger, jtype.isGenericType(),
- clazz.typeParameters)) {
- // Failed to resolve
- return false;
- }
-
- // Resolve superclass (for classes only).
- //
- if (jtype.isInterface() == null) {
- ReferenceBinding superclassRef = binding.superclass;
- assert superclassRef != null
- || "java.lang.Object".equals(jtype.getQualifiedSourceName());
- if (superclassRef != null) {
- JClassType jsuperClass = (JClassType) resolveType(logger, superclassRef);
- assert jsuperClass != null;
- jtype.setSuperclass(jsuperClass);
- }
- }
-
- // Resolve superinterfaces.
- //
- ReferenceBinding[] superintfRefs = binding.superInterfaces;
- for (int i = 0; i < superintfRefs.length; i++) {
- ReferenceBinding superintfRef = superintfRefs[i];
- JClassType jinterface = (JClassType) resolveType(logger, superintfRef);
- if (jinterface == null) {
- // Failed to resolve.
- //
+ CollectMethodData methodData) {
+ Type[] argTypes = methodData.getArgTypes();
+ String[] argNames = methodData.getArgNames();
+ boolean argNamesAreReal = methodData.hasActualArgNames();
+ List<CollectAnnotationData>[] paramAnnot = methodData.getArgAnnotations();
+ for (int i = 0; i < argTypes.length; ++i) {
+ JType argType = resolveType(argTypes[i]);
+ if (argType == null) {
return false;
}
- jtype.addImplementedInterface(jinterface);
+ // Try to resolve annotations, ignore any that fail.
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+ resolveAnnotations(logger, paramAnnot[i], declaredAnnotations);
+
+ // JParameter adds itself to the method
+ new JParameter(method, argType, argNames[i], declaredAnnotations,
+ argNamesAreReal);
}
+ return true;
+ }
- // Resolve fields.
- //
- FieldDeclaration[] fields = clazz.fields;
- if (!resolveFields(logger, jtype, fields)) {
- return false;
+ private boolean resolveThrows(JAbstractMethod method,
+ CollectMethodData methodData) {
+ if (method.getThrows().length == 0) {
+ for (String excName : methodData.getExceptions()) {
+ JType exc = resolveType(Type.getObjectType(excName));
+ if (exc == null) {
+ return false;
+ }
+ method.addThrows(exc);
+ }
}
-
- // Resolve methods. This also involves the declaration of type
- // variables on generic methods, and the resolution of the bounds
- // on these type variables.
-
- // One would think that it would be better to perform the declaration
- // of type variables on methods at the time when we are processing
- // all of the classes. Unfortunately, when we are processing classes,
- // we do not have enough information about their methods to analyze
- // their type variables. Hence, the type variable declaration and
- // bounds resolution for generic methods must happen after the resolution
- // of methods is complete.
- AbstractMethodDeclaration[] methods = clazz.methods;
- if (!resolveMethods(logger, jtype, methods)) {
- return false;
- }
-
return true;
}
/**
- * Declares TypeParameters declared on a JGenericType or a JAbstractMethod by
- * mapping the TypeParameters into JTypeParameters. <p/> This mapping has to
- * be done on the first pass through the AST in order to handle declarations
- * of the form: <<C exends GenericClass<T>, T extends SimpleClass> <p/> JDT
- * already knows that GenericClass<T> is a parameterized type with a type
- * argument of <T extends SimpleClass>. Therefore, in order to resolve
- * GenericClass<T>, we must have knowledge of <T extends SimpleClass>. <p/>
- * By calling this method on the first pass through the AST, a JTypeParameter
- * for <T extends SimpleClass> will be created.
+ * Returns a primitive, an array, or a JRealClassType.
*/
- private JTypeParameter[] resolveTypeParameters(TypeParameter[] typeParameters) {
- if (typeParameters == null || typeParameters.length == 0) {
- return null;
+ private JType resolveType(Type type) {
+ // Check for primitives.
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ return JPrimitiveType.BOOLEAN;
+ case Type.BYTE:
+ return JPrimitiveType.BYTE;
+ case Type.CHAR:
+ return JPrimitiveType.CHAR;
+ case Type.SHORT:
+ return JPrimitiveType.SHORT;
+ case Type.INT:
+ return JPrimitiveType.INT;
+ case Type.LONG:
+ return JPrimitiveType.LONG;
+ case Type.FLOAT:
+ return JPrimitiveType.FLOAT;
+ case Type.DOUBLE:
+ return JPrimitiveType.DOUBLE;
+ case Type.VOID:
+ return JPrimitiveType.VOID;
+ case Type.ARRAY:
+ return resolveArray(type);
+ case Type.OBJECT:
+ JRealClassType resolvedType = resolveObject(type);
+ return possiblySubstituteRawType(resolvedType);
+ default:
+ assert false : "Unexpected type " + type;
+ return null;
}
-
- JTypeParameter[] jtypeParamArray = new JTypeParameter[typeParameters.length];
- for (int i = 0; i < typeParameters.length; ++i) {
- jtypeParamArray[i] = tvMapper.get(typeParameters[i].binding);
- assert jtypeParamArray[i] != null;
- }
-
- return jtypeParamArray;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/TypeParameterLookup.java b/dev/core/src/com/google/gwt/dev/javac/TypeParameterLookup.java
new file mode 100644
index 0000000..df0fd20
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeParameterLookup.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2009 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.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.dev.util.collect.HashMap;
+import com.google.gwt.dev.util.collect.Maps;
+
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * Handles lookup of type parameters, using a scope stack.
+ */
+public class TypeParameterLookup {
+
+ private LinkedList<Map<String, JTypeParameter>> scopeStack = new LinkedList<Map<String, JTypeParameter>>();
+
+ public JTypeParameter lookup(String name) {
+ for (Map<String, JTypeParameter> scope : scopeStack) {
+ JTypeParameter result = scope.get(name);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ public void popScope() {
+ scopeStack.remove();
+ }
+
+ public void pushEnclosingScopes(JClassType type) {
+ if (type == null) {
+ return;
+ }
+ pushEnclosingScopes(type.getEnclosingType());
+ JGenericType genericType = type.isGenericType();
+ if (genericType != null) {
+ pushScope(genericType.getTypeParameters());
+ }
+ }
+
+ public void pushScope(JTypeParameter[] typeParams) {
+ // push empty scopes to keep pops in sync
+ scopeStack.addFirst(buildScope(typeParams));
+ }
+
+ private Map<String, JTypeParameter> buildScope(JTypeParameter[] typeParams) {
+ switch (typeParams.length) {
+ case 0:
+ return Maps.create();
+ case 1:
+ return Maps.create(typeParams[0].getName(), typeParams[0]);
+ default:
+ Map<String, JTypeParameter> scope = new HashMap<String, JTypeParameter>();
+ for (JTypeParameter typeParam : typeParams) {
+ scope.put(typeParam.getName(), typeParam);
+ }
+ return scope;
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
new file mode 100644
index 0000000..532a582
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.asm.AnnotationVisitor;
+import com.google.gwt.dev.javac.asm.CollectClassData.AnnotationEnum;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Collects data from (possibly nested) annotations on a single entity.
+ */
+public class CollectAnnotationData implements AnnotationVisitor {
+
+ /**
+ * Holds annotation fields/values.
+ */
+ public static class AnnotationData {
+ private final String desc;
+ private final Map<String, Object> values = new HashMap<String, Object>();
+ private final boolean visible;
+
+ protected AnnotationData(String desc, boolean visible) {
+ this.desc = desc;
+ this.visible = visible;
+ }
+
+ public void addValue(String name, Object value) {
+ values.put(name, value);
+ }
+
+ /**
+ * @return the desc
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * @return the values
+ */
+ public Map<String, Object> getValues() {
+ return values;
+ }
+
+ /**
+ * @return the visible
+ */
+ public boolean isVisible() {
+ return visible;
+ }
+ }
+
+ /**
+ * Collects data inside an array-valued annotation parameter.
+ */
+ public static class MyAnnotationArrayVisitor implements AnnotationVisitor {
+
+ private Callback<Object> callback;
+ private List<Object> values = new ArrayList<Object>();
+
+ public MyAnnotationArrayVisitor(Callback<Object> callback) {
+ this.callback = callback;
+ }
+
+ public void visit(String name, Object value) {
+ values.add(value);
+ }
+
+ public AnnotationVisitor visitAnnotation(String name, String desc) {
+ // TODO(jat): what should visible be set to?
+ return new CollectAnnotationData(desc, true,
+ new Callback<CollectAnnotationData.AnnotationData>() {
+ public void call(CollectAnnotationData.AnnotationData value) {
+ values.add(value);
+ }
+ });
+ }
+
+ public AnnotationVisitor visitArray(String name) {
+ return new MyAnnotationArrayVisitor(new Callback<Object>() {
+ public void call(Object value) {
+ values.add(value);
+ }
+ });
+ }
+
+ public void visitEnd() {
+ callback.call(values.toArray());
+ }
+
+ public void visitEnum(String name, String desc, String value) {
+ values.add(new AnnotationEnum(desc, value));
+ }
+ }
+
+ /**
+ * Generic callback type taking a parameter.
+ *
+ * @param <T> type of the argument to the callback
+ */
+ private interface Callback<T> {
+
+ /**
+ * Invoke the callback.
+ *
+ * @param value value to pass to the callback.
+ */
+ void call(T value);
+ }
+
+ private CollectAnnotationData.AnnotationData annotation;
+ private Callback<CollectAnnotationData.AnnotationData> callback;
+
+ /**
+ * Construct the collector.
+ *
+ * @param desc class descriptor of the annotation class
+ * @param visible true if the annotation is visible at runtime
+ */
+ public CollectAnnotationData(String desc, boolean visible) {
+ this(desc, visible, null);
+ }
+
+ /**
+ * Construct the collector.
+ *
+ * @param desc class descriptor of the annotation class
+ * @param visible true if the annotation is visible at runtime
+ * @param callback callback to be called when the annotation is finished
+ */
+ CollectAnnotationData(String desc, boolean visible,
+ Callback<CollectAnnotationData.AnnotationData> callback) {
+ annotation = new AnnotationData(desc, visible);
+ this.callback = callback;
+ }
+
+ /**
+ * @return the annotation data
+ */
+ public AnnotationData getAnnotation() {
+ return annotation;
+ }
+
+ public void visit(String name, Object value) {
+ annotation.addValue(name, value);
+ }
+
+ public AnnotationVisitor visitAnnotation(final String name, String desc) {
+ // TODO(jat): what should visible be set to?
+ return new CollectAnnotationData(desc, true,
+ new Callback<CollectAnnotationData.AnnotationData>() {
+ public void call(CollectAnnotationData.AnnotationData value) {
+ annotation.addValue(name, value);
+ }
+ });
+ }
+
+ public AnnotationVisitor visitArray(final String name) {
+ return new MyAnnotationArrayVisitor(new Callback<Object>() {
+
+ /**
+ * Called with an array of values.
+ *
+ * @param value
+ */
+ public void call(Object value) {
+ annotation.addValue(name, value);
+ }
+ });
+ }
+
+ public void visitEnd() {
+ if (callback != null) {
+ callback.call(annotation);
+ }
+ }
+
+ public void visitEnum(String name, String desc, String value) {
+ annotation.addValue(name, new AnnotationEnum(desc, value));
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
new file mode 100644
index 0000000..e7fcd08
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.asm.AnnotationVisitor;
+import com.google.gwt.dev.asm.FieldVisitor;
+import com.google.gwt.dev.asm.MethodVisitor;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.commons.EmptyVisitor;
+import com.google.gwt.dev.util.Name;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads the bytecode for a class and collects data needed for building
+ * TypeOracle structures.
+ */
+public class CollectClassData extends EmptyVisitor {
+
+ /**
+ * Type of this class.
+ */
+ public enum ClassType {
+ /**
+ * A top level class named the same as its source file.
+ */
+ TopLevel,
+
+ /**
+ * A non-static named class nested inside another class.
+ */
+ Inner {
+ @Override
+ public boolean hasHiddenConstructorArg() {
+ return true;
+ }
+ },
+
+ /**
+ * A static nested class inside another class.
+ */
+ Nested,
+
+ /**
+ * An anonymous inner class.
+ */
+ Anonymous {
+ @Override
+ public boolean hasOuter() {
+ return true;
+ }
+
+ @Override
+ public boolean isLocal() {
+ return true;
+ }
+ },
+
+ /**
+ * A named class defined inside a method.
+ */
+ Local {
+ @Override
+ public boolean hasHiddenConstructorArg() {
+ return true;
+ }
+
+ @Override
+ public boolean hasOuter() {
+ return true;
+ }
+
+ @Override
+ public boolean isLocal() {
+ return true;
+ }
+ };
+
+ public boolean hasHiddenConstructorArg() {
+ return false;
+ }
+
+ public boolean hasOuter() {
+ return false;
+ }
+
+ public boolean isLocal() {
+ return false;
+ }
+ }
+
+ /**
+ * Holds the descriptor and value for an Enum-valued annotation.
+ */
+ public static class AnnotationEnum {
+ private final String desc;
+ private final String value;
+
+ public AnnotationEnum(String desc, String value) {
+ this.desc = desc;
+ this.value = value;
+ }
+
+ /**
+ * @return the descriptor
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * @return the value
+ */
+ public String getValue() {
+ return value;
+ }
+ }
+
+ private List<CollectAnnotationData> annotations = new ArrayList<CollectAnnotationData>();
+
+ private String source = null;
+
+ // internal name
+ private String name;
+
+ private String signature;
+
+ // internal name of superclass
+ private String superName;
+
+ // internal names of interfaces
+ private String[] interfaces;
+ private byte[] bytes;
+ private List<CollectMethodData> methods = new ArrayList<CollectMethodData>();
+ private List<CollectFieldData> fields = new ArrayList<CollectFieldData>();
+ private int access;
+ private String outerClass;
+ private String outerMethodName;
+ private String outerMethodDesc;
+ private CollectClassData.ClassType classType = ClassType.TopLevel;
+
+ public CollectClassData(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ /**
+ * @return the access
+ */
+ public int getAccess() {
+ return access;
+ }
+
+ /**
+ * @return annotations on this class
+ */
+ public List<CollectAnnotationData> getAnnotations() {
+ return annotations;
+ }
+
+ /**
+ * @return the bytes
+ */
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ /**
+ * @return the class type.
+ */
+ public CollectClassData.ClassType getClassType() {
+ return classType;
+ }
+
+ /**
+ * @return the fields
+ */
+ public List<CollectFieldData> getFields() {
+ return fields;
+ }
+
+// /**
+// * @return the innerClasses
+// */
+// public List<Resource> getInnerClasses() {
+// return innerClasses;
+// }
+
+ /**
+ * @return the interfaces
+ */
+ public String[] getInterfaces() {
+ return interfaces;
+ }
+
+ /**
+ * @return the methods
+ */
+ public List<CollectMethodData> getMethods() {
+ return methods;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the outerClass
+ */
+ public String getOuterClass() {
+ return outerClass;
+ }
+
+ /**
+ * @return the outerMethodDesc
+ */
+ public String getOuterMethodDesc() {
+ return outerMethodDesc;
+ }
+
+ /**
+ * @return the outerMethodName
+ */
+ public String getOuterMethodName() {
+ return outerMethodName;
+ }
+
+ /**
+ * @return the signature
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+ /**
+ * @return the source
+ */
+ public String getSource() {
+ return source;
+ }
+
+ /**
+ * @return the superName
+ */
+ public String getSuperName() {
+ return superName;
+ }
+
+ public boolean hasNoExternalName() {
+ return classType == ClassType.Anonymous || classType == ClassType.Local;
+ }
+
+ public boolean isAnonymous() {
+ return classType == ClassType.Anonymous;
+ }
+
+ public boolean isLocal() {
+ if (name.matches("\\$\\d") && !isAnonymous()) {
+ throw new IllegalStateException("Not anonymous with name of " + name);
+ }
+ return classType.isLocal();
+ }
+
+ @Override
+ public String toString() {
+ return "class " + name;
+ }
+
+ /**
+ * Called at the beginning of visiting the class.
+ *
+ * @param version classfile version (ie, Opcodes.V1_5 etc)
+ * @param access access flags (ie, bitwise or of Opcodes.ACC_*)
+ * @param name internal name of this class (ie, com/google/Foo)
+ * @param signature generic signature or null
+ * @param superName binary name of superclass (ie, java/lang/Object)
+ * @param interfaces array of binary names of implemented interfaces
+ */
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+ this.access = access;
+ assert Name.isInternalName(name);
+ this.name = name;
+ this.signature = signature;
+ this.superName = superName;
+ this.interfaces = interfaces;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ CollectAnnotationData av = new CollectAnnotationData(desc,
+ visible);
+ annotations.add(av);
+ return av;
+ }
+
+ /**
+ * Called for each field.
+ *
+ * @param access access flags for field
+ * @param name field name
+ * @param desc type descriptor (ie, Ljava/lang/String;)
+ * @param signature generic signature (null if not generic)
+ * @param value initialized value if constant
+ */
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ // if ("this$1".equals(name) && classType == ClassType.Anonymous) {
+ // // TODO(jat): !!! really nasty hack
+ // classType = ClassType.Inner;
+ // }
+ // skip synthetic fields
+ return null;
+ }
+ CollectFieldData fv = new CollectFieldData(access, name, desc,
+ signature, value);
+ fields.add(fv);
+ return fv;
+ }
+
+ /**
+ * Called once for every inner class of this class.
+ *
+ * @param name internal name of inner class (ie, com/google/Foo$1)
+ * @param outerName internal name of enclosing class (null if not a member
+ * class or anonymous)
+ * @param innerName simple name of the inner class (null if anonymous)
+ * @param access access flags (bitwise or of Opcodes.ACC_*) as declared in the
+ * enclosing class
+ */
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName,
+ int access) {
+ // If this inner class is ourselves, merge the access flags, since
+ // static, for example, only appears in the InnerClass attribute.
+ if (this.name.equals(name)) {
+ if (outerName != null) {
+ outerClass = outerName;
+ }
+ // TODO(jat): should we only pull in a subset of these flags? Use only
+ // these flags, or what? For now, just grab ACC_STATIC and ACC_PRIVATE
+ int copyFlags = access & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE);
+ this.access |= copyFlags;
+ boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+ switch (classType) {
+ case TopLevel:
+ classType = isStatic ? ClassType.Nested : ClassType.Inner;
+ break;
+ case Anonymous:
+ if (innerName != null) {
+ classType = ClassType.Local;
+ }
+ break;
+ case Inner:
+ // Already marked as inner class by the synthetic this$1 field
+ break;
+ default:
+ throw new IllegalStateException("Unexpected INNERCLASS with type of "
+ + classType);
+ }
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+ // skip synthetic methods
+ return null;
+ }
+ CollectMethodData mv = new CollectMethodData(classType,
+ access, name, desc, signature, exceptions);
+ methods.add(mv);
+ return mv;
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ this.outerClass = owner;
+ this.outerMethodName = name;
+ this.outerMethodDesc = desc;
+ classType = ClassType.Anonymous; // Could be Local, catch that later
+ }
+
+ /**
+ * If compiled with debug, visit the source information.
+ *
+ * @param source unqualified filename containing source (ie, Foo.java)
+ * @param debug additional debug information (may be null)
+ */
+ @Override
+ public void visitSource(String source, String debug) {
+ this.source = source;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java
new file mode 100644
index 0000000..e2e9c5d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectFieldData.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.asm.AnnotationVisitor;
+import com.google.gwt.dev.asm.commons.EmptyVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Collect data from a single field.
+ */
+public class CollectFieldData extends EmptyVisitor {
+
+ private List<CollectAnnotationData> annotations = new ArrayList<CollectAnnotationData>();
+ private int access;
+ private String name;
+ private String desc;
+ private String signature;
+ private Object value;
+
+ public CollectFieldData(int access, String name, String desc,
+ String signature, Object value) {
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ this.signature = signature;
+ this.value = value;
+ }
+
+ /**
+ * @return the access
+ */
+ public int getAccess() {
+ return access;
+ }
+
+ /**
+ * @return the annotations
+ */
+ public List<CollectAnnotationData> getAnnotations() {
+ return annotations;
+ }
+
+ /**
+ * @return the desc
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the signature
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+ /**
+ * @return the value
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "field " + name;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ CollectAnnotationData av = new CollectAnnotationData(desc,
+ visible);
+ annotations.add(av);
+ return av;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
new file mode 100644
index 0000000..81aff43
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.asm.AnnotationVisitor;
+import com.google.gwt.dev.asm.Label;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.commons.EmptyVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Collects data from a single method.
+ */
+public class CollectMethodData extends EmptyVisitor {
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private List<CollectAnnotationData> annotations = new ArrayList<CollectAnnotationData>();
+ private String name;
+ private String desc;
+ private String signature;
+ private String[] exceptions;
+ private Type[] argTypes;
+ private String[] argNames;
+ private List<CollectAnnotationData>[] paramAnnots;
+ private boolean actualArgNames = false;
+ private int access;
+ private int syntheticArgs;
+
+ @SuppressWarnings("unchecked")
+ public CollectMethodData(CollectClassData.ClassType classType, int access,
+ String name, String desc, String signature, String[] exceptions) {
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ this.signature = signature;
+ this.exceptions = exceptions;
+ syntheticArgs = 0;
+ argTypes = Type.getArgumentTypes(desc);
+ // Non-static instance methods and constructors of non-static inner
+ // classes have an extra synthetic parameter that isn't in the source,
+ // so we remove it.
+ if (classType.hasHiddenConstructorArg() && "<init>".equals(name)) {
+ // remove "this$1" as a parameter
+ syntheticArgs = 1;
+ int n = argTypes.length - syntheticArgs;
+ Type[] newArgTypes = new Type[n];
+ System.arraycopy(argTypes, syntheticArgs, newArgTypes, 0, n);
+ argTypes = newArgTypes;
+ }
+ argNames = new String[argTypes.length];
+ paramAnnots = new List[argTypes.length];
+ for (int i = 0; i < argNames.length; ++i) {
+ argNames[i] = "arg" + i;
+ paramAnnots[i] = new ArrayList<CollectAnnotationData>();
+ }
+ }
+
+ /**
+ * @return the access
+ */
+ public int getAccess() {
+ return access;
+ }
+
+ /**
+ * @return the annotations
+ */
+ public List<CollectAnnotationData> getAnnotations() {
+ return annotations;
+ }
+
+ public List<CollectAnnotationData>[] getArgAnnotations() {
+ return paramAnnots;
+ }
+
+ /**
+ * @return the argNames
+ */
+ public String[] getArgNames() {
+ return argNames;
+ }
+
+ /**
+ * @return the argTypes
+ */
+ public Type[] getArgTypes() {
+ return argTypes;
+ }
+
+ /**
+ * @return the desc
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * @return the exceptions
+ */
+ public String[] getExceptions() {
+ return exceptions == null ? EMPTY_STRING_ARRAY : exceptions;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the signature
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+ /**
+ * @return the actualArgNames
+ */
+ public boolean hasActualArgNames() {
+ return actualArgNames;
+ }
+
+ @Override
+ public String toString() {
+ return "method " + name;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ CollectAnnotationData av = new CollectAnnotationData(desc,
+ visible);
+ annotations.add(av);
+ return av;
+ }
+
+ @Override
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index) {
+ if ((access & Opcodes.ACC_STATIC) == 0) {
+ // adjust for "this"
+ // TODO(jat): do we need to account for this$0 in inner classes?
+ --index;
+ }
+ // TODO(jat): is it safe to assume parameter slots don't get reused?
+ // Do we need to check if the name has already been assigned?
+ if (index >= 0 && index < argNames.length) {
+ actualArgNames = true;
+ argNames[index] = name;
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int parameter,
+ String desc, boolean visible) {
+ CollectAnnotationData av = new CollectAnnotationData(desc,
+ visible);
+ if (parameter >= syntheticArgs) {
+ // javac adds @Synthetic annotation on its synthetic constructor
+ // arg, so we ignore it since it isn't in the source.
+ paramAnnots[parameter - syntheticArgs].add(av);
+ }
+ return av;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java
new file mode 100644
index 0000000..1796706
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectReferencesVisitor.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.asm.AnnotationVisitor;
+import com.google.gwt.dev.asm.FieldVisitor;
+import com.google.gwt.dev.asm.MethodVisitor;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.commons.EmptyVisitor;
+import com.google.gwt.dev.asm.signature.SignatureReader;
+import com.google.gwt.dev.asm.signature.SignatureVisitor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Collect all the types which are referenced by a particular class.
+ */
+public class CollectReferencesVisitor extends EmptyVisitor {
+
+ /**
+ * Collect type names from generic signatures.
+ *
+ * All we care about is picking up type names, so we just return ourselves
+ * for nested visitors.
+ */
+ private class CollectGenericTypes implements SignatureVisitor {
+ public SignatureVisitor visitArrayType() {
+ return this;
+ }
+
+ public void visitBaseType(char descriptor) {
+ }
+
+ public SignatureVisitor visitClassBound() {
+ return this;
+ }
+
+ public void visitClassType(String name) {
+ referencedTypes.add(name);
+ }
+
+ public void visitEnd() {
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ return this;
+ }
+
+ public void visitFormalTypeParameter(String name) {
+ }
+
+ public void visitInnerClassType(String name) {
+ }
+
+ public SignatureVisitor visitInterface() {
+ return this;
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ return this;
+ }
+
+ public SignatureVisitor visitParameterType() {
+ return this;
+ }
+
+ public SignatureVisitor visitReturnType() {
+ return this;
+ }
+
+ public SignatureVisitor visitSuperclass() {
+ return this;
+ }
+
+ public void visitTypeArgument() {
+ }
+
+ public SignatureVisitor visitTypeArgument(char wildcard) {
+ return this;
+ }
+
+ public void visitTypeVariable(String name) {
+ }
+ }
+
+ // internal names
+ protected Set<String> referencedTypes = new HashSet<String>();
+
+ public Set<String> getReferencedTypes() {
+ return referencedTypes;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+ if (superName != null) {
+ referencedTypes.add(superName);
+ }
+ if (interfaces != null) {
+ for (String intf : interfaces) {
+ referencedTypes.add(intf);
+ }
+ }
+ collectTypesFromClassSignature(signature);
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (value instanceof Type) {
+ addTypeIfClass((Type) value);
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ // don't mark this annotation as a reference or its arguments, so we can
+ // handle binary-only annotations.
+ // TODO(jat): consider implications of updating the annotation class
+ return null;
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ addTypeIfClass(desc);
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ addTypeIfClass(desc);
+ collectTypesFromFieldSignature(signature);
+ // we don't use visitEnd, so we can just use ourselves for nested visitors
+ return this;
+ }
+
+ /**
+ * @param name internal name of the inner class
+ * @param outerName internal name of the enclosing class
+ */
+ @Override
+ public void visitInnerClass(String name, String outerName,
+ String innerName, int access) {
+ referencedTypes.add(name);
+ if (outerName != null) {
+ referencedTypes.add(outerName);
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ for (Type type : Type.getArgumentTypes(desc)) {
+ addTypeIfClass(type);
+ }
+ addTypeIfClass(Type.getReturnType(desc));
+ collectTypesFromClassSignature(signature);
+ // we don't use visitEnd, so we can just use ourselves for nested visitors
+ return this;
+ }
+
+ /**
+ * @param owner internal name of owning class
+ */
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ referencedTypes.add(owner);
+ }
+
+ protected void addTypeIfClass(String desc) {
+ addTypeIfClass(Type.getType(desc));
+ }
+
+ protected void addTypeIfClass(Type type) {
+ if (type.getSort() == Type.OBJECT) {
+ referencedTypes.add(type.getInternalName());
+ }
+ }
+
+ private void collectTypesFromClassSignature(String signature) {
+ if (signature == null) {
+ return;
+ }
+ SignatureReader reader = new SignatureReader(signature);
+ reader.accept(new CollectGenericTypes());
+ }
+
+ private void collectTypesFromFieldSignature(String signature) {
+ if (signature == null) {
+ return;
+ }
+ SignatureReader reader = new SignatureReader(signature);
+ reader.acceptType(new CollectGenericTypes());
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectTypeParams.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectTypeParams.java
new file mode 100644
index 0000000..7349525
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectTypeParams.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+
+import java.util.List;
+
+/**
+ * Collects formal type parameters into a JTypeParameter list.
+ */
+public class CollectTypeParams extends EmptySignatureVisitor {
+
+ private final List<JTypeParameter> typeParams;
+
+ /**
+ * Collect declared type parameters from a generic signature.
+ *
+ * @param typeParams list to store type parameters in
+ */
+ public CollectTypeParams(List<JTypeParameter> typeParams) {
+ this.typeParams = typeParams;
+ }
+
+ @Override
+ public void visitFormalTypeParameter(String name) {
+ typeParams.add(new JTypeParameter(name, typeParams.size()));
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java b/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java
new file mode 100644
index 0000000..a719ce6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/EmptySignatureVisitor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.asm.signature.SignatureVisitor;
+
+/**
+ * Signature visitor that does nothing.
+ *
+ * Unlike the ASM-provided EmptyVisitor (which does not implement
+ * SignatureVisitor), this class does not pass itself to unimplemented
+ * sub-visitors, so that a subclass doesn't have to worry about calls for
+ * something under a sub-visitor it doesn't care about.
+ *
+ * There is no need to call any superclass methods from any subclass as they
+ * do nothing.
+ */
+public class EmptySignatureVisitor implements SignatureVisitor {
+
+ protected static EmptySignatureVisitor ignore = new EmptySignatureVisitor();
+
+ /**
+ * Treated as a visitEnd for this visitor.
+ */
+ public SignatureVisitor visitArrayType() {
+ return ignore;
+ }
+
+ /**
+ * Treated as a visitEnd for this visitor.
+ */
+ public void visitBaseType(char descriptor) {
+ }
+
+ public SignatureVisitor visitClassBound() {
+ return ignore;
+ }
+
+ public void visitClassType(String name) {
+ }
+
+ public void visitEnd() {
+ }
+
+ public SignatureVisitor visitExceptionType() {
+ return ignore;
+ }
+
+ public void visitFormalTypeParameter(String name) {
+ }
+
+ public void visitInnerClassType(String name) {
+ }
+
+ public SignatureVisitor visitInterface() {
+ return ignore;
+ }
+
+ public SignatureVisitor visitInterfaceBound() {
+ return ignore;
+ }
+
+ public SignatureVisitor visitParameterType() {
+ return ignore;
+ }
+
+ public SignatureVisitor visitReturnType() {
+ return ignore;
+ }
+
+ public SignatureVisitor visitSuperclass() {
+ return ignore;
+ }
+
+ public void visitTypeArgument() {
+ }
+
+ public SignatureVisitor visitTypeArgument(char wildcard) {
+ return ignore;
+ }
+
+ /**
+ * Treated as a visitEnd for this visitor.
+ */
+ public void visitTypeVariable(String name) {
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveClassSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveClassSignature.java
new file mode 100644
index 0000000..70b26d1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveClassSignature.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.dev.asm.signature.SignatureVisitor;
+import com.google.gwt.dev.javac.Resolver;
+import com.google.gwt.dev.javac.TypeParameterLookup;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Signature visitor that resolves all the type variables and their bounds for
+ * a given class.
+ */
+public class ResolveClassSignature extends EmptySignatureVisitor {
+
+ private final Resolver resolver;
+ private final Map<String, JRealClassType> binaryMapper;
+ private final TreeLogger logger;
+ private final JRealClassType type;
+ private final TypeParameterLookup lookup;
+
+ private JTypeParameter currentParam = null;
+ private ArrayList<JType[]> bounds = null;
+ private JType[] superClass = new JType[1];
+ private List<JType[]> interfaces = new ArrayList<JType[]>();
+
+ public ResolveClassSignature(Resolver resolver,
+ Map<String, JRealClassType> binaryMapper, TreeLogger logger,
+ JRealClassType type, TypeParameterLookup lookup) {
+ this.resolver = resolver;
+ this.binaryMapper = binaryMapper;
+ this.logger = logger;
+ this.type = type;
+ this.lookup = lookup;
+ }
+
+ public void finish() {
+ if (currentParam != null) {
+ int n = bounds.size();
+ JClassType[] boundTypes = new JClassType[n];
+ for (int i = 0; i < n; ++i) {
+ boundTypes[i] = (JClassType) bounds.get(i)[0];
+ }
+ currentParam.setBounds(boundTypes);
+ currentParam = null;
+ // TODO(jat): remove after debugging phase
+ bounds = null;
+ }
+ if (superClass[0] != null) {
+ if (type.isInterface() != null) {
+ // The generic signature contains a superclass for interfaces,
+ // but TypeOracle doesn't like that -- verify that we were
+ // told Object is the superclass and ignore it.
+ assert superClass[0].equals(
+ resolver.getTypeOracle().getJavaLangObject());
+ } else {
+ type.setSuperclass((JClassType) superClass[0]);
+ }
+ superClass[0] = null;
+ }
+ for (JType[] intfRef : interfaces) {
+ if (intfRef[0] != null) {
+ type.addImplementedInterface((JClassType) intfRef[0]);
+ }
+ }
+ interfaces.clear();
+ }
+
+ @Override
+ public SignatureVisitor visitArrayType() {
+ throw new IllegalStateException(
+ "visitArrayType called on ResolveClassTypeVariables");
+ }
+
+ @Override
+ public SignatureVisitor visitClassBound() {
+ JType[] bound = new JType[1];
+ bounds.add(bound);
+ return new ResolveTypeSignature(resolver, binaryMapper, logger, bound,
+ lookup, null);
+ }
+
+ @Override
+ public void visitFormalTypeParameter(String name) {
+ finish();
+ currentParam = lookup.lookup(name);
+ bounds = new ArrayList<JType[]>();
+ }
+
+ @Override
+ public SignatureVisitor visitInterface() {
+ finish();
+ JType[] intf = new JType[1];
+ interfaces.add(intf);
+ return new ResolveTypeSignature(resolver, binaryMapper, logger, intf,
+ lookup, null);
+ }
+
+ @Override
+ public SignatureVisitor visitInterfaceBound() {
+ JType[] bound = new JType[1];
+ bounds.add(bound);
+ return new ResolveTypeSignature(resolver, binaryMapper, logger, bound,
+ lookup, null);
+ }
+
+ @Override
+ public SignatureVisitor visitSuperclass() {
+ finish();
+ return new ResolveTypeSignature(resolver, binaryMapper, logger,
+ superClass, lookup, null);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
new file mode 100644
index 0000000..3b714b7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.signature.SignatureVisitor;
+import com.google.gwt.dev.javac.Resolver;
+import com.google.gwt.dev.javac.TypeParameterLookup;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Resolve a method given its generic signature, including return type,
+ * parameter types, and exceptions thrown.
+ */
+public class ResolveMethodSignature extends EmptySignatureVisitor {
+
+ private final Resolver resolver;
+ private final TreeLogger logger;
+ private final JAbstractMethod method;
+ private final TypeParameterLookup typeParamLookup;
+ private final boolean hasReturnType;
+ private final CollectMethodData methodData;
+ private final Type[] argTypes;
+ private final String[] argNames;
+
+ private JType[] returnType = new JType[1];
+ private List<JType[]> params = new ArrayList<JType[]>();
+ private List<JType[]> exceptions = new ArrayList<JType[]>();
+ private JTypeParameter currentParam = null;
+ private ArrayList<JType[]> bounds = null;
+
+ public ResolveMethodSignature(Resolver resolver, TreeLogger logger,
+ JAbstractMethod method, TypeParameterLookup typeParamLookup,
+ boolean hasReturnType, CollectMethodData methodData, Type[] argTypes,
+ String[] argNames) {
+ this.resolver = resolver;
+ this.logger = logger;
+ this.method = method;
+ this.typeParamLookup = typeParamLookup;
+ this.hasReturnType = hasReturnType;
+ this.methodData = methodData;
+ this.argTypes = argTypes;
+ this.argNames = argNames;
+ }
+
+ /**
+ * @return true if resolution succeeded.
+ */
+ public boolean finish() {
+ boolean failed = false;
+
+ finishBound();
+
+ // Set return type
+ if (hasReturnType) {
+ failed |= (returnType[0] == null);
+ ((JMethod) method).setReturnType(returnType[0]);
+ }
+
+ // Create arguments
+ List<CollectAnnotationData>[] argAnnotations = methodData.getArgAnnotations();
+ if (argTypes.length != params.size()) {
+ // TODO(jat): remove this check
+ throw new IllegalStateException(
+ "Arg count mismatch between method descriptor ("
+ + methodData.getDesc() + ") and signature ("
+ + methodData.getSignature() + ")");
+ }
+ for (int i = 0; i < argTypes.length; ++i) {
+ JType argType = params.get(i)[0];
+ if (argType == null) {
+ failed = true;
+ continue;
+ }
+ // Try to resolve annotations, ignore any that fail.
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations = new HashMap<Class<? extends Annotation>, Annotation>();
+ resolver.resolveAnnotations(logger, argAnnotations[i],
+ declaredAnnotations);
+
+ // JParameter adds itself to the method
+ new JParameter(method, argType, argNames[i], declaredAnnotations);
+ }
+
+ // Handle thrown exceptions
+ for (JType[] exc : exceptions) {
+ if (exc[0] == null) {
+ failed = true;
+ continue;
+ }
+ method.addThrows(exc[0]);
+ }
+ return !failed;
+ }
+
+ @Override
+ public SignatureVisitor visitArrayType() {
+ assert false : "visitArrayType called on ResolveClassTypeVariables";
+ return null;
+ }
+
+ @Override
+ public SignatureVisitor visitClassBound() {
+ JType[] bound = new JClassType[1];
+ bounds.add(bound);
+ return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),
+ logger, bound, typeParamLookup, null);
+ }
+
+ @Override
+ public SignatureVisitor visitExceptionType() {
+ JType[] exc = new JType[1];
+ exceptions.add(exc);
+ return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),
+ logger, exc, typeParamLookup, null);
+ }
+
+ @Override
+ public void visitFormalTypeParameter(String name) {
+ finishBound();
+ currentParam = typeParamLookup.lookup(name);
+ bounds = new ArrayList<JType[]>();
+ }
+
+ @Override
+ public SignatureVisitor visitInterfaceBound() {
+ JType[] bound = new JType[1];
+ bounds.add(bound);
+ return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),
+ logger, bound, typeParamLookup, null);
+ }
+
+ @Override
+ public SignatureVisitor visitParameterType() {
+ JType[] param = new JType[1];
+ params.add(param);
+ return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),
+ logger, param, typeParamLookup, null);
+ }
+
+ @Override
+ public SignatureVisitor visitReturnType() {
+ return new ResolveTypeSignature(resolver, resolver.getBinaryMapper(),
+ logger, returnType, typeParamLookup, null);
+ }
+
+ private void finishBound() {
+ if (currentParam != null) {
+ int n = bounds.size();
+ JClassType[] boundTypes = new JClassType[n];
+ for (int i = 0; i < n; ++i) {
+ boundTypes[i] = (JClassType) bounds.get(i)[0];
+ }
+ currentParam.setBounds(boundTypes);
+ currentParam = null;
+ // TODO(jat): remove after debugging phase
+ bounds = null;
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveTypeSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveTypeSignature.java
new file mode 100644
index 0000000..f62b872
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveTypeSignature.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+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.JWildcardType.BoundType;
+import com.google.gwt.dev.asm.signature.SignatureVisitor;
+import com.google.gwt.dev.javac.Resolver;
+import com.google.gwt.dev.javac.TypeParameterLookup;
+import com.google.gwt.dev.util.Name;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Resolve a single parameterized type.
+ */
+public class ResolveTypeSignature extends EmptySignatureVisitor {
+
+ private final Resolver resolver;
+ private final Map<String, JRealClassType> binaryMapper;
+ private final TreeLogger logger;
+ private final JType[] returnTypeRef;
+ private final TypeParameterLookup lookup;
+ private final char wildcardMatch;
+ private final JClassType enclosingClass;
+
+ private JClassType outerClass;
+ private List<JType[]> args = new ArrayList<JType[]>();
+ private int arrayDepth = 0;
+
+ /**
+ * Resolve a parameterized type.
+ *
+ * @param resolver
+ * @param binaryMapper
+ * @param logger
+ * @param returnTypeRef "pointer" to return location, ie. 1-element array
+ * @param lookup
+ * @param enclosingClass
+ */
+ public ResolveTypeSignature(Resolver resolver,
+ Map<String, JRealClassType> binaryMapper, TreeLogger logger,
+ JType[] returnTypeRef, TypeParameterLookup lookup,
+ JClassType enclosingClass) {
+ this(resolver, binaryMapper, logger, returnTypeRef, lookup,
+ enclosingClass, '=');
+ }
+
+ public ResolveTypeSignature(Resolver resovler,
+ Map<String, JRealClassType> binaryMapper,
+ TreeLogger logger, JType[] returnTypeRef,
+ TypeParameterLookup lookup, JClassType enclosingClass,
+ char wildcardMatch) {
+ this.resolver = resovler;
+ this.binaryMapper = binaryMapper;
+ this.logger = logger;
+ this.returnTypeRef = returnTypeRef;
+ this.lookup = lookup;
+ this.enclosingClass = enclosingClass;
+ this.wildcardMatch = wildcardMatch;
+ }
+
+ @Override
+ public SignatureVisitor visitArrayType() {
+ ++arrayDepth;
+ return this;
+ }
+
+ @Override
+ public void visitBaseType(char descriptor) {
+ switch (descriptor) {
+ case 'V':
+ returnTypeRef[0] = JPrimitiveType.VOID;
+ break;
+ case 'B':
+ returnTypeRef[0] = JPrimitiveType.BYTE;
+ break;
+ case 'J':
+ returnTypeRef[0] = JPrimitiveType.LONG;
+ break;
+ case 'Z':
+ returnTypeRef[0] = JPrimitiveType.BOOLEAN;
+ break;
+ case 'I':
+ returnTypeRef[0] = JPrimitiveType.INT;
+ break;
+ case 'S':
+ returnTypeRef[0] = JPrimitiveType.SHORT;
+ break;
+ case 'C':
+ returnTypeRef[0] = JPrimitiveType.CHAR;
+ break;
+ case 'F':
+ returnTypeRef[0] = JPrimitiveType.FLOAT;
+ break;
+ case 'D':
+ returnTypeRef[0] = JPrimitiveType.DOUBLE;
+ break;
+ default:
+ throw new IllegalStateException("Unrecognized base type "
+ + descriptor);
+ }
+ // this is the last visitor called on this visitor
+ visitEnd();
+ }
+
+ @Override
+ public void visitClassType(String internalName) {
+ assert Name.isInternalName(internalName);
+ outerClass = enclosingClass;
+ JRealClassType classType = binaryMapper.get(internalName);
+ boolean resolveSuccess = resolver.resolveClass(logger, classType);
+ returnTypeRef[0] = classType;
+ if (!resolveSuccess || returnTypeRef[0] == null) {
+ logger.log(TreeLogger.ERROR, "Unable to resolve class " + internalName);
+ // Replace bound with Object if we can't resolve the class.
+ returnTypeRef[0] = resolver.getTypeOracle().getJavaLangObject();
+ }
+ }
+
+ @Override
+ public void visitEnd() {
+ if (returnTypeRef[0] == null) {
+ return;
+ }
+ resolveGenerics();
+ }
+
+ @Override
+ public void visitInnerClassType(String innerName) {
+ // Called after visitClass has already been called, and we will
+ // successively refine the class by going into its inner classes.
+ assert returnTypeRef[0] != null;
+ resolveGenerics();
+ outerClass = (JClassType) returnTypeRef[0];
+ JClassType searchClass = outerClass;
+ try {
+ JParameterizedType pt = searchClass.isParameterized();
+ if (pt != null) {
+ searchClass = pt.getBaseType();
+ }
+ returnTypeRef[0] = searchClass.getNestedType(innerName);
+ } catch (NotFoundException e) {
+ logger.log(TreeLogger.ERROR, "Unable to resolve inner class "
+ + innerName + " in " + searchClass, e);
+ }
+ }
+
+ @Override
+ public void visitTypeArgument() {
+ JType[] arg = new JType[1]; // This could be int[] for example
+ arg[0] = resolver.getTypeOracle().getWildcardType(
+ JWildcardType.BoundType.UNBOUND,
+ resolver.getTypeOracle().getJavaLangObject());
+ args.add(arg);
+ }
+
+ @Override
+ public SignatureVisitor visitTypeArgument(char wildcard) {
+ JType[] arg = new JType[1];
+ args.add(arg);
+ // TODO(jat): should we pass enclosingClass here instead of null?
+ // not sure what the enclosing class of a type argument means, but
+ // I haven't found a case where it is actually used while processing
+ // the type argument.
+ return new ResolveTypeSignature(resolver, binaryMapper, logger,
+ arg, lookup, null, wildcard);
+ }
+
+ @Override
+ public void visitTypeVariable(String name) {
+ returnTypeRef[0] = lookup.lookup(name);
+ // this is the last visitor called on this visitor
+ visitEnd();
+ }
+
+ /**
+ * Merge the bounds from the declared type parameters into the type arguments
+ * for this type if necessary.
+ *
+ * <pre>Example:
+ * class Foo<T extends Bar> ...
+ *
+ * Foo<?> foo
+ *
+ * foo needs to have bounds ? extends Bar.
+ * </pre>
+ *
+ * <p>Currently we only deal with unbound wildcards as above, which matches
+ * existing TypeOracleMediator behavior. However, this may need to be
+ * extended.
+ *
+ * @param typeParams
+ * @param typeArgs
+ */
+ private void mergeTypeParamBounds(JTypeParameter[] typeParams,
+ JClassType[] typeArgs) {
+ int n = typeArgs.length;
+ for (int i = 0; i < n; ++i) {
+ JWildcardType wildcard = typeArgs[i].isWildcard();
+ // right now we only replace Foo<?> with the constraints defined on the
+ // definition (which appears to match the existing TypeOracleMediator)
+ // but other cases may need to be handled.
+ if (wildcard != null && wildcard.getBoundType() == BoundType.UNBOUND
+ && wildcard.getBaseType()
+ == resolver.getTypeOracle().getJavaLangObject()
+ && typeParams[i].getBaseType() != null) {
+ typeArgs[i] = resolver.getTypeOracle().getWildcardType(
+ BoundType.UNBOUND, typeParams[i].getBaseType());
+ }
+ }
+ }
+
+ private JType resolveGeneric(JType type, JClassType outer,
+ JClassType[] typeArgs) {
+ JGenericType genericType = type.isGenericType();
+ if (genericType != null) {
+ int actual = typeArgs.length;
+ JTypeParameter[] typeParams = genericType.getTypeParameters();
+ int expected = typeParams.length;
+ if (actual == 0 && expected > 0) {
+ // If no type parameters were supplied, this is a raw type usage.
+ type = genericType.getRawType();
+ } else {
+ if (actual != expected) {
+ throw new IllegalStateException("Incorrect # of type parameters to "
+ + genericType.getQualifiedBinaryName() + ": expected " + expected
+ + ", actual=" + actual);
+ }
+ JClassType genericEnc = genericType.getEnclosingType();
+ if (outer == null && genericEnc != null) {
+ // Sometimes the signature is like Foo$Bar<H> even if Foo is a
+ // generic class. The cases I have seen are where Foo's type
+ // parameter is also named H and has the same bounds. That
+ // manifests itself as getting visitClassType("Foo$Bar") and
+ // then VisitTypeArgument/etc, rather than the usual
+ // visitClassType("Foo"), visitTypeArgument/etc,
+ // visitInnerClass("Bar"), visitTypeArgument/etc.
+ //
+ // So, in this case we have to build our own chain of enclosing
+ // classes here, properly parameterizing any generics along the
+ // way.
+ // TODO(jat): more testing to validate this assumption
+ JClassType[] outerArgs = null;
+ JGenericType genericEncGeneric = genericEnc.isGenericType();
+ if (genericEncGeneric != null) {
+ JTypeParameter[] encTypeParams = genericEncGeneric.getTypeParameters();
+ int n = encTypeParams.length;
+ outerArgs = new JClassType[n];
+ for (int i = 0; i < n; ++i) {
+ outerArgs[i] = lookup.lookup(encTypeParams[i].getName());
+ if (outerArgs[i] == null) {
+ // check to see if our current type has a parameter of the same
+ // name, and use it if so.
+ for (int j = 0; j < expected; ++j) {
+ if (typeParams[j].getName().equals(encTypeParams[j].getName())) {
+ outerArgs[i] = typeArgs[j];
+ break;
+ }
+ }
+ }
+ assert outerArgs[i] != null
+ : "Unable to resolve type parameter "
+ + encTypeParams[i].getName() + " in enclosing type "
+ + genericEnc + " of type " + genericType;
+ }
+ }
+ outer = (JClassType) resolveGeneric(genericEnc, null, outerArgs);
+ }
+ try {
+ mergeTypeParamBounds(typeParams, typeArgs);
+ type = resolver.getTypeOracle().getParameterizedType(genericType,
+ outer, typeArgs);
+ } catch (IllegalArgumentException e) {
+ // Can't use toString on typeArgs as they aren't completely built
+ // yet, so we have to roll our own.
+ StringBuilder buf = new StringBuilder();
+ buf.append("Unable to build parameterized type ");
+ buf.append(genericType);
+ String prefix = " with args <";
+ for (JClassType typeArg : typeArgs) {
+ buf.append(prefix).append(typeArg.getName());
+ prefix = ", ";
+ }
+ if (", ".equals(prefix)) {
+ buf.append('>');
+ }
+ logger.log(TreeLogger.ERROR, buf.toString(), e);
+ type = genericType.getRawType();
+ }
+ }
+ }
+ return type;
+ }
+
+ private void resolveGenerics() {
+ JGenericType genericType = returnTypeRef[0].isGenericType();
+ if (genericType != null) {
+ int actual = args.size();
+ JClassType[] typeArgs = new JClassType[actual];
+ for (int i = 0; i < actual; ++i) {
+ JType type = args.get(i)[0];
+ if (!(type instanceof JClassType)) {
+ logger.log(TreeLogger.ERROR, "Parameterized type argument is "
+ + type + ", expected reference type");
+ } else {
+ typeArgs[i] = (JClassType) type;
+ }
+ }
+ returnTypeRef[0] = resolveGeneric(genericType, outerClass, typeArgs);
+ args.clear();
+ }
+ for (int i = 0; i < arrayDepth; ++i) {
+ returnTypeRef[0] = resolver.getTypeOracle().getArrayType(
+ returnTypeRef[0]);
+ }
+ switch (wildcardMatch) {
+ case '=':
+ // nothing to do for an exact match
+ break;
+ case '*':
+ returnTypeRef[0] = resolver.getTypeOracle().getWildcardType(
+ JWildcardType.BoundType.UNBOUND, (JClassType) returnTypeRef[0]);
+ break;
+ case '+':
+ // ? extends T
+ returnTypeRef[0] = resolver.getTypeOracle().getWildcardType(
+ JWildcardType.BoundType.EXTENDS, (JClassType) returnTypeRef[0]);
+ break;
+ case '-':
+ // ? super T
+ returnTypeRef[0] = resolver.getTypeOracle().getWildcardType(
+ JWildcardType.BoundType.SUPER, (JClassType) returnTypeRef[0]);
+ break;
+ }
+ if (returnTypeRef[0] instanceof JClassType) {
+ // Only JClassTypes can be an outer class
+ outerClass = (JClassType) returnTypeRef[0];
+ }
+ }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/HookableTypeOracle.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/HookableTypeOracle.java
new file mode 100644
index 0000000..f30c225
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/HookableTypeOracle.java
@@ -0,0 +1,13 @@
+package com.google.gwt.core.ext.typeinfo;
+
+/**
+ * A minimal TypeOracle mock that exposes the addNewType hook to subclasses.
+ */
+public class HookableTypeOracle extends TypeOracle {
+
+ // Increases visibility so tests in other packages can hook this.
+ @Override
+ protected void addNewType(JRealClassType newType) {
+ super.addNewType(newType);
+ }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
index f10d243..69f1231 100644
--- a/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/JEnumTypeTest.java
@@ -17,7 +17,10 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.test.EnumInterface;
+import com.google.gwt.core.ext.typeinfo.test.EnumOfInterface;
import com.google.gwt.core.ext.typeinfo.test.MyEnum;
+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import junit.framework.TestCase;
@@ -105,7 +108,8 @@
JClassType type = typeOracle.getType(MyEnum.class.getName());
JEnumType enumType = validateTypeIsEnum(type);
- assertEquals(1, enumType.getConstructors().length);
+ // Enum constructors are not reflected.
+ assertEquals(0, enumType.getConstructors().length);
}
/**
@@ -121,6 +125,43 @@
}
/**
+ * Test an enum that implements an interface.
+ *
+ * @throws NotFoundException
+ */
+ public void testInterface() throws NotFoundException {
+ JClassType type = typeOracle.getType(EnumOfInterface.class.getName());
+ JEnumType enumType = validateTypeIsEnum(type);
+ JClassType[] intf = enumType.getImplementedInterfaces();
+ assertEquals(1, intf.length);
+ assertEquals(EnumInterface.class.getName(),
+ intf[0].getQualifiedSourceName());
+ JMethod getExtra = intf[0].getMethod("getExtra", new JType[0]);
+ TestAnnotation annotation = getExtra.getAnnotation(TestAnnotation.class);
+ assertNotNull(annotation);
+ assertEquals("EnumInterface getExtra", annotation.value());
+ JEnumConstant[] constants = enumType.getEnumConstants();
+ assertEquals(2, constants.length);
+ assertEquals("A", constants[0].getName());
+ annotation = constants[0].getAnnotation(TestAnnotation.class);
+ assertNotNull(annotation);
+ assertEquals("A", annotation.value());
+ JClassType aClass = constants[0].getType().isClass();
+ JMethod[] methods = aClass.getOverridableMethods();
+ assertEquals(7, methods.length);
+ // TODO(jat): verify getExtra is from A's anonymous subclass of
+ // EnumInterface when/if that is implemented.
+ boolean found = false;
+ for (JMethod method : methods) {
+ if ("name".equals(method.getName())) {
+ found = true;
+ // TODO(jat); any other verification here?
+ }
+ }
+ assertTrue(found);
+ }
+
+ /**
* Test method for {@link com.google.gwt.core.ext.typeinfo.JEnumType#isEnum()}.
*
* @throws NotFoundException
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumInterface.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumInterface.java
new file mode 100644
index 0000000..96eb24a
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumInterface.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 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.core.ext.typeinfo.test;
+
+/**
+ * Interface used to test enums that implement interfaces.
+ */
+public interface EnumInterface {
+
+ /**
+ * This is a built-in method from Enum<E>.
+ *
+ * @return the name of the enum
+ */
+ String name();
+
+ /**
+ * Totally new method.
+ *
+ * @return some arbitrary extra data
+ */
+ @TestAnnotation("EnumInterface getExtra")
+ String getExtra();
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumOfInterface.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumOfInterface.java
new file mode 100644
index 0000000..55a4b2b
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/EnumOfInterface.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 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.core.ext.typeinfo.test;
+
+/**
+ * Enumerated type used in the
+ * {@link com.google.gwt.core.ext.typeinfo.JEnumTypeTest}.
+ */
+public enum EnumOfInterface implements EnumInterface {
+ @TestAnnotation("A")
+ A {
+ @TestAnnotation("A getExtra")
+ @Override
+ public String getExtra() {
+ return "A extra";
+ }
+ },
+
+ B;
+
+ // Default just returns null
+ public String getExtra() {
+ return null;
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
index 9b0ceaa..7085918 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JavaCompilationSuite.java
@@ -31,6 +31,7 @@
suite.addTestSuite(CompilationStateTest.class);
suite.addTestSuite(CompilationUnitFileReferenceTest.class);
suite.addTestSuite(GWTProblemTest.class);
+ suite.addTestSuite(JavaSourceParserTest.class);
suite.addTestSuite(JdtBehaviorTest.class);
suite.addTestSuite(JdtCompilerTest.class);
suite.addTestSuite(JProgramLastFragmentLoadingBeforeTest.class);
diff --git a/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java b/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java
new file mode 100644
index 0000000..daf4f99
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/JavaSourceParserTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2009 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.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link JavaSourceParser}.
+ */
+public class JavaSourceParserTest extends CompilationStateTestBase {
+
+ private static final MockJavaResource FOO = new MockJavaResource("test.Foo") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("public class Foo {\n");
+ code.append(" public String value(String a, int val) { return \"Foo\"; }\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource BAR = new MockJavaResource("test.Bar") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("public class Bar {\n");
+ code.append(" public String value(String a, int val) { return \"Bar\"; }\n");
+ code.append(" public String value(String a) { return \"Bar\"; }\n");
+ code.append(" public String value(int val) { return \"Bar\"; }\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource BAZ = new MockJavaResource("test.Baz") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("public class Baz {\n");
+ code.append(" public static class Baz1 {\n");
+ code.append(" public String value(String a) { return \"Baz1\"; }\n");
+ code.append(" public String value(int val) { return \"Baz1\"; }\n");
+ code.append(" }\n");
+ code.append(" public class Baz2 {\n");
+ code.append(" public String value(String a) { return \"Baz2\"; }\n");
+ code.append(" public String value(int val) { return \"Baz2\"; }\n");
+ code.append(" }\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource FOO_INT = new MockJavaResource("test.FooInt") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("public interface FooInt {\n");
+ code.append(" String value(String a, int val);\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ /**
+ * Test method for {@link JavaSourceParser#getClassChain(java.lang.String)}.
+ */
+ public void testGetClassChain() {
+ assertExpected(JavaSourceParser.getClassChain("Foo"), "Foo");
+ assertExpected(JavaSourceParser.getClassChain("test.Foo"), "Foo");
+ assertExpected(JavaSourceParser.getClassChain("Foo$Bar"), "Foo", "Bar");
+ assertExpected(JavaSourceParser.getClassChain("test.Foo$Bar"),
+ "Foo", "Bar");
+ assertExpected(JavaSourceParser.getClassChain("test.test2.Foo$Bar$Baz"),
+ "Foo", "Bar", "Baz");
+ }
+
+ public void testLookup() throws NotFoundException {
+ JavaSourceParser parser = new JavaSourceParser();
+ addGeneratedUnits(FOO);
+ addGeneratedUnits(BAR);
+ addGeneratedUnits(BAZ);
+ JClassType string = state.getTypeOracle().getType("java.lang.String");
+ JClassType foo = state.getTypeOracle().getType("test.Foo");
+ parser.addSourceForType(foo, FOO);
+ JClassType bar = state.getTypeOracle().getType("test.Bar");
+ parser.addSourceForType(bar, BAR);
+ JClassType baz = state.getTypeOracle().getType("test.Baz");
+ parser.addSourceForType(baz, BAZ);
+ JClassType baz1 = state.getTypeOracle().getType("test.Baz.Baz1");
+ JClassType baz2 = state.getTypeOracle().getType("test.Baz.Baz2");
+ JMethod method = foo.getMethod("value", new JType[] { string,
+ JPrimitiveType.INT });
+ String[] arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(2, arguments.length);
+ assertEquals("a", arguments[0]);
+ assertEquals("val", arguments[1]);
+ method = bar.getMethod("value", new JType[] { string,
+ JPrimitiveType.INT });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(2, arguments.length);
+ assertEquals("a", arguments[0]);
+ assertEquals("val", arguments[1]);
+ method = bar.getMethod("value", new JType[] { JPrimitiveType.INT });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(1, arguments.length);
+ assertEquals("val", arguments[0]);
+ method = bar.getMethod("value", new JType[] { string });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(1, arguments.length);
+ assertEquals("a", arguments[0]);
+ method = baz1.getMethod("value", new JType[] { JPrimitiveType.INT });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(1, arguments.length);
+ assertEquals("val", arguments[0]);
+ method = baz1.getMethod("value", new JType[] { string });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(1, arguments.length);
+ assertEquals("a", arguments[0]);
+ method = baz2.getMethod("value", new JType[] { JPrimitiveType.INT });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(1, arguments.length);
+ assertEquals("val", arguments[0]);
+ method = baz2.getMethod("value", new JType[] { string });
+ arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(1, arguments.length);
+ assertEquals("a", arguments[0]);
+ }
+
+ public void testParamNames() throws NotFoundException {
+ JavaSourceParser parser = new JavaSourceParser();
+ addGeneratedUnits(FOO_INT);
+ JClassType string = state.getTypeOracle().getType("java.lang.String");
+ JClassType fooInt = state.getTypeOracle().getType("test.FooInt");
+ parser.addSourceForType(fooInt, FOO_INT);
+ JMethod method = fooInt.getMethod("value", new JType[] { string,
+ JPrimitiveType.INT });
+ String[] arguments = parser.getArguments(method);
+ assertNotNull(arguments);
+ assertEquals(2, arguments.length);
+ assertEquals("a", arguments[0]);
+ assertEquals("val", arguments[1]);
+ }
+
+ private void assertExpected(List<char[]> actual, String... expected) {
+ if (actual.size() != expected.length) {
+ fail("Expected " + Arrays.deepToString(expected) + ", got " + actual);
+ }
+ for (int i = 0; i < expected.length; ++i) {
+ assertTrue("index " + i + " should be " + expected[i] + ", got "
+ + Arrays.toString(actual.get(i)), Arrays.equals(actual.get(i),
+ expected[i].toCharArray()));
+ }
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java b/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
index 32d8358..7b5ce10 100644
--- a/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/TypeOracleMediatorTest.java
@@ -18,10 +18,15 @@
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;
@@ -235,6 +240,42 @@
}
};
+ 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
@@ -293,6 +334,47 @@
}
};
+ 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
@@ -442,6 +524,48 @@
}
};
+ 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
@@ -476,34 +600,62 @@
}
};
+ 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", "Enclosing.1") {
+ @Override
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
- if ("Enclosing".equals(name)) {
- checkEnclosing(type);
- } else {
- checkLocal(type);
- }
+ 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(1, nested.length);
- JClassType inner = nested[0];
- assertEquals("test.Enclosing.1", inner.getQualifiedSourceName());
+ assertEquals(0, nested.length);
}
- public void checkLocal(JClassType type) {
- assertEquals("1", type.getSimpleSourceName());
- assertEquals("test.Enclosing.1", type.getQualifiedSourceName());
- assertEquals("test.Enclosing",
- type.getEnclosingType().getQualifiedSourceName());
- }
-
+ @Override
public String getSource() {
StringBuffer sb = new StringBuffer();
sb.append("package test;\n");
@@ -591,6 +743,46 @@
}
};
+ 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
@@ -719,6 +911,26 @@
}
};
+ 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>();
@@ -832,6 +1044,71 @@
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);
@@ -840,6 +1117,69 @@
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);
@@ -848,12 +1188,13 @@
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(3, types.length);
+ assertEquals(2, types.length);
}
public void testMethodsAndParams() throws TypeOracleException {
@@ -871,6 +1212,16 @@
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);
}
/**
@@ -1034,6 +1385,24 @@
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);
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/AsmTestCase.java b/dev/core/test/com/google/gwt/dev/javac/asm/AsmTestCase.java
new file mode 100644
index 0000000..4ec878a
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/AsmTestCase.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.dev.util.Util;
+
+import junit.framework.TestCase;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Base class for ASM unit tests that defines some useful methods.
+ */
+public abstract class AsmTestCase extends TestCase {
+
+ private static final ClassLoader CLASSLOADER = CollectClassDataTest.class.getClassLoader();
+
+ public AsmTestCase() {
+ super();
+ }
+
+ public AsmTestCase(String name) {
+ super(name);
+ }
+
+ /**
+ * Read the bytes of a class.
+ *
+ * @param clazz class literal of the class to read
+ * @return bytes from class file or null if not found
+ */
+ protected byte[] getClassBytes(Class<?> clazz) {
+ return getClassBytes(clazz.getName());
+ }
+
+ /**
+ * Read the bytes of a class.
+ *
+ * @param className binary name (ie com.Foo$Bar) of the class to read
+ * @return bytes from class file or null if not found
+ */
+ protected byte[] getClassBytes(String className) {
+ URL resource = CLASSLOADER.getResource(
+ className.replace('.', '/') + ".class");
+ if (resource == null) {
+ return null;
+ }
+ return Util.readURLAsBytes(resource);
+ }
+
+ /**
+ * Reads the source for a class.
+ *
+ * @param clazz class literal of the class to read
+ * @return source from .java file or null if not found
+ */
+ protected String getClassSource(Class<?> clazz) {
+ return getClassSource(clazz.getName());
+ }
+
+ /**
+ * Reads the source for a class.
+ *
+ * @param className binary name (ie com.Foo$Bar) of the class to read
+ * @return source from .java file or null if not found
+ */
+ protected String getClassSource(String className) {
+ InputStream str = CLASSLOADER.getResourceAsStream(
+ className.replace('.', '/') + ".java");
+ if (str == null) {
+ return null;
+ }
+ return Util.readStreamAsString(str);
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/CollectClassDataTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/CollectClassDataTest.java
new file mode 100644
index 0000000..08ca510
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/CollectClassDataTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.typeinfo.test.PrimitiveValuesAnnotation;
+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;
+import com.google.gwt.dev.asm.AnnotationVisitor;
+import com.google.gwt.dev.asm.ClassReader;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.commons.EmptyVisitor;
+import com.google.gwt.dev.javac.asm.CollectAnnotationData.AnnotationData;
+import com.google.gwt.dev.javac.asm.CollectClassData.ClassType;
+
+import java.util.List;
+
+/**
+ * Tests for {@link CollectClassData}.
+ */
+public class CollectClassDataTest extends AsmTestCase {
+
+ public static class One extends EmptyVisitor {
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ new EmptyVisitor() {
+ @Override
+ public void visit(int version, int access, String name,
+ String signature, String superName, String[] interfaces) {
+ }
+ };
+ return new CollectAnnotationData(desc, visible);
+ }
+ }
+
+ @PrimitiveValuesAnnotation(b = 42, i = 42)
+ protected static class Two {
+
+ public class TwoInner {
+ }
+
+ private String field;
+
+ @TestAnnotation("field")
+ private String annotatedField;
+
+ public Two(int a) {
+ this(a, null);
+ }
+
+ @TestAnnotation("foo")
+ public String foo(int a) throws IllegalStateException {
+ return annotatedField;
+ }
+
+ public Two(int a, String b) {
+ new TwoInner();
+ field = b;
+ annotatedField = field;
+ }
+ }
+
+ public void testAnonymous() {
+ CollectClassData cd = collect(One.class.getName() + "$1");
+ // Don't access on anonymous classes, it varies among compilers
+ // assertEquals(0, cd.getAccess() & ~Opcodes.ACC_SUPER);
+ assertEquals(ClassType.Anonymous, cd.getClassType());
+ assertEquals(0, cd.getFields().size());
+ List<CollectMethodData> methods = cd.getMethods();
+ assertEquals(2, methods.size());
+ }
+
+ public void testOne() {
+ CollectClassData cd = collect(One.class);
+ // Don't check for super bit, as it will depend on the JDK used to compile.
+ assertEquals(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
+ cd.getAccess() & ~Opcodes.ACC_SUPER);
+ assertEquals(ClassType.Nested, cd.getClassType());
+ assertEquals(0, cd.getFields().size());
+ assertEquals(0, cd.getInterfaces().length);
+ assertEquals(0, cd.getAnnotations().size());
+ assertEquals("com/google/gwt/dev/asm/commons/EmptyVisitor",
+ cd.getSuperName());
+
+ List<CollectMethodData> methods = cd.getMethods();
+ assertEquals(2, methods.size());
+ // TODO(jat): Is it safe to assume the implicit constructor is always first?
+ CollectMethodData method = methods.get(0);
+ Type[] argTypes = method.getArgTypes();
+ String[] argNames = method.getArgNames();
+ assertEquals("<init>", method.getName());
+ assertEquals(0, argTypes.length);
+ assertEquals(0, argNames.length);
+ assertEquals(0, method.getArgAnnotations().length);
+ assertEquals(0, method.getAnnotations().size());
+ assertEquals(0, method.getExceptions().length);
+
+ method = methods.get(1);
+ argTypes = method.getArgTypes();
+ argNames = method.getArgNames();
+ assertEquals("visitAnnotation", method.getName());
+ assertEquals(2, argTypes.length);
+ assertEquals("java.lang.String", argTypes[0].getClassName());
+ assertEquals("boolean", argTypes[1].getClassName());
+ assertEquals(2, argNames.length);
+ assertEquals("desc", argNames[0]);
+ assertEquals("visible", argNames[1]);
+ assertEquals(2, method.getArgAnnotations().length);
+ assertEquals(0, method.getArgAnnotations()[0].size());
+ assertEquals(0, method.getArgAnnotations()[1].size());
+ // Note that @Override is a source-only annotation
+ assertEquals(0, method.getAnnotations().size());
+ assertEquals(0, method.getExceptions().length);
+ }
+
+ public void testTopLevel() {
+ CollectClassData cd = collect(CollectClassDataTest.class);
+ // Don't check for super bit, as it will depend on the JDK used to compile.
+ assertEquals(Opcodes.ACC_PUBLIC, cd.getAccess() & ~Opcodes.ACC_SUPER);
+ assertEquals(ClassType.TopLevel, cd.getClassType());
+ }
+
+ public void testTwo() {
+ CollectClassData cd = collect(Two.class);
+ // Don't check for super bit, as it will depend on the JDK used to compile.
+ assertEquals(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
+ cd.getAccess() & ~Opcodes.ACC_SUPER);
+ assertEquals(ClassType.Nested, cd.getClassType());
+ List<CollectFieldData> fields = cd.getFields();
+ assertEquals(2, fields.size());
+ CollectFieldData field = fields.get(0);
+ assertEquals("field", field.getName());
+ assertEquals("Ljava/lang/String;", field.getDesc());
+ List<CollectAnnotationData> annotations = field.getAnnotations();
+ assertEquals(0, annotations.size());
+ field = fields.get(1);
+ assertEquals("annotatedField", field.getName());
+ assertEquals("Ljava/lang/String;", field.getDesc());
+ annotations = field.getAnnotations();
+ assertEquals(1, annotations.size());
+ AnnotationData annotation = annotations.get(0).getAnnotation();
+ assertEquals("Lcom/google/gwt/core/ext/typeinfo/test/TestAnnotation;",
+ annotation.getDesc());
+ assertEquals("field", annotation.getValues().get("value"));
+ assertEquals(0, cd.getInterfaces().length);
+ annotations = cd.getAnnotations();
+ assertEquals(1, annotations.size());
+ annotation = annotations.get(0).getAnnotation();
+ assertEquals(
+ "Lcom/google/gwt/core/ext/typeinfo/test/PrimitiveValuesAnnotation;",
+ annotation.getDesc());
+ assertEquals(Byte.valueOf((byte) 42), annotation.getValues().get("b"));
+ assertEquals(42, annotation.getValues().get("i"));
+ assertEquals("java/lang/Object", cd.getSuperName());
+
+ List<CollectMethodData> methods = cd.getMethods();
+ assertEquals(3, methods.size());
+ // TODO(jat): Is it safe to assume the order?
+ CollectMethodData method = methods.get(0);
+ Type[] argTypes = method.getArgTypes();
+ String[] argNames = method.getArgNames();
+ assertEquals("<init>", method.getName());
+ assertEquals(1, argTypes.length);
+ assertEquals(1, argNames.length);
+ assertEquals(1, method.getArgAnnotations().length);
+ assertEquals(0, method.getAnnotations().size());
+ assertEquals(0, method.getExceptions().length);
+
+ method = methods.get(1);
+ argTypes = method.getArgTypes();
+ argNames = method.getArgNames();
+ assertEquals("foo", method.getName());
+ assertEquals(1, argTypes.length);
+ assertEquals("int", argTypes[0].getClassName());
+ assertEquals(1, argNames.length);
+ assertEquals("a", argNames[0]);
+ assertEquals(1, method.getArgAnnotations().length);
+ assertEquals(0, method.getArgAnnotations()[0].size());
+ assertEquals(1, method.getAnnotations().size());
+ assertEquals(1, method.getExceptions().length);
+
+ method = methods.get(2);
+ argTypes = method.getArgTypes();
+ argNames = method.getArgNames();
+ assertEquals("<init>", method.getName());
+ assertEquals(2, argTypes.length);
+ assertEquals("int", argTypes[0].getClassName());
+ assertEquals("java.lang.String", argTypes[1].getClassName());
+ assertEquals(2, argNames.length);
+ assertEquals("a", argNames[0]);
+ assertEquals("b", argNames[1]);
+ assertEquals(2, method.getArgAnnotations().length);
+ assertEquals(0, method.getArgAnnotations()[0].size());
+ assertEquals(0, method.getArgAnnotations()[1].size());
+ assertEquals(0, method.getAnnotations().size());
+ assertEquals(0, method.getExceptions().length);
+ }
+
+ public void testTwoInner() {
+ CollectClassData cd = collect(Two.TwoInner.class);
+ // Don't check for super bit, as it will depend on the JDK used to compile.
+ assertEquals(Opcodes.ACC_PUBLIC , cd.getAccess() & ~Opcodes.ACC_SUPER);
+ assertEquals(ClassType.Inner, cd.getClassType());
+ }
+
+ private CollectClassData collect(Class<?> clazz) {
+ return collect(clazz.getName());
+ }
+
+ private CollectClassData collect(String className) {
+ byte[] bytes = getClassBytes(className);
+ assertNotNull("Couldn't load bytes for " + className, bytes);
+ CollectClassData cv = new CollectClassData(bytes);
+ ClassReader reader = new ClassReader(bytes);
+ reader.accept(cv, 0);
+ return cv;
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/CollectReferencesVisitorTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/CollectReferencesVisitorTest.java
new file mode 100644
index 0000000..0d1df99
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/CollectReferencesVisitorTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.typeinfo.test.TestAnnotation;
+import com.google.gwt.dev.asm.ClassReader;
+import com.google.gwt.dev.util.Name.BinaryName;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for {@link CollectClassData}.
+ */
+public class CollectReferencesVisitorTest extends AsmTestCase {
+
+ /**
+ * This class is empty, but it still has references to itself, its superclass
+ * (Object), and its enclosing class.
+ */
+ public static class Empty {
+ }
+
+ /**
+ * In addition to the visible types, this class has references to itself, its
+ * superclass (Object), and its enclosing class.
+ */
+ public static class Full {
+
+ protected Integer i;
+ protected String s;
+
+ @TestAnnotation(value = "foo", classLiteral = Double.class)
+ public Map<Boolean, String> getMap() {
+ return null;
+ }
+ }
+
+ public void testEmpty() {
+ CollectReferencesVisitor rv = collect(Empty.class);
+ Set<String> referencedTypes = rv.getReferencedTypes();
+ assertDoesNotContainNull(referencedTypes);
+ assertEquals(3, referencedTypes.size());
+ assertContainsInternalName(Object.class, referencedTypes);
+ assertContainsInternalName(CollectReferencesVisitorTest.class,
+ referencedTypes);
+ assertContainsInternalName(Empty.class, referencedTypes);
+ }
+
+ public void testFull() {
+ CollectReferencesVisitor rv = collect(Full.class);
+ Set<String> referencedTypes = rv.getReferencedTypes();
+ assertDoesNotContainNull(referencedTypes);
+ assertEquals(7, referencedTypes.size());
+ assertContainsInternalName(Object.class, referencedTypes);
+ assertContainsInternalName(CollectReferencesVisitorTest.class,
+ referencedTypes);
+ assertContainsInternalName(Full.class, referencedTypes);
+ assertContainsInternalName(Map.class, referencedTypes);
+ assertContainsInternalName(Integer.class, referencedTypes);
+ assertContainsInternalName(String.class, referencedTypes);
+ assertContainsInternalName(Boolean.class, referencedTypes);
+ // We no longer collect references from annotations to allow for
+ // binary-only annotations and nontranslatable things like File mentioned
+ // in the annotation
+ // assertContainsInternalName(Double.class, referencedTypes);
+ // assertContainsInternalName(TestAnnotation.class, referencedTypes);
+ }
+
+ // ASM passes null for Object's superclass, so we make sure we don't
+ // insert null into the set of referenced types.
+ public void testObject() {
+ CollectReferencesVisitor rv = collect(Object.class);
+ Set<String> referencedTypes = rv.getReferencedTypes();
+ assertDoesNotContainNull(referencedTypes);
+ }
+
+ private void assertContainsInternalName(Class<?> clazz, Set<String> set) {
+ String className = BinaryName.toInternalName(clazz.getName());
+ assertTrue("Should contain " + className, set.contains(className));
+ }
+
+ private void assertDoesNotContainNull(Set<String> referencedTypes) {
+ assertFalse(referencedTypes.contains(null));
+ }
+
+ private CollectReferencesVisitor collect(Class<?> clazz) {
+ return collect(clazz.getName());
+ }
+
+ private CollectReferencesVisitor collect(String className) {
+ byte[] bytes = getClassBytes(className);
+ assertNotNull("Couldn't load bytes for " + className, bytes);
+ CollectReferencesVisitor cv = new CollectReferencesVisitor();
+ ClassReader reader = new ClassReader(bytes);
+ reader.accept(cv, 0);
+ return cv;
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
new file mode 100644
index 0000000..1e33e45
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2009 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.asm;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.HookableTypeOracle;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPackage;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.asm.Opcodes;
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.signature.SignatureReader;
+import com.google.gwt.dev.javac.Resolver;
+import com.google.gwt.dev.javac.TypeOracleMediator;
+import com.google.gwt.dev.javac.TypeOracleTestingUtils;
+import com.google.gwt.dev.javac.TypeParameterLookup;
+import com.google.gwt.dev.javac.asm.CollectClassData.ClassType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.TypeVariable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests for {@link ResolveClassSignature} and
+ * {@link ResolveMethodSignature}.
+ */
+public class ResolveGenericsTest extends AsmTestCase {
+
+ public static class FailErrorTreeLogger extends TreeLogger {
+ @Override
+ public TreeLogger branch(com.google.gwt.core.ext.TreeLogger.Type type,
+ String msg, Throwable caught, HelpInfo helpInfo) {
+ if (type == TreeLogger.ERROR) {
+ fail(msg);
+ }
+ return this;
+ }
+
+ @Override
+ public boolean isLoggable(com.google.gwt.core.ext.TreeLogger.Type type) {
+ return true;
+ }
+
+ @Override
+ public void log(com.google.gwt.core.ext.TreeLogger.Type type, String msg,
+ Throwable caught, HelpInfo helpInfo) {
+ if (type == TreeLogger.ERROR) {
+ fail(msg);
+ }
+ }
+ }
+
+ /**
+ * An extension of JMethod which keeps the reflected Method around.
+ */
+ public static class ReflectedMethod extends JMethod {
+ private Method method;
+
+ public ReflectedMethod(JClassType type, String methodName,
+ Map<Class<? extends Annotation>, Annotation> annotations,
+ JTypeParameter[] typeParams, Method method) {
+ super(type, methodName, annotations, typeParams);
+ this.method = method;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+ }
+
+ public static class ResolverMockTypeOracle extends HookableTypeOracle {
+
+ private Map<String, JRealClassType> binaryMapper;
+
+ public Map<String, JRealClassType> getBinaryMapper() {
+ ensureBinaryMapper();
+ return binaryMapper;
+ }
+
+ @Override
+ protected void addNewType(JRealClassType type) {
+ super.addNewType(type);
+ String name = type.getQualifiedBinaryName().replace('.', '/');
+ ensureBinaryMapper();
+ binaryMapper.put(name, type);
+ }
+
+ private void ensureBinaryMapper() {
+ if (binaryMapper == null) {
+ binaryMapper = new HashMap<String, JRealClassType>();
+ }
+ }
+ }
+
+ private static final TreeLogger failTreeLogger = new FailErrorTreeLogger();
+
+ private static final String OUTER_CLASS_SIG
+ = "<H:Lcom/google/gwt/dev/javac/asm/TestHandler;>Ljava/lang/Object;";
+ private static final String OUTER_METHOD_SIG = "(TH;)V";
+
+ private static final String OUTER1_CLASS_SIG
+ = "<V:Ljava/lang/Object;>Lcom/google/gwt/dev/javac/asm/TestOuter0<"
+ + "Lcom/google/gwt/dev/javac/asm/TestHandler1<TV;>;>;";
+ private static final String OUTER1_METHOD_SIG
+ = "(Lcom/google/gwt/dev/javac/asm/TestHandler1<TV;>;)V";
+
+ private static final String OUTER2_CLASS_SIG
+ = "Lcom/google/gwt/dev/javac/asm/TestOuter1<Ljava/lang/String;>;";
+ private static final String OUTER2_METHOD_SIG
+ = "(Lcom/google/gwt/dev/javac/asm/TestHandler1<Ljava/lang/String;>;)V";
+
+ private ResolverMockTypeOracle oracle = new ResolverMockTypeOracle();
+
+ @SuppressWarnings("unused")
+ private JRealClassType testHandler;
+ @SuppressWarnings("unused")
+ private JRealClassType testHandler1;
+
+ private JRealClassType testOuter0;
+ private JRealClassType testOuter1;
+ private JRealClassType testOuter2;
+
+ private ReflectedMethod testOuter0dispatch;
+ private ReflectedMethod testOuter1dispatch;
+ private ReflectedMethod testOuter2dispatch;
+
+ @SuppressWarnings("unused")
+ private JRealClassType testType;
+
+ private Resolver resolver = new Resolver() {
+ public Map<String, JRealClassType> getBinaryMapper() {
+ return oracle.getBinaryMapper();
+ }
+
+ public TypeOracle getTypeOracle() {
+ return oracle;
+ }
+
+ public boolean resolveAnnotations(TreeLogger logger,
+ List<CollectAnnotationData> annotations,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+ return true;
+ }
+
+ public boolean resolveAnnotation(TreeLogger logger,
+ CollectAnnotationData annotVisitor,
+ Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+ return true;
+ }
+
+ public boolean resolveClass(TreeLogger logger, JRealClassType type) {
+ return true;
+ }
+ };
+
+
+ public ResolveGenericsTest() {
+ TypeOracleMediator mediator = new TypeOracleMediator(oracle);
+ TypeOracleTestingUtils.buildStandardTypeOracleWith(mediator,
+ failTreeLogger);
+ createUnresolvedClass(Object.class, null);
+ createUnresolvedClass(String.class, null);
+ testHandler = createUnresolvedClass(TestHandler.class, null);
+ testHandler1 = createUnresolvedClass(TestHandler1.class, null);
+ testOuter0 = createUnresolvedClass(TestOuter0.class, null);
+ testType = createUnresolvedClass(TestOuter0.Type.class, testOuter0);
+ testOuter1 = createUnresolvedClass(TestOuter1.class, null);
+ testOuter2 = createUnresolvedClass(TestOuter2.class, null);
+ testOuter0dispatch = createUnresolvedMethod(testOuter0, TestOuter0.class,
+ "dispatch", TestHandler.class);
+ testOuter1dispatch = createUnresolvedMethod(testOuter1, TestOuter1.class,
+ "dispatch", TestHandler.class);
+ testOuter2dispatch = createUnresolvedMethod(testOuter2, TestOuter2.class,
+ "dispatch", TestHandler.class);
+ }
+
+ public void testOuter0Class() {
+ resolveClassSignature(testOuter0, OUTER_CLASS_SIG);
+ assertNotNull(testOuter0.getSuperclass());
+ // TODO(jat): additional checks?
+ }
+
+ public void testOuter0Method() {
+ resolveMethodSignature(testOuter0dispatch, OUTER_METHOD_SIG);
+ // TODO(jat): meaningful tests besides no errors?
+ }
+
+ public void testOuter1Class() {
+ resolveClassSignature(testOuter1, OUTER1_CLASS_SIG);
+ JClassType superClass = testOuter1.getSuperclass();
+ assertNotNull(superClass);
+ assertNotNull(superClass.isParameterized());
+ // TODO(jat): additional checks?
+ }
+
+ public void testOuter1Method() {
+ resolveMethodSignature(testOuter1dispatch, OUTER1_METHOD_SIG);
+ // TODO(jat): meaningful tests besides no errors?
+ }
+
+ public void testOuter2Class() {
+ resolveClassSignature(testOuter2, OUTER2_CLASS_SIG);
+ JClassType superClass = testOuter2.getSuperclass();
+ assertNotNull(superClass);
+ assertNotNull(superClass.isParameterized());
+ // TODO(jat): additional checks?
+ }
+
+ public void testOuter2Method() {
+ resolveMethodSignature(testOuter2dispatch, OUTER2_METHOD_SIG);
+ // TODO(jat): meaningful tests besides no errors?
+ }
+
+ private JTypeParameter[] createTypeParams(TypeVariable<?>[] typeParams) {
+ int n = typeParams.length;
+ JTypeParameter[] params = new JTypeParameter[n];
+ for (int i = 0; i < n; ++i) {
+ params[i] = new JTypeParameter(typeParams[i].getName(), i);
+ }
+ return params;
+ }
+
+ private JRealClassType createUnresolvedClass(Class<?> clazz,
+ JRealClassType enclosingType) {
+ String pkgName = clazz.getPackage().getName();
+ JPackage pkg = oracle.getOrCreatePackage(pkgName);
+ TypeVariable<?>[] typeParams = clazz.getTypeParameters();
+ JRealClassType type;
+ int n = typeParams.length;
+ String enclosingTypeName = null;
+ if (enclosingType != null) {
+ enclosingTypeName = enclosingType.getName();
+ }
+ if (n == 0) {
+ type = new JRealClassType(oracle, pkg, enclosingTypeName,
+ false, clazz.getSimpleName(), clazz.isInterface());
+ } else {
+ JTypeParameter[] params = createTypeParams(typeParams);
+ type = new JGenericType(oracle, pkg, enclosingTypeName, false,
+ clazz.getSimpleName(), clazz.isInterface(), params);
+ }
+ return type;
+ }
+
+ private ReflectedMethod createUnresolvedMethod(JClassType type, Class<?> clazz,
+ String methodName, Class<?>... paramTypes) {
+ Method method = null;
+ try {
+ method = clazz.getMethod(methodName, paramTypes);
+ } catch (SecurityException e) {
+ fail("Exception " + e + " creating method " + methodName + " on " + clazz);
+ } catch (NoSuchMethodException e) {
+ fail("Exception " + e + " creating method " + methodName + " on " + clazz);
+ }
+ JTypeParameter[] typeParams = createTypeParams(method.getTypeParameters());
+ Map<Class<? extends Annotation>, Annotation> emptyMap
+ = Collections.emptyMap();
+ return new ReflectedMethod(type, methodName, emptyMap, typeParams, method);
+ }
+
+ private void resolveClassSignature(JRealClassType type, String signature) {
+ Map<String, JRealClassType> binaryMapper = oracle.getBinaryMapper();
+ TypeParameterLookup lookup = new TypeParameterLookup();
+ lookup.pushEnclosingScopes(type);
+ ResolveClassSignature classResolver = new ResolveClassSignature(resolver,
+ binaryMapper, failTreeLogger, type, lookup);
+ new SignatureReader(signature).accept(classResolver);
+ classResolver.finish();
+ }
+
+ private void resolveMethodSignature(ReflectedMethod method,
+ String signature) {
+ TypeParameterLookup lookup = new TypeParameterLookup();
+ lookup.pushEnclosingScopes(method.getEnclosingType());
+ lookup.pushScope(method.getTypeParameters());
+ int access = Opcodes.ACC_PUBLIC;
+ String desc = Type.getMethodDescriptor(method.getMethod());
+ CollectMethodData methodData = new CollectMethodData(ClassType.TopLevel,
+ access, method.getName(), desc, signature, null);
+ Class<?>[] paramTypes = method.getMethod().getParameterTypes();
+ int n = paramTypes.length;
+ Type[] argTypes = new Type[n];
+ String[] argNames = new String[n];
+ for (int i = 0; i < n; ++i) {
+ argNames[i] = "arg" + i;
+ argTypes[i] = Type.getType(paramTypes[i]);
+ }
+ ResolveMethodSignature methodResolver = new ResolveMethodSignature(resolver,
+ failTreeLogger, method, lookup, true, methodData, argTypes, argNames);
+ new SignatureReader(signature).accept(methodResolver);
+ methodResolver.finish();
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler.java
new file mode 100644
index 0000000..50e74d3
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler.java
@@ -0,0 +1,19 @@
+/*
+ * 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.asm;
+
+public interface TestHandler {
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler1.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler1.java
new file mode 100644
index 0000000..a927ba7
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestHandler1.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 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.asm;
+
+/**
+ * Test class for generic signature validation.
+ *
+ * @param <V>
+ */
+public class TestHandler1<V> implements TestHandler {
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter0.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter0.java
new file mode 100644
index 0000000..ceeb5cf
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter0.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2009 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.asm;
+
+public abstract class TestOuter0<H extends TestHandler> {
+ public static class Type<H> {
+ public Type() {
+ }
+ }
+
+ public abstract void dispatch(H handler);
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter1.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter1.java
new file mode 100644
index 0000000..51c3119
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter1.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2009 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.asm;
+
+public class TestOuter1<V> extends TestOuter0<TestHandler1<V>> {
+
+ @Override
+ public void dispatch(TestHandler1<V> handler) {
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter2.java b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter2.java
new file mode 100644
index 0000000..37e4fdc
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/TestOuter2.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2009 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.asm;
+
+public class TestOuter2 extends TestOuter1<String> {
+
+ @Override
+ public void dispatch(TestHandler1<String> handler) {
+ }
+}
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index 43fd82a..30efbfb 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -641,16 +641,6 @@
{
StringBuilder code = new StringBuilder();
code.append("import java.io.Serializable;\n");
- code.append("public class AutoSerializable {\n");
- code.append(" interface IFoo extends Serializable {};\n");
- code.append(" IFoo createFoo() { return new IFoo(){};}\n");
- code.append("}\n");
- resources.add(new StaticJavaResource("AutoSerializable", code));
- }
-
- {
- StringBuilder code = new StringBuilder();
- code.append("import java.io.Serializable;\n");
code.append("public class OuterClass {\n");
code.append(" static class StaticNested implements Serializable {};\n");
code.append(" class NonStaticNested implements Serializable {};\n");
@@ -736,12 +726,6 @@
assertFalse(sob.shouldConsiderFieldsForSerialization(notSerializable,
problems));
- // Local types should not qualify for serialization
- JClassType iFoo = to.getType("AutoSerializable.IFoo");
- problems = new ProblemReport();
- assertFalse(sob.shouldConsiderFieldsForSerialization(iFoo.getSubtypes()[0],
- problems));
-
// Static nested types qualify for serialization
JClassType staticNested = to.getType("OuterClass.StaticNested");
problems = new ProblemReport();
@@ -791,9 +775,8 @@
assertTrue(sob.shouldConsiderFieldsForSerialization(enumWithSubclasses,
problems));
- problems = new ProblemReport();
- assertFalse(sob.shouldConsiderFieldsForSerialization(
- enumWithSubclasses.getSubtypes()[0], problems));
+ // There are no longer any enum subclasses in TypeOracle
+ assertEquals(0, enumWithSubclasses.getSubtypes().length);
// Enum that are not default instantiable should qualify
JClassType enumWithNonDefaultCtors = to.getType("EnumWithNonDefaultCtors");