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)
