You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/11/05 23:40:25 UTC
groovy git commit: GROOVY-7975/GROOVY-3278/GROOVY-7854: improved
accessing of constants for annotation attributes (closes #819)
Repository: groovy
Updated Branches:
refs/heads/master 44b7cdc4e -> 7d2d6319f
GROOVY-7975/GROOVY-3278/GROOVY-7854: improved accessing of constants for annotation attributes (closes #819)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/7d2d6319
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/7d2d6319
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/7d2d6319
Branch: refs/heads/master
Commit: 7d2d6319f694b87fc4e646d3d22679315e1411bf
Parents: 44b7cdc
Author: Paul King <pa...@asert.com.au>
Authored: Sun Nov 4 20:10:43 2018 +1000
Committer: Paul King <pa...@asert.com.au>
Committed: Mon Nov 5 18:37:54 2018 +1000
----------------------------------------------------------------------
.../groovy/ast/tools/ExpressionUtils.java | 278 ++++++++++++++++++-
.../groovy/classgen/AnnotationVisitor.java | 45 +--
.../org/codehaus/groovy/classgen/Verifier.java | 7 +-
.../control/AnnotationConstantsVisitor.java | 6 +-
src/test/gls/annotations/AnnotationTest.groovy | 88 +++++-
src/test/gls/annotations/Base3278.groovy | 7 +-
src/test/gls/annotations/Child2.groovy | 2 +-
src/test/gls/annotations/ConstAnnotation.groovy | 26 ++
src/test/gls/annotations/MyAnnotation.groovy | 25 --
9 files changed, 410 insertions(+), 74 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/main/java/org/apache/groovy/ast/tools/ExpressionUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/groovy/ast/tools/ExpressionUtils.java b/src/main/java/org/apache/groovy/ast/tools/ExpressionUtils.java
index c3d9737..8fab0db 100644
--- a/src/main/java/org/apache/groovy/ast/tools/ExpressionUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/ExpressionUtils.java
@@ -18,20 +18,291 @@
*/
package org.apache.groovy.ast.tools;
+import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.typehandling.NumberMath;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
+import static org.codehaus.groovy.syntax.Types.BITWISE_OR;
+import static org.codehaus.groovy.syntax.Types.BITWISE_XOR;
+import static org.codehaus.groovy.syntax.Types.DIVIDE;
+import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.MINUS;
+import static org.codehaus.groovy.syntax.Types.MULTIPLY;
+import static org.codehaus.groovy.syntax.Types.PLUS;
+import static org.codehaus.groovy.syntax.Types.POWER;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT;
+import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED;
public class ExpressionUtils {
+ private static ArrayList<Integer> handledTypes = new ArrayList<Integer>();
+
private ExpressionUtils() {
}
- // resolve constant-looking expressions statically (do here as gets transformed away later)
+ static {
+ handledTypes.add(PLUS);
+ handledTypes.add(MINUS);
+ handledTypes.add(MULTIPLY);
+ handledTypes.add(DIVIDE);
+ handledTypes.add(LEFT_SHIFT);
+ handledTypes.add(RIGHT_SHIFT);
+ handledTypes.add(RIGHT_SHIFT_UNSIGNED);
+ handledTypes.add(BITWISE_OR);
+ handledTypes.add(BITWISE_AND);
+ handledTypes.add(BITWISE_XOR);
+ handledTypes.add(POWER);
+ }
+
+ /**
+ * Turns expressions of the form ConstantExpression(40) + ConstantExpression(2)
+ * into the simplified ConstantExpression(42) at compile time.
+ *
+ * @param be the binary expression
+ * @param targetType the type of the result
+ * @return the transformed expression or the original if no transformation was performed
+ */
+ public static ConstantExpression transformBinaryConstantExpression(BinaryExpression be, ClassNode targetType) {
+ ClassNode wrapperType = ClassHelper.getWrapper(targetType);
+ if (isTypeOrArrayOfType(targetType, ClassHelper.STRING_TYPE, false)) {
+ if (be.getOperation().getType() == PLUS) {
+ Expression left = transformInlineConstants(be.getLeftExpression(), targetType);
+ Expression right = transformInlineConstants(be.getRightExpression(), targetType);
+ if (left instanceof ConstantExpression && right instanceof ConstantExpression) {
+ return configure(be, new ConstantExpression((String) ((ConstantExpression) left).getValue() +
+ ((ConstantExpression) right).getValue()));
+ }
+ }
+ } else if (isNumberOrArrayOfNumber(wrapperType, false)) {
+ int type = be.getOperation().getType();
+ if (handledTypes.contains(type)) {
+ Expression leftX = transformInlineConstants(be.getLeftExpression(), targetType);
+ Expression rightX = transformInlineConstants(be.getRightExpression(), targetType);
+ if (leftX instanceof ConstantExpression && rightX instanceof ConstantExpression) {
+ Number left = safeNumber((ConstantExpression) leftX);
+ Number right = safeNumber((ConstantExpression) rightX);
+ if (left == null || right == null) return null;
+ Number result = null;
+ switch(type) {
+ case PLUS:
+ result = NumberMath.add(left, right);
+ break;
+ case MINUS:
+ result = NumberMath.subtract(left, right);
+ break;
+ case MULTIPLY:
+ result = NumberMath.multiply(left, right);
+ break;
+ case DIVIDE:
+ result = NumberMath.divide(left, right);
+ break;
+ case LEFT_SHIFT:
+ result = NumberMath.leftShift(left, right);
+ break;
+ case RIGHT_SHIFT:
+ result = NumberMath.rightShift(left, right);
+ break;
+ case RIGHT_SHIFT_UNSIGNED:
+ result = NumberMath.rightShiftUnsigned(left, right);
+ break;
+ case BITWISE_AND:
+ result = NumberMath.and(left, right);
+ break;
+ case BITWISE_OR:
+ result = NumberMath.or(left, right);
+ break;
+ case BITWISE_XOR:
+ result = NumberMath.xor(left, right);
+ break;
+ case POWER:
+ result = DefaultGroovyMethods.power(left, right);
+ break;
+ }
+ if (result != null) {
+ if (ClassHelper.Byte_TYPE.equals(wrapperType)) {
+ return configure(be, new ConstantExpression(result.byteValue(), true));
+ }
+ if (ClassHelper.Short_TYPE.equals(wrapperType)) {
+ return configure(be, new ConstantExpression(result.shortValue(), true));
+ }
+ if (ClassHelper.Long_TYPE.equals(wrapperType)) {
+ return configure(be, new ConstantExpression(result.longValue(), true));
+ }
+ if (ClassHelper.Integer_TYPE.equals(wrapperType) || ClassHelper.Character_TYPE.equals(wrapperType)) {
+ return configure(be, new ConstantExpression(result.intValue(), true));
+ }
+ if (ClassHelper.Float_TYPE.equals(wrapperType)) {
+ return configure(be, new ConstantExpression(result.floatValue(), true));
+ }
+ if (ClassHelper.Double_TYPE.equals(wrapperType)) {
+ return configure(be, new ConstantExpression(result.doubleValue(), true));
+ }
+ return configure(be, new ConstantExpression(result, true));
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Number safeNumber(ConstantExpression constX) {
+ Object value = constX.getValue();
+ if (value instanceof Number) return (Number) value;
+ return null;
+ }
+
+ private static ConstantExpression configure(Expression origX, ConstantExpression newX) {
+ newX.setSourcePosition(origX);
+ return newX;
+ }
+
+ /**
+ * Determine if a type matches another type (or array thereof).
+ *
+ * @param targetType the candidate type
+ * @param type the type we are checking against
+ * @param recurse true if we can have multi-dimension arrays; should be false for annotation member types
+ * @return true if the type equals the targetType or array thereof
+ */
+ public static boolean isTypeOrArrayOfType(ClassNode targetType, ClassNode type, boolean recurse) {
+ if (targetType == null) return false;
+ return type.equals(targetType) ||
+ (targetType.isArray() && recurse
+ ? isTypeOrArrayOfType(targetType.getComponentType(), type, recurse)
+ : type.equals(targetType.getComponentType()));
+ }
+
+ /**
+ * Determine if a type is derived from Number (or array thereof).
+ *
+ * @param targetType the candidate type
+ * @param recurse true if we can have multi-dimension arrays; should be false for annotation member types
+ * @return true if the type equals the targetType or array thereof
+ */
+ public static boolean isNumberOrArrayOfNumber(ClassNode targetType, boolean recurse) {
+ if (targetType == null) return false;
+ return targetType.isDerivedFrom(ClassHelper.Number_TYPE) ||
+ (targetType.isArray() && recurse
+ ? isNumberOrArrayOfNumber(targetType.getComponentType(), recurse)
+ : targetType.isArray() && targetType.getComponentType().isDerivedFrom(ClassHelper.Number_TYPE));
+ }
+
+ /**
+ * Converts simple expressions of constants into pre-evaluated simple constants.
+ * Handles:
+ * <ul>
+ * <li>Property expressions - referencing constants</li>
+ * <li>Simple binary expressions - String concatenation and numeric +, -, /, *</li>
+ * <li>List expressions - list of constants</li>
+ * <li>Variable expressions - referencing constants</li>
+ * </ul>
+ * @param exp the original expression
+ * @param attrType the type that the final constant should be
+ * @return the transformed type or the original if no transformation was possible
+ */
+ public static Expression transformInlineConstants(final Expression exp, final ClassNode attrType) {
+ if (exp instanceof PropertyExpression) {
+ PropertyExpression pe = (PropertyExpression) exp;
+ if (pe.getObjectExpression() instanceof ClassExpression) {
+ ClassExpression ce = (ClassExpression) pe.getObjectExpression();
+ ClassNode type = ce.getType();
+ if (type.isEnum() || !(type.isResolved() || type.isPrimaryClassNode()))
+ return exp;
+
+ if (type.isPrimaryClassNode()) {
+ FieldNode fn = type.redirect().getField(pe.getPropertyAsString());
+ if (fn != null && fn.isStatic() && fn.isFinal()) {
+ Expression ce2 = transformInlineConstants(fn.getInitialValueExpression(), attrType);
+ if (ce2 != null) {
+ return ce2;
+ }
+ }
+ } else {
+ try {
+ Field field = type.redirect().getTypeClass().getField(pe.getPropertyAsString());
+ if (field != null && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) {
+ ConstantExpression ce3 = new ConstantExpression(field.get(null), true);
+ ce3.setSourcePosition(exp);
+ return ce3;
+ }
+ } catch(Exception e) {
+ // ignore, leave property expression in place and we'll report later
+ }
+ }
+ }
+ } else if (exp instanceof BinaryExpression) {
+ ConstantExpression ce = transformBinaryConstantExpression((BinaryExpression) exp, attrType);
+ if (ce != null) {
+ return ce;
+ }
+ } else if (exp instanceof VariableExpression) {
+ VariableExpression ve = (VariableExpression) exp;
+ if (ve.getAccessedVariable() instanceof FieldNode) {
+ FieldNode fn = (FieldNode) ve.getAccessedVariable();
+ if (fn.isStatic() && fn.isFinal()) {
+ Expression ce = transformInlineConstants(fn.getInitialValueExpression(), attrType);
+ if (ce != null) {
+ return ce;
+ }
+ }
+ }
+ } else if (exp instanceof ListExpression) {
+ return transformListOfConstants((ListExpression) exp, attrType);
+ }
+ return exp;
+ }
+
+ /**
+ * Given a list of constants, transform each item in the list.
+ *
+ * @param origList the list to transform
+ * @param attrType the target type
+ * @return the transformed list or the original if nothing was changed
+ */
+ public static Expression transformListOfConstants(ListExpression origList, ClassNode attrType) {
+ ListExpression newList = new ListExpression();
+ boolean changed = false;
+ for (Expression e : origList.getExpressions()) {
+ try {
+ Expression transformed = transformInlineConstants(e, attrType);
+ newList.addExpression(transformed);
+ if (transformed != e) changed = true;
+ } catch(Exception ignored) {
+ newList.addExpression(e);
+ }
+ }
+ if (changed) {
+ newList.setSourcePosition(origList);
+ return newList;
+ }
+ return origList;
+ }
+
+ /**
+ * The attribute values of annotations must be primitive or String constants.
+ * In various places, such constants can be seen during type resolution but won't be
+ * readily accessible in later phases, e.g. they might be embedded into constructor code.
+ * This method transforms constants that would appear in annotations early so they aren't lost.
+ * Subsequent processing determines whether they are valid, this method simply retains
+ * the constant value as a constant expression.
+ *
+ * @param exp the original expression
+ * @return the converted expression
+ */
public static Expression transformInlineConstants(final Expression exp) {
if (exp instanceof PropertyExpression) {
PropertyExpression pe = (PropertyExpression) exp;
@@ -42,6 +313,11 @@ public class ExpressionUtils {
Expression constant = findConstant(ClassNodeUtils.getField(type, pe.getPropertyAsString()));
if (constant != null) return constant;
}
+ } else if (exp instanceof BinaryExpression) {
+ BinaryExpression be = (BinaryExpression) exp;
+ be.setLeftExpression(transformInlineConstants(be.getLeftExpression()));
+ be.setRightExpression(transformInlineConstants(be.getRightExpression()));
+ return be;
} else if (exp instanceof ListExpression) {
ListExpression origList = (ListExpression) exp;
ListExpression newList = new ListExpression();
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/main/java/org/codehaus/groovy/classgen/AnnotationVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/AnnotationVisitor.java b/src/main/java/org/codehaus/groovy/classgen/AnnotationVisitor.java
index 103d5e1..46c471e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AnnotationVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AnnotationVisitor.java
@@ -39,11 +39,11 @@ import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
+import static org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
+
/**
* An Annotation visitor responsible for:
* <ul>
@@ -89,9 +89,9 @@ public class AnnotationVisitor {
Map<String, Expression> attributes = node.getMembers();
for (Map.Entry<String, Expression> entry : attributes.entrySet()) {
String attrName = entry.getKey();
- Expression attrExpr = transformInlineConstants(entry.getValue());
- entry.setValue(attrExpr);
ClassNode attrType = getAttributeType(node, attrName);
+ Expression attrExpr = transformInlineConstants(entry.getValue(), attrType);
+ entry.setValue(attrExpr);
visitExpression(attrName, attrExpr, attrType);
}
VMPluginFactory.getPlugin().configureAnnotation(node);
@@ -132,35 +132,6 @@ public class AnnotationVisitor {
return true;
}
- private Expression transformInlineConstants(Expression exp) {
- if (exp instanceof PropertyExpression) {
- PropertyExpression pe = (PropertyExpression) exp;
- if (pe.getObjectExpression() instanceof ClassExpression) {
- ClassExpression ce = (ClassExpression) pe.getObjectExpression();
- ClassNode type = ce.getType();
- if (type.isEnum() || !type.isResolved())
- return exp;
-
- try {
- Field field = type.redirect().getTypeClass().getField(pe.getPropertyAsString());
- if (field != null && Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) {
- return new ConstantExpression(field.get(null));
- }
- } catch(Exception e) {
- // ignore, leave property expression in place and we'll report later
- }
- }
- } else if (exp instanceof ListExpression) {
- ListExpression le = (ListExpression) exp;
- ListExpression result = new ListExpression();
- for (Expression e : le.getExpressions()) {
- result.addExpression(transformInlineConstants(e));
- }
- return result;
- }
- return exp;
- }
-
private boolean checkIfMandatoryAnnotationValuesPassed(AnnotationNode node) {
boolean ok = true;
Map attributes = node.getMembers();
@@ -250,8 +221,12 @@ public class AnnotationVisitor {
}
private ConstantExpression getConstantExpression(Expression exp, ClassNode attrType) {
- if (exp instanceof ConstantExpression) {
- return (ConstantExpression) exp;
+ Expression result = exp;
+ if (!(result instanceof ConstantExpression)) {
+ result = transformInlineConstants(result, attrType);
+ }
+ if (result instanceof ConstantExpression) {
+ return (ConstantExpression) result;
}
String base = "Expected '" + exp.getText() + "' to be an inline constant of type " + attrType.getName();
if (exp instanceof PropertyExpression) {
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/main/java/org/codehaus/groovy/classgen/Verifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 439eefb..c42fcc8 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -89,6 +89,7 @@ import static java.lang.reflect.Modifier.isPrivate;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
+import static org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
import static org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType;
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
@@ -1142,10 +1143,12 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
// GROOVY-3311: pre-defined constants added by groovy compiler for numbers/characters should be
// initialized first so that code dependent on it does not see their values as empty
Expression initialValueExpression = fieldNode.getInitialValueExpression();
- if (initialValueExpression instanceof ConstantExpression) {
- ConstantExpression cexp = (ConstantExpression) initialValueExpression;
+ Expression transformed = transformInlineConstants(initialValueExpression, fieldNode.getType());
+ if (transformed instanceof ConstantExpression) {
+ ConstantExpression cexp = (ConstantExpression) transformed;
cexp = transformToPrimitiveConstantIfPossible(cexp);
if (fieldNode.isFinal() && ClassHelper.isStaticConstantInitializerType(cexp.getType()) && cexp.getType().equals(fieldNode.getType())) {
+ fieldNode.setInitialValueExpression(transformed);
return; // GROOVY-5150: primitive type constants will be initialized directly
}
staticList.add(0, statement);
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java b/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
index b1610df..a3785f8 100644
--- a/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
@@ -32,10 +32,10 @@ import org.codehaus.groovy.classgen.Verifier;
import java.math.BigDecimal;
+import static org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
+
/**
* Visitor to resolve constants in annotation definitions.
- *
- * @author Paul King
*/
public class AnnotationConstantsVisitor extends ClassCodeVisitorSupport {
private SourceUnit source;
@@ -89,7 +89,7 @@ public class AnnotationConstantsVisitor extends ClassCodeVisitorSupport {
return castee;
}
}
- return val;
+ return transformInlineConstants(val, returnType);
}
private static Expression revertType(Expression val, ClassNode returnWrapperType) {
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/test/gls/annotations/AnnotationTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/gls/annotations/AnnotationTest.groovy b/src/test/gls/annotations/AnnotationTest.groovy
index c3461ed..c79b290 100644
--- a/src/test/gls/annotations/AnnotationTest.groovy
+++ b/src/test/gls/annotations/AnnotationTest.groovy
@@ -755,20 +755,96 @@ class AnnotationTest extends CompilableTestSupport {
'''
}
+ void testVariableExpressionsReferencingConstantsSeenForAnnotationAttributes() {
+ shouldCompile '''
+ class C {
+ public static final String VALUE = 'rawtypes'
+ @SuppressWarnings(VALUE)
+ def method() { }
+ }
+ '''
+ }
+
+ void testSimpleBinaryExpressions() {
+ assertScript '''
+ import java.lang.annotation.*
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface Pattern {
+ String regexp()
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface LimitedDouble {
+ double max() default Double.MAX_VALUE
+ double min() default Double.MIN_VALUE
+ double zero() default (1.0 - 1.0d) * 1L
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface IntegerConst {
+ int kilo() default 0b1 << 10
+ int two() default 64 >> 5
+ int nine() default 0b1100 ^ 0b101
+ int answer() default 42
+ }
+
+ class MyClass {
+ private static interface Constants {
+ static final String CONST = 'foo' + 'bar'
+ }
+
+ @Pattern(regexp=Constants.CONST)
+ String myString
+
+ @LimitedDouble(min=0.0d, max=50.0d * 2)
+ double myDouble
+
+ @IntegerConst(answer=(5 ** 2) * (2 << 1))
+ int myInteger
+ }
+
+ assert MyClass.getDeclaredField('myString').annotations[0].regexp() == 'foobar'
+
+ MyClass.getDeclaredField('myDouble').annotations[0].with {
+ assert max() == 100.0d
+ assert min() == 0.0d
+ assert zero() == 0.0d
+ }
+
+ MyClass.getDeclaredField('myInteger').annotations[0].with {
+ assert kilo() == 1024
+ assert two() == 2
+ assert nine() == 9
+ assert answer() == 100
+ }
+ '''
+ }
+
void testAnnotationAttributeConstantFromPrecompiledGroovyClass() {
// GROOVY-3278
assertScript '''
- @MyAnnotation(groups = 42)
+ @ConstAnnotation(ints = 42)
class Child1 extends Base3278 {}
+
+ class OtherConstants {
+ static final Integer CONST3 = 3278
+ }
- @MyAnnotation(groups = [-1, Base3278.CONST, Integer.MIN_VALUE])
+ @ConstAnnotation(ints = [-1, Base3278.CONST, Base3278.CONST1, Base3278.CONST2, OtherConstants.CONST3, Integer.MIN_VALUE],
+ strings = ['foo', Base3278.CONST4, Base3278.CONST5, Base3278.CONST5 + 'bing'])
class Child3 extends Base3278 {}
- assert new Child1().run() == [42]
- assert new Child2().run() == [2147483647]
- assert new Child3().run() == [-1, 3278, -2147483648]
+ assert new Child1().ints() == [42]
+ assert new Child2().ints() == [2147483647]
+ new Child3().with {
+ assert ints() == [-1, 3278, 2048, 2070, 3278, -2147483648]
+ assert strings() == ['foo', 'foobar', 'foobarbaz', 'foobarbazbing']
+ }
'''
-
}
void testAnnotationWithRepeatableSupportedPrecompiledJava() {
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/test/gls/annotations/Base3278.groovy
----------------------------------------------------------------------
diff --git a/src/test/gls/annotations/Base3278.groovy b/src/test/gls/annotations/Base3278.groovy
index 05da7fa..a3ecd3b 100644
--- a/src/test/gls/annotations/Base3278.groovy
+++ b/src/test/gls/annotations/Base3278.groovy
@@ -18,5 +18,10 @@
*/
class Base3278 {
static final int CONST = 3278
- def run() { getClass().annotations[0].groups() }
+ static final int CONST1 = 1024 * 2
+ static final int CONST2 = CONST1 + 22
+ static final String CONST4 = 'foobar'
+ static final String CONST5 = CONST4 + 'baz'
+ def ints() { getClass().annotations[0].ints() }
+ def strings() { getClass().annotations[0].strings() }
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/test/gls/annotations/Child2.groovy
----------------------------------------------------------------------
diff --git a/src/test/gls/annotations/Child2.groovy b/src/test/gls/annotations/Child2.groovy
index 16fe19a..afa762d 100644
--- a/src/test/gls/annotations/Child2.groovy
+++ b/src/test/gls/annotations/Child2.groovy
@@ -16,5 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
-@MyAnnotation(groups = Integer.MAX_VALUE)
+@ConstAnnotation(ints = Integer.MAX_VALUE)
class Child2 extends Base3278 {}
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/test/gls/annotations/ConstAnnotation.groovy
----------------------------------------------------------------------
diff --git a/src/test/gls/annotations/ConstAnnotation.groovy b/src/test/gls/annotations/ConstAnnotation.groovy
new file mode 100644
index 0000000..e1e780f
--- /dev/null
+++ b/src/test/gls/annotations/ConstAnnotation.groovy
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+import java.lang.annotation.*
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@interface ConstAnnotation {
+ int[] ints() default []
+ String[] strings() default []
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/7d2d6319/src/test/gls/annotations/MyAnnotation.groovy
----------------------------------------------------------------------
diff --git a/src/test/gls/annotations/MyAnnotation.groovy b/src/test/gls/annotations/MyAnnotation.groovy
deleted file mode 100644
index 5a4d0f3..0000000
--- a/src/test/gls/annotations/MyAnnotation.groovy
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-import java.lang.annotation.*
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-@interface MyAnnotation {
- int[] groups() default []
-}