You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/11/20 21:33:17 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-9541: determine retention policy of annotation container
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new acd0bb1cc8 GROOVY-9541: determine retention policy of annotation container
acd0bb1cc8 is described below
commit acd0bb1cc8eb3de9055b499af5ea964d8bcbe470
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Nov 20 14:58:11 2022 -0600
GROOVY-9541: determine retention policy of annotation container
---
.../codehaus/groovy/classgen/ExtendedVerifier.java | 50 ++++++++++++----------
src/test/gls/annotations/AnnotationTest.groovy | 26 ++++++++++-
2 files changed, 52 insertions(+), 24 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
index 1d587c362d..79dad6f107 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -34,7 +34,7 @@ import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
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.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
@@ -45,15 +45,14 @@ import org.codehaus.groovy.control.SourceUnit;
import org.objectweb.asm.Opcodes;
import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
+import static java.util.stream.Collectors.toList;
import static org.codehaus.groovy.ast.AnnotationNode.ANNOTATION_TARGET;
import static org.codehaus.groovy.ast.AnnotationNode.CONSTRUCTOR_TARGET;
import static org.codehaus.groovy.ast.AnnotationNode.FIELD_TARGET;
@@ -65,7 +64,9 @@ import static org.codehaus.groovy.ast.AnnotationNode.RECORD_COMPONENT_TARGET;
import static org.codehaus.groovy.ast.AnnotationNode.TYPE_PARAMETER_TARGET;
import static org.codehaus.groovy.ast.AnnotationNode.TYPE_TARGET;
import static org.codehaus.groovy.ast.AnnotationNode.TYPE_USE_TARGET;
+import static org.codehaus.groovy.ast.ClassHelper.makeCached;
import static org.codehaus.groovy.ast.tools.GeneralUtils.getInterfacesAndSuperInterfaces;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.listX;
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
@@ -345,10 +346,10 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
}
private void processDuplicateAnnotationContainers(AnnotatedNode node, Map<String, List<AnnotationNode>> nonSourceAnnotations) {
- for (Map.Entry<String, List<AnnotationNode>> next : nonSourceAnnotations.entrySet()) {
- if (next.getValue().size() > 1) {
+ for (Map.Entry<String, List<AnnotationNode>> entry : nonSourceAnnotations.entrySet()) {
+ if (entry.getValue().size() > 1) {
ClassNode repeatable = null;
- AnnotationNode repeatee = next.getValue().get(0);
+ AnnotationNode repeatee = entry.getValue().get(0);
for (AnnotationNode anno : repeatee.getClassNode().getAnnotations()) {
if (anno.getClassNode().getName().equals("java.lang.annotation.Repeatable")) {
Expression value = anno.getMember("value");
@@ -360,29 +361,32 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
}
if (repeatable != null) {
if (nonSourceAnnotations.containsKey(repeatable.getName())) {
- addError("Cannot specify duplicate annotation on the same member. Explicit " + repeatable.getName()
- + " found when creating implicit container for " + next.getKey(), node);
+ addError("Cannot specify duplicate annotation on the same member. Explicit " + repeatable.getName() + " found when creating implicit container for " + entry.getKey(), node);
}
AnnotationNode collector = new AnnotationNode(repeatable);
- if (repeatee.hasRuntimeRetention()) {
+ if (repeatee.hasClassRetention()) {
+ collector.setClassRetention(true);
+ } else if (repeatee.hasRuntimeRetention()) {
collector.setRuntimeRetention(true);
- } else if (repeatable.isResolved()) {
- Class<?> repeatableType = repeatable.getTypeClass();
- Retention retention = repeatableType.getAnnotation(Retention.class);
- collector.setRuntimeRetention(retention != null && retention.value().equals(RetentionPolicy.RUNTIME));
- } else {
- for (AnnotationNode annotation : repeatable.getAnnotations()) {
- if (annotation.getClassNode().getName().equals("java.lang.annotation.Retention")) {
- Expression value = annotation.getMember("value"); assert value != null;
- Object retention = evaluateExpression(value, source.getConfiguration());
- collector.setRuntimeRetention(retention != null && retention.toString().equals("RUNTIME"));
- break;
+ } else { // load retention policy from annotation definition
+ List<AnnotationNode> retention = repeatable.getAnnotations(makeCached(Retention.class));
+ if (!retention.isEmpty()) {
+ Object policy;
+ Expression value = retention.get(0).getMember("value");
+ if (value instanceof PropertyExpression) {
+ policy = ((PropertyExpression) value).getPropertyAsString();
+ } else { // NOTE: it is risky to evaluate the expression from repeatable's source this way:
+ policy = evaluateExpression(value, source.getConfiguration(), source.getClassLoader());
+ }
+ if ("CLASS".equals(policy)) {
+ collector.setClassRetention(true);
+ } else if ("RUNTIME".equals(policy)) {
+ collector.setRuntimeRetention(true);
}
}
}
- collector.addMember("value", new ListExpression(next.getValue().stream()
- .map(AnnotationConstantExpression::new).collect(Collectors.toList())));
- node.getAnnotations().removeAll(next.getValue());
+ collector.addMember("value", listX(entry.getValue().stream().map(AnnotationConstantExpression::new).collect(toList())));
+ node.getAnnotations().removeAll(entry.getValue());
node.addAnnotation(collector);
}
}
diff --git a/src/test/gls/annotations/AnnotationTest.groovy b/src/test/gls/annotations/AnnotationTest.groovy
index e10abf4f3b..ed5adac997 100644
--- a/src/test/gls/annotations/AnnotationTest.groovy
+++ b/src/test/gls/annotations/AnnotationTest.groovy
@@ -996,6 +996,31 @@ final class AnnotationTest {
'''
}
+ @Test
+ void testAnnotationWithRepeatableSupported2() {
+ for (policy in ['CLASS', 'SOURCE']) {
+ assertScript shell, """
+ @Retention(RetentionPolicy.$policy)
+ @Repeatable(B)
+ @interface A {
+ String value() default "foo"
+ }
+
+ @Retention(RetentionPolicy.$policy)
+ @interface B {
+ }
+
+ @A @A('bar')
+ class C {
+ }
+
+ // not available at run-time
+ assert C.getAnnotationsByType(A).length == 0
+ assert C.getAnnotationsByType(B).length == 0
+ """
+ }
+ }
+
@Test // GROOVY-9452
void testDuplicationAnnotationOnClassWithParams() {
def err = shouldFail shell, '''
@@ -1016,7 +1041,6 @@ final class AnnotationTest {
@As([@A("c")])
class Foo {}
'''
-
assert err =~ /Cannot specify duplicate annotation/
}