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 444e236fd4 GROOVY-6663: `super` calls and bridge methods
444e236fd4 is described below

commit 444e236fd48303b5af73550c8f1b8cce959db574
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Thu Sep 7 11:10:08 2023 -0500

    GROOVY-6663: `super` calls and bridge methods
---
 src/main/java/groovy/lang/MetaClassImpl.java       |  12 +-
 .../apache/groovy/ast/tools/ClassNodeUtils.java    |  13 +-
 .../org/codehaus/groovy/classgen/Verifier.java     | 160 ++++---
 src/test/gls/invocation/CovariantReturnTest.groovy | 506 +++++++++++++--------
 src/test/groovy/OverrideTest.groovy                |  99 ++--
 src/test/groovy/ThisAndSuperTest.groovy            |  34 +-
 src/test/groovy/bugs/G4410JavaStringProducer.java  |  27 --
 src/test/groovy/bugs/G4410Producer1.java           |  25 -
 src/test/groovy/bugs/G4410Producer2.java           |  25 -
 src/test/groovy/bugs/Groovy4410Bug.groovy          |  65 ---
 10 files changed, 507 insertions(+), 459 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java 
b/src/main/java/groovy/lang/MetaClassImpl.java
index 43f235bce6..31cea6b0e3 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -508,7 +508,8 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
                         int matchedMethod = mopArrayIndex(method, c);
                         if (matchedMethod >= 0) {
                             methods.set(i, mopMethods[matchedMethod]);
-                        } else if (!useThis && !isDGM(method) && c == 
method.getDeclaringClass().getTheClass()) {
+                        } else if (!useThis && !isDGM(method) && 
(isBridge(method)
+                                || c == 
method.getDeclaringClass().getTheClass())) {
                             methods.remove(i--); // not fit for super usage
                         }
                     }
@@ -523,7 +524,8 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
                     if (matchedMethod >= 0) {
                         if (useThis) e.methods = mopMethods[matchedMethod];
                         else e.methodsForSuper = mopMethods[matchedMethod];
-                    } else if (!useThis && !isDGM(method) && c == 
method.getDeclaringClass().getTheClass()) {
+                    } else if (!useThis && !isDGM(method) && (isBridge(method)
+                            || c == method.getDeclaringClass().getTheClass())) 
{
                         e.methodsForSuper = null; // not fit for super usage
                     }
                 }
@@ -536,6 +538,8 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
                 // 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!
                 int distance = 
ReflectionCache.getCachedClass(c).getSuperClassDistance() - 1;
+                if (isBridge(method)) // GROOVY-6663
+                    return mopArrayIndex(method, "super$" + distance + "$" + 
method.getName());
                 while (distance > 0) {
                     int index = mopArrayIndex(method, "super$" + distance + 
"$" + method.getName());
                     if (index >= 0) return index;
@@ -562,6 +566,10 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
                 return -1;
             }
 
+            private boolean isBridge(final MetaMethod method) {
+                return (method.getModifiers() & Opcodes.ACC_BRIDGE) != 0;
+            }
+
             private boolean isDGM(final MetaMethod method) {
                 return method instanceof GeneratedMetaMethod || method 
instanceof NewMetaMethod;
             }
diff --git a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java 
b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
index 7b1bc71b3c..065237b9af 100644
--- a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
@@ -211,16 +211,13 @@ public class ClassNodeUtils {
      * @param methodsMap A map of existing methods to alter
      */
     public static void addDeclaredMethodsFromAllInterfaces(final ClassNode 
cNode, final Map<String, MethodNode> methodsMap) {
-        List<?> cnInterfaces = Arrays.asList(cNode.getInterfaces());
-        ClassNode parent = cNode.getSuperClass();
-        while (parent != null && !isObjectType(parent)) {
-            ClassNode[] interfaces = parent.getInterfaces();
-            for (ClassNode iface : interfaces) {
-                if (!cnInterfaces.contains(iface)) {
-                    methodsMap.putAll(iface.getDeclaredMethodsMap());
+        List<ClassNode> cnInterfaces = Arrays.asList(cNode.getInterfaces());
+        for (ClassNode sc = cNode.getSuperClass(); sc != null && 
!isObjectType(sc); sc = sc.getSuperClass()) {
+            for (ClassNode i : sc.getInterfaces()) {
+                if (!cnInterfaces.contains(i)) {
+                    methodsMap.putAll(i.getDeclaredMethodsMap());
                 }
             }
-            parent = parent.getSuperClass();
         }
     }
 
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java 
b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 2b017ab41b..b6b37a8736 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -690,7 +690,7 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
 
     @Override
     public void visitMethod(final MethodNode node) {
-        // GROOVY-3712: if it's an MOP method, it's an error as they aren't 
supposed to exist before ACG is invoked
+        // GROOVY-3712: if it's a MOP method, it's an error as they aren't 
supposed to exist before ACG is invoked
         if (MopWriter.isMopMethod(node.getName())) {
             throw new RuntimeParserException("Found unexpected MOP methods in 
the class node for " + classNode.getName() + "(" + node.getName() + ")", 
classNode);
         }
@@ -1359,97 +1359,103 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
     }
 
     protected void addCovariantMethods(final ClassNode classNode) {
-        Map<String, MethodNode> methodsToAdd = new HashMap<>();
-        Map<String, ClassNode> genericsSpec = new HashMap<>();
-
         // unimplemented abstract methods from interfaces
-        Map<String, MethodNode> abstractMethods = 
ClassNodeUtils.getDeclaredMethodsFromInterfaces(classNode);
-        Map<String, MethodNode> allInterfaceMethods = new 
HashMap<>(abstractMethods);
+        Map<String, MethodNode> absInterfaceMethods = 
ClassNodeUtils.getDeclaredMethodsFromInterfaces(classNode);
+        Map<String, MethodNode> allInterfaceMethods = new 
HashMap<>(absInterfaceMethods);
         ClassNodeUtils.addDeclaredMethodsFromAllInterfaces(classNode, 
allInterfaceMethods);
+
         List<MethodNode> declaredMethods = new 
ArrayList<>(classNode.getMethods());
-        // remove all static, private and package private methods
         for (Iterator<MethodNode> methodsIterator = 
declaredMethods.iterator(); methodsIterator.hasNext(); ) {
             MethodNode m = methodsIterator.next();
-            abstractMethods.remove(m.getTypeDescriptor());
+            // remove all static, private and package-private methods
             if (m.isStatic() || !(m.isPublic() || m.isProtected())) {
                 methodsIterator.remove();
             }
-            MethodNode intfMethod = 
allInterfaceMethods.get(m.getTypeDescriptor());
-            if (intfMethod != null && ((m.getModifiers() & ACC_SYNTHETIC) == 0)
-                    && !m.isPublic() && !m.isStaticConstructor()) {
-                throw new RuntimeParserException("The method " + m.getName() +
-                        " should be public as it implements the corresponding 
method from interface " +
-                        intfMethod.getDeclaringClass(), sourceOf(m));
-
+            absInterfaceMethods.remove(m.getTypeDescriptor());
+            MethodNode interfaceMethod = 
allInterfaceMethods.get(m.getTypeDescriptor());
+            if (interfaceMethod != null && !m.isPublic() && 
!m.isStaticConstructor() && ((m.getModifiers() & ACC_SYNTHETIC) == 0)) {
+                throw new RuntimeParserException("The method " + m.getName() + 
" should be public as it implements the corresponding method from interface " + 
interfaceMethod.getDeclaringClass(), sourceOf(m));
             }
         }
+        // interfaces may have private/static methods in JDK9
+        absInterfaceMethods.values().removeIf(interfaceMethod -> 
interfaceMethod.isPrivate() || interfaceMethod.isStatic());
 
-        addCovariantMethods(classNode, declaredMethods, abstractMethods, 
methodsToAdd, genericsSpec);
+        Map<String, MethodNode> methodsToAdd = new HashMap<>();
+        Map<String, ClassNode > genericsSpec = Collections.emptyMap();
+        addCovariantMethods(classNode, declaredMethods, absInterfaceMethods, 
methodsToAdd, genericsSpec);
 
-        Map<String, MethodNode> declaredMethodsMap = new HashMap<>();
         if (!methodsToAdd.isEmpty()) {
+            Map<String, MethodNode> declaredMethodsMap = new HashMap<>();
             for (MethodNode mn : declaredMethods) {
                 declaredMethodsMap.put(mn.getTypeDescriptor(), mn);
             }
-        }
 
-        for (Map.Entry<String, MethodNode> entry : methodsToAdd.entrySet()) {
-            // we skip bridge methods implemented in current class already
-            MethodNode mn = declaredMethodsMap.get(entry.getKey());
-            if (mn == null || !mn.getDeclaringClass().equals(classNode)) {
-                addPropertyMethod(entry.getValue());
+            for (Map.Entry<String, MethodNode> entry : 
methodsToAdd.entrySet()) {
+                // we skip bridge methods implemented in current class already
+                MethodNode mn = declaredMethodsMap.get(entry.getKey());
+                if (mn == null || !mn.getDeclaringClass().equals(classNode)) {
+                    addPropertyMethod(entry.getValue());
+                }
             }
         }
     }
 
-    private void addCovariantMethods(final ClassNode classNode, final 
List<MethodNode> declaredMethods, final Map<String, MethodNode> 
abstractMethods, final Map<String, MethodNode> methodsToAdd, final Map<String, 
ClassNode> oldGenericsSpec) {
-        ClassNode sn = classNode.getUnresolvedSuperClass(false);
-        if (sn != null) {
-            Map<String, ClassNode> genericsSpec = createGenericsSpec(sn, 
oldGenericsSpec);
-            List<MethodNode> classMethods = sn.getMethods();
-            // original class causing bridge methods for methods in super class
-            storeMissingCovariantMethods(declaredMethods, methodsToAdd, 
genericsSpec, classMethods);
-            // super class causing bridge methods for abstract methods in 
original class
-            if (!abstractMethods.isEmpty()) {
-                for (MethodNode method : classMethods) {
-                    if (method.isStatic()) continue;
-                    storeMissingCovariantMethods(abstractMethods.values(), 
method, methodsToAdd, Collections.emptyMap(), true);
+    private void addCovariantMethods(final ClassNode classNode, final 
List<MethodNode> declaredMethods, final Map<String, MethodNode> 
interfaceMethods, final Map<String, MethodNode> methodsToAdd, final Map<String, 
ClassNode> oldGenericsSpec) {
+        ClassNode sc = classNode.getUnresolvedSuperClass(false);
+        if (sc != null) {
+            Map<String, ClassNode> genericsSpec = createGenericsSpec(sc, 
oldGenericsSpec);
+            List<MethodNode> scMethods = sc.getMethods();
+            // add bridge methods in original class for overridden methods 
with changed signature
+            storeMissingCovariantMethods(declaredMethods, methodsToAdd, 
genericsSpec, scMethods);
+            // add bridge methods in original class for any interface methods 
overridden by super
+            if (!interfaceMethods.isEmpty()) {
+                for (MethodNode method : scMethods) {
+                    if (!method.isStatic())
+                        
storeMissingCovariantMethods(interfaceMethods.values(), method, methodsToAdd, 
Collections.emptyMap(), true);
                 }
             }
 
-            addCovariantMethods(sn.redirect(), declaredMethods, 
abstractMethods, methodsToAdd, genericsSpec);
+            addCovariantMethods(sc.redirect(), declaredMethods, 
interfaceMethods, methodsToAdd, genericsSpec);
         }
 
         for (ClassNode anInterface : classNode.getInterfaces()) {
-            List<MethodNode> interfacesMethods = anInterface.getMethods();
+            List<MethodNode> ifaceMethods = anInterface.getMethods();
             Map<String, ClassNode> genericsSpec = 
createGenericsSpec(anInterface, oldGenericsSpec);
-            storeMissingCovariantMethods(declaredMethods, methodsToAdd, 
genericsSpec, interfacesMethods);
-            addCovariantMethods(anInterface, declaredMethods, abstractMethods, 
methodsToAdd, genericsSpec);
+            storeMissingCovariantMethods(declaredMethods, methodsToAdd, 
genericsSpec, ifaceMethods);
+            addCovariantMethods(anInterface, declaredMethods, 
interfaceMethods, methodsToAdd, genericsSpec);
         }
     }
 
-    private void storeMissingCovariantMethods(final List<MethodNode> 
declaredMethods, final Map<String, MethodNode> methodsToAdd, final Map<String, 
ClassNode> genericsSpec, final List<MethodNode> methodNodeList) {
-        for (MethodNode method : declaredMethods) {
-            if (method.isStatic()) continue;
-            storeMissingCovariantMethods(methodNodeList, method, methodsToAdd, 
genericsSpec, false);
+    private void storeMissingCovariantMethods(final List<MethodNode> 
declaredMethods, final Map<String, MethodNode> methodsToAdd, final Map<String, 
ClassNode> genericsSpec, final List<MethodNode> superClassMethods) {
+        for (MethodNode declaredMethod : declaredMethods) {
+            if (!declaredMethod.isStatic())
+                storeMissingCovariantMethods(superClassMethods, 
declaredMethod, methodsToAdd, genericsSpec, false);
         }
     }
 
-    private MethodNode getCovariantImplementation(final MethodNode oldMethod, 
final MethodNode overridingMethod, Map<String, ClassNode> genericsSpec, final 
boolean ignoreError) {
-        if (!oldMethod.getName().equals(overridingMethod.getName())) return 
null;
-        if ((overridingMethod.getModifiers() & ACC_BRIDGE) != 0) return null;
-        if ((oldMethod.getModifiers() & ACC_BRIDGE) != 0) return null;
-        if (oldMethod.isPrivate()) return null;
+    private void storeMissingCovariantMethods(final Iterable<MethodNode> 
superClassMethods, final MethodNode overrideCandidate, final Map<String, 
MethodNode> methodsToAdd, final Map<String, ClassNode> genericsSpec, final 
boolean ignoreError) {
+        for (MethodNode method : superClassMethods) {
+            if (!method.isPrivate()
+                    && (method.getModifiers() & ACC_BRIDGE) == 0
+                    && (overrideCandidate.getModifiers() & ACC_BRIDGE) == 0
+                    && method.getName().equals(overrideCandidate.getName())) {
+                MethodNode bridgeMethod = getCovariantImplementation(method, 
overrideCandidate, genericsSpec, ignoreError);
+                if (bridgeMethod != null) {
+                    methodsToAdd.put(bridgeMethod.getTypeDescriptor(), 
bridgeMethod);
+                    return;
+                }
+            }
+        }
+    }
 
+    private MethodNode getCovariantImplementation(final MethodNode oldMethod, 
final MethodNode overrideCandidate, Map<String, ClassNode> genericsSpec, final 
boolean ignoreError) {
         if (oldMethod.getGenericsTypes() != null)
             genericsSpec = addMethodGenerics(oldMethod, genericsSpec);
 
-        // parameters
-        boolean equalParameters = equalParametersNormal(overridingMethod, 
oldMethod);
-        if (!equalParameters && !equalParametersWithGenerics(overridingMethod, 
oldMethod, genericsSpec)) return null;
+        boolean equalParameters = equalParametersNormal(overrideCandidate, 
oldMethod);
+        if (!equalParameters && 
!equalParametersWithGenerics(overrideCandidate, oldMethod, genericsSpec)) 
return null;
 
-        // return type
-        ClassNode nmr = overridingMethod.getReturnType();
+        ClassNode nmr = overrideCandidate.getReturnType();
         ClassNode omr = oldMethod.getReturnType();
         boolean equalReturnType = nmr.equals(omr);
 
@@ -1459,11 +1465,11 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
             if (ignoreError) return null;
             throw new RuntimeParserException(
                     "The return type of " +
-                            overridingMethod.getTypeDescriptor() +
-                            " in " + 
overridingMethod.getDeclaringClass().getName() +
+                            overrideCandidate.getTypeDescriptor() +
+                            " in " + 
overrideCandidate.getDeclaringClass().getName() +
                             " is incompatible with " + omrCorrected.getName() +
                             " in " + oldMethod.getDeclaringClass().getName(),
-                    sourceOf(overridingMethod));
+                    sourceOf(overrideCandidate));
         }
 
         if (equalReturnType && equalParameters) return null;
@@ -1473,15 +1479,15 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
                     "Cannot override final method " +
                             oldMethod.getTypeDescriptor() +
                             " in " + oldMethod.getDeclaringClass().getName(),
-                    sourceOf(overridingMethod));
+                    sourceOf(overrideCandidate));
         }
-        if (oldMethod.isStatic() != overridingMethod.isStatic()) {
+        if (oldMethod.isStatic() != overrideCandidate.isStatic()) {
             throw new RuntimeParserException(
                     "Cannot override method " +
                             oldMethod.getTypeDescriptor() +
                             " in " + oldMethod.getDeclaringClass().getName() +
                             " with disparate static modifier",
-                    sourceOf(overridingMethod));
+                    sourceOf(overrideCandidate));
         }
         if (!equalReturnType) {
             boolean oldM = ClassHelper.isPrimitiveType(omr);
@@ -1500,16 +1506,34 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
                                 oldMethod.getTypeDescriptor() +
                                 " in " + 
oldMethod.getDeclaringClass().getName() +
                                 message,
-                        sourceOf(overridingMethod));
+                        sourceOf(overrideCandidate));
             }
         }
 
+        /*
+         * abstract class A {
+         *   def m(Object o)
+         * }
+         * class B extends A {
+         *   def m(String s) { ... }
+         * //def m(Object o) { this.m((String)o) } -- bridge method
+         * class C extends B {
+         *   def m(String s) { ... }
+         *   // no bridge necessary; B#m(Object) uses INVOKEVIRTUAL
+         * }
+         */
+        MethodNode newMethod = 
ClassNodeUtils.getMethod(overrideCandidate.getDeclaringClass(), 
overrideCandidate.getName(),
+                (MethodNode m) -> !m.isAbstract() && 
m.getReturnType().equals(omr) && equalParametersNormal(m, oldMethod));
+        if (newMethod != null && newMethod != oldMethod && newMethod != 
overrideCandidate) {
+            return null; // bridge method would be redundant
+        }
+
         // if we reach this point there is least one parameter or return type
         // that is different in its specified form, so create a bridge method
         String owner = BytecodeHelper.getClassInternalName(classNode);
         return new MethodNode(
                 oldMethod.getName(),
-                overridingMethod.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE,
+                overrideCandidate.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE,
                 cleanType(omr),
                 cleanParameters(oldMethod.getParameters()),
                 oldMethod.getExceptions(),
@@ -1518,7 +1542,7 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
                     public void visit(final MethodVisitor mv) {
                         mv.visitVarInsn(ALOAD, 0);
                         Parameter[] para = oldMethod.getParameters();
-                        Parameter[] goal = overridingMethod.getParameters();
+                        Parameter[] goal = overrideCandidate.getParameters();
                         int doubleSlotOffset = 0;
                         for (int i = 0, n = para.length; i < n; i += 1) {
                             ClassNode type = para[i].getType();
@@ -1531,7 +1555,7 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
                                 BytecodeHelper.doCast(mv, goal[i].getType());
                             }
                         }
-                        mv.visitMethodInsn(INVOKEVIRTUAL, owner, 
overridingMethod.getName(), BytecodeHelper.getMethodDescriptor(nmr, 
overridingMethod.getParameters()), false);
+                        mv.visitMethodInsn(INVOKEVIRTUAL, owner, 
overrideCandidate.getName(), BytecodeHelper.getMethodDescriptor(nmr, 
overrideCandidate.getParameters()), false);
 
                         BytecodeHelper.doReturn(mv, oldMethod.getReturnType());
                     }
@@ -1566,16 +1590,6 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
         return type.getPlainNodeReference();
     }
 
-    private void storeMissingCovariantMethods(final Iterable<MethodNode> 
methods, final MethodNode method, final Map<String, MethodNode> methodsToAdd, 
final Map<String, ClassNode> genericsSpec, final boolean ignoreError) {
-        for (MethodNode toOverride : methods) {
-            MethodNode bridgeMethod = getCovariantImplementation(toOverride, 
method, genericsSpec, ignoreError);
-            if (bridgeMethod != null) {
-                methodsToAdd.put(bridgeMethod.getTypeDescriptor(), 
bridgeMethod);
-                return;
-            }
-        }
-    }
-
     private static boolean equalParametersNormal(final MethodNode m1, final 
MethodNode m2) {
         Parameter[] p1 = m1.getParameters();
         Parameter[] p2 = m2.getParameters();
diff --git a/src/test/gls/invocation/CovariantReturnTest.groovy 
b/src/test/gls/invocation/CovariantReturnTest.groovy
index 4ecfeea6d5..cdff82ea6e 100644
--- a/src/test/gls/invocation/CovariantReturnTest.groovy
+++ b/src/test/gls/invocation/CovariantReturnTest.groovy
@@ -18,316 +18,446 @@
  */
 package gls.invocation
 
-import gls.CompilableTestSupport
+import org.junit.Test
 
-class CovariantReturnTest extends CompilableTestSupport {
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
 
+final class CovariantReturnTest {
+
+    @Test
     void testCovariantReturn() {
-        assertScript """
+        assertScript '''
             class A {
-                Object foo() {1}
+                Object foo() { 1 }
             }
-            class B extends A{
-                String foo(){"2"}
+            class B extends A {
+                String foo() { "2" }
             }
-            def b = new B();
-            assert b.foo()=="2"
-            assert B.declaredMethods.findAll{it.name=="foo"}.size()==2
-        """
+
+            b = new B()
+            assert b.foo() == "2"
+            assert B.declaredMethods.count{ it.name=="foo" } == 2
+        '''
     }
 
+    @Test
     void testCovariantReturnOverwritingAbstractMethod() {
-        assertScript """
+        assertScript '''
             abstract class Numeric {
-                abstract Numeric eval();
+                abstract Numeric eval()
             }
             class Rational extends Numeric {
-                Rational eval() {this}
+                Rational eval() { this }
             }
-            assert Rational.declaredMethods.findAll{it.name=="eval"}.size()==2
-        """
+
+            assert Rational.declaredMethods.count{ it.name=="eval" } == 2
+        '''
     }
 
-    void testCovariantReturnOverwritingObjectMethod() {
-        shouldNotCompile """
-            class X {
+    @Test
+    void testCovariantReturnOverwritingAnObjectMethod() {
+        def err = shouldFail '''
+            class C {
                 Long toString() { 333L }
                 String hashCode() { "hash" }
             }
-        """
+        '''
+        assert err.message.contains('The return type of java.lang.Long 
toString() in C is incompatible with java.lang.String in java.lang.Object')
     }
 
+    @Test
     void testCovariantOverwritingMethodWithPrimitives() {
-        assertScript """
-            class Base {
-                Object foo(boolean i) {i}
+        assertScript '''
+            class B {
+                Object foo(boolean b) { b }
             }
-            class Child extends Base {
-                String foo(boolean i) {""+super.foo(i)}
+            class C extends B {
+                String foo(boolean b) { ""+super.foo(b) }
             }
-            def x = new Child()
-            assert x.foo(true) == "true"
-            assert x.foo(false) == "false"
-        """
+
+            c = new C()
+            assert c.foo(true) == "true"
+            assert c.foo(false) == "false"
+        '''
     }
 
+    @Test
     void testCovariantOverwritingMethodWithInterface() {
-        assertScript """
-            interface Base {
+        assertScript '''
+            interface A {
                 List foo()
-                Base baz()
+                A baz()
             }
-            interface Child extends Base {
+            interface B extends A {
                 ArrayList foo()
-                Child baz()
+                B baz()
             }
-            class GroovyChildImpl implements Child {
+            class C implements B {
                 ArrayList foo() {}
-                GroovyChildImpl baz() {}
+                C baz() {}
             }
-            def x = new GroovyChildImpl()
-            x.foo()
-            x.baz()
-        """
+
+            c = new C()
+            c.foo()
+            c.baz()
+        '''
     }
 
+    @Test
     void testCovariantOverwritingMethodWithInterfaceAndInheritance() {
-        assertScript """
-            interface Base {
+        assertScript '''
+            interface A {
                 List foo()
                 List bar()
-            Base baz()
+                A baz()
             }
-            interface Child extends Base {
+            interface B extends A {
                 ArrayList foo()
             }
             class MyArrayList extends ArrayList { }
-            class GroovyChildImpl implements Child {
+            class C implements B {
                 MyArrayList foo() {}
                 MyArrayList bar() {}
-                GroovyChildImpl baz() {}
+                C baz() {}
             }
-            def x = new GroovyChildImpl()
-            x.foo()
-            x.bar()
-            x.baz()
-        """
+
+            c = new C()
+            c.foo()
+            c.bar()
+            c.baz()
+        '''
     }
 
-    void 
testCovariantMethodFromParentOverwritingMethodFromInterfaceInCurrentclass() {
-        assertScript """
-            interface I {
+    // GROOVY-2582
+    @Test
+    void 
testCovariantMethodFromParentOverwritingMethodFromInterfaceInCurrentClass() {
+        assertScript '''
+            interface A {
                 def foo()
             }
-            class A {
-                String foo(){""}
+            class B {
+                String foo() { "" }
             }
-            class B extends A implements I{}
-            def b = new B()
-            assert b.foo() == ""
-        """
+            class C extends B implements A {
+            }
+
+            c = new C()
+            assert c.foo() == ""
+        '''
 
-        // basically the same as above, but with an example
-        // from an error report (GROOVY-2582)
+        // basically the same as above, but with an example from an error 
report
         // Properties has a method "String getProperty(String)", this class
         // is also a GroovyObject, meaning a "Object getProperty(String)" 
method
         // should be implemented. But this method should not be the usual 
automatically
         // added getProperty, but a bridge to the getProperty method provided 
by Properties
-        assertScript """
-            class Configuration extends java.util.Properties {}
-            assert 
Configuration.declaredMethods.findAll{it.name=="getProperty"}.size() == 1
+        assertScript '''
+            class Configuration extends java.util.Properties {
+            }
+
+            assert Configuration.declaredMethods.count{ it.name=="getProperty" 
} == 1
             def conf = new Configuration()
             conf.setProperty("a","b")
-            // the following assert would fail if standard getProperty method 
was added
-            // by the compiler
+            // the following assert would fail if standard getProperty method 
was added by the compiler
             assert conf.getProperty("a") == "b"
-        """
+        '''
 
-        assertScript """
+        assertScript '''
             class A {}
             class B extends A {}
             interface Contract {
                 A method()
             }
-            abstract class AbstractContract implements Contract {}
-            class ContractImpl extends AbstractContract {
-                B method(String foo='default') { new B() }
+            abstract class C implements Contract {
             }
-            assert new ContractImpl().method() instanceof B
-        """
+            class D extends C {
+                B method(String foo = 'default') { new B() }
+            }
+
+            assert new D().method() instanceof B
+        '''
     }
 
+    // GROOVY-3229
+    @Test
     void testImplementedInterfacesNotInfluencing() {
-        // in GROOVY-3229 some methods from Appendable were not correctly 
recognized
+        // some methods from Appendable were not correctly recognized
         // as already being overridden (PrintWriter<Writer<Appendable)
-        shouldCompile """
-            class IndentWriter extends java.io.PrintWriter {
-               public IndentWriter(Writer w)  { super(w, true) }
+        assertScript '''
+            class IndentWriter extends PrintWriter {
+                IndentWriter(Writer w) {
+                    super(w, true)
+                }
             }
-        """
+
+            new IndentWriter(new StringWriter())
+        '''
     }
 
-    void testCovariantMethodReturnTypeFromGenericsInterface() {
-        shouldCompile """
-            interface MyCallable<T> {
-                T myCall() throws Exception;
-            }
-            class Task implements MyCallable<List> {
-                List myCall() throws Exception {
-                    return [ 42 ]
+    @Test
+    void testCovariantReturnFromGenericsInterface() {
+        assertScript '''
+            class Task implements java.util.concurrent.Callable<List> {
+                List call() throws Exception {
+                    [ 42 ]
                 }
             }
-        """
+
+            assert new Task().call() == [42]
+        '''
     }
 
-    //GROOVY-7495
-    void testCovariantMerhodReturnTypeFromParentInterface() {
-        shouldCompile """
-            interface Item {}
-            interface DerivedItem extends Item {}
+    // GROOVY-7849
+    @Test
+    void testCovariantArrayReturnType1() {
+        assertScript '''
+            interface Base {}
+
+            interface Derived extends Base {}
+
+            interface I {
+                Base[] foo()
+            }
+
+            class C implements I {
+                Derived[] foo() { null }
+            }
+            new C().foo()
+        '''
+    }
+
+    // GROOVY-7185
+    @Test
+    void testCovariantArrayReturnType2() {
+        assertScript '''
+            interface A<T> {
+                T[] process();
+            }
+
+            class B implements A<String> {
+                @Override
+                public String[] process() {
+                    ['foo']
+                }
+            }
+
+            class C extends B {
+                @Override
+                String[] process() {
+                    super.process()
+                }
+            }
+            assert new C().process()[0] == 'foo'
+        '''
+    }
 
+    // GROOVY-7495
+    @Test
+    void testCovariantReturnFromIndirectInterface() {
+        assertScript '''
+            interface Item {
+            }
+            interface DerivedItem extends Item {
+            }
             interface Base {
                 Item getItem()
             }
             class BaseImpl implements Base {
                 Item getItem() { null }
             }
-
             interface First extends Base {
                 DerivedItem getItem()
             }
-
             class FirstImpl extends BaseImpl implements First {
-                DerivedItem getItem() { null }
+                DerivedItem getItem() { new DerivedItem(){} }
             }
+            interface Second extends First {
+            }
+            class SecondImpl extends FirstImpl implements Second {
+            }
+
+            assert new SecondImpl().item instanceof DerivedItem
+        '''
 
-            interface Second extends First {}
-            class SecondImpl extends FirstImpl implements Second {}
-        """
-        shouldCompile """
+        assertScript '''
             interface A {
-               B foo()
+                B foo()
+            }
+            interface B {
             }
-            interface B {}
             interface A2 extends A {
-               B2 foo()
+                B2 foo()
+            }
+            interface B2 extends B {
             }
-            interface B2 extends B {}
-
             class AA implements A {
-               BB foo() { return new BB() }
+                BB foo() { new BB() }
+            }
+            class AA_A2 extends AA implements A2 {
+                BB_B2 foo() { new BB_B2() }
+            }
+            class BB implements B {
             }
-            class AA2 extends AA implements A2 {
-               BB2 foo() { return new BB2() }
+            class BB_B2 extends BB implements B2 {
             }
-            class BB implements B {}
-            class BB2 extends BB implements B2 {}
-        """
+
+            assert new AA_A2().foo() instanceof BB_B2
+        '''
     }
 
-    void testCovariantParameterType() {
-        assertScript """
-            class BaseA implements Comparable<BaseA> {
-                int index
-                public int compareTo(BaseA a){
-                    return index <=> a.index
+    // GROOVY-4410
+    @Test
+    void testCovariantReturnFromMultipleInterfaces() {
+        def config = new org.codehaus.groovy.control.CompilerConfiguration(
+            jointCompilationOptions: [memStub: true],
+            targetDirectory: File.createTempDir()
+        )
+
+        def parentDir = File.createTempDir()
+        try {
+            def a = new File(parentDir, 'G4410Producer1.java')
+            a.write '''
+                public interface G4410Producer1 {
+                    Object gimme(String[] array);
+                }
+            '''
+            def b = new File(parentDir, 'G4410Producer2.java')
+            b.write '''
+                public interface G4410Producer2<T> {
+                    T gimme(String[] array);
+                }
+            '''
+            def c = new File(parentDir, 'G4410JavaStringProducer.java')
+            c.write '''
+                public class G4410JavaStringProducer implements G4410Producer1 
{
+                    public String gimme(String[] array) {
+                        return "Hello World";
+                    }
+                }
+            '''
+            def d = new File(parentDir, 'G4410GroovyStringProducers.groovy')
+            d.write '''
+                class HackProducer1 implements G4410Producer1 {
+                    Object gimme(String[] array) {
+                        "Hello World"
+                    }
                 }
-            }
 
-            def a = new BaseA(index:1)
-            def b = new BaseA(index:2)
-            assert a < b
-        """
-    }
+                class StringProducer1 implements G4410Producer1 {
+                    String gimme(String[] array) {
+                        "Hello World"
+                    }
+                }
 
-    void testBridgeMethodExistsOnImplementingClass() {
-        shouldCompile """
-            interface IBar<T>{
-                int testMethod(T obj)
-            }
-            class Bar implements IBar<Date>{
-                int testMethod(Date dt){}
-                int testMethod(Object obj){}
-            }
-        """
+                class HackProducer2 implements G4410Producer2<Object> {
+                    Object gimme(String[] array) {
+                        "Hello World"
+                    }
+                }
+
+                class StringProducer2 implements G4410Producer2<String> {
+                    String gimme(String[] array) {
+                        "Hello World"
+                    }
+                }
+            '''
+            def e = new File(parentDir, 'Main.groovy')
+            e.write '''
+                def sp1 = new StringProducer1()
+                assert sp1.gimme(null) == "Hello World"
+
+                def jsp = new G4410JavaStringProducer()
+                assert jsp.gimme(null) == "Hello World"
+
+                def hp1 = new HackProducer1()
+                assert hp1.gimme(null) == "Hello World"
+
+                def sp2 = new StringProducer2()
+                assert sp2.gimme(null) == "Hello World"
+
+                def hp2 = new HackProducer2()
+                assert hp2.gimme(null) == "Hello World"
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new 
org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b, c, d, e)
+            cu.compile()
+
+            loader.loadClass('Main').main()
+        } finally {
+            config.targetDirectory.deleteDir()
+            parentDir.deleteDir()
+        }
     }
 
-    void testCovariantVoidReturnTypeOverridingObjectType() {
-        shouldNotCompile """
-            class BaseClass {
+    @Test
+    void testCovariantVoidReturnOverridingObjectType() {
+        def err = shouldFail '''
+            class B {
                 def foo() {}
             }
-            class DerivedClass extends BaseClass {
+            class C extends B {
                 void foo() {}
             }
-        """
+        '''
+        assert err.message.contains('The return type of void foo() in C is 
incompatible with java.lang.Object in B')
     }
 
+    @Test
     void testPrimitiveObjectMix() {
-        //Object overridden by primitive
-        shouldNotCompile """
-            class BaseClass{ def foo(){} }
-            class DerivedClass extends BaseClass {
-                boolean foo(){}
-            }
-        """
-        //primitive overridden by Object
-        shouldNotCompile """
-            class BaseClass{ boolean foo(){} }
-            class DerivedClass extends BaseClass {
-                Object foo(){}
-            }
-        """
-        //primitive overriding different primitive
-        shouldNotCompile """
-            class BaseClass{ boolean foo(){} }
-            class DerivedClass extends BaseClass {
-                int foo(){}
-            }
-        """
+        // Object overridden by primitive
+        shouldFail '''
+            class B { def foo() {} }
+            class C extends B {
+                boolean foo() {}
+            }
+        '''
+        // primitive overridden by Object
+        shouldFail '''
+            class B { boolean foo() {} }
+            class C extends B {
+                Object foo() {}
+            }
+        '''
+        // primitive overriding different primitive
+        shouldFail '''
+            class B { boolean foo() {} }
+            class C extends B {
+                int foo() {}
+            }
+        '''
     }
 
-    void testCovariantMethodGenerics_groovy6330() {
-        shouldNotCompile """
+    // GROOVY-6330
+    @Test
+    void testCovariantMethodGenerics() {
+        def err = shouldFail '''
             class StringIterator implements Iterator<String> {
-                void remove() { }
                 boolean hasNext() { false }
-                def next() { 'dummy' }
+                def next() { null }
+                void remove() {}
             }
-        """
-        shouldNotCompile """
+        '''
+        assert err.message.contains('The return type of java.lang.Object 
next() in StringIterator is incompatible with java.lang.String in 
java.util.Iterator')
+
+        err = shouldFail '''
             class StringIterator implements Iterator<String> {
-                void remove() { }
                 boolean hasNext() { false }
                 CharSequence next() { null }
+                void remove() {}
             }
-        """
-        assertScript """
+        '''
+        assert err.message.contains('The return type of java.lang.CharSequence 
next() in StringIterator is incompatible with java.lang.String in 
java.util.Iterator')
+
+        assertScript '''
             class StringIterator implements Iterator<CharSequence> {
-                void remove() { }
                 boolean hasNext() { false }
                 String next() { 'dummy' }
+                void remove() {}
             }
-            assert new StringIterator().next() == 'dummy'
-        """
-    }
 
-    void testCovariantParameter() {
-        assertScript """
-          interface Interface<SomeType> {
-             public int handle(long a, SomeType x);
-          }
-
-          class InterfaceImpl
-          implements Interface<String> {
-              public int handle(long a, String something) {
-                  return 1
-              }
-          }
-
-          InterfaceImpl test = new InterfaceImpl()
-          assert test.handle(5, "hi") == 1
-        """
+            assert new StringIterator().next() == 'dummy'
+        '''
     }
-
 }
diff --git a/src/test/groovy/OverrideTest.groovy 
b/src/test/groovy/OverrideTest.groovy
index c29618a8d9..d16dbdb70d 100644
--- a/src/test/groovy/OverrideTest.groovy
+++ b/src/test/groovy/OverrideTest.groovy
@@ -140,79 +140,88 @@ final class OverrideTest {
         assert err.message.contains("Method 'methodTakesObject' from class 
'HasMethodWithBadArgType' does not override method from its superclass or 
interfaces but is annotated with @Override.")
     }
 
-    @Test // GROOVY-6654
+    @Test
     void testCovariantParameterType1() {
         assertScript '''
-            class C<T> {
-                void proc(T t) {}
-            }
-
-            class D extends C<String> {
-                @Override
-                void proc(String s) {}
+            class C implements Comparable<C> {
+                int index
+                int compareTo(C c) {
+                    index <=> c.index
+                }
             }
 
-            def d = new D()
+            one = new C(index:1)
+            two = new C(index:2)
+            assert one < two
         '''
     }
 
-    @Test // GROOVY-10675
+    @Test
     void testCovariantParameterType2() {
         assertScript '''
-            @FunctionalInterface
-            interface A<I, O> {
-                O apply(I in)
-            }
-            interface B<X, Y> extends A<X, Y> {
+            interface I<T> {
+               int handle(long n, T t)
             }
-            class C implements B<Number, String> {
-                @Override String apply(Number n) { 'x' }
+
+            class C implements I<String> {
+                int handle(long n, String something) {
+                    1
+                }
             }
 
-            def result = new C().apply(42)
-            assert result == 'x'
+            c = new C()
+            assert c.handle(5,"hi") == 1
         '''
     }
 
-    @Test // GROOVY-7849
-    void testCovariantArrayReturnType1() {
+    @Test
+    void testCovariantParameterType3() {
         assertScript '''
-            interface Base {}
-
-            interface Derived extends Base {}
-
-            interface I {
-                Base[] foo()
+            interface I<T> {
+                int testMethod(T t)
             }
-
-            class C implements I {
-                Derived[] foo() { null }
+            class C implements I<Date> {
+                int testMethod(Date date) {}
+                int testMethod(Object obj) {}
             }
-            new C().foo()
+
+            assert C.declaredMethods.count{ it.name=="testMethod" } == 2
         '''
     }
 
-    @Test // GROOVY-7185
-    void testCovariantArrayReturnType2() {
+    // GROOVY-6654
+    @Test
+    void testCovariantParameterType4() {
         assertScript '''
-            interface A<T> {
-                T[] process();
+            class C<T> {
+                void proc(T t) {}
             }
 
-            class B implements A<String> {
+            class D extends C<String> {
                 @Override
-                public String[] process() {
-                    ['foo']
-                }
+                void proc(String s) {}
             }
 
-            class C extends B {
-                @Override
-                String[] process() {
-                    super.process()
-                }
+            def d = new D()
+        '''
+    }
+
+    // GROOVY-10675
+    @Test
+    void testCovariantParameterType5() {
+        assertScript '''
+            @FunctionalInterface
+            interface A<I, O> {
+                O apply(I in)
+            }
+            interface B<X, Y> extends A<X, Y> {
             }
-            assert new C().process()[0] == 'foo'
+            class C implements B<Number, String> {
+                @Override String apply(Number n) { 'x' }
+            }
+
+            def result = new C().apply(42)
+            assert result == 'x'
         '''
     }
 
diff --git a/src/test/groovy/ThisAndSuperTest.groovy 
b/src/test/groovy/ThisAndSuperTest.groovy
index 41b7f36bc0..b6efb04651 100644
--- a/src/test/groovy/ThisAndSuperTest.groovy
+++ b/src/test/groovy/ThisAndSuperTest.groovy
@@ -390,9 +390,41 @@ final class ThisAndSuperTest {
         '''
     }
 
-    // GROOVY-10302
+    // GROOVY-6663
     @Test
     void testSuperDotMethod4() {
+        assertScript '''
+            class A<T> {
+                protected String getText(T t) {
+                    'A with ' + t
+                }
+            }
+
+            class B extends A<String> {
+                @Override
+                protected String getText(String s) {
+                    'B then ' + super.getText(s) // invoke super$2$getText
+                }
+                // bridge String getText(Object o) { this.getText((String) o); 
}
+                //        String super$2$getText(Object o) { super.getText(o); 
}
+            }
+
+            class C extends B {
+                @Override
+                protected String getText(String s) {
+                    'C then ' + super.getText(s) // invoke super$3$getText
+                }
+                //        String super$3$getText(String s) { super.getText(s); 
}
+            }
+
+            String result = new C().getText(null) // invoke bridge method
+            assert result == 'C then B then A with null'
+        '''
+    }
+
+    // GROOVY-10302
+    @Test
+    void testSuperDotMethod5() {
         shouldFail ClassNotFoundException, '''
             void test() {
                 def list = []
diff --git a/src/test/groovy/bugs/G4410JavaStringProducer.java 
b/src/test/groovy/bugs/G4410JavaStringProducer.java
deleted file mode 100644
index 3188eec869..0000000000
--- a/src/test/groovy/bugs/G4410JavaStringProducer.java
+++ /dev/null
@@ -1,27 +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;
-
-@SuppressWarnings("all")
-public class G4410JavaStringProducer implements G4410Producer1 {
-
-    public String gimme(final String[] a1) {
-        return "Hello World";
-    }
-}
diff --git a/src/test/groovy/bugs/G4410Producer1.java 
b/src/test/groovy/bugs/G4410Producer1.java
deleted file mode 100644
index ff7d99b825..0000000000
--- a/src/test/groovy/bugs/G4410Producer1.java
+++ /dev/null
@@ -1,25 +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;
-
-@SuppressWarnings("all")
-public interface G4410Producer1 {
-
-    Object gimme(String[] a2);
-}
\ No newline at end of file
diff --git a/src/test/groovy/bugs/G4410Producer2.java 
b/src/test/groovy/bugs/G4410Producer2.java
deleted file mode 100644
index 2c1983ceb9..0000000000
--- a/src/test/groovy/bugs/G4410Producer2.java
+++ /dev/null
@@ -1,25 +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;
-
-@SuppressWarnings("all")
-public interface G4410Producer2<T> {
-
-    T gimme(String[] a2);
-}
diff --git a/src/test/groovy/bugs/Groovy4410Bug.groovy 
b/src/test/groovy/bugs/Groovy4410Bug.groovy
deleted file mode 100644
index 67f6d9df91..0000000000
--- a/src/test/groovy/bugs/Groovy4410Bug.groovy
+++ /dev/null
@@ -1,65 +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
-
-class Groovy4410Bug extends GroovyTestCase {
-    void testBridgeMethodWithArrayTypeParameterV1() {
-        StringProducer1 sp = new StringProducer1()
-        assert sp.gimme(null) == 'Hello World'
-
-        G4410JavaStringProducer jsp = new G4410JavaStringProducer()
-        assert jsp.gimme(null) == 'Hello World'
-
-        HackProducer1 hp = new HackProducer1()
-        assert hp.gimme(null) == 'Hello World'
-    }
-
-    void testBridgeMethodWithArrayTypeParameterV2() {
-        StringProducer2 sp = new StringProducer2()
-        assert sp.gimme(null) == 'Hello World'
-
-        HackProducer2 hp = new HackProducer2()
-        assert hp.gimme(null) == 'Hello World'
-    }
-}
-class HackProducer1 implements G4410Producer1 {
-    Object gimme(String[] a) {
-        "Hello World"
-    }
-}
-
-class StringProducer1 implements G4410Producer1 {
-    String gimme(String[] a1) {
-        "Hello World"
-    }
-}
-
-class HackProducer2 implements G4410Producer2<Object> {
-    Object gimme(String[] a1) {
-        "Hello World"
-    }
-}
-
-class StringProducer2 implements G4410Producer2<String> {
-    String gimme(String[] a1) {
-        "Hello World"
-    }
-}
\ No newline at end of file

Reply via email to