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 2021/12/03 20:50:19 UTC
[groovy] branch master updated: GROOVY-8693, GROOVY-9851, GROOVY-9884, GROOVY-9909, GROOVY-10302: super!
This is an automated email from the ASF dual-hosted git repository.
emilles 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 a63ee5e GROOVY-8693, GROOVY-9851, GROOVY-9884, GROOVY-9909, GROOVY-10302: super!
a63ee5e is described below
commit a63ee5e26023dd22db81834d7134053bd566f441
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Dec 3 13:58:14 2021 -0600
GROOVY-8693, GROOVY-9851, GROOVY-9884, GROOVY-9909, GROOVY-10302: super!
---
src/main/java/groovy/lang/MetaClassImpl.java | 242 +++----
.../groovy/classgen/asm/InvocationWriter.java | 15 +-
.../codehaus/groovy/classgen/asm/MopWriter.java | 94 ++-
.../groovy/runtime/DefaultGroovyMethods.java | 9 +-
.../transform/stc/StaticTypeCheckingVisitor.java | 4 +
src/test/gls/invocation/MethodSelectionTest.groovy | 787 +++++++++------------
src/test/groovy/ThisAndSuperTest.groovy | 397 +++++++++--
src/test/groovy/bugs/Groovy5212.groovy | 62 ++
src/test/groovy/bugs/Groovy5212Bug.groovy | 107 ---
.../{Groovy5260Bug.groovy => Groovy5260.groovy} | 61 +-
src/test/groovy/bugs/Groovy9615.groovy | 63 --
src/test/groovy/transform/stc/BugsSTCTest.groovy | 29 +
.../stc/Groovy9909.java} | 19 +-
.../classgen/asm/sc/BugsStaticCompileTest.groovy | 16 -
14 files changed, 939 insertions(+), 966 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 4e2e327..45f506d 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -503,8 +503,6 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
private void replaceWithMOPCalls(final CachedMethod[] mopMethods) {
- if (mopMethods == null || mopMethods.length == 0) return;
-
class MOPIter extends MethodIndexAction {
boolean useThis;
@@ -516,12 +514,11 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
Object[] data = methods.getArray();
for (int i = 0; i < methods.size(); i += 1) {
MetaMethod method = (MetaMethod) data[i];
- int matchingMethod = mopArrayIndex(method);
- if (matchingMethod >= 0) {
- methods.set(i, mopMethods[matchingMethod]);
- } else if (!useThis && !(method instanceof NewMetaMethod)
- && c == method.getDeclaringClass().getTheClass()) {
- methods.remove(i--); // not fit for super usage; StackOverflowError
+ int matchedMethod = mopArrayIndex(method, c);
+ if (matchedMethod >= 0) {
+ methods.set(i, mopMethods[matchedMethod]);
+ } else if (!useThis && !isDGM(method) && c == method.getDeclaringClass().getTheClass()) {
+ methods.remove(i--); // not fit for super usage
}
}
if (!useThis) {
@@ -530,29 +527,26 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
else if (n == 1) e.methodsForSuper = data[0];
}
} else if (arrayOrMethod != null) {
- int matchingMethod = mopArrayIndex((MetaMethod) arrayOrMethod);
- if (matchingMethod >= 0) {
- if (useThis) e.methods = mopMethods[matchingMethod];
- else e.methodsForSuper = mopMethods[matchingMethod];
+ MetaMethod method = (MetaMethod) arrayOrMethod;
+ int matchedMethod = mopArrayIndex(method, c);
+ if (matchedMethod >= 0) {
+ if (useThis) e.methods = mopMethods[matchedMethod];
+ else e.methodsForSuper = mopMethods[matchedMethod];
+ } else if (!useThis && !isDGM(method) && c == method.getDeclaringClass().getTheClass()) {
+ e.methodsForSuper = null; // not fit for super usage
}
}
}
- private int mopArrayIndex(final MetaMethod method) {
- if (method instanceof NewMetaMethod) return -1;
- if (useThis ^ method.isPrivate()) return -1;
-
- String mopName = method.getMopName();
- if (useThis) {
- return mopArrayIndex(method, mopName);
- }
+ private int mopArrayIndex(final MetaMethod method, final Class<?> c) {
+ if (mopMethods == null || mopMethods.length == 0) return -1;
+ if (isDGM(method) || (useThis ^ method.isPrivate())) return -1;
+ if (useThis) return mopArrayIndex(method, method.getMopName());
// GROOVY-4922: Due to a numbering scheme change, find the super$number$methodName with
// the highest value. If we don't, no method may be found, leading to a stack overflow!
- String[] tokens = decomposeMopName(mopName);
- int distance = Integer.parseInt(tokens[1]);
+ int distance = ReflectionCache.getCachedClass(c).getSuperClassDistance() - 1;
while (distance > 0) {
- mopName = tokens[0] + distance + tokens[2];
- int index = mopArrayIndex(method, mopName);
+ int index = mopArrayIndex(method, "super$" + distance + "$" + method.getName());
if (index >= 0) return index;
distance -= 1;
}
@@ -577,19 +571,8 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
return -1;
}
- private String[] decomposeMopName(final String mopName) {
- int idx = mopName.indexOf('$');
- if (idx > 0) {
- int eidx = mopName.indexOf('$', idx + 1);
- if (eidx > 0) {
- return new String[]{
- mopName.substring(0, idx + 1),
- mopName.substring(idx + 1, eidx),
- mopName.substring(eidx)
- };
- }
- }
- return new String[]{"", "0", mopName};
+ private boolean isDGM(final MetaMethod method) {
+ return method instanceof GeneratedMetaMethod || method instanceof NewMetaMethod;
}
}
MOPIter iter = new MOPIter();
@@ -597,6 +580,9 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
// replace all calls for super with the correct MOP method
iter.useThis = false;
iter.iterate();
+
+ if (mopMethods == null || mopMethods.length == 0) return;
+
// replace all calls for this with the correct MOP method
iter.useThis = true;
iter.iterate();
@@ -1092,87 +1078,55 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
/**
- * <p>Invokes a method on the given receiver for the specified arguments. The sender is the class that invoked the method on the object.
+ * Invokes a method on the given receiver for the specified arguments. The sender is the class that invoked the method on the object.
* The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
- *
- * <p>The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
+ * <p>
+ * The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
* to the super class if necessary
*
* @param sender The java.lang.Class instance that invoked the method
* @param object The object which the method was invoked on
* @param methodName The name of the method
- * @param originalArguments The arguments to the method
+ * @param arguments The arguments to the method
* @param isCallToSuper Whether the method is a call to a super class method
* @param fromInsideClass Whether the call was invoked from the inside or the outside of the class
* @return The return value of the method
* @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, boolean)
*/
@Override
- public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
+ public Object invokeMethod(final Class sender, final Object object, final String methodName, final Object[] arguments, final boolean isCallToSuper, final boolean fromInsideClass) {
try {
- return doInvokeMethod(sender, object, methodName, originalArguments, isCallToSuper, fromInsideClass);
+ return doInvokeMethod(sender, object, methodName, arguments, isCallToSuper, fromInsideClass);
} catch (MissingMethodException mme) {
- return doInvokeMethodFallback(sender, object, methodName, originalArguments, isCallToSuper, mme);
- }
- }
-
- private static class MethodHandleHolder {
- private static final MethodHandle CLONE_ARRAY_METHOD_HANDLE;
- static {
- Optional<Method> methodOptional = Arrays.stream(ArrayUtil.class.getDeclaredMethods()).filter(m -> "cloneArray".equals(m.getName())).findFirst();
- if (!methodOptional.isPresent()) {
- throw new GroovyBugError("Failed to find `cloneArray` method in class `" + ArrayUtil.class.getName() + "`");
+ MethodHandles.Lookup lookup = null;
+ if (object instanceof GroovyObject) {
+ lookup = GroovyObjectHelper.lookup((GroovyObject) object).orElseThrow(() -> mme);
}
- Method cloneArrayMethod = methodOptional.get();
- try {
- CLONE_ARRAY_METHOD_HANDLE = MethodHandles.lookup().in(ArrayUtil.class).unreflect(cloneArrayMethod);
- } catch (IllegalAccessException e) {
- throw new GroovyBugError("Failed to create method handle for " + cloneArrayMethod);
- }
- }
- private MethodHandleHolder() {}
- }
+ Class<?> receiverClass = object.getClass();
- private static final ClassValue<Map<String, Set<Method>>> SPECIAL_METHODS_MAP = new ClassValue<Map<String, Set<Method>>>() {
- @Override
- protected Map<String, Set<Method>> computeValue(Class<?> type) {
- return new ConcurrentHashMap<>(4);
- }
- };
-
- private Object doInvokeMethodFallback(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, MissingMethodException mme) {
- MethodHandles.Lookup lookup = null;
- if (object instanceof GroovyObject) {
- Optional<MethodHandles.Lookup> lookupOptional = GroovyObjectHelper.lookup((GroovyObject) object);
- if (!lookupOptional.isPresent()) throw mme;
- lookup = lookupOptional.get();
- }
-
- final Class<?> receiverClass = object.getClass();
- if (isCallToSuper) {
- if (null == lookup) throw mme;
- MethodHandles.Lookup tmpLookup = lookup;
- MethodHandle superMethodHandle = findMethod(sender, methodName, castArgumentsToClassArray(originalArguments), superMethod -> {
+ if (isCallToSuper) {
+ if (lookup == null) throw mme;
+ MethodHandles.Lookup theLookup = lookup;
+ MethodHandle methodHandle = findMethod(sender.getSuperclass(), methodName, castArgumentsToClassArray(arguments), method -> {
+ try {
+ return theLookup.unreflectSpecial(method, receiverClass);
+ } catch (IllegalAccessException e) {
+ return null;
+ }
+ });
+ if (methodHandle == null) throw mme;
try {
- return tmpLookup.unreflectSpecial(superMethod, receiverClass);
- } catch (IllegalAccessException e) {
- return null;
+ return methodHandle.bindTo(object).invokeWithArguments(arguments);
+ } catch (Throwable t) {
+ throw new GroovyRuntimeException(t);
}
- });
- if (null == superMethodHandle) throw mme;
- try {
- return superMethodHandle.bindTo(object).invokeWithArguments(originalArguments);
- } catch (Throwable t) {
- throw new GroovyRuntimeException(t);
}
- } else {
+
if (receiverClass.isArray()) {
- if ("clone".equals(methodName) && 0 == originalArguments.length) {
+ if (methodName.equals("clone") && arguments.length == 0) {
try {
- Object[] array = (Object[]) object;
- Object[] result = (Object[]) MethodHandleHolder.CLONE_ARRAY_METHOD_HANDLE.invokeExact(array);
- return result;
+ return (Object[]) MethodHandleHolder.CLONE_ARRAY_METHOD_HANDLE.invokeExact((Object[]) object);
} catch (Throwable t) {
throw new GroovyRuntimeException(t);
}
@@ -1180,7 +1134,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
throw mme;
}
- final boolean spyFound = null != lookup;
+ boolean spyFound = (lookup != null);
if (!spyFound) {
try {
lookup = MethodHandles.lookup().in(receiverClass);
@@ -1188,31 +1142,53 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
throw mme;
}
}
- MethodHandles.Lookup tmpLookup = lookup;
- MethodHandle thisMethodHandle = findMethod(receiverClass, methodName, castArgumentsToClassArray(originalArguments), thisMethod -> {
+ MethodHandles.Lookup theLookup = lookup;
+ MethodHandle methodHandle = findMethod(receiverClass, methodName, castArgumentsToClassArray(arguments), method -> {
try {
- if (spyFound) return tmpLookup.unreflectSpecial(thisMethod, receiverClass);
-
- return tmpLookup.unreflect(thisMethod);
+ return spyFound ? theLookup.unreflectSpecial(method, receiverClass) : theLookup.unreflect(method);
} catch (IllegalAccessException e) {
return null;
}
});
- if (null == thisMethodHandle) throw mme;
+ if (methodHandle == null) throw mme;
try {
- return thisMethodHandle.bindTo(object).invokeWithArguments(originalArguments);
+ return methodHandle.bindTo(object).invokeWithArguments(arguments);
} catch (Throwable t) {
throw new GroovyRuntimeException(t);
}
}
}
- private static MethodHandle findMethod(Class<?> clazz, String methodName, Class[] argTypes, Function<Method, MethodHandle> mhFunc) {
- final Map<String, Set<Method>> methodMap = SPECIAL_METHODS_MAP.get(clazz);
- final Set<Method> methods = methodMap.get(methodName);
+ private static class MethodHandleHolder {
+ private static final MethodHandle CLONE_ARRAY_METHOD_HANDLE;
+ static {
+ Optional<Method> methodOptional = Arrays.stream(ArrayUtil.class.getDeclaredMethods()).filter(m -> "cloneArray".equals(m.getName())).findFirst();
+ if (!methodOptional.isPresent()) {
+ throw new GroovyBugError("Failed to find `cloneArray` method in class `" + ArrayUtil.class.getName() + "`");
+ }
+ Method cloneArrayMethod = methodOptional.get();
+
+ try {
+ CLONE_ARRAY_METHOD_HANDLE = MethodHandles.lookup().in(ArrayUtil.class).unreflect(cloneArrayMethod);
+ } catch (IllegalAccessException e) {
+ throw new GroovyBugError("Failed to create method handle for " + cloneArrayMethod);
+ }
+ }
+ private MethodHandleHolder() {}
+ }
+
+ private static final ClassValue<Map<String, Set<Method>>> SPECIAL_METHODS_MAP = new ClassValue<Map<String, Set<Method>>>() {
+ @Override
+ protected Map<String, Set<Method>> computeValue(final Class<?> type) {
+ return new ConcurrentHashMap<>(4);
+ }
+ };
+ private static MethodHandle findMethod(final Class<?> clazz, final String methodName, final Class[] argTypes, final Function<Method, MethodHandle> mhFunc) {
+ Map<String, Set<Method>> map = SPECIAL_METHODS_MAP.get(clazz);
+ Set<Method> methods = map.get(methodName);
Method foundMethod = null;
- if (null != methods) {
+ if (methods != null) {
for (Method method : methods) {
if (parameterTypeMatches(method.getParameterTypes(), argTypes)) {
foundMethod = method;
@@ -1221,30 +1197,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
}
- if (null == foundMethod) {
- foundMethod = doFindMethod(clazz, methodName, argTypes);
- if (null == foundMethod) return null;
- methodMap.computeIfAbsent(
- methodName,
- k -> Collections.newSetFromMap(new ConcurrentHashMap<>(2))
- ).add(foundMethod);
- }
-
- return mhFunc.apply(foundMethod);
- }
-
- private static Method doFindMethod(Class clazz, String messageName, Class[] argTypes) {
- for (Class<?> c = clazz; null != c; c = c.getSuperclass()) {
- List<Method> declaredMethodList = ReflectionUtils.getDeclaredMethods(c, messageName, argTypes);
- if (!declaredMethodList.isEmpty()) {
- Method superMethod = declaredMethodList.get(0);
- if (Modifier.isAbstract(superMethod.getModifiers())) {
- continue;
+ if (foundMethod == null) {
+ for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
+ List<Method> declaredMethods = ReflectionUtils.getDeclaredMethods(c, methodName, argTypes);
+ if (!declaredMethods.isEmpty()) {
+ Method method = declaredMethods.get(0);
+ if (!Modifier.isAbstract(method.getModifiers())) {
+ foundMethod = method;
+ break;
+ }
}
- return superMethod;
}
+ if (foundMethod == null) return null;
+ map.computeIfAbsent(methodName, k -> Collections.newSetFromMap(new ConcurrentHashMap<>(2))).add(foundMethod);
}
- return null;
+
+ return mhFunc.apply(foundMethod);
}
private Object doInvokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
@@ -1494,16 +1462,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
public MetaMethod getMethodWithCaching(Class sender, String methodName, Object[] arguments, boolean isCallToSuper) {
- // let's try use the cache to find the method
if (!isCallToSuper && GroovyCategorySupport.hasCategoryInCurrentThread()) {
return getMethodWithoutCaching(sender, methodName, MetaClassHelper.convertToTypeArray(arguments), isCallToSuper);
}
- final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, methodName);
+ MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, methodName);
+ if (isCallToSuper && e != null && e.methodsForSuper == null) {
+ // allow "super.name()" to find DGM if class declares method "name"
+ e = metaMethodIndex.getMethods(sender.getSuperclass(), methodName);
+ }
if (e == null) {
return null;
}
-
- return isCallToSuper ? getSuperMethodWithCaching(arguments, e) : getNormalMethodWithCaching(arguments, e);
+ if (isCallToSuper) {
+ return getSuperMethodWithCaching(arguments, e);
+ } else {
+ return getNormalMethodWithCaching(arguments, e);
+ }
}
private static boolean sameClasses(final Class[] params, final Class[] arguments) {
@@ -2983,7 +2957,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
private MetaProperty getMetaProperty(final Class clazz, final String name, final boolean useSuper, final boolean useStatic) {
- if (clazz == theClass)
+ if (clazz == theClass && !useSuper)
return getMetaProperty(name, useStatic);
CachedClass cachedClass = ReflectionCache.getCachedClass(clazz);
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
index 4757f8f..9181b6b 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
@@ -110,17 +110,14 @@ public class InvocationWriter {
public void makeCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, boolean safe, final boolean spreadSafe, boolean implicitThis) {
ClassNode sender;
- if (isSuperExpression(receiver) || (isThisExpression(receiver) && !implicitThis)) {
+ if (isSuperExpression(receiver) || isThisExpression(receiver) && !implicitThis) {
+ // GROOVY-6045, GROOVY-8693, et al.
sender = controller.getThisType();
- if (isSuperExpression(receiver)) {
- sender = sender.getSuperClass(); // GROOVY-4035
- implicitThis = false; // prevent recursion
- safe = false; // GROOVY-6045
- }
+ implicitThis = false;
+ safe = false;
} else {
sender = controller.getClassNode();
}
-
makeCall(origin, new ClassExpression(sender), receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
}
@@ -187,8 +184,8 @@ public class InvocationWriter {
}
ClassNode ownerClass = declaringClass;
- if (opcode == INVOKESPECIAL) {
- ownerClass = receiverType; // GROOVY-8693
+ if (opcode == INVOKESPECIAL) { // GROOVY-8693, GROOVY-9909
+ if (!declaringClass.isInterface() || receiverType.implementsInterface(declaringClass)) ownerClass = receiverType;
} else if (opcode == INVOKEVIRTUAL && isObjectType(declaringClass)) {
// avoid using a narrowed type if the method is defined on Object, because it can interfere
// with delegate type inference in static compilation mode and trigger a ClassCastException
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java
index fa463ff..c096cbf 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java
@@ -30,9 +30,9 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveDouble;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveLong;
@@ -84,14 +84,14 @@ public class MopWriter {
private final WriterController controller;
public MopWriter(final WriterController controller) {
- this.controller = Objects.requireNonNull(controller);
+ this.controller = requireNonNull(controller);
}
public void createMopMethods() {
ClassNode classNode = controller.getClassNode();
if (!ClassHelper.isGeneratedFunction(classNode)) {
visitMopMethodList(classNode.getMethods(), true, Collections.emptySet(), Collections.emptyList());
- visitMopMethodList(classNode.getSuperClass().getAllDeclaredMethods(), false, classNode.getMethods().stream()
+ visitMopMethodList(getSuperMethods(classNode)/*GROOVY-8693, et al.*/, false, classNode.getMethods().stream()
.map(mn -> new MopKey(mn.getName(), mn.getParameters())).collect(toSet()), controller.getSuperMethodNames());
}
}
@@ -108,7 +108,7 @@ public class MopWriter {
*
* @see #generateMopCalls(LinkedList, boolean)
*/
- private void visitMopMethodList(final List<MethodNode> methods, final boolean isThis, final Set<MopKey> useOnlyIfDeclaredHereToo, final List<String> orNameMentionedHere) {
+ private void visitMopMethodList(final Iterable<MethodNode> methods, final boolean isThis, final Set<MopKey> onlyIfThis, final List<String> orThis) {
LinkedList<MethodNode> list = new LinkedList<>();
Map<MopKey, MethodNode> map = new HashMap<>();
for (MethodNode mn : methods) {
@@ -124,10 +124,7 @@ public class MopWriter {
Parameter[] parameters = mn.getParameters();
if (isMopMethod(methodName)) {
map.put(new MopKey(methodName, parameters), mn);
- } else if (!methodName.startsWith("<")) {
- if (!useOnlyIfDeclaredHereToo.contains(new MopKey(methodName, parameters)) && !orNameMentionedHere.contains(methodName)) {
- continue;
- }
+ } else if (!methodName.startsWith("<") && (onlyIfThis.contains(new MopKey(methodName, parameters)) || orThis.contains(methodName))) {
if (map.put(new MopKey(getMopMethodName(mn, isThis), parameters), mn) == null) {
list.add(mn);
}
@@ -137,36 +134,6 @@ public class MopWriter {
}
/**
- * Creates a MOP method name from a method.
- *
- * @param method the method to be called by the mop method
- * @param useThis if true, then it is a call on "this", "super" else
- * @return the mop method name
- */
- public static String getMopMethodName(final MethodNode method, final boolean useThis) {
- ClassNode declaringClass = method.getDeclaringClass();
- int distance = 1;
- if (!declaringClass.isInterface()) { // GROOVY-8693: fixed distance for interface methods
- for (ClassNode sc = declaringClass.getSuperClass(); sc != null; sc = sc.getSuperClass()) {
- distance += 1;
- }
- }
- return (useThis ? "this" : "super") + "$" + distance + "$" + method.getName();
- }
-
- /**
- * Determines if a method is a MOP method. This is done by the method name.
- * If the name starts with "this$" or "super$" but does not contain "$dist$",
- * then it is an MOP method.
- *
- * @param methodName name of the method to test
- * @return true if the method is a MOP method
- */
- public static boolean isMopMethod(final String methodName) {
- return (methodName.startsWith("this$") || methodName.startsWith("super$")) && !methodName.contains("$dist$");
- }
-
- /**
* Generates a Meta Object Protocol method that is used to call a non-public
* method or to make a call to super.
*
@@ -196,7 +163,12 @@ public class MopWriter {
// make call to this or super method with operands
ClassNode receiverType = controller.getThisType();
- if (!useThis) receiverType = receiverType.getSuperClass();
+ if (!useThis) {
+ receiverType = receiverType.getSuperClass();
+ ClassNode declaringType = method.getDeclaringClass();
+ // GROOVY-8693, GROOVY-9909, et al.: method from interface not implemented by super class
+ if (declaringType.isInterface() && !receiverType.implementsInterface(declaringType)) receiverType = declaringType;
+ }
mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(receiverType), method.getName(), signature, receiverType.isInterface());
BytecodeHelper.doReturn(mv, returnType);
@@ -207,6 +179,50 @@ public class MopWriter {
}
}
+ //--------------------------------------------------------------------------
+
+ private static Iterable<MethodNode> getSuperMethods(final ClassNode classNode) {
+ Map<String, MethodNode> result = classNode.getSuperClass().getDeclaredMethodsMap();
+ for (ClassNode in : classNode.getInterfaces()) { // declared!
+ if (!classNode.getSuperClass().implementsInterface(in)) {
+ for (MethodNode mn : in.getMethods()) { // only direct default methods!
+ if (mn.isDefault()) result.putIfAbsent(mn.getTypeDescriptor(), mn);
+ }
+ }
+ }
+ return result.values();
+ }
+
+ /**
+ * Creates a MOP method name from a method.
+ *
+ * @param method the method to be called by the mop method
+ * @param useThis if true, then it is a call on "this", "super" else
+ * @return the mop method name
+ */
+ public static String getMopMethodName(final MethodNode method, final boolean useThis) {
+ ClassNode declaringClass = method.getDeclaringClass();
+ int distance = 1;
+ if (!declaringClass.isInterface()) { // GROOVY-8693: fixed distance for interface methods
+ for (ClassNode sc = declaringClass.getSuperClass(); sc != null; sc = sc.getSuperClass()) {
+ distance += 1;
+ }
+ }
+ return (useThis ? "this" : "super") + "$" + distance + "$" + method.getName();
+ }
+
+ /**
+ * Determines if a method is a MOP method. This is done by the method name.
+ * If the name starts with "this$" or "super$" but does not contain "$dist$",
+ * then it is an MOP method.
+ *
+ * @param methodName name of the method to test
+ * @return true if the method is a MOP method
+ */
+ public static boolean isMopMethod(final String methodName) {
+ return (methodName.startsWith("this$") || methodName.startsWith("super$")) && !methodName.contains("$dist$");
+ }
+
@Deprecated
public static boolean equalParameterTypes(final Parameter[] p1, final Parameter[] p2) {
return ParameterUtils.parametersEqual(p1, p2);
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 7283925..6a3dd4a 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -17685,7 +17685,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * Set the metaclass for an object.
+ * Sets the metaclass for an object.
+ *
* @param self the object whose metaclass we want to set
* @param metaClass the new metaclass value
* @since 1.6.0
@@ -17695,20 +17696,20 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
metaClass = ((HandleMetaClass)metaClass).getAdaptee();
if (self instanceof Class) {
- GroovySystem.getMetaClassRegistry().setMetaClass((Class) self, metaClass);
+ GroovySystem.getMetaClassRegistry().setMetaClass((Class)self, metaClass);
} else {
((MetaClassRegistryImpl)GroovySystem.getMetaClassRegistry()).setMetaClass(self, metaClass);
}
}
/**
- * Set the metaclass for a GroovyObject.
+ * Sets the metaclass for a {@code GroovyObject}.
+ *
* @param self the object whose metaclass we want to set
* @param metaClass the new metaclass value
* @since 2.0.0
*/
public static void setMetaClass(GroovyObject self, MetaClass metaClass) {
- // this method was introduced as to prevent from a stack overflow, described in GROOVY-5285
if (metaClass instanceof HandleMetaClass)
metaClass = ((HandleMetaClass)metaClass).getAdaptee();
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 73d239c..5d7b402 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3743,6 +3743,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
addTraitType(receiver, owners);
if (receiver.redirect().isInterface()) {
owners.add(Receiver.make(OBJECT_TYPE));
+ } else if (isSuperExpression(objectExpression)) { //GROOVY-9909: super.defaultMethod()
+ for (ClassNode in : typeCheckingContext.getEnclosingClassNode().getInterfaces()) {
+ if (!receiver.implementsInterface(in)) owners.add(Receiver.make(in));
+ }
}
}
}
diff --git a/src/test/gls/invocation/MethodSelectionTest.groovy b/src/test/gls/invocation/MethodSelectionTest.groovy
index 97379c9..e347bbb 100644
--- a/src/test/gls/invocation/MethodSelectionTest.groovy
+++ b/src/test/gls/invocation/MethodSelectionTest.groovy
@@ -18,500 +18,375 @@
*/
package gls.invocation
-import gls.CompilableTestSupport
-
-class MethodSelectionTest extends CompilableTestSupport {
-
- /**
- * This test ensures Groovy can choose a method based on interfaces.
- * Choosing such an interface should not be hidden by subclasses.
- */
- void testMostSpecificInterface() {
- assertScript """
- interface A{}
- interface B extends A{}
- class C implements B,A{}
- class D extends C{}
- class E implements B{}
-
- def m(A a){1}
- def m(B b){2}
-
- assert m(new D()) == 2
- assert m(new C()) == 2
- assert m(new E()) == 2
- """
-
- assertScript """
- class A implements I1 {}
- class B extends A implements I2 {}
- class C extends B implements I3 {}
- interface I1 {}
- interface I2 extends I1 {}
- interface I3 extends I2 {}
-
- def foo(bar) {0}
- def foo(I1 bar) {1}
- def foo(I2 bar) {2}
- def foo(I3 bar) {3}
-
- assert foo(new A())==1
- assert foo(new B())==2
- assert foo(new C())==3
- """
- }
-
- void testMostGeneralForNull() {
- // we use the same signatures with different method orders,
- // because we want to catch method ordering bugs
- assertScript """
- def m(String x){1}
- def m(Integer x){2}
- assert m(null) == 1
-
- def n(Integer x){2}
- def n(String x){1}
- assert n(null) == 1
- """
-
- // Exception defines Throwable and String versions, which are both equal
- shouldFail """
- new Exception(null)
- """
-
- shouldFail """
- class A {
- public A(String a){}
- public A(Throwable t){}
- public A(){this(null)}
- }
- new A()
- """
-
- shouldFail """
- class A{}
- class B{}
- def m(A a){}
- def m(B b){}
- m(null)
- """
- shouldFail """
- class A extends Exception {
- public A(){super(null)}
- }
- new A()
- """
- }
-
- void testMethodSelectionException() {
- assertScript """
- import org.codehaus.groovy.runtime.metaclass.MethodSelectionException as MSE
-
- def foo(int x) {}
- def foo(Number x) {}
-
- try {
- foo()
- assert false
- } catch (MSE mse) {
- assert mse.message.indexOf("foo()") >0
- assert mse.message.indexOf("#foo(int)") >0
- assert mse.message.indexOf("#foo(java.lang.Number)") >0
- }
-
- """
- }
-
- void testMethodSelectionWithInterfaceVargsMethod() {
+final class MethodSelectionTest extends gls.CompilableTestSupport {
+
+ /**
+ * This test ensures Groovy can choose a method based on interfaces.
+ * Choosing such an interface should not be hidden by subclasses.
+ */
+ void testMostSpecificInterface() {
+ assertScript '''
+ interface A{}
+ interface B extends A{}
+ class C implements B,A{}
+ class D extends C{}
+ class E implements B{}
+
+ def m(A a){1}
+ def m(B b){2}
+
+ assert m(new D()) == 2
+ assert m(new C()) == 2
+ assert m(new E()) == 2
+ '''
+ assertScript '''
+ class A implements I1 {}
+ class B extends A implements I2 {}
+ class C extends B implements I3 {}
+ interface I1 {}
+ interface I2 extends I1 {}
+ interface I3 extends I2 {}
+
+ def foo(bar) {0}
+ def foo(I1 bar) {1}
+ def foo(I2 bar) {2}
+ def foo(I3 bar) {3}
+
+ assert foo(new A())==1
+ assert foo(new B())==2
+ assert foo(new C())==3
+ '''
+ }
+
+ void testMostGeneralForNull() {
+ // we use the same signatures with different method orders,
+ // because we want to catch method ordering bugs
+ assertScript '''
+ def m(String x){1}
+ def m(Integer x){2}
+ assert m(null) == 1
+
+ def n(Integer x){2}
+ def n(String x){1}
+ assert n(null) == 1
+ '''
+ // Exception defines Throwable and String versions, which are both equal
+ shouldFail '''
+ new Exception(null)
+ '''
+ shouldFail '''
+ class A {
+ public A(String a){}
+ public A(Throwable t){}
+ public A(){this(null)}
+ }
+ new A()
+ '''
+ shouldFail '''
+ class A{}
+ class B{}
+ def m(A a){}
+ def m(B b){}
+ m(null)
+ '''
+ shouldFail '''
+ class A extends Exception {
+ public A(){super(null)}
+ }
+ new A()
+ '''
+ }
+
+ void testMethodSelectionException() {
+ assertScript '''
+ import org.codehaus.groovy.runtime.metaclass.MethodSelectionException as MSE
+
+ def foo(int x) {}
+ def foo(Number x) {}
+
+ try {
+ foo()
+ assert false
+ } catch (MSE mse) {
+ assert mse.message.indexOf("foo()") >0
+ assert mse.message.indexOf("#foo(int)") >0
+ assert mse.message.indexOf("#foo(java.lang.Number)") >0
+ }
+ '''
+ }
+
// GROOVY-2719
- assertScript """
- public class Thing {}
- public class Subthing extends Thing {}
+ void testMethodSelectionWithInterfaceVargsMethod() {
+ assertScript '''
+ public class Thing {}
+ public class Subthing extends Thing {}
- def foo(Thing i) {1}
- def foo(Runnable[] l) {2}
+ def foo(Thing i) {1}
+ def foo(Runnable[] l) {2}
- assert foo(new Subthing())==1
- """
- }
+ assert foo(new Subthing())==1
+ '''
+ }
- void testComplexInterfaceInheritance() {
// GROOVY-2698
- assertScript """
- import javax.swing.*
+ void testComplexInterfaceInheritance() {
+ assertScript '''
+ import javax.swing.*
- interface ISub extends Action {}
+ interface ISub extends Action {}
- class Super extends AbstractAction {
- void actionPerformed(java.awt.event.ActionEvent e){}
- }
- class Sub extends AbstractAction implements ISub {
- void actionPerformed(java.awt.event.ActionEvent e){}
- }
+ class Super extends AbstractAction {
+ void actionPerformed(java.awt.event.ActionEvent e){}
+ }
+ class Sub extends AbstractAction implements ISub {
+ void actionPerformed(java.awt.event.ActionEvent e){}
+ }
- static String foo(Action x) { "super" }
- static String foo(ISub x) { "sub" }
+ static String foo(Action x) { "super" }
+ static String foo(ISub x) { "sub" }
- def result = [new Super(), new Sub()].collect { foo(it) }
+ def result = [new Super(), new Sub()].collect { foo(it) }
- assert ["super","sub"] == result
- """
+ assert ["super","sub"] == result
+ '''
+ }
- // GROOVY-6001
- assertScript """
- class Mark {
- static log = ""
+ void testNullUsageForPrimitivesWithExplicitNull() {
+ [byte,int,short,float,double,boolean,char].each { type ->
+ assertScript """
+ def foo($type x) {}
+
+ boolean catched = false
+ try {
+ foo(null)
+ } catch (MissingMethodException mme) {
+ catched = true
+ }
+ def txt = "expected to see a MissingMethodEception when using foo(null) "+
+ "to call foo($type), but no exception was thrown"
+ assert catched, txt
+ """
}
+ }
- interface PortletRequest {}
- interface PortletResponse {}
- interface HttpServletRequest {}
- interface HttpServletResponse {}
- class HttpServletRequestWrapper implements HttpServletRequest {}
- class PortletRequestImpl extends HttpServletRequestWrapper implements PortletRequest {}
- class ClientDataRequestImpl extends PortletRequestImpl {}
- class ResourceRequestImpl extends ClientDataRequestImpl {}
- class Application {}
- interface HttpServletRequestListener {
- void onRequestStart(HttpServletRequest request, HttpServletResponse response);
- void onRequestEnd(HttpServletRequest request, HttpServletResponse response);
+ void testNullUsageForPrimitivesWithImplicitNull() {
+ [byte,int,short,float,double,boolean,char].each { type ->
+ assertScript """
+ def foo($type x) {}
+
+ boolean catched = false
+ try {
+ foo()
+ } catch (MissingMethodException mme) {
+ catched = true
+ }
+ def txt = "expected to see a MissingMethodEception when using foo(null) "+
+ "to call foo($type), but no exception was thrown"
+ assert catched, txt
+ """
}
- interface PortletRequestListener {
- void onRequestStart(PortletRequest request, PortletResponse response);
- void onRequestEnd(PortletRequest request, PortletResponse response);
+ }
+
+ void testNullUsageForPrimitivesAndOverloading() {
+ [byte,int,short,float,double].each { type ->
+ assertScript """
+ def foo(String x){1}
+ def foo($type x) {2}
+
+ def txt = "foo($type) was called where foo(String) should have been called"
+ assert foo(null)==1, txt
+ """
}
+ }
- class FooApplication extends Application implements HttpServletRequestListener, PortletRequestListener{
- void onRequestStart(HttpServletRequest request, HttpServletResponse response) {
- Mark.log += "FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)"
+ void testPrivateMethodSelectionFromClosure(){
+ assertScript '''
+ class I1 {
+ private foo() {1}
+ def bar(){
+ def x = "foo"
+ return {this."\$x"()}
+ }
}
- void onRequestEnd(HttpServletRequest request, HttpServletResponse response) {
- Mark.log += "FooApplication.onRequestEnd(HttpServletRequest, HttpServletResponse)"
+ class I2 extends I1 {
+ def foo(){2}
}
- void onRequestStart(PortletRequest request, PortletResponse response) {
- Mark.log += "FooApplication.onRequestStart(PortletRequest, PortletResponse)"
- }
- void onRequestEnd(PortletRequest request, PortletResponse response) {
- Mark.log += "FooApplication.onRequestEnd(PortletRequest, PortletResponse)"
- }
- }
+ def i = new I2()
+ assert i.bar()() == 1
+ '''
+ }
- class BazApplication extends FooApplication {
- void onRequestStart(PortletRequest request, PortletResponse response) {
- Mark.log += 'BazApplication.onRequestStart(PortletRequest, PortletResponse)'
- super.onRequestStart(request, response);
+ void testCachingForNullAndPrimitive(){
+ assertScript '''
+ boolean shaky(boolean defaultValue) {false}
+ shaky(false)
+ try {
+ shaky(null)
+ assert false : "method caching allowed null for primitive"
+ } catch (MissingMethodException mme){
+ assert true
}
- }
-
- BazApplication application = new BazApplication()
- Mark.log = ""
- PortletRequest request = new ResourceRequestImpl()
- application.onRequestStart(request, null)
- assert Mark.log == "BazApplication.onRequestStart(PortletRequest, PortletResponse)FooApplication.onRequestStart(PortletRequest, PortletResponse)"
- """
- }
-
- void testNullUsageForPrimitivesWithExplicitNull() {
- [byte,int,short,float,double,boolean,char].each { type ->
- assertScript """
- def foo($type x) {}
-
- boolean catched = false
- try {
- foo(null)
- } catch (MissingMethodException mme) {
- catched = true
- }
- def txt = "expected to see a MissingMethodEception when using foo(null) "+
- "to call foo($type), but no exception was thrown"
- assert catched, txt
- """
+ '''
}
- }
-
- void testNullUsageForPrimitivesWithImplicitNull() {
- [byte,int,short,float,double,boolean,char].each { type ->
- assertScript """
- def foo($type x) {}
-
- boolean catched = false
- try {
- foo()
- } catch (MissingMethodException mme) {
- catched = true
- }
- def txt = "expected to see a MissingMethodEception when using foo(null) "+
- "to call foo($type), but no exception was thrown"
- assert catched, txt
- """
+
+ void testSpreadOperatorAndVarargs(){
+ assertScript '''
+ class SpreadBug {
+ def foo(String... args) {
+ bar(*args)
+ }
+ def bar(String... args) {args.length}
+ }
+ def sb = new SpreadBug()
+ assert sb.foo("1","42")==2
+ '''
}
- }
-
- void testNullUsageForPrimitivesAndOverloading() {
- [byte,int,short,float,double].each { type ->
- assertScript """
- def foo(String x){1}
- def foo($type x) {2}
-
- def txt = "foo($type) was called where foo(String) should have been called"
- assert foo(null)==1, txt
- """
- }
- }
-
- void testPrivateMethodSelectionFromClosure(){
- assertScript """
- class I1 {
- private foo() {1}
- def bar(){
- def x = "foo"
- return {this."\$x"()}
- }
- }
- class I2 extends I1 {
- def foo(){2}
- }
- def i = new I2()
- assert i.bar()() == 1
-
- """
- }
-
- void testCachingForNullAndPrimitive(){
- assertScript """
- boolean shaky(boolean defaultValue) {false}
- shaky(false)
- try {
- shaky(null)
- assert false : "method caching allowed null for primitive"
- } catch (MissingMethodException mme){
- assert true
- }
- """
- }
-
- void testSpreadOperatorAndVarargs(){
- assertScript """
- class SpreadBug {
- def foo(String... args) {
- bar(*args)
- }
- def bar(String... args) {args.length}
- }
- def sb = new SpreadBug()
- assert sb.foo("1","42")==2
- """
- }
-
- void testBDandBIToFloatAutoConversionInMethodSelection() {
- def foo = new Foo3977()
-
- // double examples were working already but float ones were not
- // adding both here just to make sure the inconsistency doesn't creep in again
- foo.setMyDouble1 1.0
- assert foo.getMyDouble1() == 1.0
-
- foo.setMyDouble1 new BigInteger('1')
- assert foo.getMyDouble1() == 1.0
-
- foo.setMyDouble2 1.0
- assert foo.getMyDouble2() == 1.0
-
- foo.setMyDouble2 new BigInteger('1')
- assert foo.getMyDouble2() == 1.0
-
- foo.setMyFloat1 1.0
- assert foo.getMyFloat1() == 1.0
-
- foo.setMyFloat1 new BigInteger('1')
- assert foo.getMyFloat1() == 1.0
-
- foo.setMyFloat2 1.0
- assert foo.getMyFloat2() == 1.0
-
- foo.setMyFloat2 new BigInteger('1')
- assert foo.getMyFloat2() == 1.0
- }
-
- void testCallWithExendedBigDecimal() {
- assertScript """
- BigDecimal f (BigDecimal x) {
- return x
- }
- public class ExtendedBigDecimal extends java.math.BigDecimal {
- public ExtendedBigDecimal (int i) {
- super (i)
+ // GROOVY-3977
+ void testBDandBIToFloatAutoConversionInMethodSelection() {
+ assertScript '''
+ class Foo3977 {
+ double myDouble1
+ Double myDouble2
+ float myFloat1
+ Float myFloat2
}
- }
- assert f(new ExtendedBigDecimal(1)) == 1
- """
- }
+ def foo = new Foo3977()
- void testVargsClass() {
- assertScript """
- interface Parent {}
- interface Child extends Parent {}
+ // double examples were working already but float ones were not
+ // adding both here just to make sure the inconsistency doesn't creep in again
+ foo.setMyDouble1 1.0
+ assert foo.getMyDouble1() == 1.0
- class Child1 implements Child { }
- def a = new Child1()
+ foo.setMyDouble1 new BigInteger('1')
+ assert foo.getMyDouble1() == 1.0
- class Child2 implements Child { }
- def b = new Child2()
+ foo.setMyDouble2 1.0
+ assert foo.getMyDouble2() == 1.0
- def foo(Parent... values) {
- assert values.class == Parent[]
- }
- foo(a, b)
- """
- }
-
- // GROOVY-5812
- void testDirectMethodCall() {
- assertScript """
- private String[] getStringArrayDirectly() { ["string_00", "string_01"] }
- private String[] getStringArrayIndirectlyWithType(String[] stringarray) { stringarray }
- private String[] getStringArrayIndirectlyWithoutType(stringarray) { stringarray }
-
- public int getStringArrayDirectly_Length() { getStringArrayDirectly().length }
- public int getStringArrayIndirectlyWithType_Length() { getStringArrayIndirectlyWithType(getStringArrayDirectly()).length }
- public int getStringArrayIndirectlyWithoutType_Length() { getStringArrayIndirectlyWithoutType(getStringArrayDirectly()).length }
-
- assert getStringArrayDirectly_Length() == getStringArrayIndirectlyWithoutType_Length()
- assert getStringArrayDirectly_Length() == getStringArrayIndirectlyWithType_Length()
- assert getStringArrayIndirectlyWithType_Length() == getStringArrayIndirectlyWithoutType_Length()
- """
- }
-
- // GROOVY-6189, GROOVY-9852
- void testSAMs() {
- // simple direct case
- assertScript '''
- interface MySAM {
- def someMethod()
- }
- def foo(MySAM sam) {sam.someMethod()}
- assert foo {1} == 1
- '''
-
- // overloads with classes implemented by Closure
- [
- 'groovy.lang.Closure' : 'not',
- 'groovy.lang.GroovyCallable' : 'not',
- 'groovy.lang.GroovyObject' : 'not',
- 'groovy.lang.GroovyObjectSupport': 'not',
-
- 'java.lang.Object' : 'sam',
- 'java.lang.Runnable' : 'not',
- 'java.lang.Cloneable' : 'not',
- 'java.io.Serializable' : 'not',
- 'java.util.concurrent.Callable' : 'not',
- ].each { type, which ->
- assertScript """
- interface MySAM {
- def someMethod()
- }
- def foo($type ref) { 'not' }
- def foo(MySAM sam) { sam.someMethod() }
- assert foo { 'sam' } == '$which' : '$type'
- """
- }
- }
-
- // GROOVY-6431
- void testBigDecAndBigIntSubClass() {
- assertScript'''
- class MyDecimal extends BigDecimal {
- public MyDecimal(String s) {super(s)}
- }
- class MyInteger extends BigInteger {
- public MyInteger(String s) {super(s)}
- }
- class Expression {
- public int takeNumber(Number a) {return 1}
- public int takeBigDecimal(BigDecimal a) {return 2}
- public int takeBigInteger(BigInteger a) {return 3}
- }
-
- Expression exp = new Expression();
- assert 1 == exp.takeNumber(new MyInteger("3"))
- assert 2 == exp.takeBigDecimal(new MyDecimal("3.0"))
- assert 3 == exp.takeBigInteger(new MyInteger("3"))
- '''
- }
-
- // GROOVY-7655
- void testOverloadAndSuper() {
- assertScript '''
- class A {
- boolean aCalled = false
- def myMethod( def item ) {
- aCalled = true
- }
- }
+ foo.setMyDouble2 new BigInteger('1')
+ assert foo.getMyDouble2() == 1.0
- class B extends A {
- boolean bCalled = false
- def myMethod( def item ) {
- super.myMethod( item+"B" )
- bCalled = true
- }
- }
+ foo.setMyFloat1 1.0
+ assert foo.getMyFloat1() == 1.0
- class C extends B {
- boolean cCalled = false
- def cMethod( def item ) {
- super.myMethod( item )
- cCalled = true
- }
- }
+ foo.setMyFloat1 new BigInteger('1')
+ assert foo.getMyFloat1() == 1.0
+
+ foo.setMyFloat2 1.0
+ assert foo.getMyFloat2() == 1.0
+
+ foo.setMyFloat2 new BigInteger('1')
+ assert foo.getMyFloat2() == 1.0
+ '''
+ }
- def c = new C()
- c.cMethod( "stuff" )
-
- assert c.aCalled
- assert c.bCalled
- assert c.cCalled
- '''
- assertScript '''
- class A {
- boolean aCalled = false
- def myMethod( def item ) {
- aCalled = true
+ void testCallWithExendedBigDecimal() {
+ assertScript '''
+ BigDecimal f (BigDecimal x) {
+ return x
}
- }
- class B extends A {
- boolean bCalled = false
- def myMethod( def item ) {
- super.myMethod( item+"B" )
- bCalled = true
+ public class ExtendedBigDecimal extends java.math.BigDecimal {
+ public ExtendedBigDecimal (int i) {
+ super (i)
+ }
}
- }
- class C extends B { }
- class D extends C {
- boolean dCalled = false
- def dMethod( def item ) {
- super.myMethod( item )
- dCalled = true
+ assert f(new ExtendedBigDecimal(1)) == 1
+ '''
+ }
+
+ // GROOVY-6431
+ void testBigDecAndBigIntSubClass() {
+ assertScript'''
+ class MyDecimal extends BigDecimal {
+ public MyDecimal(String s) {super(s)}
}
- }
+ class MyInteger extends BigInteger {
+ public MyInteger(String s) {super(s)}
+ }
+ class Expression {
+ public int takeNumber(Number a) {return 1}
+ public int takeBigDecimal(BigDecimal a) {return 2}
+ public int takeBigInteger(BigInteger a) {return 3}
+ }
+
+ Expression exp = new Expression();
+ assert 1 == exp.takeNumber(new MyInteger("3"))
+ assert 2 == exp.takeBigDecimal(new MyDecimal("3.0"))
+ assert 3 == exp.takeBigInteger(new MyInteger("3"))
+ '''
+ }
- def d = new D()
- d.dMethod( "stuff" )
+ void testVargsClass() {
+ assertScript '''
+ interface Parent {}
+ interface Child extends Parent {}
- assert d.aCalled
- assert d.bCalled
- assert d.dCalled
- '''
- }
-}
+ class Child1 implements Child { }
+ def a = new Child1()
-class Foo3977 {
- double myDouble1
- Double myDouble2
- float myFloat1
- Float myFloat2
+ class Child2 implements Child { }
+ def b = new Child2()
+
+ def foo(Parent... values) {
+ assert values.class == Parent[]
+ }
+ foo(a, b)
+ '''
+ }
+
+ // GROOVY-5812
+ void testDirectMethodCall() {
+ assertScript '''
+ private String[] getStringArrayDirectly() { ["string_00", "string_01"] }
+ private String[] getStringArrayIndirectlyWithType(String[] stringarray) { stringarray }
+ private String[] getStringArrayIndirectlyWithoutType(stringarray) { stringarray }
+
+ public int getStringArrayDirectly_Length() { getStringArrayDirectly().length }
+ public int getStringArrayIndirectlyWithType_Length() { getStringArrayIndirectlyWithType(getStringArrayDirectly()).length }
+ public int getStringArrayIndirectlyWithoutType_Length() { getStringArrayIndirectlyWithoutType(getStringArrayDirectly()).length }
+
+ assert getStringArrayDirectly_Length() == getStringArrayIndirectlyWithoutType_Length()
+ assert getStringArrayDirectly_Length() == getStringArrayIndirectlyWithType_Length()
+ assert getStringArrayIndirectlyWithType_Length() == getStringArrayIndirectlyWithoutType_Length()
+ '''
+ }
+
+ // GROOVY-6189, GROOVY-9852
+ void testSAMs() {
+ // simple direct case
+ assertScript '''
+ interface MySAM {
+ def someMethod()
+ }
+ def foo(MySAM sam) {sam.someMethod()}
+ assert foo {1} == 1
+ '''
+
+ // overloads with classes implemented by Closure
+ [
+ 'groovy.lang.Closure' : 'not',
+ 'groovy.lang.GroovyCallable' : 'not',
+ 'groovy.lang.GroovyObject' : 'not',
+ 'groovy.lang.GroovyObjectSupport': 'not',
+
+ 'java.lang.Object' : 'sam',
+ 'java.lang.Runnable' : 'not',
+ 'java.lang.Cloneable' : 'not',
+ 'java.io.Serializable' : 'not',
+ 'java.util.concurrent.Callable' : 'not',
+ ].each { type, which ->
+ assertScript """
+ interface MySAM {
+ def someMethod()
+ }
+ def foo($type ref) { 'not' }
+ def foo(MySAM sam) { sam.someMethod() }
+ assert foo { 'sam' } == '$which' : '$type'
+ """
+ }
+ }
}
diff --git a/src/test/groovy/ThisAndSuperTest.groovy b/src/test/groovy/ThisAndSuperTest.groovy
index b948b76..79ecefc 100644
--- a/src/test/groovy/ThisAndSuperTest.groovy
+++ b/src/test/groovy/ThisAndSuperTest.groovy
@@ -18,6 +18,7 @@
*/
package groovy
+import org.junit.Ignore
import org.junit.Test
import static groovy.test.GroovyAssert.assertScript
@@ -39,10 +40,10 @@ final class ThisAndSuperTest {
assert helper.aClosureUsingSuper() == 1
// accessing private method should not be changed
// by a public method of the same name and signature!
- assert helper.closureUsingPrivateMethod() == "bar"
- assert helper.bar() == "no bar"
+ assert helper.closureUsingPrivateMethod() == 'bar'
+ assert helper.bar() == 'no bar'
- assert helper.aField == "I am a field"
+ assert helper.aField == 'I am a field'
helper.closureFieldAccessUsingImplicitThis(1)
assert helper.aField == 1
helper.closureFieldAccessUsingExplicitThis(2)
@@ -54,13 +55,13 @@ final class ThisAndSuperTest {
def map = [:]
def helper = new TestForSuperHelper2()
- helper.aField = "I am a field"
+ helper.aField = 'I am a field'
helper.closureFieldAccessUsingExplicitThis.delegate = map
helper.closureFieldAccessUsingExplicitThis(3)
assert helper.aField == 3
assert map.aField == null
- helper.aField = "I am a field"
+ helper.aField = 'I am a field'
helper.closureFieldAccessUsingImplicitThis.delegate = map
helper.closureFieldAccessUsingImplicitThis(4)
assert helper.aField == 4
@@ -87,49 +88,333 @@ final class ThisAndSuperTest {
@Test
void testConstructorChain() {
- def helper = new TestForSuperHelper4()
- assert helper.x == 1
- helper = new TestForSuperHelper4("foo")
- assert helper.x == "Object"
+ assertScript '''
+ class TestForSuperHelper3 {
+ def x
+
+ TestForSuperHelper3(int i) {
+ this("1")
+ x = 1
+ }
+
+ TestForSuperHelper3(Object j) {
+ x = "Object"
+ }
+ }
+
+ class TestForSuperHelper4 extends TestForSuperHelper3 {
+ TestForSuperHelper4() {
+ super(1)
+ }
+
+ TestForSuperHelper4(Object j) {
+ super(j)
+ }
+ }
+
+ def helper = new TestForSuperHelper4()
+ assert helper.x == 1
+ helper = new TestForSuperHelper4("foo")
+ assert helper.x == "Object"
+ '''
}
@Test
- void testChainingForAsType() {
- def helper = new TestForSuperHelper1()
- def ret = helper as Object[]
- assert ret instanceof Object[]
- assert ret[0] == helper
-
- shouldFail(ClassCastException) {
- helper as Integer
- }
+ void testSuperAsTypeChain() {
+ assertScript '''
+ class C {
+ def <T> T asType(Class<T> type) {
+ if (type == Object[]) {
+ return [this] as Object[]
+ }
+ return super.asType(type)
+ }
+ }
+
+ def x = new C()
+ def y = x as Object[]
+ assert y instanceof Object[]
+ assert y[0] === x
+
+ try {
+ x as Integer
+ assert false : 'should fail'
+ } catch (ClassCastException cce) {
+ }
+ '''
}
@Test
- void testSuperEach() {
- def x = new TestForSuperEach()
- x.each {
- x.res << "I am it: ${it.class.name}"
- }
+ void testSuperEachChain() {
+ assertScript '''
+ class C {
+ def out = []
+ def each(Closure c) {
+ out << "start each in subclass"
+ super.each(c)
+ out << "end of each in subclass"
+ }
+ }
+
+ def x = new C()
+ x.each {
+ x.out << "I am ${it.class.name}"
+ }
+
+ assert x.out.size() == 3
+ assert x.out[0] == "start each in subclass"
+ assert x.out[1] == "I am C"
+ assert x.out[2] == "end of each in subclass"
+ '''
+ }
- assert x.res.size() == 3
- assert x.res[0] == "start each in subclass"
- assert x.res[1] == "I am it: groovy.TestForSuperEach"
- assert x.res[2] == "end of each in subclass"
+ @Ignore @Test // GROOVY-5285
+ void testSuperSetMetaClassChain() {
+ assertScript '''
+ class C {
+ void setMetaClass(MetaClass metaClass) {
+ super.setMetaClass(metaClass)
+ }
+ }
+
+ def x = new C()
+ x.metaClass = x.metaClass
+ '''
+ }
+
+ @Test // GROOVY-9884
+ void testSuperSetPropertyChain() {
+ assertScript '''
+ class A {
+ def p = "p"
+ }
+ class B extends A {
+ }
+ class C extends B {
+ void setProperty(String name, Object value) {
+ super.setProperty(name, value)
+ }
+ }
+
+ def x = new C()
+ x.setProperty("p", "q")
+ assert x.p == "q"
+ '''
}
@Test // GROOVY-2555
- void testAbstractSuperMethodShouldBeTreatedLikeMissingMethod() {
+ void testSuperDotAbstractMethodShouldBeTreatedLikeMissingMethod() {
shouldFail MissingMethodException, '''
abstract class A {
abstract void m()
}
- class B extends A {
+ class C extends A {
void m() {
super.m()
}
}
- new B().m()
+ new C().m()
+ '''
+ }
+
+ @Test // GROOVY-4945
+ void testSuperDotSelfMethod1() {
+ def err = shouldFail MissingMethodException, '''
+ class C {
+ void test() {
+ super.whatever()
+ }
+ void whatever() {
+ assert false : 'should not have been called!'
+ }
+ }
+ new C().test()
+ '''
+ assert err =~ /No signature of method: java\.lang\.Object\.whatever\(\) is applicable for argument types: \(\) values: \[\]/
+ }
+
+ @Test // GROOVY-9615
+ void testSuperDotSelfMethod2() {
+ def err = shouldFail MissingMethodException, '''
+ class Outer {
+ class Inner {
+ void test() {
+ super.whatever()
+ }
+ }
+ void whatever() {
+ assert false : 'should not have been called!'
+ }
+ }
+ new Outer.Inner(new Outer()).test()
+ '''
+ assert err =~ /No signature of method: java\.lang\.Object\.whatever\(\) is applicable for argument types: \(\) values: \[\]/
+ }
+
+ @Test // GROOVY-6001
+ void testSuperDotMethod1() {
+ assertScript '''
+ class The {
+ static log = ""
+ }
+
+ interface PortletRequest {}
+ interface PortletResponse {}
+ interface HttpServletRequest {}
+ interface HttpServletResponse {}
+ class HttpServletRequestWrapper implements HttpServletRequest {}
+ class PortletRequestImpl extends HttpServletRequestWrapper implements PortletRequest {}
+ class ClientDataRequestImpl extends PortletRequestImpl {}
+ class ResourceRequestImpl extends ClientDataRequestImpl {}
+ class Application {}
+ interface HttpServletRequestListener {
+ void onRequestStart(HttpServletRequest request, HttpServletResponse response);
+ void onRequestEnd(HttpServletRequest request, HttpServletResponse response);
+ }
+ interface PortletRequestListener {
+ void onRequestStart(PortletRequest request, PortletResponse response);
+ void onRequestEnd(PortletRequest request, PortletResponse response);
+ }
+
+ class FooApplication extends Application implements HttpServletRequestListener, PortletRequestListener{
+ void onRequestStart(HttpServletRequest request, HttpServletResponse response) {
+ The.log += "FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)"
+ }
+ void onRequestEnd(HttpServletRequest request, HttpServletResponse response) {
+ The.log += "FooApplication.onRequestEnd(HttpServletRequest, HttpServletResponse)"
+ }
+ void onRequestStart(PortletRequest request, PortletResponse response) {
+ The.log += "FooApplication.onRequestStart(PortletRequest, PortletResponse)"
+ }
+ void onRequestEnd(PortletRequest request, PortletResponse response) {
+ The.log += "FooApplication.onRequestEnd(PortletRequest, PortletResponse)"
+ }
+ }
+
+ class BazApplication extends FooApplication {
+ void onRequestStart(PortletRequest request, PortletResponse response) {
+ The.log += 'BazApplication.onRequestStart(PortletRequest, PortletResponse)'
+ super.onRequestStart(request, response);
+ }
+ }
+
+ BazApplication application = new BazApplication()
+ The.log = ""
+ PortletRequest request = new ResourceRequestImpl()
+ application.onRequestStart(request, null)
+ assert The.log == "BazApplication.onRequestStart(PortletRequest, PortletResponse)FooApplication.onRequestStart(PortletRequest, PortletResponse)"
+ '''
+ }
+
+ @Test // GROOVY-7655
+ void testSuperDotMethod2() {
+ assertScript '''
+ class A {
+ def myMethod(item) {
+ aCalled = true
+ item+"A"
+ }
+ protected boolean aCalled
+ }
+
+ class B extends A {
+ def myMethod(item) {
+ bCalled = true
+ super.myMethod(item+"B")
+ }
+ protected boolean bCalled
+ }
+
+ class C extends B {
+ def cMethod(item) {
+ cCalled = true
+ super.myMethod(item+"C")
+ }
+ protected boolean cCalled
+ }
+
+ def c = new C()
+ c.cMethod("")
+
+ assert c.aCalled
+ assert c.bCalled
+ assert c.cCalled
+ '''
+ }
+
+ @Test // GROOVY-7655
+ void testSuperDotMethod3() {
+ assertScript '''
+ class A {
+ def myMethod(item) {
+ aCalled = true
+ item+"A"
+ }
+ protected boolean aCalled
+ }
+
+ class B extends A {
+ def myMethod(item) {
+ bCalled = true
+ super.myMethod(item+"B")
+ }
+ protected boolean bCalled
+ }
+
+ class C extends B {
+ }
+
+ class D extends C {
+ def dMethod( def item ) {
+ dCalled = true
+ super.myMethod(item+"D")
+ }
+ protected boolean dCalled
+ }
+
+ def d = new D()
+ d.dMethod("")
+
+ assert d.aCalled
+ assert d.bCalled
+ assert d.dCalled
+ '''
+ }
+
+ @Test // GROOVY-10302
+ void testSuperDotMethod4() {
+ shouldFail ClassNotFoundException, '''
+ void test() {
+ def list = []
+ def loader = new GroovyClassLoader() {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ list.add(name); super.findClass(name)
+ }
+ }
+ try (loader) {
+ loader.findClass('foo.bar.Baz')
+ } finally {
+ assert 'foo.bar.Baz' in list
+ }
+ }
+ test()
+ '''
+ }
+
+ @Test // GROOVY-9851
+ void testPrivateSuperMethod() {
+ shouldFail MissingMethodException, '''
+ abstract class A {
+ private x() {
+ }
+ }
+ class C extends A {
+ void m() {
+ super.x()
+ }
+ }
+ new C().m()
'''
}
@@ -140,11 +425,11 @@ final class ThisAndSuperTest {
private x = 1
def getX() { 2 }
}
- class B extends A {
+ class C extends A {
private x = 3
def m() { super.@x }
}
- new B().m()
+ new C().m()
'''
assert err =~ /No such field: x for class: A/
@@ -158,11 +443,11 @@ final class ThisAndSuperTest {
def getX() { 2 }
void setX(x) { this.x = 3 }
}
- class B extends A {
+ class C extends A {
private x = 4
def m() { super.@x = 5; return x }
}
- new B().m()
+ new C().m()
'''
assert err =~ /No such field: x for class: A/
@@ -183,27 +468,12 @@ final class ThisAndSuperTest {
//------------------------------------------------------------------------------
-class TestForSuperEach {
- def res = []
-
- def each(Closure c) {
- res << "start each in subclass"
- super.each(c)
- res << "end of each in subclass"
- }
-}
-
class TestForSuperHelper1 {
def foo() {1}
- private bar() {"bar"}
+ private bar() {'bar'}
def closureUsingPrivateMethod() {bar()}
-
- def asType(Class c) {
- if (c == Object[]) return [this] as Object[]
- return super.asType(c)
- }
}
class TestForSuperHelper2 extends TestForSuperHelper1 {
@@ -214,32 +484,9 @@ class TestForSuperHelper2 extends TestForSuperHelper1 {
def aClosureUsingSuper = {super.foo()}
def aClosureUsingThis = {this.foo()}
- def bar() {"no bar"}
+ def bar() {'no bar'}
- public aField = "I am a field"
+ public aField = 'I am a field'
def closureFieldAccessUsingImplicitThis = {x -> aField = x}
def closureFieldAccessUsingExplicitThis = {x -> this.aField = x}
}
-
-class TestForSuperHelper3 {
- def x
-
- TestForSuperHelper3(int i) {
- this("1")
- x = 1
- }
-
- TestForSuperHelper3(Object j) {
- x = "Object"
- }
-}
-
-class TestForSuperHelper4 extends TestForSuperHelper3 {
- TestForSuperHelper4() {
- super(1)
- }
-
- TestForSuperHelper4(Object j) {
- super(j)
- }
-}
diff --git a/src/test/groovy/bugs/Groovy5212.groovy b/src/test/groovy/bugs/Groovy5212.groovy
new file mode 100644
index 0000000..8c2dc4e
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy5212.groovy
@@ -0,0 +1,62 @@
+/*
+ * 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 groovy.bugs
+
+import groovy.transform.AutoFinal
+import org.codehaus.groovy.antlr.EnumHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.CompileUnit
+import org.codehaus.groovy.ast.ModuleNode
+import org.codehaus.groovy.tools.javac.JavaStubGenerator
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.shouldFail
+import static org.objectweb.asm.Opcodes.*
+
+@AutoFinal
+final class Groovy5212 {
+
+ @Test
+ void testShouldNotAllowExtendingEnum() {
+ shouldFail '''
+ enum MyEnum { X }
+ enum MyExtendedEnum extends MyEnum { Y }
+ '''
+ }
+
+ @Test
+ void testShouldNotAllowExtendingEnumWithClass() {
+ shouldFail '''
+ enum MyEnum { X }
+ class MyExtendedEnum extends MyEnum { }
+ '''
+ }
+
+ @Test
+ void testGeneratedEnumJavaStubShouldNotHaveFinalModifier() {
+ ClassNode cn = EnumHelper.makeEnumNode('MyEnum', ACC_PUBLIC, ClassNode.EMPTY_ARRAY, null)
+ ModuleNode module = new ModuleNode(new CompileUnit(null, null))
+ cn.module = module
+
+ String stub = new JavaStubGenerator(null).with {
+ generateClass(cn); javaStubCompilationUnitSet[0].getCharContent(true)
+ }
+ assert !(stub =~ /final/)
+ }
+}
diff --git a/src/test/groovy/bugs/Groovy5212Bug.groovy b/src/test/groovy/bugs/Groovy5212Bug.groovy
deleted file mode 100644
index 0f09cd0..0000000
--- a/src/test/groovy/bugs/Groovy5212Bug.groovy
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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 groovy.bugs
-
-import groovy.test.GroovyTestCase
-import org.codehaus.groovy.ast.ClassNode
-import org.objectweb.asm.Opcodes
-import org.codehaus.groovy.tools.javac.JavaStubGenerator
-import org.codehaus.groovy.ast.ModuleNode
-import org.codehaus.groovy.ast.CompileUnit
-import org.codehaus.groovy.antlr.EnumHelper
-
-class Groovy5212Bug extends GroovyTestCase implements Opcodes {
- File outputDir
-
- @Override
- protected void setUp() {
- super.setUp()
- outputDir = File.createTempFile("stub","groovy")
- outputDir.mkdirs()
- }
-
- @Override
- protected void tearDown() {
- super.tearDown()
- outputDir.delete()
- }
-
- void testGeneratedEnumJavaStubShouldNotHaveFinalModifier() {
- ClassNode cn = EnumHelper.makeEnumNode("MyEnum", ACC_PUBLIC, ClassNode.EMPTY_ARRAY, null)
- ModuleNode module = new ModuleNode(new CompileUnit(null,null))
- cn.setModule(module)
- StringWriter wrt = new StringWriter()
- PrintWriter out = new PrintWriter(wrt)
- JavaStubGenerator generator = new StringJavaStubGenerator(outputDir, out)
- generator.generateClass(cn)
-
- String stub = wrt.toString()
- assert !(stub =~ /final/)
- }
-
- void testShouldNotAllowExtendingEnum() {
- shouldFail {
- assertScript '''
- enum MyEnum { a }
- enum MyExtendedEnum extends MyEnum { b }
- '''
- }
- }
-
- void testShouldNotAllowExtendingEnumWithClass() {
- shouldFail {
- assertScript '''
- enum MyEnum { a }
- class MyExtendedEnum extends MyEnum { }
- new MyExtendedEnum()
- '''
- }
- }
-
- /**
- * Helper class, which generates code in a string instead of an output file.
- */
- private static final class StringJavaStubGenerator extends JavaStubGenerator {
- PrintWriter out
- StringJavaStubGenerator(File outFile, PrintWriter out) {
- super(outFile)
- this.out = out
- }
- public void generateClass(ClassNode classNode) throws FileNotFoundException {
-
-
- try {
- String packageName = classNode.getPackageName();
- if (packageName != null) {
- out.println("package " + packageName + ";\n");
- }
-
- super.printImports(out, classNode);
- super.printClassContents(out, classNode);
-
- } finally {
- try {
- out.close();
- } catch (Exception e) {
- // ignore
- }
- }
- }
- }
-}
diff --git a/src/test/groovy/bugs/Groovy5260Bug.groovy b/src/test/groovy/bugs/Groovy5260.groovy
similarity index 63%
rename from src/test/groovy/bugs/Groovy5260Bug.groovy
rename to src/test/groovy/bugs/Groovy5260.groovy
index b193651..60dffde 100644
--- a/src/test/groovy/bugs/Groovy5260Bug.groovy
+++ b/src/test/groovy/bugs/Groovy5260.groovy
@@ -18,59 +18,51 @@
*/
package groovy.bugs
-import groovy.test.GroovyTestCase
import groovy.transform.AutoFinal
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.CompileUnit
import org.codehaus.groovy.ast.ModuleNode
import org.codehaus.groovy.tools.javac.JavaStubGenerator
+import org.junit.Test
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
import static org.objectweb.asm.Opcodes.*
@AutoFinal
-final class Groovy5260Bug extends GroovyTestCase {
-
- private File outputDir
-
- @Override
- protected void setUp() {
- super.setUp()
- outputDir = File.createTempFile('stub', 'groovy')
- outputDir.mkdirs()
- }
-
- @Override
- protected void tearDown() {
- super.tearDown()
- outputDir.delete()
- }
+final class Groovy5260 {
+ @Test
void testIntConstant() {
checkConstant(ClassHelper.int_TYPE, 666, /final int constant = 666;/)
}
+ @Test
void testLongConstant() {
checkConstant(ClassHelper.long_TYPE, 666, 'final long constant = 666L;')
}
+ @Test
void testFloatConstant() {
checkConstant(ClassHelper.float_TYPE, 3.14f, 'final float constant = 3.14f;')
}
+ @Test
void testByteConstant() {
checkConstant(ClassHelper.byte_TYPE, 123, /final byte constant = \(byte\)123;/)
}
+ @Test
void testBooleanConstant() {
checkConstant(ClassHelper.boolean_TYPE, true, 'final boolean constant = true;')
}
+ @Test
void testCharConstant() {
checkConstant(ClassHelper.char_TYPE, 'c', 'final char constant = \'c\';')
}
+ @Test
void testStringConstant() {
checkConstant(ClassHelper.STRING_TYPE, 'foo', 'final java.lang.String constant = "foo";')
}
@@ -84,38 +76,11 @@ final class Groovy5260Bug extends GroovyTestCase {
ClassNode cn = new ClassNode('script', ACC_PUBLIC, ClassHelper.OBJECT_TYPE)
cn.addField('constant', ACC_PUBLIC | ACC_STATIC | ACC_FINAL, constant.type, constant)
ModuleNode module = new ModuleNode(new CompileUnit(null, null))
- cn.setModule(module)
-
- StringWriter wrt = new StringWriter()
- PrintWriter out = new PrintWriter(wrt)
- JavaStubGenerator generator = new StringJavaStubGenerator(outputDir, out)
- generator.generateClass(cn)
-
- String stub = wrt.toString()
- assert (stub =~ expectation)
- }
-
- /**
- * Helper class, which generates code in a string instead of an output file.
- */
- private static final class StringJavaStubGenerator extends JavaStubGenerator {
-
- PrintWriter out
-
- StringJavaStubGenerator(File outFile, PrintWriter out) {
- super(outFile)
- this.out = out
- }
+ cn.module = module
- public void generateClass(ClassNode classNode) throws FileNotFoundException {
- try (out) {
- String packageName = classNode.getPackageName();
- if (packageName != null) {
- out.println("package " + packageName + ";\n");
- }
- super.printImports(out, classNode);
- super.printClassContents(out, classNode);
- }
+ String stub = new JavaStubGenerator(null).with {
+ generateClass(cn); javaStubCompilationUnitSet[0].getCharContent(true)
}
+ assert stub =~ expectation
}
}
diff --git a/src/test/groovy/bugs/Groovy9615.groovy b/src/test/groovy/bugs/Groovy9615.groovy
deleted file mode 100644
index fd8e6ab..0000000
--- a/src/test/groovy/bugs/Groovy9615.groovy
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 groovy.bugs
-
-import org.junit.Test
-
-import static groovy.test.GroovyAssert.shouldFail
-
-final class Groovy9615 {
-
- @Test // GROOVY-4945
- void testCallSuperMethod1() {
- def err = shouldFail MissingMethodException, '''
- class C {
- void test() {
- super.whatever()
- }
- void whatever() {
- assert false : 'should not have been called!'
- }
- }
- new C().test()
- '''
-
- assert err =~ /No signature of method: java\.lang\.Object\.whatever\(\) is applicable for argument types: \(\) values: \[\]
-Possible solutions: every\(\)$/
- }
-
- @Test // GROOVY-9615
- void testCallSuperMethod2() {
- def err = shouldFail MissingMethodException, '''
- class Outer {
- class Inner {
- void test() {
- super.whatever()
- }
- }
- void whatever() {
- assert false : 'should not have been called!'
- }
- }
- new Outer.Inner(new Outer()).test()
- '''
-
- assert err =~ /No signature of method: java\.lang\.Object\.whatever\(\) is applicable for argument types: \(\) values: \[\]/
- }
-}
diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy b/src/test/groovy/transform/stc/BugsSTCTest.groovy
index 07c965f..c405b65 100644
--- a/src/test/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy
@@ -1055,6 +1055,35 @@ Printer
'''
}
+ // GROOVY-8693
+ void testInvokeDefaultMethodFromPackagePrivateInterface2() {
+ assertScript '''
+ class C extends groovy.transform.stc.Groovy10380 {
+ @Override String m() {
+ super.m() // default method
+ }
+ void test() {
+ String result = this.m()
+ assert result == 'works'
+ }
+ }
+ new C().test()
+ '''
+ }
+
+ // GROOVY-9909
+ void testInvokeDefaultMethodFromDirectInterface() {
+ assertScript '''
+ class C implements groovy.transform.stc.Groovy9909 {
+ @Override String m() {
+ 'it ' + super.m() // default method
+ }
+ }
+ String result = new C().m()
+ assert result == 'it works'
+ '''
+ }
+
void testInvokeSuperMethodFromCovariantOverride() {
assertScript '''
abstract class A { int i = 0
diff --git a/src/test/groovy/bugs/Groovy5285Bug.groovy b/src/test/groovy/transform/stc/Groovy9909.java
similarity index 66%
rename from src/test/groovy/bugs/Groovy5285Bug.groovy
rename to src/test/groovy/transform/stc/Groovy9909.java
index a704627..83d5913 100644
--- a/src/test/groovy/bugs/Groovy5285Bug.groovy
+++ b/src/test/groovy/transform/stc/Groovy9909.java
@@ -16,21 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-package groovy.bugs
+package groovy.transform.stc;
-import groovy.test.GroovyTestCase
-
-class Groovy5285Bug extends GroovyTestCase {
- void testShouldNotThrowStackOverflow() {
- assertScript '''
- class Test {
- void setMetaClass(MetaClass metaClass) {
- super.setMetaClass(metaClass)
- }
- }
-
- def obj = new Test()
- obj.metaClass = obj.metaClass
- '''
+public interface Groovy9909 {
+ default String m() {
+ return "works";
}
}
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
index 77abc66..5576b16 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/BugsStaticCompileTest.groovy
@@ -1524,20 +1524,4 @@ println someInt
test()
'''
}
-
- // GROOVY-8693
- void testInvokeDefaultMethodFromPackagePrivateInterface2() {
- assertScript '''
- class C extends groovy.transform.stc.Groovy10380 {
- @Override String m() {
- super.m() // default method
- }
- void test() {
- String result = this.m()
- assert result == 'works'
- }
- }
- new C().test()
- '''
- }
}