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/08/08 13:05:11 UTC

[groovy] branch danielsun/tweak-permittedsubclasses created (now e24c02f)

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

sunlan pushed a change to branch danielsun/tweak-permittedsubclasses
in repository https://gitbox.apache.org/repos/asf/groovy.git.


      at e24c02f  Tweak the initialization of permitted subclasses

This branch includes the following new commits:

     new e24c02f  Tweak the initialization of permitted subclasses

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[groovy] 01/01: Tweak the initialization of permitted subclasses

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch danielsun/tweak-permittedsubclasses
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e24c02fe2b45faa70048574beb0d4a70c0e02810
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Aug 8 21:04:53 2021 +0800

    Tweak the initialization of permitted subclasses
---
 .../java/org/codehaus/groovy/ast/ClassNode.java    | 27 ++++++++++++++------
 .../groovy/reflection/ReflectionUtils.java         | 29 +++++++++++++++++++---
 .../transform/DelegateASTTransformation.java       |  2 +-
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     | 11 ++++++++
 .../org/codehaus/groovy/ast/ClassNodeTest.java     |  9 +++++++
 5 files changed, 67 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 87b278f..d7d6c73 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -29,7 +29,6 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.ast.tools.ParameterUtils;
 import org.codehaus.groovy.control.CompilePhase;
-import org.codehaus.groovy.reflection.ReflectionUtils;
 import org.codehaus.groovy.transform.ASTTransformation;
 import org.codehaus.groovy.transform.GroovyASTTransformation;
 import org.codehaus.groovy.vmplugin.VMPluginFactory;
@@ -148,6 +147,7 @@ public class ClassNode extends AnnotatedNode {
     private int modifiers;
     private boolean syntheticPublic;
     private ClassNode[] interfaces;
+    private List<ClassNode> permittedSubclasses = new ArrayList<>(4);
     private MixinNode[] mixins;
     private List<Statement> objectInitializers;
     private List<ConstructorNode> constructors;
@@ -164,7 +164,6 @@ public class ClassNode extends AnnotatedNode {
     private ClassNode superClass;
     protected boolean isPrimaryNode;
     protected List<InnerClassNode> innerClasses;
-    private final List<ClassNode> permittedSubclasses = new ArrayList<>(4);
     private List<AnnotationNode> typeAnnotations = Collections.emptyList();
 
     /**
@@ -244,10 +243,6 @@ public class ClassNode extends AnnotatedNode {
         return redirect().isPrimaryNode || (componentType != null && componentType.isPrimaryClassNode());
     }
 
-    public List<ClassNode> getPermittedSubclasses() {
-        return redirect().permittedSubclasses;
-    }
-
     /**
      * Constructor used by {@code makeArray()} if no real class is available.
      */
@@ -393,6 +388,24 @@ public class ClassNode extends AnnotatedNode {
     }
 
     /**
+     * @return permitted subclasses of sealed type
+     */
+    public List<ClassNode> getPermittedSubclasses() {
+        if (redirect != null)
+            return redirect.getPermittedSubclasses();
+        lazyClassInit();
+        return permittedSubclasses;
+    }
+
+    public void setPermittedSubclasses(List<ClassNode> permittedSubclasses) {
+        if (redirect != null) {
+            redirect.setPermittedSubclasses(permittedSubclasses);
+        } else {
+            this.permittedSubclasses = permittedSubclasses;
+        }
+    }
+
+    /**
      * @return the mixins associated with this {@code ClassNode}
      */
     public MixinNode[] getMixins() {
@@ -1349,7 +1362,7 @@ public class ClassNode extends AnnotatedNode {
     }
 
     public boolean isSealed() {
-        return !getAnnotations(SEALED_TYPE).isEmpty() || !permittedSubclasses.isEmpty() || (null != clazz && ReflectionUtils.isSealed(clazz));
+        return !getAnnotations(SEALED_TYPE).isEmpty() || !getPermittedSubclasses().isEmpty();
     }
 
     public boolean isResolved() {
diff --git a/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java b/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
index bdb23c2..0057c02 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
@@ -51,6 +51,8 @@ public class ReflectionUtils {
 
     /** The packages in the call stack that are only part of the Groovy MOP. */
     private static final Set<String> IGNORED_PACKAGES;
+    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+
     static {
         Set<String> set = new HashSet<>();
 
@@ -240,6 +242,7 @@ public class ReflectionUtils {
 
     public static boolean isSealed(Class<?> clazz) {
         if (null == IS_SEALED_METHODHANDLE) return false;
+        if (null == clazz) return false;
 
         boolean sealed = false;
         try {
@@ -249,6 +252,18 @@ public class ReflectionUtils {
         return sealed;
     }
 
+    public static Class<?>[] getPermittedSubclasses(Class<?> clazz) {
+        if (null == GET_PERMITTED_SUBCLASSES_METHODHANDLE) return EMPTY_CLASS_ARRAY;
+        if (null == clazz) return EMPTY_CLASS_ARRAY;
+
+        Class<?>[] result = EMPTY_CLASS_ARRAY;
+        try {
+            result = (Class<?>[]) GET_PERMITTED_SUBCLASSES_METHODHANDLE.bindTo(clazz).invokeExact();
+        } catch (Throwable ignored) {
+        }
+        return result;
+    }
+
     private static boolean classShouldBeIgnored(final Class c, final Collection<String> extraIgnoredPackages) {
         return (c != null
                 && (c.isSynthetic()
@@ -265,12 +280,20 @@ public class ReflectionUtils {
     }
 
     private static final MethodHandle IS_SEALED_METHODHANDLE;
+    private static final MethodHandle GET_PERMITTED_SUBCLASSES_METHODHANDLE;
     static {
-        MethodHandle mh = null;
+        MethodHandle isSealedMethodHandle = null;
+        try {
+            isSealedMethodHandle = MethodHandles.lookup().findVirtual(Class.class, "isSealed", MethodType.methodType(boolean.class, new Class[0]));
+        } catch (NoSuchMethodException | IllegalAccessException ignored) {
+        }
+        IS_SEALED_METHODHANDLE = isSealedMethodHandle;
+
+        MethodHandle getPermittedSubclassesMethodHandle = null;
         try {
-            mh = MethodHandles.lookup().findVirtual(Class.class, "isSealed", MethodType.methodType(boolean.class, new Class[0]));
+            getPermittedSubclassesMethodHandle = MethodHandles.lookup().findVirtual(Class.class, "getPermittedSubclasses", MethodType.methodType(Class[].class, new Class[0]));
         } catch (NoSuchMethodException | IllegalAccessException ignored) {
         }
-        IS_SEALED_METHODHANDLE = mh;
+        GET_PERMITTED_SUBCLASSES_METHODHANDLE = getPermittedSubclassesMethodHandle;
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
index af03683..4080e31 100644
--- a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -200,7 +200,7 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
 
             final Set<ClassNode> allInterfaces =
                     getInterfacesAndSuperInterfaces(delegate.type).stream()
-                            .filter(c -> !c.redirect().isSealed())
+                            .filter(c -> !c.isSealed())
                             .collect(Collectors.toSet());
             final Set<ClassNode> ownerIfaces = delegate.owner.getAllInterfaces();
             Map<String,ClassNode> genericsSpec = createGenericsSpec(delegate.owner);
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index ff6da29..18fd483 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -69,8 +69,10 @@ import java.lang.reflect.WildcardType;
 import java.security.AccessController;
 import java.security.Permission;
 import java.security.PrivilegedAction;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * Java 8 based functions.
@@ -456,6 +458,7 @@ public class Java8 implements VMPlugin {
             Class<?> sc = clazz.getSuperclass();
             if (sc != null) classNode.setUnresolvedSuperClass(makeClassNode(compileUnit, clazz.getGenericSuperclass(), sc));
             makeInterfaceTypes(compileUnit, classNode, clazz);
+            makePermittedSubclasses(compileUnit, classNode, clazz);
             setAnnotationMetaData(clazz.getAnnotations(), classNode);
 
             PackageNode packageNode = classNode.getPackage();
@@ -514,6 +517,14 @@ public class Java8 implements VMPlugin {
         return annotations;
     }
 
+    private void makePermittedSubclasses(CompileUnit cu, ClassNode classNode, Class<?> clazz) {
+        if (!ReflectionUtils.isSealed(clazz)) return;
+        List<ClassNode> permittedSubclasses = Arrays.stream(ReflectionUtils.getPermittedSubclasses(clazz))
+                .map(c -> makeClassNode(cu, c, c))
+                .collect(Collectors.toList());
+        classNode.setPermittedSubclasses(permittedSubclasses);
+    }
+
     private void makeInterfaceTypes(CompileUnit cu, ClassNode classNode, Class<?> clazz) {
         Type[] interfaceTypes = clazz.getGenericInterfaces();
         if (interfaceTypes.length == 0) {
diff --git a/src/test/org/codehaus/groovy/ast/ClassNodeTest.java b/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
index 864d8a2..22eaef1 100644
--- a/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
+++ b/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
@@ -21,6 +21,9 @@ package org.codehaus.groovy.ast;
 import junit.framework.TestCase;
 import org.objectweb.asm.Opcodes;
 
+import static groovy.test.GroovyAssert.isAtLeastJdk;
+import static org.junit.Assume.assumeTrue;
+
 /**
  * Tests the ClassNode
  */
@@ -49,4 +52,10 @@ public class ClassNodeTest extends TestCase implements Opcodes {
         ClassNode packageNode = new ClassNode("com.acme.Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
         assertEquals("Package", "com.acme", packageNode.getPackageName());
     }
+
+    public void testPermittedSubclasses() throws ClassNotFoundException {
+        assumeTrue(isAtLeastJdk("12.0"));
+        Class<?> clazz = Class.forName("java.lang.constant.ConstantDesc");
+        assertTrue(!new ClassNode(clazz).getPermittedSubclasses().isEmpty());
+    }
 }