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 2023/08/25 15:17:57 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-10756: fix erasure (like `ResolveVisitor#resolveGenericsHeader`)
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 1815cdc8ea GROOVY-10756: fix erasure (like `ResolveVisitor#resolveGenericsHeader`)
1815cdc8ea is described below
commit 1815cdc8ea2d42e34470ad98c08198cdbd8bcbd8
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Aug 25 08:22:33 2023 -0500
GROOVY-10756: fix erasure (like `ResolveVisitor#resolveGenericsHeader`)
4_0_X backport
---
.../groovy/ast/decompiled/TypeSignatureParser.java | 10 +-
.../org/codehaus/groovy/vmplugin/v8/Java8.java | 296 ++++++++++-----------
.../stc/ClosureParamTypeInferenceSTCTest.groovy | 5 +
.../groovy/ast/decompiled/AsmDecompilerTest.groovy | 215 ++++++++-------
4 files changed, 275 insertions(+), 251 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java
index fd9b38e9c3..c623f61b79 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java
@@ -121,11 +121,11 @@ abstract class TypeSignatureParser extends SignatureVisitor {
try {
// GROOVY-10153, GROOVY-10651, GROOVY-10671: "?" or "? super T" (see ResolveVisitor#resolveWildcardBounding)
for (int i = 0, n = arguments.size(); i < n; i += 1) { GenericsType argument = arguments.get(i);
- if (!argument.isWildcard() || argument.getUpperBounds() != null) continue; //
- ClassNode[] implicitBounds = baseType.getGenericsTypes()[i].getUpperBounds();
- if (implicitBounds != null && !ClassHelper.isObjectType(implicitBounds[0])) {
- argument.getType().setRedirect(implicitBounds[0]); // bound is not Object
- }
+ if (!argument.isWildcard() || argument.getUpperBounds() != null) continue; //
+ ClassNode[] implicitBounds = baseType.getGenericsTypes()[i].getUpperBounds();
+ if (implicitBounds != null && !ClassHelper.isObjectType(implicitBounds[0])) {
+ argument.getType().setRedirect(implicitBounds[0]); // bound is not Object
+ }
}
} catch (StackOverflowError ignore) {
// TODO: self-referential type parameter
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 a02c2de94f..9152f6011a 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -61,6 +61,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.MalformedParameterizedTypeException;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.ReflectPermission;
@@ -70,7 +71,6 @@ import java.lang.reflect.WildcardType;
import java.security.Permission;
import java.util.Arrays;
import java.util.List;
-import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -84,14 +84,16 @@ public class Java8 implements VMPlugin {
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private static final Permission ACCESS_PERMISSION = new ReflectPermission("suppressAccessChecks");
- public static GenericsType configureTypeVariableDefinition(final ClassNode base, final ClassNode[] cBounds) {
+ public static GenericsType configureTypeVariableDefinition(final ClassNode base, final ClassNode[] bounds) {
ClassNode redirect = base.redirect();
base.setRedirect(null);
GenericsType gt;
- if (cBounds == null || cBounds.length == 0) {
+ if (bounds == null || bounds.length == 0) {
gt = new GenericsType(base);
} else {
- gt = new GenericsType(base, cBounds, null);
+ // GROOVY-10756: fix erasure -- ResolveVisitor#resolveGenericsHeader
+ if (!ClassHelper.isObjectType(bounds[0])) redirect = bounds[0];
+ gt = new GenericsType(base, bounds, null);
gt.setName(base.getName());
gt.setPlaceholder(true);
}
@@ -99,46 +101,42 @@ public class Java8 implements VMPlugin {
return gt;
}
- private static ClassNode configureClass(final Class<?> c) {
- if (c.isPrimitive()) {
- return ClassHelper.make(c);
- } else {
- return ClassHelper.makeWithoutCaching(c, false);
- }
- }
-
public static ClassNode configureTypeVariableReference(final String name) {
ClassNode cn = ClassHelper.makeWithoutCaching(name);
cn.setGenericsPlaceHolder(true);
ClassNode cn2 = ClassHelper.makeWithoutCaching(name);
cn2.setGenericsPlaceHolder(true);
- GenericsType[] gts = new GenericsType[]{new GenericsType(cn2)};
- cn.setGenericsTypes(gts);
+
+ cn.setGenericsTypes(new GenericsType[]{new GenericsType(cn2)});
cn.setRedirect(ClassHelper.OBJECT_TYPE);
return cn;
}
+ private static ClassNode configureClass(final Class<?> c) {
+ if (c.isPrimitive()) {
+ return ClassHelper.make(c);
+ } else {
+ return ClassHelper.makeWithoutCaching(c, false);
+ }
+ }
+
private static void setRetentionPolicy(final RetentionPolicy value, final AnnotationNode node) {
switch (value) {
- case RUNTIME:
- node.setRuntimeRetention(true);
- break;
- case SOURCE:
- node.setSourceRetention(true);
- break;
- case CLASS:
- node.setClassRetention(true);
- break;
- default:
- throw new GroovyBugError("unsupported Retention " + value);
+ case RUNTIME:
+ node.setRuntimeRetention(true);
+ break;
+ case SOURCE:
+ node.setSourceRetention(true);
+ break;
+ case CLASS:
+ node.setClassRetention(true);
+ break;
+ default:
+ throw new GroovyBugError("unsupported Retention " + value);
}
}
- private static void setMethodDefaultValue(final MethodNode mn, final Method m) {
- ConstantExpression cExp = new ConstantExpression(m.getDefaultValue());
- mn.setCode(new ReturnStatement(cExp));
- mn.setAnnotationDefault(true);
- }
+ //--------------------------------------------------------------------------
@Override
public Class<?>[] getPluginDefaultGroovyMethods() {
@@ -161,32 +159,32 @@ public class Java8 implements VMPlugin {
@Deprecated
protected int getElementCode(final ElementType value) {
switch (value) {
- case TYPE:
- return AnnotationNode.TYPE_TARGET;
- case CONSTRUCTOR:
- return AnnotationNode.CONSTRUCTOR_TARGET;
- case METHOD:
- return AnnotationNode.METHOD_TARGET;
- case FIELD:
- return AnnotationNode.FIELD_TARGET;
- case PARAMETER:
- return AnnotationNode.PARAMETER_TARGET;
- case LOCAL_VARIABLE:
- return AnnotationNode.LOCAL_VARIABLE_TARGET;
- case ANNOTATION_TYPE:
- return AnnotationNode.ANNOTATION_TARGET;
- case PACKAGE:
- return AnnotationNode.PACKAGE_TARGET;
- case TYPE_PARAMETER:
- return AnnotationNode.TYPE_PARAMETER_TARGET;
- case TYPE_USE:
- return AnnotationNode.TYPE_USE_TARGET;
- default:
- // falls through
+ case TYPE:
+ return AnnotationNode.TYPE_TARGET;
+ case CONSTRUCTOR:
+ return AnnotationNode.CONSTRUCTOR_TARGET;
+ case METHOD:
+ return AnnotationNode.METHOD_TARGET;
+ case FIELD:
+ return AnnotationNode.FIELD_TARGET;
+ case PARAMETER:
+ return AnnotationNode.PARAMETER_TARGET;
+ case LOCAL_VARIABLE:
+ return AnnotationNode.LOCAL_VARIABLE_TARGET;
+ case ANNOTATION_TYPE:
+ return AnnotationNode.ANNOTATION_TARGET;
+ case PACKAGE:
+ return AnnotationNode.PACKAGE_TARGET;
+ case TYPE_PARAMETER:
+ return AnnotationNode.TYPE_PARAMETER_TARGET;
+ case TYPE_USE:
+ return AnnotationNode.TYPE_USE_TARGET;
+ default:
+ // falls through
}
String name = value.name();
if ("MODULE".equals(name)) { // JDK 9+
- return AnnotationNode.TYPE_TARGET; // TODO Add MODULE_TARGET too?
+ return AnnotationNode.TYPE_TARGET;
} else if ("RECORD_COMPONENT".equals(name)) { // JDK 16+
return AnnotationNode.RECORD_COMPONENT_TARGET;
} else {
@@ -196,27 +194,7 @@ public class Java8 implements VMPlugin {
@Override
public void setAdditionalClassInformation(final ClassNode cn) {
- setGenericsTypes(cn);
- }
-
- private void setGenericsTypes(final ClassNode cn) {
- TypeVariable[] tvs = cn.getTypeClass().getTypeParameters();
- GenericsType[] gts = configureTypeVariable(tvs);
- cn.setGenericsTypes(gts);
- }
-
- private GenericsType[] configureTypeVariable(final TypeVariable[] tvs) {
- final int n = tvs.length;
- if (n == 0) return null;
- GenericsType[] gts = new GenericsType[n];
- for (int i = 0; i < n; i += 1) {
- gts[i] = configureTypeVariableDefinition(tvs[i]);
- }
- return gts;
- }
-
- private GenericsType configureTypeVariableDefinition(final TypeVariable tv) {
- return configureTypeVariableDefinition(configureTypeVariableReference(tv.getName()), configureTypes(tv.getBounds()));
+ cn.setGenericsTypes(configureTypeParameters(cn.getTypeClass().getTypeParameters()));
}
private ClassNode[] configureTypes(final Type[] types) {
@@ -237,10 +215,10 @@ public class Java8 implements VMPlugin {
} else if (type instanceof GenericArrayType) {
return configureGenericArray((GenericArrayType) type);
} else if (type instanceof TypeVariable) {
- return configureTypeVariableReference(((TypeVariable) type).getName());
+ return configureTypeVariableReference(((TypeVariable<?>) type).getName());
} else if (type instanceof Class) {
return configureClass((Class<?>) type);
- } else if (type==null) {
+ } else if (type == null) {
throw new GroovyBugError("Type is null. Most probably you let a transform reuse existing ClassNodes with generics information, that is now used in a wrong context.");
} else {
throw new GroovyBugError("unknown type: " + type + " := " + type.getClass());
@@ -295,6 +273,18 @@ public class Java8 implements VMPlugin {
return gts;
}
+ private GenericsType[] configureTypeParameters(final TypeVariable<?>[] tp) {
+ final int n = tp.length;
+ if (n == 0) return null;
+ GenericsType[] gt = new GenericsType[n];
+ for (int i = 0; i < n; i += 1) {
+ ClassNode t = configureTypeVariableReference(tp[i].getName());
+ ClassNode[] bounds = configureTypes(tp[i].getBounds());
+ gt[i] = configureTypeVariableDefinition(t, bounds);
+ }
+ return gt;
+ }
+
//
@Override
@@ -387,8 +377,7 @@ public class Java8 implements VMPlugin {
@Override
public void configureAnnotationNodeFromDefinition(final AnnotationNode definition, final AnnotationNode root) {
- ClassNode type = definition.getClassNode();
- final String typeName = type.getName();
+ String typeName = definition.getClassNode().getName();
if ("java.lang.annotation.Retention".equals(typeName)) {
Expression exp = definition.getMember("value");
if (!(exp instanceof PropertyExpression)) return;
@@ -399,16 +388,16 @@ public class Java8 implements VMPlugin {
} else if ("java.lang.annotation.Target".equals(typeName)) {
Expression exp = definition.getMember("value");
if (!(exp instanceof ListExpression)) return;
- ListExpression le = (ListExpression) exp;
- int bitmap = 0;
- for (Expression e : le.getExpressions()) {
+ ListExpression list = (ListExpression) exp;
+ int targets = 0;
+ for (Expression e : list.getExpressions()) {
if (!(e instanceof PropertyExpression)) return;
PropertyExpression element = (PropertyExpression) e;
String name = element.getPropertyAsString();
- ElementType value = ElementType.valueOf(name);
- bitmap |= getElementCode(value);
+ ElementType type = ElementType.valueOf(name);
+ targets |= getElementCode(type);
}
- root.setAllowedTargets(bitmap);
+ root.setAllowedTargets(targets);
}
}
@@ -418,29 +407,32 @@ public class Java8 implements VMPlugin {
Class<?> clazz = classNode.getTypeClass();
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
- ClassNode ret = makeClassNode(compileUnit, f.getGenericType(), f.getType());
- FieldNode fn = new FieldNode(f.getName(), f.getModifiers(), ret, classNode, null);
+ ClassNode rt = makeClassNode(compileUnit, f.getGenericType(), f.getType());
+ FieldNode fn = new FieldNode(f.getName(), f.getModifiers(), rt, classNode, null);
setAnnotationMetaData(f.getAnnotations(), fn);
classNode.addField(fn);
}
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
- ClassNode ret = makeClassNode(compileUnit, m.getGenericReturnType(), m.getReturnType());
+ ClassNode rt = makeClassNode(compileUnit, m.getGenericReturnType(), m.getReturnType());
Parameter[] params = makeParameters(compileUnit, m.getGenericParameterTypes(), m.getParameterTypes(), m.getParameterAnnotations(), m);
ClassNode[] exceptions = makeClassNodes(compileUnit, m.getGenericExceptionTypes(), m.getExceptionTypes());
- MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ret, params, exceptions, null);
- mn.setSynthetic(m.isSynthetic());
- setMethodDefaultValue(mn, m);
+ MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), rt, params, exceptions, null);
setAnnotationMetaData(m.getAnnotations(), mn);
- mn.setGenericsTypes(configureTypeVariable(m.getTypeParameters()));
+ if (true) { // TODO: GROOVY-10862
+ mn.setAnnotationDefault(true);
+ mn.setCode(new ReturnStatement(new ConstantExpression(m.getDefaultValue())));
+ }
+ mn.setGenericsTypes(configureTypeParameters(m.getTypeParameters()));
+ mn.setSynthetic(m.isSynthetic());
classNode.addMethod(mn);
}
- Constructor[] constructors = clazz.getDeclaredConstructors();
- for (Constructor ctor : constructors) {
- Parameter[] params = makeParameters(compileUnit, ctor.getGenericParameterTypes(), ctor.getParameterTypes(), getConstructorParameterAnnotations(ctor), ctor);
- ClassNode[] exceptions = makeClassNodes(compileUnit, ctor.getGenericExceptionTypes(), ctor.getExceptionTypes());
- ConstructorNode cn = classNode.addConstructor(ctor.getModifiers(), params, exceptions, null);
- setAnnotationMetaData(ctor.getAnnotations(), cn);
+ Constructor<?>[] constructors = clazz.getDeclaredConstructors();
+ for (Constructor<?> c : constructors) {
+ Parameter[] params = makeParameters(compileUnit, c.getGenericParameterTypes(), c.getParameterTypes(), getConstructorParameterAnnotations(c), c);
+ ClassNode[] exceptions = makeClassNodes(compileUnit, c.getGenericExceptionTypes(), c.getExceptionTypes());
+ ConstructorNode cn = classNode.addConstructor(c.getModifiers(), params, exceptions, null);
+ setAnnotationMetaData(c.getAnnotations(), cn);
}
Class<?> sc = clazz.getSuperclass();
@@ -561,7 +553,7 @@ public class Java8 implements VMPlugin {
return back.getPlainNodeReference();
}
- private Parameter[] makeParameters(final CompileUnit cu, final Type[] types, final Class<?>[] cls, final Annotation[][] parameterAnnotations, final java.lang.reflect.Member member) {
+ private Parameter[] makeParameters(final CompileUnit cu, final Type[] types, final Class<?>[] cls, final Annotation[][] parameterAnnotations, final Member member) {
Parameter[] params = Parameter.EMPTY_ARRAY;
final int n = types.length;
if (n > 0) {
@@ -576,7 +568,7 @@ public class Java8 implements VMPlugin {
return params;
}
- protected void fillParameterNames(final String[] names, final java.lang.reflect.Member member) {
+ protected void fillParameterNames(final String[] names, final Member member) {
try {
java.lang.reflect.Parameter[] parameters = ((java.lang.reflect.Executable) member).getParameters();
for (int i = 0, n = names.length; i < n; i += 1) {
@@ -587,6 +579,8 @@ public class Java8 implements VMPlugin {
}
}
+ //--------------------------------------------------------------------------
+
/**
* The following scenarios can not set accessible, i.e. the return value is false
* 1) SecurityException occurred
@@ -636,27 +630,13 @@ public class Java8 implements VMPlugin {
}
@Override
- public MetaMethod transformMetaMethod(final MetaClass metaClass, final MetaMethod metaMethod, final Class<?> caller) {
- return metaMethod;
- }
-
- @Override
- @Deprecated
- @SuppressWarnings("removal") // TODO a future Groovy version will remove this method
- public <T> T doPrivileged(java.security.PrivilegedAction<T> action) {
- throw new UnsupportedOperationException("doPrivileged is no longer supported");
- }
-
- @Override
- @Deprecated
- @SuppressWarnings("removal") // TODO a future Groovy version will remove this method
- public <T> T doPrivileged(java.security.PrivilegedExceptionAction<T> action) throws java.security.PrivilegedActionException {
- throw new UnsupportedOperationException("doPrivileged is no longer supported");
+ public MetaMethod transformMetaMethod(final MetaClass metaClass, final MetaMethod metaMethod) {
+ return transformMetaMethod(metaClass, metaMethod, null);
}
@Override
- public MetaMethod transformMetaMethod(final MetaClass metaClass, final MetaMethod metaMethod) {
- return transformMetaMethod(metaClass, metaMethod, null);
+ public MetaMethod transformMetaMethod(final MetaClass metaClass, final MetaMethod metaMethod, final Class<?> caller) {
+ return metaMethod;
}
@Override
@@ -664,48 +644,40 @@ public class Java8 implements VMPlugin {
IndyInterface.invalidateSwitchPoints();
}
- @Deprecated
- protected MethodHandles.Lookup getLookup(final Object receiver) {
- Optional<MethodHandles.Lookup> lookup = Optional.empty();
- if (receiver instanceof GroovyObject) {
- lookup = GroovyObjectHelper.lookup((GroovyObject) receiver);
- }
- return lookup.orElseGet(() -> newLookup(receiver.getClass()));
- }
-
@Override
public Object getInvokeSpecialHandle(final Method method, final Object receiver) {
+ final Class<?> receiverClass = receiver.getClass();
try {
- return getLookup(receiver)
- .unreflectSpecial(method, receiver.getClass())
- .bindTo(receiver);
- } catch (ReflectiveOperationException e) {
- return getInvokeSpecialHandleFallback(method, receiver);
+ return getLookup(receiver).unreflectSpecial(method, receiverClass).bindTo(receiver);
+ } catch (ReflectiveOperationException e1) {
+ if (!method.isAccessible()) {
+ doPrivilegedInternal(() -> ReflectionUtils.trySetAccessible(method));
+ }
+ final Class<?> declaringClass = method.getDeclaringClass();
+ try {
+ return newLookup(declaringClass).unreflectSpecial(method, declaringClass).bindTo(receiver);
+ } catch (ReflectiveOperationException e2) {
+ Error e3 = new GroovyBugError(e1);
+ e3.addSuppressed(e2);
+ throw e3;
+ }
}
}
- private Object getInvokeSpecialHandleFallback(final Method method, final Object receiver) {
- if (!method.isAccessible()) {
- doPrivilegedInternal(() -> {
- ReflectionUtils.trySetAccessible(method);
- return null;
- });
- }
- Class<?> declaringClass = method.getDeclaringClass();
- try {
- return newLookup(declaringClass).unreflectSpecial(method, declaringClass).bindTo(receiver);
- } catch (ReflectiveOperationException e) {
- throw new GroovyBugError(e);
- }
- }
- @SuppressWarnings("removal") // TODO a future Groovy version should perform the operation not as a privileged action
- private static <T> T doPrivilegedInternal(java.security.PrivilegedAction<T> action) {
- return java.security.AccessController.doPrivileged(action);
+ @Override
+ public Object invokeHandle(final Object handle, final Object[] arguments) throws Throwable {
+ return ((MethodHandle) handle).invokeWithArguments(arguments);
}
- @Override
- public Object invokeHandle(final Object handle, final Object[] args) throws Throwable {
- return ((MethodHandle) handle).invokeWithArguments(args);
+ //--------------------------------------------------------------------------
+
+ @Deprecated
+ protected MethodHandles.Lookup getLookup(final Object receiver) {
+ if (receiver instanceof GroovyObject) {
+ return GroovyObjectHelper.lookup((GroovyObject) receiver)
+ .orElseGet(() -> newLookup(receiver.getClass()));
+ }
+ return newLookup(receiver.getClass());
}
protected MethodHandles.Lookup newLookup(final Class<?> declaringClass) {
@@ -735,10 +707,7 @@ public class Java8 implements VMPlugin {
try {
if (!lookup.isAccessible()) {
final Constructor<MethodHandles.Lookup> finalReference = lookup;
- doPrivilegedInternal(() -> {
- ReflectionUtils.trySetAccessible(finalReference);
- return null;
- });
+ doPrivilegedInternal(() -> ReflectionUtils.trySetAccessible(finalReference));
}
} catch (SecurityException ignore) {
lookup = null;
@@ -748,4 +717,21 @@ public class Java8 implements VMPlugin {
LOOKUP_Constructor = lookup;
}
}
+
+ @Override
+ @Deprecated
+ public <T> T doPrivileged(final java.security.PrivilegedAction<T> action) {
+ throw new UnsupportedOperationException("doPrivileged is no longer supported");
+ }
+
+ @Override
+ @Deprecated
+ public <T> T doPrivileged(final java.security.PrivilegedExceptionAction<T> action) {
+ throw new UnsupportedOperationException("doPrivileged is no longer supported");
+ }
+
+ @SuppressWarnings("removal") // TODO a future Groovy version should perform the operation not as a privileged action
+ private static <T> T doPrivilegedInternal(final java.security.PrivilegedAction<T> action) {
+ return java.security.AccessController.doPrivileged(action);
+ }
}
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index 6366dceb81..49f381a3af 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -769,6 +769,11 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
def file = Pogo10756.files[0]
file?.name
"""
+
+ assertScript """import ${Pogo10756.name.replace('$','.')}
+ def files = Pogo10756.files
+ files*.name
+ """
}
static class Pogo10756 {
diff --git a/src/test/org/codehaus/groovy/ast/decompiled/AsmDecompilerTest.groovy b/src/test/org/codehaus/groovy/ast/decompiled/AsmDecompilerTest.groovy
index a02ae19abd..bbe96f953f 100644
--- a/src/test/org/codehaus/groovy/ast/decompiled/AsmDecompilerTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/decompiled/AsmDecompilerTest.groovy
@@ -18,7 +18,6 @@
*/
package org.codehaus.groovy.ast.decompiled
-import junit.framework.TestCase
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
@@ -26,24 +25,27 @@ import org.codehaus.groovy.ast.decompiled.support.*
import org.codehaus.groovy.ast.expr.*
import org.codehaus.groovy.control.ClassNodeResolver
import org.codehaus.groovy.control.CompilationUnit
-import org.objectweb.asm.Opcodes
+import org.junit.Test
-final class AsmDecompilerTest extends TestCase {
+import static java.lang.reflect.Modifier.*
- void "test decompile class"() {
+final class AsmDecompilerTest {
+
+ @Test
+ void "basic class"() {
ClassNode node = decompile()
- assert AsmDecompilerTestData.name == node.name
- assert (node.modifiers & Opcodes.ACC_PUBLIC) != 0
+ assert node.name == AsmDecompilerTestData.name
assert !node.genericsPlaceHolder
+ assert isPublic(node.modifiers)
assert node.usingGenerics
def t = node.genericsTypes[0]
assert t.name == 'T'
assert t.placeholder
- assert t.type.name == Object.name
assert t.type.genericsPlaceHolder
- assert !t.lowerBound
+ assert t.type.name == List.name // erasure of "T extends List<? super T>"
+ assert t.lowerBound == null
def list = t.upperBounds[0]
assert list.name == List.name
@@ -52,35 +54,41 @@ final class AsmDecompilerTest extends TestCase {
def wildcard = list.genericsTypes[0]
assert wildcard.wildcard
- assert !wildcard.type.genericsPlaceHolder //todo?
+ assert !wildcard.type.genericsPlaceHolder
+ assert wildcard.type.name == Object.name // erasure of "? super T"
assert !wildcard.upperBounds
def tRef = wildcard.lowerBound
assert tRef.genericsPlaceHolder
assert tRef.usingGenerics
assert tRef.name == Object.name
+ assert tRef.unresolvedName == 'T'
assert tRef.genericsTypes[0].name == 'T'
def v = node.genericsTypes[1]
assert v.name == 'V'
assert v.placeholder
- assert v.upperBounds[0].toString() == Object.name
+ assert v.type.name == Object.name
+ assert v.upperBounds[0].name == Object.name
}
- void "test method object return type"() {
- def method = decompile().getDeclaredMethod("objectMethod")
+ @Test
+ void "method object return type"() {
+ def method = decompile().getDeclaredMethod('objectMethod')
assert method.returnType.name == ClassNode.name
}
- void "test method primitive array return type"() {
- def method = decompile().getDeclaredMethod("primitiveArrayMethod")
+ @Test
+ void "method primitive array return type"() {
+ def method = decompile().getDeclaredMethod('primitiveArrayMethod')
assert method.returnType.array
assert method.returnType.componentType.array
assert method.returnType.componentType.componentType == ClassHelper.int_TYPE
}
- void "test method parameters and exceptions"() {
- def method = decompile().getDeclaredMethods("withParametersThrowing")[0]
+ @Test
+ void "method parameters and exceptions"() {
+ def method = decompile().getDeclaredMethods('withParametersThrowing')[0]
assert method.parameters.length == 2
assert method.parameters[0].type == ClassHelper.int_TYPE
@@ -90,17 +98,20 @@ final class AsmDecompilerTest extends TestCase {
assert method.exceptions[0].name == IOException.name
}
- void "test field"() {
- def field = decompile().getDeclaredField("aField")
+ @Test
+ void "basic field"() {
+ def field = decompile().getDeclaredField('aField')
assert field.type.name == Object.name
}
- void "test constructor"() {
+ @Test
+ void "constructor"() {
def constructor = decompile().getDeclaredConstructors()[0]
assert constructor.parameters[0].type == ClassHelper.boolean_TYPE
}
- void "test supers"() {
+ @Test
+ void "supers"() {
def node = decompile()
assert node.superClass.name == SuperClass.name
assert !node.superClass.usingGenerics
@@ -126,12 +137,13 @@ final class AsmDecompilerTest extends TestCase {
assert !string.type.usingGenerics
}
- void "test simple class annotations"() {
+ @Test
+ void "simple class annotations"() {
def node = decompile().annotations[0]
assert node.classNode.name == Anno.name
- assert ((ConstantExpression) node.members.stringAttr).value == "s"
+ assert ((ConstantExpression) node.members.stringAttr).value == 's'
assert !node.members.booleanAttr
assert ((ClassExpression) node.members.clsAttr).type.name == String.name
@@ -139,35 +151,40 @@ final class AsmDecompilerTest extends TestCase {
assert ((PropertyExpression) node.members.enumAttr).objectExpression.type.name == SomeEnum.name
}
- void "test member annotations"() {
+ @Test
+ void "member annotations"() {
def node = decompile()
- assert node.getDeclaredField("aField").annotations[0].classNode.name == Anno.name
- assert node.getDeclaredMethod("objectMethod").annotations[0].classNode.name == Anno.name
- assert !node.getDeclaredMethods("withParametersThrowing")[0].annotations
+ assert node.getDeclaredField('aField').annotations[0].classNode.name == Anno.name
+ assert node.getDeclaredMethod('objectMethod').annotations[0].classNode.name == Anno.name
+ assert !node.getDeclaredMethods('withParametersThrowing')[0].annotations
}
- void "test parameter annotations"() {
+ @Test
+ void "parameter annotations"() {
def node = decompile()
- def params = node.getDeclaredMethods("withParametersThrowing")[0].parameters
+ def params = node.getDeclaredMethods('withParametersThrowing')[0].parameters
assert params[0].annotations.collect { it.classNode.name } == [Anno.name]
assert !params[1].annotations
}
- void "test primitive array attribute"() {
+ @Test
+ void "primitive array attribute"() {
def node = decompile().annotations[0]
def list = ((ListExpression) node.members.intArrayAttr).expressions
assert list.collect { ((ConstantExpression) it).value } == [4, 2]
}
- void "test class array attribute"() {
+ @Test
+ void "class array attribute"() {
def node = decompile().annotations[0]
def list = ((ListExpression) node.members.classArrayAttr).expressions
assert list.collect { ((ClassExpression) it).type.name } == [AsmDecompilerTestData.name]
}
- void "test annotation array attribute"() {
+ @Test
+ void "annotation array attribute"() {
def node = decompile().annotations[0]
def list = ((ListExpression) node.members.annoArrayAttr).expressions
@@ -181,14 +198,16 @@ final class AsmDecompilerTest extends TestCase {
assert ((ConstantExpression) annotationNode.members.booleanAttr).value == false
}
- void "test annotation default method"() {
- def method = decompile(Anno.name).getDeclaredMethod('booleanAttr')
+ @Test
+ void "annotation default method"() {
+ def method = decompile(Anno).getDeclaredMethod('booleanAttr')
assert method.hasAnnotationDefault()
assert method.code
}
- void "test annotation retention and targets"() {
- def anno = decompile().getAnnotations(decompile(Anno.name))[0]
+ @Test
+ void "annotation retention and targets"() {
+ def anno = decompile().getAnnotations(decompile(Anno))[0]
assert anno.hasRuntimeRetention()
assert !anno.hasClassRetention()
assert !anno.hasSourceRetention()
@@ -198,8 +217,9 @@ final class AsmDecompilerTest extends TestCase {
assert !anno.isTargetAllowed(AnnotationNode.LOCAL_VARIABLE_TARGET)
}
- void "test enum field"() {
- def node = decompile(SomeEnum.name).plainNodeReference
+ @Test
+ void "enum field"() {
+ def node = decompile(SomeEnum).plainNodeReference
for (s in ['FOO', 'BAR']) {
def field = node.getDeclaredField(s)
assert field
@@ -207,8 +227,9 @@ final class AsmDecompilerTest extends TestCase {
}
}
- void "test generic method"() {
- def method = decompile().getDeclaredMethods("genericMethod")[0]
+ @Test
+ void "generic method"() {
+ def method = decompile().getDeclaredMethods('genericMethod')[0]
assert method.genericsTypes.size() == 2
assert method.genericsTypes[0].name == 'A'
@@ -240,18 +261,21 @@ final class AsmDecompilerTest extends TestCase {
assert wildcard.upperBounds == null
}
- void "test non-generic exceptions"() {
- def method = decompile().getDeclaredMethods("nonGenericExceptions")[0]
+ @Test
+ void "non-generic exceptions"() {
+ def method = decompile().getDeclaredMethods('nonGenericExceptions')[0]
assert method.exceptions[0].name == IOException.name
}
- void "test non-generic parameters"() {
- def method = decompile().getDeclaredMethods("nonGenericParameters")[0]
+ @Test
+ void "non-generic parameters"() {
+ def method = decompile().getDeclaredMethods('nonGenericParameters')[0]
assert method.parameters[0].type == ClassHelper.boolean_TYPE
}
- void "test generic field"() {
- def type = decompile().getDeclaredField("genericField").type
+ @Test
+ void "generic field"() {
+ def type = decompile().getDeclaredField('genericField').type
assert type.name == List.name
assert type.usingGenerics
@@ -262,83 +286,92 @@ final class AsmDecompilerTest extends TestCase {
assert tRef.genericsTypes[0].name == 'T'
}
- void "test non-trivial erasure"() {
- def cls = decompile(NonTrivialErasure.name)
+ @Test
+ void "non-trivial erasure"() {
+ def cls = decompile(NonTrivialErasure)
- def method = cls.getDeclaredMethods("method")[0]
+ def method = cls.getDeclaredMethods('method')[0]
assert method.returnType.toString() == 'V -> java.lang.RuntimeException'
assert method.parameters[0].type.toString() == 'V -> java.lang.RuntimeException'
assert method.exceptions[0].toString() == 'V -> java.lang.RuntimeException'
- def field = cls.getDeclaredField("field")
+ def field = cls.getDeclaredField('field')
assert field.type.toString() == 'V -> java.lang.RuntimeException'
}
- void "test static inner class"() {
- ClassNode cn = decompile(AsmDecompilerTestData.InnerStatic.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) != 0
+ @Test
+ void "static inner class"() {
+ ClassNode cn = decompile(AsmDecompilerTestData.InnerStatic)
+ assert isStatic(cn.modifiers)
}
- void "test static inner with dollar"() {
- ClassNode cn = decompile(AsmDecompilerTestData.Inner$WithDollar.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) != 0
+ @Test
+ void "static inner with dollar"() {
+ ClassNode cn = decompile(AsmDecompilerTestData.Inner$WithDollar)
+ assert isStatic(cn.modifiers)
}
- void "test static inner classes with same name"() {
- ClassNode cn = decompile(Groovy8632Abstract.Builder.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) != 0
- assert (cn.modifiers & Opcodes.ACC_ABSTRACT) != 0
+ @Test
+ void "static inner classes with same name"() {
+ ClassNode cn = decompile(Groovy8632Abstract.Builder)
+ assert isStatic(cn.modifiers)
+ assert isAbstract(cn.modifiers)
- cn = decompile(Groovy8632.Builder.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) != 0
- assert (cn.modifiers & Opcodes.ACC_ABSTRACT) == 0
+ cn = decompile(Groovy8632.Builder)
+ assert isStatic(cn.modifiers)
+ assert !isAbstract(cn.modifiers)
- cn = decompile(Groovy8632Groovy.Builder.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) != 0
- assert (cn.modifiers & Opcodes.ACC_ABSTRACT) == 0
+ cn = decompile(Groovy8632Groovy.Builder)
+ assert isStatic(cn.modifiers)
+ assert !isAbstract(cn.modifiers)
}
- void "test inner classes with same name"() {
- ClassNode cn = decompile(Groovy8632Abstract.InnerBuilder.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) == 0
- assert (cn.modifiers & Opcodes.ACC_ABSTRACT) != 0
+ @Test
+ void "inner classes with same name"() {
+ ClassNode cn = decompile(Groovy8632Abstract.InnerBuilder)
+ assert !isStatic(cn.modifiers)
+ assert isAbstract(cn.modifiers)
- cn = decompile(Groovy8632.InnerBuilder.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) == 0
- assert (cn.modifiers & Opcodes.ACC_ABSTRACT) == 0
+ cn = decompile(Groovy8632.InnerBuilder)
+ assert !isStatic(cn.modifiers)
+ assert !isAbstract(cn.modifiers)
- cn = decompile(Groovy8632Groovy.InnerBuilder.name)
- assert (cn.modifiers & Opcodes.ACC_STATIC) == 0
- assert (cn.modifiers & Opcodes.ACC_ABSTRACT) == 0
+ cn = decompile(Groovy8632Groovy.InnerBuilder)
+ assert !isStatic(cn.modifiers)
+ assert !isAbstract(cn.modifiers)
}
- void "test private inner class"() {
- ClassNode cn = decompile(Groovy8632.InnerPrivate.name)
- assert (cn.modifiers & Opcodes.ACC_PRIVATE) != 0
- assert (cn.modifiers & Opcodes.ACC_PUBLIC) == 0
+ @Test
+ void "private inner class"() {
+ ClassNode cn = decompile(Groovy8632.InnerPrivate)
+ assert isPrivate(cn.modifiers)
+ assert !isPublic(cn.modifiers)
}
- void "test protected inner class"() {
- ClassNode cn = decompile(Groovy8632.InnerProtected.name)
- assert (cn.modifiers & Opcodes.ACC_PROTECTED) != 0
- assert (cn.modifiers & Opcodes.ACC_PUBLIC) == 0
+ @Test
+ void "protected inner class"() {
+ ClassNode cn = decompile(Groovy8632.InnerProtected)
+ assert isProtected(cn.modifiers)
+ assert !isPublic(cn.modifiers)
}
- void "test non-parameterized generics"() {
- assert decompile().getDeclaredMethod("nonParameterizedGenerics").genericsTypes == null
+ @Test
+ void "non-parameterized generics"() {
+ assert decompile().getDeclaredMethod('nonParameterizedGenerics').genericsTypes == null
}
- void "test non-static parameterized inner"() {
- def asmType = decompile().getDeclaredMethod("returnInner").returnType
- def jvmType = new ClassNode(AsmDecompilerTestData).getDeclaredMethod("returnInner").returnType
+ @Test
+ void "non-static parameterized inner"() {
+ def asmType = decompile().getDeclaredMethod('returnInner').returnType
+ def jvmType = new ClassNode(AsmDecompilerTestData).getDeclaredMethod('returnInner').returnType
assert asmType == jvmType
- assert asmType.genericsTypes.collect { it.name } == jvmType.genericsTypes.collect { it.name }
+ assert asmType.genericsTypes*.name == jvmType.genericsTypes*.name
}
//--------------------------------------------------------------------------
- private static ClassNode decompile(String className = AsmDecompilerTestData.name) {
- def classFileName = className.replace('.', '/') + '.class'
+ private static ClassNode decompile(Class clazz = AsmDecompilerTestData) {
+ def classFileName = clazz.name.replace('.', '/') + '.class'
def resource = AsmDecompilerTest.classLoader.getResource(classFileName)
def stub = AsmDecompiler.parseClass(resource)