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 88e170b4687eb0545579863091175d9681857ea6 Author: Eric Milles <[email protected]> AuthorDate: Sun Mar 1 14:28:02 2026 -0600 GROOVY-6526: compute allowed targets lazily --- .../org/codehaus/groovy/ast/AnnotationNode.java | 55 ++++++++++++++++++---- .../groovy/ast/decompiled/Annotations.java | 3 +- .../org/codehaus/groovy/vmplugin/v16/Java16.java | 6 --- .../org/codehaus/groovy/vmplugin/v8/Java8.java | 43 ++--------------- .../org/codehaus/groovy/vmplugin/v9/Java9.java | 7 --- .../codehaus/groovy/ast/AnnotationNodeTest.groovy | 47 ++++++++++++++---- 6 files changed, 91 insertions(+), 70 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java index b39906a136..d125fff2d6 100644 --- a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java +++ b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java @@ -22,7 +22,10 @@ import org.codehaus.groovy.GroovyBugError; 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 java.lang.annotation.ElementType; +import java.lang.annotation.Target; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -49,7 +52,6 @@ public class AnnotationNode extends ASTNode { private final ClassNode classNode; private Map<String, Expression> members; - private int allowedTargets = 0x4FF; // GROOVY-11838: JLS 9.6.4.1 private boolean runtimeRetention = false, sourceRetention = false, /*explicit*/ classRetention = false; public AnnotationNode(final ClassNode type) { @@ -77,8 +79,8 @@ public class AnnotationNode extends ASTNode { } } - public void setAllowedTargets(final int bitmap) { - allowedTargets = bitmap; + @Deprecated(since = "6.0.0") + public void setAllowedTargets(final int ignored) { } /** @@ -137,12 +139,48 @@ public class AnnotationNode extends ASTNode { } public boolean isTargetAllowed(final int target) { - return (this.allowedTargets & target) == target; + if (!(classNode.isPrimaryClassNode() || classNode.isResolved())) + throw new IllegalStateException("cannot check target at this time"); + + // GROOVY-6526: check class for @Target + int allowedTargets = classNode.getNodeMetaData(Target.class, (k) -> { + for (AnnotationNode an : classNode.getAnnotations()) { + if ("java.lang.annotation.Target".equals(an.getClassNode().getName()) + && an.getMember("value") instanceof ListExpression list) { + int bits = 0; + for (Expression e : list.getExpressions()) { + if (e instanceof PropertyExpression item) { + String name = item.getPropertyAsString(); + bits |= switch (ElementType.valueOf(name)) { + case TYPE -> TYPE_TARGET; + case FIELD -> FIELD_TARGET; + case METHOD -> METHOD_TARGET; + case PARAMETER -> PARAMETER_TARGET; + case CONSTRUCTOR -> CONSTRUCTOR_TARGET; + case LOCAL_VARIABLE -> LOCAL_VARIABLE_TARGET; + case ANNOTATION_TYPE -> ANNOTATION_TARGET; + case PACKAGE -> PACKAGE_TARGET; + case TYPE_PARAMETER -> TYPE_PARAMETER_TARGET; + case TYPE_USE -> TYPE_USE_TARGET; + case MODULE -> TYPE_TARGET; //TODO + case RECORD_COMPONENT -> RECORD_COMPONENT_TARGET; + default -> throw new GroovyBugError("unsupported Target " + name); + }; + } + } + return bits; + } + } + return 0x4FF; // GROOVY-11838: JLS 9.6.4.1 + }); + + return (target & allowedTargets) == target; } /** * Flag corresponding to <code>RetentionPolicy.RUNTIME</code>. - * @return <tt>true</tt> if the annotation should be visible at runtime, + * + * @return <tt>true</tt> if the annotation should be visible at runtime; * <tt>false</tt> otherwise */ public boolean hasRuntimeRetention() { @@ -151,7 +189,8 @@ public class AnnotationNode extends ASTNode { /** * Flag corresponding to <code>RetentionPolicy.SOURCE</code>. - * @return <tt>true</tt> if the annotation is only allowed in sources + * + * @return <tt>true</tt> if the annotation is only allowed in sources; * <tt>false</tt> otherwise */ public boolean hasSourceRetention() { @@ -160,9 +199,9 @@ public class AnnotationNode extends ASTNode { /** * Flag corresponding to <code>RetentionPolicy.CLASS</code>. - * This is the default when no <code>RetentionPolicy</code> annotations are present. + * This is the default when no <code>Retention</code> annotation is present. * - * @return <tt>true</tt> if the annotation is written in the bytecode, but not visible at runtime + * @return <tt>true</tt> if the annotation is written in the bytecode but not visible at runtime; * <tt>false</tt> otherwise */ public boolean hasClassRetention() { diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java b/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java index dbd671d7e9..713c75c398 100644 --- a/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java @@ -143,8 +143,7 @@ class Annotations { } @Override - public boolean isTargetAllowed(int target) { - lazyInit(); + public boolean isTargetAllowed(final int target) { return super.isTargetAllowed(target); } diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java index f8169a73af..46bbcd5f45 100644 --- a/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java +++ b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java @@ -20,13 +20,11 @@ package org.codehaus.groovy.vmplugin.v16; import groovy.lang.GroovyRuntimeException; import org.apache.groovy.lang.annotation.Incubating; -import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.CompileUnit; import org.codehaus.groovy.ast.RecordComponentNode; import org.codehaus.groovy.vmplugin.v10.Java10; -import java.lang.annotation.ElementType; import java.lang.invoke.MethodHandle; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -37,10 +35,6 @@ import java.util.stream.Collectors; public class Java16 extends Java10 { - { - elementTypeToTarget.put(ElementType.RECORD_COMPONENT, AnnotationNode.RECORD_COMPONENT_TARGET); - } - @Override public int getVersion() { return 16; 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 67860da52a..2e31e7e3bb 100644 --- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java +++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java @@ -20,7 +20,6 @@ package org.codehaus.groovy.vmplugin.v8; import groovy.lang.MetaClass; import groovy.lang.MetaMethod; -import org.apache.groovy.util.Maps; import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; @@ -70,9 +69,7 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.security.Permission; import java.util.Arrays; -import java.util.EnumMap; import java.util.List; -import java.util.Map; /** * Java 8 based functions. @@ -344,44 +341,12 @@ public class Java8 implements VMPlugin { return null; } - // - - protected final Map<ElementType, Integer> elementTypeToTarget = new EnumMap<>(Maps.of( - ElementType.TYPE, AnnotationNode.TYPE_TARGET, - ElementType.FIELD, AnnotationNode.FIELD_TARGET, - ElementType.METHOD, AnnotationNode.METHOD_TARGET, - ElementType.PARAMETER, AnnotationNode.PARAMETER_TARGET, - ElementType.CONSTRUCTOR, AnnotationNode.CONSTRUCTOR_TARGET, - ElementType.LOCAL_VARIABLE, AnnotationNode.LOCAL_VARIABLE_TARGET, - ElementType.ANNOTATION_TYPE, AnnotationNode.ANNOTATION_TARGET, - ElementType.PACKAGE, AnnotationNode.PACKAGE_TARGET, - ElementType.TYPE_PARAMETER, AnnotationNode.TYPE_PARAMETER_TARGET, - ElementType.TYPE_USE, AnnotationNode.TYPE_USE_TARGET - )); - @Override public void configureAnnotationNodeFromDefinition(final AnnotationNode definition, final AnnotationNode node) { - switch (definition.getClassNode().getName()) { - case "java.lang.annotation.Retention": - if (definition.getMember("value") instanceof PropertyExpression value) { - var policy = RetentionPolicy.valueOf(value.getPropertyAsString()); - setRetentionPolicy(policy, node); - } - break; - case "java.lang.annotation.Target": - if (definition.getMember("value") instanceof ListExpression list) { - int targets = 0; - for (Expression e : list.getExpressions()) { - if (e instanceof PropertyExpression item) { - String name = item.getPropertyAsString(); - ElementType type = ElementType.valueOf(name); - Integer target = elementTypeToTarget.get(type); - if (target == null) throw new GroovyBugError("unsupported Target " + type); - targets |= target; - } - } - node.setAllowedTargets(targets); - } + if ("java.lang.annotation.Retention".equals(definition.getClassNode().getName()) + && definition.getMember("value") instanceof PropertyExpression value) { + var policy = RetentionPolicy.valueOf(value.getPropertyAsString()); + setRetentionPolicy(policy, node); } } diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java index 6981ee17a2..2f0af57de4 100644 --- a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java +++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java @@ -56,15 +56,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import static java.lang.annotation.ElementType.MODULE; -import static org.codehaus.groovy.ast.AnnotationNode.TYPE_TARGET; - public class Java9 extends Java8 { - { - elementTypeToTarget.put(MODULE, TYPE_TARGET); // TODO Add MODULE_TARGET? - } - @Override public int getVersion() { return 9; diff --git a/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy b/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy index aa33bcdcc7..ba2d0f671e 100644 --- a/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy +++ b/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy @@ -21,21 +21,52 @@ package org.codehaus.groovy.ast import org.codehaus.groovy.ast.expr.ConstantExpression import org.junit.jupiter.api.Test +import static org.codehaus.groovy.ast.AnnotationNode.* import static org.junit.jupiter.api.Assertions.* final class AnnotationNodeTest { - // GROOVY-11838 + // GROOVY-6526 @Test - void testIsTargetAllowed() { + void testIsTargetAllowed1() { def node = new AnnotationNode(ClassHelper.OVERRIDE_TYPE) - assertTrue(node.isTargetAllowed(AnnotationNode.TYPE_TARGET)) - assertTrue(node.isTargetAllowed(AnnotationNode.FIELD_TARGET)) - assertTrue(node.isTargetAllowed(AnnotationNode.METHOD_TARGET)) + assertTrue(node.isTargetAllowed(METHOD_TARGET)) + + for (target in [TYPE_TARGET, CONSTRUCTOR_TARGET, FIELD_TARGET, PARAMETER_TARGET, + LOCAL_VARIABLE_TARGET, ANNOTATION_TARGET, PACKAGE_TARGET, + TYPE_PARAMETER_TARGET, TYPE_USE_TARGET, RECORD_COMPONENT_TARGET]) { + assertFalse(node.isTargetAllowed(target)) + } + } + + // GROOVY-6526 + @Test + void testIsTargetAllowed2() { + def node = new AnnotationNode(ClassHelper.DEPRECATED_TYPE) + + for (target in [TYPE_TARGET, CONSTRUCTOR_TARGET, METHOD_TARGET, FIELD_TARGET, + LOCAL_VARIABLE_TARGET, PARAMETER_TARGET, PACKAGE_TARGET]) { + assertTrue(node.isTargetAllowed(target)) + } + + for (target in [TYPE_PARAMETER_TARGET, TYPE_USE_TARGET, RECORD_COMPONENT_TARGET]) { + assertFalse(node.isTargetAllowed(target)) + } + } + + // GROOVY-11838 + @Test + void testIsTargetAllowed3() { + def node = new AnnotationNode(new ClassNode("A", 0x2000, ClassHelper.Annotation_TYPE)) - assertFalse(node.isTargetAllowed(AnnotationNode.TYPE_USE_TARGET)) - assertFalse(node.isTargetAllowed(AnnotationNode.TYPE_PARAMETER_TARGET)) + for (target in [TYPE_TARGET, CONSTRUCTOR_TARGET, METHOD_TARGET, FIELD_TARGET, + PARAMETER_TARGET, LOCAL_VARIABLE_TARGET, ANNOTATION_TARGET, + PACKAGE_TARGET, RECORD_COMPONENT_TARGET]) { + assertTrue(node.isTargetAllowed(target)) + } + assertFalse(node.isTargetAllowed(TYPE_USE_TARGET)) + assertFalse(node.isTargetAllowed(TYPE_PARAMETER_TARGET)) } @Test @@ -47,7 +78,7 @@ final class AnnotationNodeTest { @Test void testGetText2() { - def node = new AnnotationNode(ClassHelper.make(Deprecated)) + def node = new AnnotationNode(ClassHelper.DEPRECATED_TYPE) node.addMember('since', new ConstantExpression('1.2.3')) assertEquals('@java.lang.Deprecated(since="1.2.3")', node.getText())
