Updated TypeOracleBuilder to allow initializing annotation values from constant fields such as Long.MAX_VALUE and from constant conditional expressions. I renamed evaluateExpression to evaluateConstantExpression to more closely reflect its purpose. The annotation test cases were updated to check for these new cases. Patch by: mmendez Review by: scottb (TBR) git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1654 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java index 30ace23..25ae687 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java +++ b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
@@ -55,25 +55,21 @@ 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.CharLiteral; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; import org.eclipse.jdt.internal.compiler.ast.Clinit; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral; +import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; -import org.eclipse.jdt.internal.compiler.ast.FloatLiteral; import org.eclipse.jdt.internal.compiler.ast.Initializer; -import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.Javadoc; -import org.eclipse.jdt.internal.compiler.ast.LongLiteral; import org.eclipse.jdt.internal.compiler.ast.MagicLiteral; 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.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.NumberLiteral; -import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; @@ -83,13 +79,13 @@ import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.IGenericType; +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.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; -import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; @@ -261,6 +257,39 @@ tagValues.clear(); } + /** + * 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; + } + + assert false : "Unknown constant type"; + return null; + } + private static String getMethodName(JClassType enclosingType, AbstractMethodDeclaration jmethod) { if (jmethod.isConstructor()) { @@ -270,6 +299,13 @@ } } + /** + * Returns the number associated with a JDT numeric literal. + */ + private static Object getNumericLiteralValue(NumberLiteral expression) { + return getConstantValue(expression.constant); + } + private static boolean isAnnotation(TypeDeclaration typeDecl) { if (typeDecl.kind() == IGenericType.ANNOTATION_TYPE_DECL) { return true; @@ -612,40 +648,7 @@ return oracle; } - private JUpperBound createTypeVariableBounds(TreeLogger logger, - TypeVariableBinding tvBinding) { - TypeBinding jfirstBound = tvBinding.firstBound; - if (jfirstBound == null) { - // No bounds were specified, so we default to Object - JClassType upperBound = (JClassType) resolveType(logger, - tvBinding.superclass); - /* - * Can't test for equality with TypeOracle.getJavaLangObject() since it - * may not be initialized at this point - */ - assert (Object.class.getName().equals(upperBound.getQualifiedSourceName())); - return new JUpperBound(upperBound); - } - - List<JClassType> bounds = new ArrayList<JClassType>(); - JClassType firstBound = (JClassType) resolveType(logger, jfirstBound); - if (firstBound.isClass() != null) { - bounds.add(firstBound); - } - - ReferenceBinding[] jsuperInterfaces = tvBinding.superInterfaces(); - for (ReferenceBinding jsuperInterface : jsuperInterfaces) { - JClassType superInterface = (JClassType) resolveType(logger, - jsuperInterface); - assert (superInterface != null); - assert (superInterface.isInterface() != null); - bounds.add(superInterface); - } - - return new JUpperBound(bounds.toArray(NO_JCLASSES)); - } - - private Object evaluateAnnotationExpression(TreeLogger logger, + private Object createAnnotationInstance(TreeLogger logger, Expression expression) { Annotation annotation = (Annotation) expression; @@ -665,7 +668,7 @@ // Value Expression expressionValue = mvp.value; - Object value = evaluateExpression(logger, expressionValue); + Object value = evaluateConstantExpression(logger, expressionValue); if (value == null) { return null; } @@ -703,11 +706,14 @@ identifierToValue); } - private Object evaluateArrayInitializerExpression(TreeLogger logger, - Expression expression) { - ArrayInitializer arrayInitializer = (ArrayInitializer) expression; + private Object createArrayConstant(TreeLogger logger, + ArrayInitializer arrayInitializer) { Class<?> leafComponentClass = getClassLiteral(logger, arrayInitializer.binding.leafComponentType); + if (leafComponentClass == null) { + return null; + } + int[] dimensions = new int[arrayInitializer.binding.dimensions]; Expression[] initExpressions = arrayInitializer.expressions; @@ -720,7 +726,7 @@ if (initExpressions != null) { for (int i = 0; i < initExpressions.length; ++i) { Expression arrayInitExp = initExpressions[i]; - Object value = evaluateExpression(logger, arrayInitExp); + Object value = evaluateConstantExpression(logger, arrayInitExp); if (value != null) { Array.set(array, i, value); } else { @@ -737,7 +743,41 @@ return null; } - private Object evaluateExpression(TreeLogger logger, Expression expression) { + private JUpperBound createTypeVariableBounds(TreeLogger logger, + TypeVariableBinding tvBinding) { + TypeBinding jfirstBound = tvBinding.firstBound; + if (jfirstBound == null) { + // No bounds were specified, so we default to Object + JClassType upperBound = (JClassType) resolveType(logger, + tvBinding.superclass); + /* + * Can't test for equality with TypeOracle.getJavaLangObject() since it + * may not be initialized at this point + */ + assert (Object.class.getName().equals(upperBound.getQualifiedSourceName())); + return new JUpperBound(upperBound); + } + + List<JClassType> bounds = new ArrayList<JClassType>(); + JClassType firstBound = (JClassType) resolveType(logger, jfirstBound); + if (firstBound.isClass() != null) { + bounds.add(firstBound); + } + + ReferenceBinding[] jsuperInterfaces = tvBinding.superInterfaces(); + for (ReferenceBinding jsuperInterface : jsuperInterfaces) { + JClassType superInterface = (JClassType) resolveType(logger, + jsuperInterface); + assert (superInterface != null); + assert (superInterface.isInterface() != null); + bounds.add(superInterface); + } + + return new JUpperBound(bounds.toArray(NO_JCLASSES)); + } + + private Object evaluateConstantExpression(TreeLogger logger, + Expression expression) { if (expression instanceof MagicLiteral) { if (expression instanceof FalseLiteral) { return Boolean.FALSE; @@ -747,7 +787,7 @@ return Boolean.TRUE; } } else if (expression instanceof NumberLiteral) { - Object value = evaluateNumericExpression(expression); + Object value = getNumericLiteralValue((NumberLiteral) expression); if (value != null) { return value; } @@ -761,67 +801,90 @@ return clazz; } } else if (expression instanceof ArrayInitializer) { - Object value = evaluateArrayInitializerExpression(logger, expression); + Object value = createArrayConstant(logger, (ArrayInitializer) expression); if (value != null) { return value; } - } else if (expression instanceof QualifiedNameReference) { - QualifiedNameReference qualifiedNameRef = (QualifiedNameReference) expression; - Class clazz = getClassLiteral(logger, qualifiedNameRef.actualReceiverType); - assert (clazz.isEnum()); - if (clazz != null) { - FieldBinding fieldBinding = qualifiedNameRef.fieldBinding(); - String enumName = String.valueOf(fieldBinding.name); - return Enum.valueOf(clazz, enumName); + } else if (expression instanceof NameReference) { + /* + * This name reference can only be to something that is a constant value, + * or an enumerated type since annotation values must be constants. + */ + NameReference nameRef = (NameReference) expression; + + if (nameRef.constant != Constant.NotAConstant) { + return getConstantValue(nameRef.constant); + } else { + Class clazz = getClassLiteral(logger, + nameRef.actualReceiverType); + if (clazz.isEnum()) { + String enumName = String.valueOf(nameRef.fieldBinding().name); + return Enum.valueOf(clazz, enumName); + } else { + /* + * If this is not an enumeration, then fall through to failure case + * below. + */ + } } } else if (expression instanceof Annotation) { - Object annotationInstance = evaluateAnnotationExpression(logger, + Object annotationInstance = createAnnotationInstance(logger, expression); if (annotationInstance != null) { return annotationInstance; } + } else if (expression instanceof ConditionalExpression) { + ConditionalExpression condExpression = (ConditionalExpression) expression; + assert (condExpression.constant != Constant.NotAConstant); + + return getConstantValue(condExpression.constant); } assert (false); return null; } - - private Object evaluateNumericExpression(Expression expression) { - if (expression instanceof CharLiteral) { - CharLiteral charLiteral = (CharLiteral) expression; - return Character.valueOf(charLiteral.constant.charValue()); - } else if (expression instanceof DoubleLiteral) { - DoubleLiteral doubleLiteral = (DoubleLiteral) expression; - return Double.valueOf(doubleLiteral.constant.doubleValue()); - } else if (expression instanceof FloatLiteral) { - FloatLiteral floatLiteral = (FloatLiteral) expression; - return Float.valueOf(floatLiteral.constant.floatValue()); - } else if (expression instanceof IntLiteral) { - IntLiteral intLiteral = (IntLiteral) expression; - return Integer.valueOf(intLiteral.constant.intValue()); - } else if (expression instanceof LongLiteral) { - LongLiteral longLiteral = (LongLiteral) expression; - return Long.valueOf(longLiteral.constant.longValue()); + + private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) { + JType type = resolveType(logger, resolvedType); + if (type == null) { + return null; } - return null; + if (type.isPrimitive() != null) { + return getClassLiteralForPrimitive(type.isPrimitive()); + } else { + try { + String className = computeBinaryClassName(type); + Class<?> clazz = Class.forName(className); + return clazz; + } catch (ClassNotFoundException e) { + logger.log(TreeLogger.ERROR, "", e); + return null; + } + } } - private Class<?> getClassLiteral(TreeLogger logger, TypeBinding resolvedType) { - JClassType annotationType = (JClassType) resolveType(logger, resolvedType); - if (annotationType == null) { - return null; + private Class<?> getClassLiteralForPrimitive(JPrimitiveType type) { + if (type == JPrimitiveType.BOOLEAN) { + return boolean.class; + } else if (type == JPrimitiveType.BYTE) { + return byte.class; + } else if (type == JPrimitiveType.CHAR) { + return char.class; + } else if (type == JPrimitiveType.DOUBLE) { + return double.class; + } else if (type == JPrimitiveType.FLOAT) { + return float.class; + } else if (type == JPrimitiveType.INT) { + return int.class; + } else if (type == JPrimitiveType.LONG) { + return long.class; + } else if (type == JPrimitiveType.SHORT) { + return short.class; } - String className = computeBinaryClassName(annotationType); - try { - Class<?> clazz = Class.forName(className); - return clazz; - } catch (ClassNotFoundException e) { - logger.log(TreeLogger.ERROR, "", e); - // TODO(mmendez): how should we deal with this error? - return null; - } + assert (false); + return null; } private CompilationUnitProvider getCup(TypeDeclaration typeDecl) { @@ -835,14 +898,14 @@ return String.valueOf(CharOperation.concatWith(pkgParts, '.')); } - /** + /** * Returns the qualified name of the binding, excluding any type parameter * information. */ 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. + // The real name of a local type is its constant pool name. qualifiedName = CharOperation.charToString(binding.constantPoolName()); qualifiedName = qualifiedName.replace('/', '.'); } else { @@ -942,8 +1005,12 @@ return false; } - java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) evaluateExpression( + java.lang.annotation.Annotation annotation = (java.lang.annotation.Annotation) createAnnotationInstance( logger, jannotation); + if (annotation == null) { + return false; + } + declaredAnnotations.put(clazz, annotation); return (annotation != null) ? true : false; } @@ -1067,7 +1134,7 @@ AnnotationMethodDeclaration annotationMethod = (AnnotationMethodDeclaration) jmethod; Object defaultValue = null; if (annotationMethod.defaultValue != null) { - defaultValue = evaluateExpression(logger, + defaultValue = evaluateConstantExpression(logger, annotationMethod.defaultValue); } method = new JAnnotationMethod(enclosingType, name, declStart, declEnd,
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java index 3db837a..9b86591 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/TypeOracleAnnotationSupportTest.java
@@ -59,6 +59,19 @@ assertEquals(testAnnotationValue, testAnnotation.value()); assertEquals(nestedAnnotationValue, testAnnotation.nestedAnnotation().value()); + + // Tests default values using conditional statements. + assertEquals(realAnnotation.longValue(), testAnnotation.longValue()); + + // Tests default value of array type. + assertEquals(realAnnotation.intArrayValue().length, + testAnnotation.intArrayValue().length); + + // Tests default value which is a field reference. + assertEquals(realAnnotation.stringValue(), testAnnotation.stringValue()); + + // Tests default value that is a class literal. + assertEquals(realAnnotation.classLiteral(), testAnnotation.classLiteral()); } private final TreeLogger logger = TreeLogger.NULL;
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java index 76fa48e..bb88bfc 100644 --- a/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java +++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/test/TestAnnotation.java
@@ -42,5 +42,34 @@ */ NestedAnnotation nestedAnnotation() default @NestedAnnotation("Not assigned"); - int x = 0; + /** + * Used to test initialization using conditional statements. + */ + boolean useMinLong = true; + + /** + * Used to test initialization using SingleNameReferences. + */ + String defaultStringValue = "Hello There"; + + /** + * Tests default values using conditional statements. + */ + long longValue() default useMinLong ? Long.MIN_VALUE : Long.MAX_VALUE; + + /** + * Tests array default values. + */ + int[] intArrayValue() default {1,2,3}; + + /** + * Tests default value initialization via a QualifiedNameReference. + */ + String stringValue() default TestAnnotation.defaultStringValue; + + /** + * Tests default value initialization of class literals. + */ + Class<?> classLiteral() default Object.class; } +