You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2021/09/16 02:47:46 UTC

[groovy] branch master updated: GROOVY-10233: Sealed classes should generate native class information for JDK17+

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

paulk 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 7db88ea  GROOVY-10233: Sealed classes should generate native class information for JDK17+
7db88ea is described below

commit 7db88ea2837e11527f8b2c7d1060a6ef1185a685
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Sep 14 22:24:58 2021 +1000

    GROOVY-10233: Sealed classes should generate native class information for JDK17+
---
 .../groovy/classgen/AsmClassGenerator.java         |  9 ++++
 .../groovy/control/CompilerConfiguration.java      | 60 ++++++++++++++++++++++
 .../core/SealedTypeDeclaration_01x.groovy          |  1 -
 .../groovy/transform/SealedTransformTest.groovy    | 17 ++++++
 4 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 39458bb..aaba8cc 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.classgen;
 
 import groovy.lang.GroovyRuntimeException;
+import groovy.transform.Sealed;
 import org.apache.groovy.ast.tools.ExpressionUtils;
 import org.apache.groovy.io.StringBuilderWriter;
 import org.codehaus.groovy.GroovyBugError;
@@ -110,6 +111,7 @@ import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.TypePath;
 import org.objectweb.asm.TypeReference;
@@ -372,6 +374,12 @@ public class AsmClassGenerator extends ClassGenerator {
             for (Iterator<InnerClassNode> it = classNode.getInnerClasses(); it.hasNext(); ) {
                 makeInnerClassEntry(it.next());
             }
+            if (controller.getBytecodeVersion() >= Opcodes.V17 &&
+                    context.getCompileUnit().getConfig().isSealedNative()) {
+                for (ClassNode sub: classNode.getPermittedSubclasses()) {
+                    classVisitor.visitPermittedSubclass(sub.getName());
+                }
+            }
 
             classVisitor.visitEnd();
         } catch (GroovyRuntimeException e) {
@@ -2065,6 +2073,7 @@ public class AsmClassGenerator extends ClassGenerator {
             // skip built-in properties
             if (an.isBuiltIn()) continue;
             if (an.hasSourceRetention()) continue;
+            if (an.getClassNode().getName().equals(Sealed.class.getName()) && !context.getCompileUnit().getConfig().isSealedAnnotations()) continue;
 
             AnnotationVisitor av = getAnnotationVisitor(targetNode, an, visitor);
             visitAnnotationAttributes(an, av);
diff --git a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
index 4d0b789..e5fe194 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
@@ -385,6 +385,16 @@ public class CompilerConfiguration {
     private boolean previewFeatures;
 
     /**
+     * Whether Sealed class information is natively generated for target bytecode >= 17 (JEP 360/397/409)
+     */
+    private boolean sealedNative;
+
+    /**
+     * Whether Sealed annotations are generated
+     */
+    private boolean sealedAnnotations;
+
+    /**
      * options for joint compilation (null by default == no joint compilation)
      */
     private Map<String, Object> jointCompilationOptions;
@@ -418,6 +428,8 @@ public class CompilerConfiguration {
      *   <tr><td><code>groovy.target.directory</code></td><td>{@link #getTargetDirectory}</td></tr>
      *   <tr><td><code>groovy.parameters</code></td><td>{@link #getParameters()}</td></tr>
      *   <tr><td><code>groovy.preview.features</code></td><td>{@link #isPreviewFeatures}</td></tr>
+     *   <tr><td><code>groovy.sealed.native.disable</code></td><td>{@link #isSealedNative}</td></tr>
+     *   <tr><td><code>groovy.sealed.annotations.disable</code></td><td>{@link #isSealedAnnotations}</td></tr>
      *   <tr><td><code>groovy.default.scriptExtension</code></td><td>{@link #getDefaultScriptExtension}</td></tr>
      * </table>
      * </blockquote>
@@ -441,6 +453,8 @@ public class CompilerConfiguration {
         warningLevel = WarningMessage.LIKELY_ERRORS;
         parameters = getBooleanSafe("groovy.parameters");
         previewFeatures = getBooleanSafe("groovy.preview.features");
+        sealedNative = !getBooleanSafe("groovy.sealed.native.disable");
+        sealedAnnotations = !getBooleanSafe("groovy.sealed.annotations.disable");
         sourceEncoding = getSystemPropertySafe("groovy.source.encoding",
                 getSystemPropertySafe("file.encoding", DEFAULT_SOURCE_ENCODING));
         setTargetDirectorySafe(getSystemPropertySafe("groovy.target.directory"));
@@ -487,6 +501,8 @@ public class CompilerConfiguration {
         setMinimumRecompilationInterval(configuration.getMinimumRecompilationInterval());
         setTargetBytecode(configuration.getTargetBytecode());
         setPreviewFeatures(configuration.isPreviewFeatures());
+        setSealedNative(configuration.isSealedNative());
+        setSealedAnnotations(configuration.isSealedAnnotations());
         setDefaultScriptExtension(configuration.getDefaultScriptExtension());
         setSourceEncoding(configuration.getSourceEncoding());
         Map<String, Object> jointCompilationOptions = configuration.getJointCompilationOptions();
@@ -540,6 +556,8 @@ public class CompilerConfiguration {
      *   <tr><td><code>groovy.target.bytecode</code></td><td>{@link #getTargetBytecode}</td></tr>
      *   <tr><td><code>groovy.parameters</code></td><td>{@link #getParameters()}</td></tr>
      *   <tr><td><code>groovy.preview.features</code></td><td>{@link #isPreviewFeatures}</td></tr>
+     *   <tr><td><code>groovy.sealed.native.disable</code></td><td>{@link #isSealedNative}</td></tr>
+     *   <tr><td><code>groovy.sealed.annotations.disable</code></td><td>{@link #isSealedAnnotations}</td></tr>
      *   <tr><td><code>groovy.classpath</code></td><td>{@link #getClasspath}</td></tr>
      *   <tr><td><code>groovy.output.verbose</code></td><td>{@link #getVerbose}</td></tr>
      *   <tr><td><code>groovy.output.debug</code></td><td>{@link #getDebug}</td></tr>
@@ -646,6 +664,12 @@ public class CompilerConfiguration {
         text = configuration.getProperty("groovy.preview.features");
         if (text != null) setPreviewFeatures(text.equalsIgnoreCase("true"));
 
+        text = configuration.getProperty("groovy.sealed.native.disable");
+        if (text != null) setSealedNative(!text.equalsIgnoreCase("false"));
+
+        text = configuration.getProperty("groovy.sealed.annotations.disable");
+        if (text != null) setSealedAnnotations(!text.equalsIgnoreCase("false"));
+
         text = configuration.getProperty("groovy.classpath");
         if (text != null) setClasspath(text);
 
@@ -974,6 +998,42 @@ public class CompilerConfiguration {
     }
 
     /**
+     * Whether Sealed class information is natively generated (JEP 360/397/409).
+     *
+     * @return sealedNative
+     */
+    public boolean isSealedNative() {
+        return sealedNative;
+    }
+
+    /**
+     * Sets whether Sealed class information is natively generated (JEP 360/397/409).
+     *
+     * @param sealedNative whether to generate permittedSubclass information in the class file for target bytecode >= 17
+     */
+    public void setSealedNative(final boolean sealedNative) {
+        this.sealedNative = sealedNative;
+    }
+
+    /**
+     * Whether Sealed annotations are generated.
+     *
+     * @return sealedAnnotations
+     */
+    public boolean isSealedAnnotations() {
+        return sealedAnnotations;
+    }
+
+    /**
+     * Sets whether Sealed annotations are generated.
+     *
+     * @param sealedAnnotations whether to generate {@code @Sealed} annotations for sealed types
+     */
+    public void setSealedAnnotations(final boolean sealedAnnotations) {
+        this.sealedAnnotations = sealedAnnotations;
+    }
+
+    /**
      * Gets the joint compilation options for this configuration.
      * @return the options
      */
diff --git a/src/test-resources/core/SealedTypeDeclaration_01x.groovy b/src/test-resources/core/SealedTypeDeclaration_01x.groovy
index eec62f4..c5374a0 100644
--- a/src/test-resources/core/SealedTypeDeclaration_01x.groovy
+++ b/src/test-resources/core/SealedTypeDeclaration_01x.groovy
@@ -1,4 +1,3 @@
-import groovy.transform.NonSealed
 import groovy.transform.Sealed
 
 /*
diff --git a/src/test/org/codehaus/groovy/transform/SealedTransformTest.groovy b/src/test/org/codehaus/groovy/transform/SealedTransformTest.groovy
index 19b9584..bdcc8d4 100644
--- a/src/test/org/codehaus/groovy/transform/SealedTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/SealedTransformTest.groovy
@@ -18,6 +18,8 @@
  */
 package org.codehaus.groovy.transform
 
+import groovy.transform.Sealed
+import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.MultipleCompilationErrorsException
 import org.junit.Test
 
@@ -203,4 +205,19 @@ class SealedTransformTest {
             Shape.getAnnotation(Sealed).permittedSubclasses()*.name
         ''') == ['Shape$Triangle', 'Shape$Polygon']
     }
+
+    @Test
+    void testInferredPermittedNestedClassesWithAnnosDisabled() {
+        def config = new CompilerConfiguration(sealedAnnotations: false)
+        def shapeClass = new GroovyShell(config).evaluate('''
+            import groovy.transform.Sealed
+
+            @Sealed class Shape {
+                final class Triangle extends Shape { }
+                final class Polygon extends Shape { }
+            }
+            Shape
+        ''')
+        assert shapeClass.getAnnotation(Sealed) == null
+    }
 }