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:58:03 UTC

[groovy] branch GROOVY_3_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_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 6d48f4b262 GROOVY-9541: determine retention policy of annotation container
6d48f4b262 is described below

commit 6d48f4b2623b7be054c8515d48f6067d5ab0348c
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     | 27 +++++++++++-
 2 files changed, 53 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 ad5cb2712d..254892b88b 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -32,7 +32,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.stmt.ReturnStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.control.AnnotationConstantsVisitor;
@@ -42,14 +42,15 @@ 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.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.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;
@@ -191,10 +192,10 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
     }
 
     private void checkForDuplicateAnnotations(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");
@@ -206,29 +207,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 288869e531..e9243e0d05 100644
--- a/src/test/gls/annotations/AnnotationTest.groovy
+++ b/src/test/gls/annotations/AnnotationTest.groovy
@@ -766,6 +766,32 @@ final class AnnotationTest extends CompilableTestSupport {
         '''
     }
 
+    void testAnnotationWithRepeatableSupported2() {
+        for (policy in ['CLASS', 'SOURCE']) {
+            assertScript """
+                import java.lang.annotation.*
+
+                @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
+            """
+        }
+    }
+
     // GROOVY-9452
     void testDuplicationAnnotationOnClassWithParams() {
         def err = shouldFail '''
@@ -788,7 +814,6 @@ final class AnnotationTest extends CompilableTestSupport {
             @As([@A("c")])
             class Foo {}
         '''
-
         assert err =~ /Cannot specify duplicate annotation/
     }