You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/10/10 15:35:54 UTC
[groovy] branch master updated: GROOVY-10279: support type
annotation for native record
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 27232f3 GROOVY-10279: support type annotation for native record
27232f3 is described below
commit 27232f3a946021d02d4a3778b2d2542ccbf504a1
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Oct 10 21:57:09 2021 +0800
GROOVY-10279: support type annotation for native record
---
.../groovy/ast/decompiled/Annotations.java | 13 +++++
.../groovy/ast/decompiled/AsmDecompiler.java | 6 +++
.../ast/decompiled/ClassSignatureParser.java | 4 +-
.../codehaus/groovy/ast/decompiled/ClassStub.java | 25 ++++++++-
.../groovy/classgen/AsmClassGenerator.java | 6 ++-
.../org/codehaus/groovy/vmplugin/v16/Java16.java | 8 ++-
.../org/codehaus/groovy/classgen/RecordTest.groovy | 59 +++++++++++++++++++---
7 files changed, 109 insertions(+), 12 deletions(-)
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 04a3c23..1101017 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java
@@ -109,6 +109,19 @@ class Annotations {
return node;
}
+ static <T extends ClassNode> T addTypeAnnotations(AnnotatedTypeStub stub, T node, AsmReferenceResolver resolver) {
+ List<TypeAnnotationStub> annotations = stub.getTypeAnnotations();
+ if (annotations != null) {
+ for (TypeAnnotationStub annotation : annotations) {
+ AnnotationNode annotationNode = createAnnotationNode(annotation, resolver);
+ if (annotationNode != null) {
+ node.addTypeAnnotation(annotationNode);
+ }
+ }
+ }
+ return node;
+ }
+
private static class DecompiledAnnotationNode extends AnnotationNode {
private final Object initLock;
private volatile boolean lazyInitDone;
diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java b/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java
index 396181c..3a66c08 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java
@@ -27,6 +27,7 @@ import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.RecordComponentVisitor;
import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -204,6 +205,11 @@ public abstract class AsmDecompiler {
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
return readAnnotationMembers(recordComponentStub.addAnnotation(descriptor));
}
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+ return readAnnotationMembers(recordComponentStub.addTypeAnnotation(descriptor));
+ }
};
}
diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java
index f4fc855..c0ad604 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java
@@ -65,7 +65,9 @@ class ClassSignatureParser {
}
});
}
- RecordComponentNode recordComponentNode = new RecordComponentNode(classNode, r.name, typeHolder.getObject());
+ ClassNode cn = typeHolder.getObject();
+ Annotations.addTypeAnnotations(r, cn, resolver);
+ RecordComponentNode recordComponentNode = new RecordComponentNode(classNode, r.name, cn);
Annotations.addAnnotations(r, recordComponentNode, resolver);
return recordComponentNode;
}).collect(Collectors.toList()));
diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java
index d79986a..d11ae22 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java
@@ -53,6 +53,10 @@ interface AnnotatedStub {
List<AnnotationStub> getAnnotations();
}
+interface AnnotatedTypeStub {
+ List<TypeAnnotationStub> getTypeAnnotations();
+}
+
class MemberStub implements AnnotatedStub {
List<AnnotationStub> annotations = null;
@@ -117,6 +121,12 @@ class AnnotationStub {
}
}
+class TypeAnnotationStub extends AnnotationStub {
+ public TypeAnnotationStub(String className) {
+ super(className);
+ }
+}
+
class TypeWrapper {
final String desc;
@@ -135,11 +145,12 @@ class EnumConstantWrapper {
}
}
-class RecordComponentStub implements AnnotatedStub {
+class RecordComponentStub implements AnnotatedStub, AnnotatedTypeStub {
final String name;
final String descriptor;
final String signature;
List<AnnotationStub> annotations;
+ List<TypeAnnotationStub> typeAnnotations;
public RecordComponentStub(String name, String descriptor, String signature) {
this.name = name;
@@ -158,4 +169,16 @@ class RecordComponentStub implements AnnotatedStub {
public List<AnnotationStub> getAnnotations() {
return annotations;
}
+
+ public TypeAnnotationStub addTypeAnnotation(String desc) {
+ TypeAnnotationStub stub = new TypeAnnotationStub(desc);
+ if (typeAnnotations == null) typeAnnotations = new ArrayList<TypeAnnotationStub>(1);
+ typeAnnotations.add(stub);
+ return stub;
+ }
+
+ @Override
+ public List<TypeAnnotationStub> getTypeAnnotations() {
+ return typeAnnotations;
+ }
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 6406e09..e8f69cb 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -412,7 +412,11 @@ public class AsmClassGenerator extends ClassGenerator {
BytecodeHelper.getTypeGenericsSignature(type));
visitAnnotations(recordComponentNode, rcv);
- TypeReference typeRef = newTypeParameterReference(CLASS_TYPE_PARAMETER, i);
+
+ // the int encoded value of the type reference is ALWAYS `318767104`
+ // TODO Get the magic number `318767104` via `TypeReference.newXXX()`
+ TypeReference typeRef = new TypeReference(318767104);
+
visitTypeAnnotations(recordComponentNode.getType(), rcv, typeRef, "", true);
rcv.visitEnd();
}
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 efb5231..c44c9a7 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
@@ -121,7 +121,13 @@ public class Java16 extends Java10 {
configureAnnotation(node, annotation);
return node;
}).collect(Collectors.toList());
- return new RecordComponentNode(classNode, r.getName(), makeClassNode(cu, r.getGenericType(), r.getType()), annotationNodeList);
+ ClassNode type = makeClassNode(cu, r.getGenericType(), r.getType());
+ type.addTypeAnnotations(Arrays.stream(r.getAnnotatedType().getAnnotations()).map(annotation -> {
+ AnnotationNode node = new AnnotationNode(ClassHelper.make(annotation.annotationType()));
+ configureAnnotation(node, annotation);
+ return node;
+ }).collect(Collectors.toList()));
+ return new RecordComponentNode(classNode, r.getName(), type, annotationNodeList);
})
.collect(Collectors.toList());
classNode.setRecordComponentNodes(recordComponentNodeList);
diff --git a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
index 2891d20..c58e904 100644
--- a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
@@ -84,21 +84,36 @@ class RecordTest {
@Target([ElementType.RECORD_COMPONENT])
@interface NotNull {}
- record Person(@NotNull String name, int age, List<String> locations, String[] titles) {}
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target([ElementType.RECORD_COMPONENT, ElementType.TYPE_USE])
+ @interface NotNull2 {}
+
+ record Person(@NotNull @NotNull2 String name, int age, @NotNull2 List<String> locations, String[] titles) {}
RecordComponent[] rcs = Person.class.getRecordComponents()
assert 4 == rcs.length
assert 'name' == rcs[0].name && String.class == rcs[0].type
Annotation[] annotations = rcs[0].getAnnotations()
- assert 1 == annotations.length
+ assert 2 == annotations.length
assert NotNull.class == annotations[0].annotationType()
+ assert NotNull2.class == annotations[1].annotationType()
+ def typeAnnotations = rcs[0].getAnnotatedType().getAnnotations()
+ assert 1 == typeAnnotations.length
+ assert NotNull2.class == typeAnnotations[0].annotationType()
assert 'age' == rcs[1].name && int.class == rcs[1].type
assert 'locations' == rcs[2].name && List.class == rcs[2].type
assert 'Ljava/util/List<Ljava/lang/String;>;' == rcs[2].genericSignature
assert 'java.util.List<java.lang.String>' == rcs[2].genericType.toString()
+ def annotations2 = rcs[2].getAnnotations()
+ assert 1 == annotations2.length
+ assert NotNull2.class == annotations2[0].annotationType()
+ def typeAnnotations2 = rcs[2].getAnnotatedType().getAnnotations()
+ assert 1 == typeAnnotations2.length
+ assert NotNull2.class == typeAnnotations2[0].annotationType()
assert 'titles' == rcs[3].name && String[].class == rcs[3].type
'''
@@ -118,13 +133,19 @@ class RecordTest {
def b = new File(parentDir, 'Person.java')
b.write '''
import java.lang.annotation.*;
+ import java.util.*;
- public record Person(@NotNull String name, int age, java.util.List<String> locations, String[] titles) {}
+ public record Person(@NotNull @NotNull2 String name, int age, @NotNull2 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 {}
'''
def loader = new GroovyClassLoader(this.class.classLoader)
@@ -134,46 +155,62 @@ class RecordTest {
Class personClazz = loader.loadClass("Person")
Class notNullClazz = loader.loadClass("NotNull")
+ Class notNull2Clazz = loader.loadClass("NotNull2")
def rcs = personClazz.getRecordComponents()
assert 4 == rcs.length
assert 'name' == rcs[0].name && String.class == rcs[0].type
def annotations = rcs[0].getAnnotations()
- assert 1 == annotations.length
+ assert 2 == annotations.length
assert notNullClazz == annotations[0].annotationType()
+ assert notNull2Clazz == annotations[1].annotationType()
+ def typeAnnotations = rcs[0].getAnnotatedType().getAnnotations()
+ assert 1 == typeAnnotations.length
+ assert notNull2Clazz == typeAnnotations[0].annotationType()
assert 'age' == rcs[1].name && int.class == rcs[1].type
assert 'locations' == rcs[2].name && List.class == rcs[2].type
assert 'Ljava/util/List<Ljava/lang/String;>;' == rcs[2].genericSignature
assert 'java.util.List<java.lang.String>' == rcs[2].genericType.toString()
+ def annotations2 = rcs[2].getAnnotations()
+ assert 1 == annotations2.length
+ assert notNull2Clazz == annotations2[0].annotationType()
+ def typeAnnotations2 = rcs[2].getAnnotatedType().getAnnotations()
+ assert 1 == typeAnnotations2.length
+ assert notNull2Clazz == typeAnnotations2[0].annotationType()
assert 'titles' == rcs[3].name && String[].class == rcs[3].type
ClassNode personClassNode = ClassHelper.make(personClazz)
ClassNode notNullClassNode = ClassHelper.make(notNullClazz)
- doTestNativeRecordClassNode(personClassNode, notNullClassNode)
+ ClassNode notNull2ClassNode = ClassHelper.make(notNull2Clazz)
+ doTestNativeRecordClassNode(personClassNode, notNullClassNode, notNull2ClassNode)
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)
+ doTestNativeRecordClassNode(personDecompiledClassNode, notNullClassNode, notNull2ClassNode)
} finally {
parentDir.deleteDir()
config.targetDirectory.deleteDir()
}
}
- private static void doTestNativeRecordClassNode(ClassNode personClassNode, ClassNode notNullClassNode) {
+ private static void doTestNativeRecordClassNode(ClassNode personClassNode, ClassNode notNullClassNode, ClassNode notNull2ClassNode) {
assert personClassNode.isRecord()
def rcns = personClassNode.getRecordComponentNodes()
assert 4 == rcns.size()
assert 'name' == rcns[0].name && ClassHelper.STRING_TYPE == rcns[0].type
List<AnnotationNode> annotationNodes = rcns[0].getAnnotations()
- assert 1 == annotationNodes.size()
+ assert 2 == annotationNodes.size()
assert notNullClassNode == annotationNodes[0].getClassNode()
+ assert notNull2ClassNode == annotationNodes[1].getClassNode()
+ def typeAnnotationNodes = rcns[0].getType().getTypeAnnotations()
+ assert 1 == typeAnnotationNodes.size()
+ assert notNull2ClassNode == typeAnnotationNodes[0].getClassNode()
assert 'age' == rcns[1].name && ClassHelper.int_TYPE == rcns[1].type
@@ -181,6 +218,12 @@ class RecordTest {
def genericsTypes = rcns[2].type.genericsTypes
assert 1 == genericsTypes.size()
assert ClassHelper.STRING_TYPE == genericsTypes[0].type
+ def annotationNodes2 = rcns[2].getAnnotations()
+ assert 1 == annotationNodes2.size()
+ assert notNull2ClassNode == annotationNodes2[0].getClassNode()
+ def typeAnnotationNodes2 = rcns[2].getType().getTypeAnnotations()
+ assert 1 == typeAnnotationNodes2.size()
+ assert notNull2ClassNode == typeAnnotationNodes2[0].getClassNode()
assert 'titles' == rcns[3].name && ClassHelper.make(String[].class) == rcns[3].type
}