This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY-6526
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit b8d64b47270825381e4c2076a7187f83470d6db5
Author: Eric Milles <[email protected]>
AuthorDate: Thu Mar 5 10:35:55 2026 -0600

    GROOVY-6526: compute retention policy lazily
---
 .../org/codehaus/groovy/ast/AnnotationNode.java    | 59 +++++++++++-----------
 .../codehaus/groovy/classgen/ExtendedVerifier.java |  6 +--
 .../org/codehaus/groovy/vmplugin/VMPlugin.java     | 14 +++--
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     | 50 +++---------------
 .../codehaus/groovy/ast/AnnotationNodeTest.groovy  | 36 +++++++++++++
 5 files changed, 86 insertions(+), 79 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java 
b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java
index d125fff2d6..d43197be20 100644
--- a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java
@@ -25,6 +25,8 @@ import org.codehaus.groovy.ast.expr.ListExpression;
 import org.codehaus.groovy.ast.expr.PropertyExpression;
 
 import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -52,7 +54,6 @@ public class AnnotationNode extends ASTNode {
 
     private final ClassNode classNode;
     private Map<String, Expression> members;
-    private boolean runtimeRetention = false, sourceRetention = false, 
/*explicit*/ classRetention = false;
 
     public AnnotationNode(final ClassNode type) {
         classNode = requireNonNull(type);
@@ -83,34 +84,16 @@ public class AnnotationNode extends ASTNode {
     public void setAllowedTargets(final int ignored) {
     }
 
-    /**
-     * Sets the internal flag if the current annotation has 
<code>RetentionPolicy.RUNTIME</code>.
-     *
-     * @param value if <tt>true</tt> then current annotation is marked as 
having
-     *     <code>RetentionPolicy.RUNTIME</code>.
-     */
-    public void setRuntimeRetention(final boolean value) {
-        runtimeRetention = value;
+    @Deprecated(since = "6.0.0")
+    public void setClassRetention(final boolean ignored) {
     }
 
-    /**
-     * Sets the internal flag if the current annotation has 
<code>RetentionPolicy.SOURCE</code>.
-     *
-     * @param value if <tt>true</tt> then current annotation is marked as 
having
-     *     <code>RetentionPolicy.SOURCE</code>.
-     */
-    public void setSourceRetention(final boolean value) {
-        sourceRetention = value;
+    @Deprecated(since = "6.0.0")
+    public void setSourceRetention(final boolean ignored) {
     }
 
-    /**
-     * Sets the internal flag if the current annotation has an explicit 
<code>RetentionPolicy.CLASS</code>.
-     *
-     * @param value if <tt>true</tt> then current annotation is marked as 
having
-     *     <code>RetentionPolicy.CLASS</code>.
-     */
-    public void setClassRetention(final boolean value) {
-        classRetention = value;
+    @Deprecated(since = "6.0.0")
+    public void setRuntimeRetention(final boolean ignored) {
     }
 
     
//--------------------------------------------------------------------------
@@ -177,6 +160,24 @@ public class AnnotationNode extends ASTNode {
         return (target & allowedTargets) == target;
     }
 
+    private RetentionPolicy getRetentionPolicy() {
+        if (!(classNode.isPrimaryClassNode() || classNode.isResolved()))
+            throw new IllegalStateException("cannot check retention at this 
time");
+
+        // GROOVY-6526: check class for @Retention
+        return classNode.getNodeMetaData(Retention.class, (k) -> {
+            for (AnnotationNode an : classNode.getAnnotations()) {
+                if 
("java.lang.annotation.Retention".equals(an.getClassNode().getName())) {
+                    if (an.getMember("value") instanceof PropertyExpression 
pe) {
+                        return 
RetentionPolicy.valueOf(pe.getPropertyAsString());
+                    }
+                    break;
+                }
+            }
+            return null;
+        });
+    }
+
     /**
      * Flag corresponding to <code>RetentionPolicy.RUNTIME</code>.
      *
@@ -184,7 +185,7 @@ public class AnnotationNode extends ASTNode {
      *         <tt>false</tt> otherwise
      */
     public boolean hasRuntimeRetention() {
-        return this.runtimeRetention;
+        return RetentionPolicy.RUNTIME.equals(getRetentionPolicy());
     }
 
     /**
@@ -194,7 +195,7 @@ public class AnnotationNode extends ASTNode {
      *         <tt>false</tt> otherwise
      */
     public boolean hasSourceRetention() {
-        return this.sourceRetention;
+        return RetentionPolicy.SOURCE.equals(getRetentionPolicy());
     }
 
     /**
@@ -205,8 +206,8 @@ public class AnnotationNode extends ASTNode {
      *         <tt>false</tt> otherwise
      */
     public boolean hasClassRetention() {
-        if (!runtimeRetention && !sourceRetention) return true;
-        return this.classRetention;
+        RetentionPolicy retentionPolicy = getRetentionPolicy();
+        return retentionPolicy == null || 
retentionPolicy.equals(RetentionPolicy.CLASS);
     }
 
     @Override
diff --git a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java 
b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
index 9649ab87c4..917d08ce67 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -35,7 +35,6 @@ import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
 import org.codehaus.groovy.ast.expr.Expression;
-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;
@@ -43,7 +42,6 @@ import org.codehaus.groovy.control.ErrorCollector;
 import org.codehaus.groovy.control.SourceUnit;
 import org.objectweb.asm.Opcodes;
 
-import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -67,13 +65,11 @@ 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.DEPRECATED_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
-import static org.codehaus.groovy.ast.ClassHelper.makeCached;
 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.createGenericsSpec;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.parameterizeType;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersEqual;
-import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.evaluateExpression;
 
 /**
  * A specialized Groovy AST visitor meant to perform additional verifications 
upon the
@@ -377,6 +373,7 @@ public class ExtendedVerifier extends 
ClassCodeVisitorSupport {
                         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.hasClassRetention()) {
                         collector.setClassRetention(true);
                     } else if (repeatee.hasRuntimeRetention()) {
@@ -398,6 +395,7 @@ public class ExtendedVerifier extends 
ClassCodeVisitorSupport {
                             }
                         }
                     }
+*/
                     collector.addMember("value", 
listX(entry.getValue().stream().map(AnnotationConstantExpression::new).collect(toList())));
                     node.getAnnotations().removeAll(entry.getValue());
                     node.addAnnotation(collector);
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java 
b/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java
index 602b452bf6..17e7e21686 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java
@@ -40,13 +40,19 @@ import java.util.Set;
  * This interface is for internal use only!
  */
 public interface VMPlugin {
-    void setAdditionalClassInformation(ClassNode c);
+
     Class[] getPluginDefaultGroovyMethods();
     Class[] getPluginStaticGroovyMethods();
-    void configureAnnotationNodeFromDefinition(AnnotationNode definition, 
AnnotationNode root);
-    void configureAnnotation(AnnotationNode an);
-    void configureClassNode(CompileUnit compileUnit, ClassNode classNode);
+
+    void setAdditionalClassInformation(ClassNode node);
+    void configureClassNode(CompileUnit unit, ClassNode node);
+
+    void configureAnnotation(AnnotationNode node);
+    default void configureAnnotationNodeFromDefinition(AnnotationNode 
definition, AnnotationNode node) {
+    }
+
     void invalidateCallSites();
+
     /**
      * Returns a handle with bound receiver to invokeSpecial the given method.
      * This method will require at least Java 7, but since the source has to 
compile
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java 
b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index 2e31e7e3bb..826a177b86 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -118,22 +118,6 @@ public class Java8 implements VMPlugin {
         }
     }
 
-    private static void setRetentionPolicy(final RetentionPolicy value, final 
AnnotationNode node) {
-        switch (value) {
-          case RUNTIME:
-            node.setRuntimeRetention(true);
-            break;
-          case SOURCE:
-            node.setSourceRetention(true);
-            break;
-          case CLASS:
-            node.setClassRetention(true);
-            break;
-          default:
-            throw new GroovyBugError("unsupported Retention " + value);
-        }
-    }
-
     
//--------------------------------------------------------------------------
 
     @Override
@@ -269,27 +253,19 @@ public class Java8 implements VMPlugin {
     }
 
     private void configureAnnotation(final AnnotationNode node, final 
Annotation annotation) {
-        Class<?> type = annotation.annotationType();
-        if (type == Retention.class) {
-            Retention r = (Retention) annotation;
-            RetentionPolicy value = r.value();
-            setRetentionPolicy(value, node);
-            node.setMember("value", new PropertyExpression(
-                    new 
ClassExpression(ClassHelper.makeWithoutCaching(RetentionPolicy.class, false)),
-                    value.toString()));
-        } else if (type == Target.class) {
-            Target t = (Target) annotation;
-            ElementType[] elements = t.value();
-            ListExpression elementExprs = new ListExpression();
-            for (ElementType element : elements) {
-                elementExprs.addExpression(new PropertyExpression(
-                        new ClassExpression(ClassHelper.ELEMENT_TYPE_TYPE), 
element.name()));
+        if (annotation instanceof Retention r) {
+            final ClassNode retentionPolicy = 
ClassHelper.makeWithoutCaching(RetentionPolicy.class, false);
+            node.setMember("value", new PropertyExpression(new 
ClassExpression(retentionPolicy), r.value().toString()));
+        } else if (annotation instanceof Target t) {
+            var elementExprs = new ListExpression();
+            for (ElementType elementTypes : t.value()) {
+                elementExprs.addExpression(new PropertyExpression(new 
ClassExpression(ClassHelper.ELEMENT_TYPE_TYPE), elementTypes.name()));
             }
             node.setMember("value", elementExprs);
         } else {
             Method[] declaredMethods;
             try {
-                declaredMethods = type.getDeclaredMethods();
+                declaredMethods = 
annotation.annotationType().getDeclaredMethods();
             } catch (SecurityException se) {
                 declaredMethods = EMPTY_METHOD_ARRAY;
             }
@@ -298,7 +274,6 @@ public class Java8 implements VMPlugin {
                     Object value = declaredMethod.invoke(annotation);
                     Expression valueExpression = 
toAnnotationValueExpression(value);
                     if (valueExpression != null) 
node.setMember(declaredMethod.getName(), valueExpression);
-
                 } catch (IllegalAccessException | InvocationTargetException 
ignore) {
                 }
             }
@@ -341,15 +316,6 @@ public class Java8 implements VMPlugin {
         return null;
     }
 
-    @Override
-    public void configureAnnotationNodeFromDefinition(final AnnotationNode 
definition, final AnnotationNode node) {
-        if 
("java.lang.annotation.Retention".equals(definition.getClassNode().getName())
-                 && definition.getMember("value") instanceof 
PropertyExpression value) {
-            var policy = RetentionPolicy.valueOf(value.getPropertyAsString());
-            setRetentionPolicy(policy, node);
-        }
-    }
-
     @Override
     public void configureClassNode(final CompileUnit compileUnit, final 
ClassNode classNode) {
         try {
diff --git a/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy 
b/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy
index ba2d0f671e..c52c0531d6 100644
--- a/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy
@@ -69,6 +69,42 @@ final class AnnotationNodeTest {
         assertFalse(node.isTargetAllowed(TYPE_PARAMETER_TARGET))
     }
 
+    @Test
+    void testRetentionPolicy1() {
+        def node = new 
AnnotationNode(ClassHelper.make(gls.annotations.HasExplicitClassRetention))
+
+        assertTrue (node.hasClassRetention())
+        assertFalse(node.hasSourceRetention())
+        assertFalse(node.hasRuntimeRetention())
+    }
+
+    @Test
+    void testRetentionPolicy2() {
+        def node = new AnnotationNode(ClassHelper.OVERRIDE_TYPE)
+
+            assertFalse(node.hasClassRetention())
+            assertTrue (node.hasSourceRetention())
+            assertFalse(node.hasRuntimeRetention())
+    }
+
+    @Test
+    void testRetentionPolicy3() {
+        def node = new AnnotationNode(ClassHelper.DEPRECATED_TYPE)
+
+        assertFalse(node.hasClassRetention())
+        assertFalse(node.hasSourceRetention())
+        assertTrue (node.hasRuntimeRetention())
+    }
+
+    @Test
+    void testRetentionPolicy4() {
+        def node = new AnnotationNode(new ClassNode("A", 0x2000, 
ClassHelper.Annotation_TYPE))
+
+        assertTrue (node.hasClassRetention())
+        assertFalse(node.hasSourceRetention())
+        assertFalse(node.hasRuntimeRetention())
+    }
+
     @Test
     void testGetText() {
         def node = new AnnotationNode(ClassHelper.OVERRIDE_TYPE)

Reply via email to