You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/11/06 20:33:52 UTC

[groovy] branch GROOVY_4_0_X updated: resolve `record` super class

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

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


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new c2e59017b7 resolve `record` super class
c2e59017b7 is described below

commit c2e59017b74a5586266ebfe027b6c70fdf42fc21
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Nov 6 14:05:08 2022 -0600

    resolve `record` super class
---
 .../transform/RecordTypeASTTransformation.java     | 136 ++++++++++-----------
 1 file changed, 68 insertions(+), 68 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java
index 0cefcbc5f2..ddd212d27f 100644
--- a/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java
@@ -30,7 +30,6 @@ import org.apache.groovy.lang.annotation.Incubating;
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.FieldNode;
 import org.codehaus.groovy.ast.GenericsType;
@@ -65,8 +64,14 @@ import java.util.stream.Collectors;
 import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
 import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT;
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.TUPLE_CLASSES;
+import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
 import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
@@ -93,6 +98,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.ternaryX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.runtime.StringGroovyMethods.isAtLeast;
 import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
@@ -108,18 +114,18 @@ import static org.objectweb.asm.Opcodes.IRETURN;
  */
 @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
 public class RecordTypeASTTransformation extends AbstractASTTransformation implements CompilationUnitAware {
-    private CompilationUnit compilationUnit;
+
     private static final ClassNode ARRAYLIST_TYPE = makeWithoutCaching(ArrayList.class, false);
+    private static final ClassNode ILLEGAL_ARGUMENT = makeWithoutCaching(IllegalArgumentException.class);
+    private static final ClassNode LHMAP_TYPE = makeWithoutCaching(LinkedHashMap.class, false);
+    private static final ClassNode NAMED_PARAM_TYPE = make(NamedParam.class);
+    private static final ClassNode RECORD_OPTIONS_TYPE = make(RecordOptions.class);
+
     private static final String COMPONENTS = "components";
     private static final String COPY_WITH = "copyWith";
     private static final String GET_AT = "getAt";
-    private static final ClassNode ILLEGAL_ARGUMENT = makeWithoutCaching(IllegalArgumentException.class);
-    private static final ClassNode LHMAP_TYPE = makeWithoutCaching(LinkedHashMap.class, false);
     private static final String NAMED_ARGS = "namedArgs";
-    private static final ClassNode NAMED_PARAM_TYPE = makeWithoutCaching(NamedParam.class, false);
-    private static final int PUBLIC_FINAL = ACC_PUBLIC | ACC_FINAL;
     private static final String RECORD_CLASS_NAME = "java.lang.Record";
-    private static final ClassNode RECORD_OPTIONS_TYPE = make(RecordOptions.class);
     private static final String SIZE = "size";
     private static final String TO_LIST = "toList";
     private static final String TO_MAP = "toMap";
@@ -128,51 +134,71 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
     public static final ClassNode MY_TYPE = makeWithoutCaching(MY_CLASS, false);
     private static final String MY_TYPE_NAME = MY_TYPE.getNameWithoutPackage();
 
+    private CompilationUnit compilationUnit;
+
     @Override
     public String getAnnotationName() {
         return MY_TYPE_NAME;
     }
 
     @Override
-    public void visit(ASTNode[] nodes, SourceUnit source) {
+    public void setCompilationUnit(final CompilationUnit unit) {
+        this.compilationUnit = unit;
+    }
+
+    protected GroovyClassLoader getTransformLoader() {
+        return compilationUnit != null ? compilationUnit.getTransformLoader() : sourceUnit.getClassLoader();
+    }
+
+    /**
+     * Indicates that the given classnode is a native JVM record class.
+     * For classes being compiled, this will only be valid after the
+     * {@code RecordTypeASTTransformation} transform has been invoked.
+     */
+    @Incubating
+    public static boolean recordNative(final ClassNode node) {
+        return node.getUnresolvedSuperClass() != null && RECORD_CLASS_NAME.equals(node.getUnresolvedSuperClass().getName());
+    }
+
+    @Override
+    public void visit(final ASTNode[] nodes, final SourceUnit source) {
         init(nodes, source);
         AnnotatedNode parent = (AnnotatedNode) nodes[1];
         AnnotationNode anno = (AnnotationNode) nodes[0];
-        if (!MY_TYPE.equals(anno.getClassNode())) return;
-
-        if (parent instanceof ClassNode) {
-            final GroovyClassLoader classLoader = compilationUnit != null ? compilationUnit.getTransformLoader() : source.getClassLoader();
-            final PropertyHandler handler = PropertyHandler.createPropertyHandler(this, classLoader, (ClassNode) parent);
-            if (handler == null) return;
-            if (!handler.validateAttributes(this, anno)) return;
-            doProcessRecordType((ClassNode) parent, handler);
+        if (parent instanceof ClassNode && MY_TYPE.equals(anno.getClassNode())) {
+            PropertyHandler handler = PropertyHandler.createPropertyHandler(this, getTransformLoader(), (ClassNode) parent);
+            if (handler != null && handler.validateAttributes(this, anno)) {
+                doProcessRecordType((ClassNode) parent, handler);
+            }
         }
     }
 
-    private void doProcessRecordType(ClassNode cNode, PropertyHandler handler) {
+    //--------------------------------------------------------------------------
+
+    private void doProcessRecordType(final ClassNode cNode, final PropertyHandler handler) {
         if (cNode.getNodeMetaData("_RECORD_HEADER") != null) {
             cNode.putNodeMetaData("_SKIPPABLE_ANNOTATIONS", Boolean.TRUE);
         }
         List<AnnotationNode> annotations = cNode.getAnnotations(RECORD_OPTIONS_TYPE);
         AnnotationNode options = annotations.isEmpty() ? null : annotations.get(0);
         RecordTypeMode mode = getMode(options, "mode");
-        boolean isPostJDK16 = false;
+        boolean isAtLeastJDK16 = false;
         String message = "Expecting JDK16+ but unable to determine target bytecode";
         if (sourceUnit != null) {
             CompilerConfiguration config = sourceUnit.getConfiguration();
             String targetBytecode = config.getTargetBytecode();
-            isPostJDK16 = CompilerConfiguration.isPostJDK16(targetBytecode);
+            isAtLeastJDK16 = isAtLeast(targetBytecode, CompilerConfiguration.JDK16);
             message = "Expecting JDK16+ but found " + targetBytecode;
         }
-        boolean isNative = isPostJDK16 && mode != RecordTypeMode.EMULATE;
+        boolean isNative = (isAtLeastJDK16 && mode != RecordTypeMode.EMULATE);
         if (isNative) {
             String sName = cNode.getUnresolvedSuperClass().getName();
             // don't expect any parent to be set at this point but we only check at grammar
             // level when using the record keyword so do a few more sanity checks here
-            if (!sName.equals("java.lang.Object") && !sName.equals(RECORD_CLASS_NAME)) {
+            if (!sName.equals(OBJECT) && !sName.equals(RECORD_CLASS_NAME)) {
                 addError("Invalid superclass for native record found: " + sName, cNode);
             }
-            cNode.setSuperClass(ClassHelper.makeWithoutCaching(RECORD_CLASS_NAME));
+            cNode.setSuperClass(compilationUnit.getClassNodeResolver().resolveName(RECORD_CLASS_NAME, compilationUnit).getClassNode());
             cNode.setModifiers(cNode.getModifiers() | Opcodes.ACC_RECORD);
             final List<PropertyNode> pList = getInstanceProperties(cNode);
             if (!pList.isEmpty()) {
@@ -207,7 +233,7 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
         }
         // 0L serialVersionUID by default
         if (cNode.getDeclaredField("serialVersionUID") == null) {
-            cNode.addField("serialVersionUID", ACC_PRIVATE | ACC_STATIC | ACC_FINAL, ClassHelper.long_TYPE, constX(0L));
+            cNode.addField("serialVersionUID", ACC_PRIVATE | ACC_STATIC | ACC_FINAL, long_TYPE, constX(0L));
         }
 
         if (!hasAnnotation(cNode, ToStringASTTransformation.MY_TYPE)) {
@@ -260,30 +286,18 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
         }
 
         if ((options == null || !memberHasValue(options, SIZE, Boolean.FALSE)) && !hasDeclaredMethod(cNode, SIZE, 0)) {
-            addGeneratedMethod(cNode, SIZE, PUBLIC_FINAL, int_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, returnS(constX(pList.size())));
+            addGeneratedMethod(cNode, SIZE, ACC_PUBLIC | ACC_FINAL, int_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, returnS(constX(pList.size())));
         }
     }
 
-    /**
-     * Indicates that the given classnode is a native JVM record class.
-     * For classes being compiled, this will only be valid after the
-     * {@code RecordTypeASTTransformation} transform has been invoked.
-     */
-    @Incubating
-    public static boolean recordNative(ClassNode node) {
-        return node.getUnresolvedSuperClass() != null &&
-                "java.lang.Record".equals(node.getUnresolvedSuperClass().getName());
-    }
-
     private void createComponents(ClassNode cNode, List<PropertyNode> pList) {
         if (pList.size() > 16) { // Groovy currently only goes to Tuple16
             addError("Record has too many components for a components() method", cNode);
         }
-        ClassNode tupleClass = getClass(cNode, "groovy.lang.Tuple" + pList.size());
-        if (tupleClass == null) return;
+        ClassNode tuple = makeWithoutCaching(TUPLE_CLASSES[pList.size()], false);
         Statement body;
         if (pList.isEmpty()) {
-            body = returnS(propX(classX(tupleClass), "INSTANCE"));
+            body = returnS(propX(classX(tuple), "INSTANCE"));
         } else {
             List<GenericsType> gtypes = new ArrayList<>();
             ArgumentListExpression args = new ArgumentListExpression();
@@ -291,19 +305,10 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
                 args.addExpression(callThisX(pNode.getName()));
                 gtypes.add(new GenericsType(getWrapper(pNode.getType())));
             }
-            tupleClass.setGenericsTypes(gtypes.toArray(GenericsType.EMPTY_ARRAY));
-            body = returnS(ctorX(tupleClass, args));
-        }
-        addGeneratedMethod(cNode, COMPONENTS, PUBLIC_FINAL, tupleClass, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
-    }
-
-    private ClassNode getClass(ClassNode cNode, String tupleName) {
-        try {
-            return ClassHelper.makeWithoutCaching(Class.forName(tupleName)).getPlainNodeReference();
-        } catch(ClassNotFoundException cnfe) {
-            addError("Unable to find Tuple class '" + tupleName + "'", cNode);
-            return null;
+            tuple.setGenericsTypes(gtypes.toArray(GenericsType.EMPTY_ARRAY));
+            body = returnS(ctorX(tuple, args));
         }
+        addGeneratedMethod(cNode, COMPONENTS, ACC_PUBLIC | ACC_FINAL, tuple, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createToList(ClassNode cNode, List<PropertyNode> pList) {
@@ -312,7 +317,7 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
             args.add(callThisX(pNode.getName()));
         }
         Statement body = returnS(ctorX(ARRAYLIST_TYPE.getPlainNodeReference(), listX(args)));
-        addGeneratedMethod(cNode, TO_LIST, PUBLIC_FINAL, LIST_TYPE.getPlainNodeReference(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, TO_LIST, ACC_PUBLIC | ACC_FINAL, LIST_TYPE.getPlainNodeReference(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createToMap(ClassNode cNode, List<PropertyNode> pList) {
@@ -322,7 +327,7 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
             entries.add(mapEntryX(name, callThisX(name)));
         }
         Statement body = returnS(ctorX(LHMAP_TYPE.getPlainNodeReference(), args(mapX(entries))));
-        addGeneratedMethod(cNode, TO_MAP, PUBLIC_FINAL, MAP_TYPE.getPlainNodeReference(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, TO_MAP, ACC_PUBLIC | ACC_FINAL, MAP_TYPE.getPlainNodeReference(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createGetAt(ClassNode cNode, List<PropertyNode> pList) {
@@ -331,7 +336,7 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
         for (int j = 0; j < pList.size(); j++) {
             body.addCase(caseS(constX(j), returnS(callThisX(pList.get(j).getName()))));
         }
-        addGeneratedMethod(cNode, GET_AT, PUBLIC_FINAL, ClassHelper.OBJECT_TYPE.getPlainNodeReference(), params(param(int_TYPE, "i")), ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, GET_AT, ACC_PUBLIC | ACC_FINAL, OBJECT_TYPE, params(param(int_TYPE, "i")), ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createCopyWith(ClassNode cNode, List<PropertyNode> pList) {
@@ -352,12 +357,12 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
             mapParam.addAnnotation(namedParam);
         }
         Statement body = returnS(ctorX(cNode.getPlainNodeReference(), args));
-        addGeneratedMethod(cNode, COPY_WITH, PUBLIC_FINAL, cNode.getPlainNodeReference(), params(mapParam), ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, COPY_WITH, ACC_PUBLIC | ACC_FINAL, cNode.getPlainNodeReference(), params(mapParam), ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createRecordToString(ClassNode cNode) {
-        String desc = BytecodeHelper.getMethodDescriptor(ClassHelper.STRING_TYPE, new ClassNode[]{cNode});
-        Statement body = stmt(bytecodeX(ClassHelper.STRING_TYPE, mv -> {
+        String desc = BytecodeHelper.getMethodDescriptor(STRING_TYPE, new ClassNode[]{cNode});
+        Statement body = stmt(bytecodeX(STRING_TYPE, mv -> {
                     mv.visitVarInsn(ALOAD, 0);
                     mv.visitInvokeDynamicInsn("toString", desc, createBootstrapMethod(), createBootstrapMethodArguments(cNode));
                     mv.visitInsn(ARETURN);
@@ -365,12 +370,12 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
                     mv.visitEnd();
                 })
         );
-        addGeneratedMethod(cNode, "toString", PUBLIC_FINAL, ClassHelper.STRING_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, "toString", ACC_PUBLIC | ACC_FINAL, STRING_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createRecordEquals(ClassNode cNode) {
-        String desc = BytecodeHelper.getMethodDescriptor(ClassHelper.boolean_TYPE, new ClassNode[]{cNode, ClassHelper.OBJECT_TYPE});
-        Statement body = stmt(bytecodeX(ClassHelper.boolean_TYPE, mv -> {
+        String desc = BytecodeHelper.getMethodDescriptor(boolean_TYPE, new ClassNode[]{cNode, OBJECT_TYPE});
+        Statement body = stmt(bytecodeX(boolean_TYPE, mv -> {
                     mv.visitVarInsn(ALOAD, 0);
                     mv.visitVarInsn(ALOAD, 1);
                     mv.visitInvokeDynamicInsn("equals", desc, createBootstrapMethod(), createBootstrapMethodArguments(cNode));
@@ -379,12 +384,12 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
                     mv.visitEnd();
                 })
         );
-        addGeneratedMethod(cNode, "equals", PUBLIC_FINAL, ClassHelper.boolean_TYPE, params(param(ClassHelper.OBJECT_TYPE, "other")), ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, "equals", ACC_PUBLIC | ACC_FINAL, boolean_TYPE, params(param(OBJECT_TYPE, "other")), ClassNode.EMPTY_ARRAY, body);
     }
 
     private void createRecordHashCode(ClassNode cNode) {
-        String desc = BytecodeHelper.getMethodDescriptor(ClassHelper.int_TYPE, new ClassNode[]{cNode});
-        Statement body = stmt(bytecodeX(ClassHelper.int_TYPE, mv -> {
+        String desc = BytecodeHelper.getMethodDescriptor(int_TYPE, new ClassNode[]{cNode});
+        Statement body = stmt(bytecodeX(int_TYPE, mv -> {
                     mv.visitVarInsn(ALOAD, 0);
                     mv.visitInvokeDynamicInsn("hashCode", desc, createBootstrapMethod(), createBootstrapMethodArguments(cNode));
                     mv.visitInsn(IRETURN);
@@ -392,7 +397,7 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
                     mv.visitEnd();
                 })
         );
-        addGeneratedMethod(cNode, "hashCode", PUBLIC_FINAL, ClassHelper.int_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
+        addGeneratedMethod(cNode, "hashCode", ACC_PUBLIC | ACC_FINAL, int_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body);
     }
 
     private Object[] createBootstrapMethodArguments(ClassNode cNode) {
@@ -490,9 +495,4 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
             }
         }
     }
-
-    @Override
-    public void setCompilationUnit(CompilationUnit unit) {
-        this.compilationUnit = unit;
-    }
 }