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:12 UTC
[groovy] 01/01: Tweak the initialization of permitted subclasses
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());
+ }
}