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
}