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

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

commit e103144ad76baac6a576a983b54cd3bc53b3cba9
Author: Paul King <[email protected]>
AuthorDate: Wed Jan 19 15:04:35 2022 +1000

    GROOVY-10434: ClassNode isSealed() refactoring: mark methods @Incubating, 
move sealed detection to an earlier phase and fix redirect issue
---
 src/main/java/groovy/transform/Sealed.java         |  4 +-
 .../java/org/codehaus/groovy/ast/ClassNode.java    |  8 ++-
 .../org/codehaus/groovy/classgen/Verifier.java     | 38 +++-------
 .../SealedCompletionASTTransformation.java         | 81 ++++++++++++++++++++++
 4 files changed, 99 insertions(+), 32 deletions(-)

diff --git a/src/main/java/groovy/transform/Sealed.java 
b/src/main/java/groovy/transform/Sealed.java
index fef0bff..3fa96a1 100644
--- a/src/main/java/groovy/transform/Sealed.java
+++ b/src/main/java/groovy/transform/Sealed.java
@@ -35,7 +35,9 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.TYPE})
 @Incubating
-@GroovyASTTransformationClass("org.codehaus.groovy.transform.SealedASTTransformation")
+@GroovyASTTransformationClass({
+        "org.codehaus.groovy.transform.SealedASTTransformation",
+        "org.codehaus.groovy.transform.SealedCompletionASTTransformation"})
 public @interface Sealed {
     /**
      * List of the permitted subclasses.
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java 
b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 81fa7dd..0874d61 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.ast;
 
 import org.apache.groovy.ast.tools.ClassNodeUtils;
+import org.apache.groovy.lang.annotation.Incubating;
 import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.ast.expr.BinaryExpression;
 import org.codehaus.groovy.ast.expr.Expression;
@@ -388,8 +389,9 @@ public class ClassNode extends AnnotatedNode {
     }
 
     /**
-     * @return permitted subclasses of sealed type
+     * @return permitted subclasses of sealed type, may initially be empty in 
early compiler phases
      */
+    @Incubating
     public List<ClassNode> getPermittedSubclasses() {
         if (redirect != null)
             return redirect.getPermittedSubclasses();
@@ -397,6 +399,7 @@ public class ClassNode extends AnnotatedNode {
         return permittedSubclasses;
     }
 
+    @Incubating
     public void setPermittedSubclasses(List<ClassNode> permittedSubclasses) {
         if (redirect != null) {
             redirect.setPermittedSubclasses(permittedSubclasses);
@@ -1420,7 +1423,10 @@ public class ClassNode extends AnnotatedNode {
     /**
      * @return true for native and emulated (annotation based) sealed classes
      */
+    @Incubating
     public boolean isSealed() {
+        if (redirect != null) return redirect.isSealed();
+        lazyClassInit();
         return !getAnnotations(SEALED_TYPE).isEmpty() || 
!getPermittedSubclasses().isEmpty();
     }
 
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java 
b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 1a99ca4..81314b2 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -56,7 +56,6 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.FieldExpression;
-import org.codehaus.groovy.ast.expr.ListExpression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
@@ -103,7 +102,6 @@ import static 
org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
 import static org.apache.groovy.ast.tools.MethodNodeUtils.getPropertyName;
 import static 
org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType;
 import static org.codehaus.groovy.ast.AnnotationNode.METHOD_TARGET;
-import static org.codehaus.groovy.ast.ClassHelper.SEALED_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.isObjectType;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean;
 import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveDouble;
@@ -113,7 +111,6 @@ import static 
org.codehaus.groovy.ast.tools.GeneralUtils.block;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.bytecodeX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
@@ -165,9 +162,14 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
     // NOTE: timeStamp constants shouldn't belong to Verifier but kept here 
for binary compatibility
     public static final String __TIMESTAMP = "__timeStamp";
     public static final String __TIMESTAMP__ = "__timeStamp__239_neverHappen";
+    @Deprecated
     public static final Class<Sealed> SEALED_CLASS = Sealed.class;
+    @Deprecated
     public static final Class<NonSealed> NON_SEALED_CLASS = NonSealed.class;
 
+    private static final Class<Sealed> SEALED_TYPE = Sealed.class;
+    private static final Class<NonSealed> NON_SEALED_TYPE = NonSealed.class;
+
     private ClassNode classNode;
     private MethodNode methodNode;
 
@@ -243,7 +245,6 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
             if (classNode.getNodeMetaData(ClassNodeSkip.class) == null) {
                 classNode.setNodeMetaData(ClassNodeSkip.class, true);
             }
-            addDetectedSealedClasses(node);
             return;
         }
 
@@ -275,8 +276,6 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
         checkForDuplicateMethods(node);
         addCovariantMethods(node);
         detectNonSealedClasses(node);
-        addDetectedSealedClasses(node);
-
         checkFinalVariables(node);
     }
 
@@ -292,8 +291,8 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
 
     private void detectNonSealedClasses(ClassNode node) {
         if (isFinal(node.getModifiers())) return;
-        if (Boolean.TRUE.equals(node.getNodeMetaData(SEALED_CLASS))) return;
-        if (Boolean.TRUE.equals(node.getNodeMetaData(NON_SEALED_CLASS))) 
return;
+        if (Boolean.TRUE.equals(node.getNodeMetaData(SEALED_TYPE))) return;
+        if (Boolean.TRUE.equals(node.getNodeMetaData(NON_SEALED_TYPE))) return;
         ClassNode sn = node.getSuperClass();
         boolean found = false;
         while (sn != null && !sn.equals(ClassHelper.OBJECT_TYPE)) {
@@ -304,31 +303,10 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
             sn = sn.getSuperClass();
         }
         if (found) {
-            node.putNodeMetaData(NON_SEALED_CLASS, Boolean.TRUE);
+            node.putNodeMetaData(NON_SEALED_TYPE, Boolean.TRUE);
         }
     }
 
-    private void addDetectedSealedClasses(ClassNode node) {
-        boolean sealed = 
Boolean.TRUE.equals(node.getNodeMetaData(SEALED_CLASS));
-        List<ClassNode> permitted = node.getPermittedSubclasses();
-        if (!sealed || !permitted.isEmpty() || node.getModule() == null) 
return;
-        for (ClassNode possibleSubclass : node.getModule().getClasses()) {
-            if (possibleSubclass.getSuperClass().equals(node)) {
-                permitted.add(possibleSubclass);
-            }
-            for (ClassNode iface : possibleSubclass.getInterfaces()) {
-                if (iface.equals(node)) {
-                    permitted.add(possibleSubclass);
-                }
-            }
-        }
-        List<Expression> names = new ArrayList<>();
-        for (ClassNode next : permitted) {
-            names.add(classX(ClassHelper.make(next.getName())));
-        }
-        AnnotationNode an = node.getAnnotations(SEALED_TYPE).get(0);
-        an.addMember("permittedSubclasses", new ListExpression(names));
-    }
 
     private void checkFinalVariables(final ClassNode node) {
         GroovyClassVisitor visitor = new FinalVariableAnalyzer(null, 
getFinalVariablesCallback());
diff --git 
a/src/main/java/org/codehaus/groovy/transform/SealedCompletionASTTransformation.java
 
b/src/main/java/org/codehaus/groovy/transform/SealedCompletionASTTransformation.java
new file mode 100644
index 0000000..c7f880c
--- /dev/null
+++ 
b/src/main/java/org/codehaus/groovy/transform/SealedCompletionASTTransformation.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.transform;
+
+import groovy.transform.NonSealed;
+import groovy.transform.Sealed;
+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.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+
+/**
+ * Handles sealed class permitted subclass detection.
+ */
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
+public class SealedCompletionASTTransformation extends 
AbstractASTTransformation {
+
+    private static final Class<Sealed> SEALED_CLASS = Sealed.class;
+    private static final ClassNode SEALED_TYPE = make(SEALED_CLASS);
+
+    @Override
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        init(nodes, source);
+        AnnotatedNode parent = (AnnotatedNode) nodes[1];
+        AnnotationNode anno = (AnnotationNode) nodes[0];
+        if (!SEALED_TYPE.equals(anno.getClassNode())) return;
+
+        if (parent instanceof ClassNode) {
+            addDetectedSealedClasses((ClassNode) parent);
+        }
+    }
+
+    private void addDetectedSealedClasses(ClassNode node) {
+        boolean sealed = 
Boolean.TRUE.equals(node.getNodeMetaData(SEALED_CLASS));
+        List<ClassNode> permitted = node.getPermittedSubclasses();
+        if (!sealed || !permitted.isEmpty() || node.getModule() == null) 
return;
+        for (ClassNode possibleSubclass : node.getModule().getClasses()) {
+            if (possibleSubclass.getSuperClass().equals(node)) {
+                permitted.add(possibleSubclass);
+            }
+            for (ClassNode iface : possibleSubclass.getInterfaces()) {
+                if (iface.equals(node)) {
+                    permitted.add(possibleSubclass);
+                }
+            }
+        }
+        List<Expression> names = new ArrayList<>();
+        for (ClassNode next : permitted) {
+            names.add(classX(ClassHelper.make(next.getName())));
+        }
+        AnnotationNode an = node.getAnnotations(SEALED_TYPE).get(0);
+        an.addMember("permittedSubclasses", new ListExpression(names));
+    }
+}

Reply via email to