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

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 3e3d489  Tweak type annotation support for native record
3e3d489 is described below

commit 3e3d489a279fc55dbfa70447947185c482d67027
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Oct 17 03:10:14 2021 +0800

    Tweak type annotation support for native record
---
 .../apache/groovy/parser/antlr4/AstBuilder.java    |  5 +--
 .../groovy/classgen/AsmClassGenerator.java         |  3 +-
 .../codehaus/groovy/classgen/ExtendedVerifier.java | 19 ++++++++++
 .../org/codehaus/groovy/classgen/RecordTest.groovy | 40 ++++++++++++----------
 4 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java 
b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 7cb7807..00eb43a 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -1662,11 +1662,12 @@ public class AstBuilder extends 
GroovyParserBaseVisitor<Object> {
             Parameter parameter = parameters[i];
             FormalParameterContext parameterCtx = 
parameter.getNodeMetaData(PARAMETER_CONTEXT);
             ModifierManager parameterModifierManager = 
parameter.getNodeMetaData(PARAMETER_MODIFIER_MANAGER);
-            PropertyNode propertyNode = declareProperty(parameterCtx, 
parameterModifierManager, parameter.getType(),
+            ClassNode originType = parameter.getOriginType();
+            PropertyNode propertyNode = declareProperty(parameterCtx, 
parameterModifierManager, originType,
                     classNode, i, parameter, parameter.getName(), 
parameter.getModifiers(), parameter.getInitialExpression());
             propertyNode.getField().putNodeMetaData(IS_RECORD_GENERATED, 
Boolean.TRUE);
 
-            components.add(new RecordComponentNode(classNode, 
parameter.getName(), parameter.getOriginType(), parameter.getAnnotations()));
+            components.add(new RecordComponentNode(classNode, 
parameter.getName(), originType, parameter.getAnnotations()));
         }
         return components;
     }
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java 
b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index e8f69cb..1ba6a2a 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -403,8 +403,7 @@ public class AsmClassGenerator extends ClassGenerator {
         List<RecordComponentNode> recordComponentNodeList = 
classNode.getRecordComponentNodes();
         if (null == recordComponentNodeList) return;
 
-        for (int i = 0, n = recordComponentNodeList.size(); i < n; i++) {
-            RecordComponentNode recordComponentNode = 
recordComponentNodeList.get(i);
+        for (RecordComponentNode recordComponentNode : 
recordComponentNodeList) {
             final ClassNode type = recordComponentNode.getType();
             RecordComponentVisitor rcv =
                     
classVisitor.visitRecordComponent(recordComponentNode.getName(),
diff --git a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java 
b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
index 00bbf4b..0313c7f 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -29,6 +29,7 @@ import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.PackageNode;
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.RecordComponentNode;
 import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
 import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
@@ -62,6 +63,7 @@ import static 
org.codehaus.groovy.ast.AnnotationNode.LOCAL_VARIABLE_TARGET;
 import static org.codehaus.groovy.ast.AnnotationNode.METHOD_TARGET;
 import static org.codehaus.groovy.ast.AnnotationNode.PACKAGE_TARGET;
 import static org.codehaus.groovy.ast.AnnotationNode.PARAMETER_TARGET;
+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;
@@ -115,12 +117,29 @@ public class ExtendedVerifier extends 
ClassCodeVisitorSupport {
         for (ClassNode anInterface : interfaces) {
             visitTypeAnnotations(anInterface);
         }
+        if (node.isRecord()) {
+            visitRecordComponents(node);
+        }
         node.visitContents(this);
     }
 
+    private void visitRecordComponents(ClassNode node) {
+        for (RecordComponentNode recordComponentNode : 
node.getRecordComponentNodes()) {
+            visitAnnotations(recordComponentNode, RECORD_COMPONENT_TARGET);
+            visitTypeAnnotations(recordComponentNode.getType());
+            extractTypeUseAnnotations(recordComponentNode.getAnnotations(), 
recordComponentNode.getType(), RECORD_COMPONENT_TARGET);
+        }
+    }
+
     @Override
     public void visitField(FieldNode node) {
         visitAnnotations(node, FIELD_TARGET);
+
+        if (!node.isStatic() && this.currentClass.isRecord()) {
+            // record's instance fields are created by compiler and reuse type 
instance of record components.
+            // return here to avoid processing type instance repeatedly.
+            return;
+        }
         visitTypeAnnotations(node.getType());
         extractTypeUseAnnotations(node.getAnnotations(), node.getType(), 
FIELD_TARGET);
     }
diff --git a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy 
b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
index 1d2dd93..a3e2160 100644
--- a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
@@ -71,25 +71,22 @@ class RecordTest {
     void testNativeRecordOnJDK16plus() {
         assumeTrue(isAtLeastJdk('16.0'))
         assertScript '''
-            import java.lang.annotation.Annotation
-            import java.lang.annotation.Documented
-            import java.lang.annotation.ElementType
-            import java.lang.annotation.Retention
-            import java.lang.annotation.RetentionPolicy
-            import java.lang.annotation.Target
+            import java.lang.annotation.*
             import java.lang.reflect.RecordComponent
 
-            @Documented
             @Retention(RetentionPolicy.RUNTIME)
             @Target([ElementType.RECORD_COMPONENT])
             @interface NotNull {}
 
-            @Documented
             @Retention(RetentionPolicy.RUNTIME)
             @Target([ElementType.RECORD_COMPONENT, ElementType.TYPE_USE])
             @interface NotNull2 {}
+            
+            @Retention(RetentionPolicy.RUNTIME)
+            @Target([ElementType.TYPE_USE])
+            @interface NotNull3 {}
 
-            record Person(@NotNull @NotNull2 String name, int age, @NotNull2 
List<String> locations, String[] titles) {}
+            record Person(@NotNull @NotNull2 String name, int age, @NotNull2 
@NotNull3 List<String> locations, String[] titles) {}
 
             RecordComponent[] rcs = Person.class.getRecordComponents()
             assert 4 == rcs.length
@@ -112,8 +109,9 @@ class RecordTest {
             assert 1 == annotations2.length
             assert NotNull2.class == annotations2[0].annotationType()
             def typeAnnotations2 = rcs[2].getAnnotatedType().getAnnotations()
-            assert 1 == typeAnnotations2.length
+            assert 2 == typeAnnotations2.length
             assert NotNull2.class == typeAnnotations2[0].annotationType()
+            assert NotNull3.class == typeAnnotations2[1].annotationType()
 
             assert 'titles' == rcs[3].name && String[].class == rcs[3].type
         '''
@@ -135,17 +133,19 @@ class RecordTest {
                 import java.lang.annotation.*;
                 import java.util.*;
 
-                public record Person(@NotNull @NotNull2 String name, int age, 
@NotNull2 List<String> locations, String[] titles) {}
+                public record Person(@NotNull @NotNull2 String name, int age, 
@NotNull2 @NotNull3 List<String> locations, String[] titles) {}
 
-                @Documented
                 @Retention(RetentionPolicy.RUNTIME)
                 @Target({ElementType.RECORD_COMPONENT})
                 @interface NotNull {}
 
-                @Documented
                 @Retention(RetentionPolicy.RUNTIME)
                 @Target({ElementType.RECORD_COMPONENT, ElementType.TYPE_USE})
                 @interface NotNull2 {}
+                
+                @Retention(RetentionPolicy.RUNTIME)
+                @Target({ElementType.TYPE_USE})
+                @interface NotNull3 {}
             '''
 
             def loader = new GroovyClassLoader(this.class.classLoader)
@@ -156,6 +156,7 @@ class RecordTest {
             Class personClazz = loader.loadClass("Person")
             Class notNullClazz = loader.loadClass("NotNull")
             Class notNull2Clazz = loader.loadClass("NotNull2")
+            Class notNull3Clazz = loader.loadClass("NotNull3")
 
             def rcs = personClazz.recordComponents
             assert rcs.length == 4
@@ -180,26 +181,28 @@ class RecordTest {
             assert annotations2.length == 1
             assert annotations2[0].annotationType() == notNull2Clazz
             def typeAnnotations2 = rcs[2].annotatedType.annotations
-            assert typeAnnotations2.length == 1
+            assert typeAnnotations2.length == 2
             assert typeAnnotations2[0].annotationType() == notNull2Clazz
+            assert typeAnnotations2[1].annotationType() == notNull3Clazz
 
             ClassNode personClassNode = ClassHelper.make(personClazz)
             ClassNode notNullClassNode = ClassHelper.make(notNullClazz)
             ClassNode notNull2ClassNode = ClassHelper.make(notNull2Clazz)
-            doTestNativeRecordClassNode(personClassNode, notNullClassNode, 
notNull2ClassNode)
+            ClassNode notNull3ClassNode = ClassHelper.make(notNull3Clazz)
+            doTestNativeRecordClassNode(personClassNode, notNullClassNode, 
notNull2ClassNode, notNull3ClassNode)
 
             def resource = 
loader.getResource(personClazz.getName().replace('.', '/') + '.class')
             def stub = AsmDecompiler.parseClass(resource)
             def unit = new CompilationUnit(loader)
             def personDecompiledClassNode = new DecompiledClassNode(stub, new 
AsmReferenceResolver(new ClassNodeResolver(), unit))
-            doTestNativeRecordClassNode(personDecompiledClassNode, 
notNullClassNode, notNull2ClassNode)
+            doTestNativeRecordClassNode(personDecompiledClassNode, 
notNullClassNode, notNull2ClassNode, notNull3ClassNode)
         } finally {
             parentDir.deleteDir()
             config.targetDirectory.deleteDir()
         }
     }
 
-    private static void doTestNativeRecordClassNode(ClassNode personClassNode, 
ClassNode notNullClassNode, ClassNode notNull2ClassNode) {
+    private static void doTestNativeRecordClassNode(ClassNode personClassNode, 
ClassNode notNullClassNode, ClassNode notNull2ClassNode, ClassNode 
notNull3ClassNode) {
         assert personClassNode.isRecord()
         def rcns = personClassNode.getRecordComponentNodes()
         assert 4 == rcns.size()
@@ -222,8 +225,9 @@ class RecordTest {
         assert 1 == annotationNodes2.size()
         assert notNull2ClassNode == annotationNodes2[0].getClassNode()
         def typeAnnotationNodes2 = rcns[2].getType().getTypeAnnotations()
-        assert 1 == typeAnnotationNodes2.size()
+        assert 2 == typeAnnotationNodes2.size()
         assert notNull2ClassNode == typeAnnotationNodes2[0].getClassNode()
+        assert notNull3ClassNode == typeAnnotationNodes2[1].getClassNode()
 
         assert 'titles' == rcns[3].name && ClassHelper.make(String[].class) == 
rcns[3].type
     }

Reply via email to