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 2022/10/12 15:50:55 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-8243: support closure for functional interface that extends trait

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

emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 5e59a42b34 GROOVY-8243: support closure for functional interface that extends trait
5e59a42b34 is described below

commit 5e59a42b3424f263ee9cb772c1c3d568a934a7eb
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Oct 12 10:33:19 2022 -0500

    GROOVY-8243: support closure for functional interface that extends trait
    
    2_5_X backport
---
 .../reflection/stdclasses/CachedSAMClass.java      | 190 ++++++++++-----------
 .../groovy/runtime/DefaultGroovyMethods.java       |  39 ++---
 .../groovy/runtime/ProxyGeneratorAdapter.java      |  65 ++++---
 .../traitx/TraitASTTransformationTest.groovy       |  75 +++++---
 4 files changed, 193 insertions(+), 176 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/reflection/stdclasses/CachedSAMClass.java b/src/main/java/org/codehaus/groovy/reflection/stdclasses/CachedSAMClass.java
index 7c3291c78f..b2656fc358 100644
--- a/src/main/java/org/codehaus/groovy/reflection/stdclasses/CachedSAMClass.java
+++ b/src/main/java/org/codehaus/groovy/reflection/stdclasses/CachedSAMClass.java
@@ -30,95 +30,100 @@ import org.codehaus.groovy.transform.trait.Traits;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+import java.util.ArrayDeque;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
 
 public class CachedSAMClass extends CachedClass {
 
-    private static final int ABSTRACT_STATIC_PRIVATE =
-            Modifier.ABSTRACT|Modifier.PRIVATE|Modifier.STATIC;
-    private static final int VISIBILITY = 5; // public|protected
-    private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
     private final Method method;
 
-    public CachedSAMClass(Class klazz, ClassInfo classInfo) {
-        super(klazz, classInfo);
-        method = getSAMMethod(klazz);
-        if (method==null) throw new GroovyBugError("assigned method should not have been null!");
+    public CachedSAMClass(Class clazz, ClassInfo classInfo) {
+        super(clazz, classInfo);
+        method = getSAMMethod(clazz);
+        if (method == null) throw new GroovyBugError("assigned method should not have been null!");
     }
 
     @Override
     public boolean isAssignableFrom(Class argument) {
-        return argument == null ||
-                Closure.class.isAssignableFrom(argument) ||
-                ReflectionCache.isAssignableFrom(getTheClass(), argument);
+        return argument == null
+            || Closure.class.isAssignableFrom(argument)
+            || ReflectionCache.isAssignableFrom(getTheClass(), argument);
+    }
+
+    @Override
+    public Object coerceArgument(Object argument) {
+        if (argument instanceof Closure) {
+            Class<?> clazz = getTheClass();
+            return coerceToSAM((Closure<?>) argument, method, clazz);
+        } else {
+            return argument;
+        }
     }
 
+    //--------------------------------------------------------------------------
+
+    private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
+    private static final int PUBLIC_OR_PROTECTED = Modifier.PUBLIC | Modifier.PROTECTED;
+    private static final int ABSTRACT_STATIC_PRIVATE = Modifier.ABSTRACT | Modifier.STATIC | Modifier.PRIVATE;
+
     public static Object coerceToSAM(Closure argument, Method method, Class clazz) {
         return coerceToSAM(argument, method, clazz, clazz.isInterface());
     }
 
-    /* Should we make the following method private? */
-    @SuppressWarnings("unchecked")
     public static Object coerceToSAM(Closure argument, Method method, Class clazz, boolean isInterface) {
-        if (argument!=null && clazz.isAssignableFrom(argument.getClass())) {
+        if (argument != null && clazz.isAssignableFrom(argument.getClass())) {
             return argument;
         }
-        if (isInterface) {
-            if (Traits.isTrait(clazz)) {
-                Map<String,Closure> impl = Collections.singletonMap(
-                        method.getName(),
-                        argument
-                );
-                return ProxyGenerator.INSTANCE.instantiateAggregate(impl,Collections.singletonList(clazz));
-            }
-            return Proxy.newProxyInstance(
-                    clazz.getClassLoader(),
-                    new Class[]{clazz},
-                    new ConvertedClosure(argument));
+
+        if (!isInterface) {
+            return ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(Collections.singletonMap(method.getName(), argument), clazz);
+        } else if (method != null && isOrImplementsTrait(clazz)) { // GROOVY-8243
+            return ProxyGenerator.INSTANCE.instantiateAggregate(Collections.singletonMap(method.getName(), argument), Collections.singletonList(clazz));
         } else {
-            Map<String, Object> m = new HashMap<String,Object>();
-            m.put(method.getName(), argument);
-            return ProxyGenerator.INSTANCE.
-                    instantiateAggregateFromBaseClass(m, clazz);
+            return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new ConvertedClosure(argument));
         }
     }
-    
-    @Override
-    public Object coerceArgument(Object argument) {
-        if (argument instanceof Closure) {
-            Class clazz = getTheClass();
-            return coerceToSAM((Closure) argument, method, clazz);
-        } else {
-            return argument;
+
+    private static boolean isOrImplementsTrait(Class<?> c) {
+        if (Traits.isTrait(c)) return true; // quick check
+
+        Queue<Class<?>> todo = new ArrayDeque<>(Arrays.asList(c.getInterfaces()));
+        Set<Class<?>> done = new HashSet<>();
+        while ((c = todo.poll()) != null) {
+            if (done.add(c)) {
+                if (Traits.isTrait(c)) return true;
+                Collections.addAll(todo, c.getInterfaces());
+            }
         }
+        return false;
     }
 
-    private static Method[] getDeclaredMethods(final Class c) {
+    private static Method[] getDeclaredMethods(final Class<?> c) {
         try {
-            Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
+            Method[] methods = java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Method[]>() {
+                @Override
                 public Method[] run() {
                     return c.getDeclaredMethods();
                 }
             });
-            if (methods!=null) return methods;
+            if (methods != null) return methods;
         } catch (java.security.AccessControlException ace) {
             // swallow and do as if no method is available
         }
         return EMPTY_METHOD_ARRAY;
     }
 
-    private static void getAbstractMethods(Class c, List<Method> current) {
-        if (c==null || !Modifier.isAbstract(c.getModifiers())) return;
+    private static void getAbstractMethods(Class<?> c, List<Method> current) {
+        if (c == null || !Modifier.isAbstract(c.getModifiers())) return;
         getAbstractMethods(c.getSuperclass(), current);
-        for (Class ci : c.getInterfaces()) {
+        for (Class<?> ci : c.getInterfaces()) {
             getAbstractMethods(ci, current);
         }
         for (Method m : getDeclaredMethods(c)) {
@@ -127,16 +132,17 @@ public class CachedSAMClass extends CachedClass {
         }
     }
 
-    private static boolean hasUsableImplementation(Class c, Method m) {
-        if (c==m.getDeclaringClass()) return false;
+    private static boolean hasUsableImplementation(Class<?> c, Method m) {
+        if (c == m.getDeclaringClass()) return false;
         Method found;
         try {
             found = c.getMethod(m.getName(), m.getParameterTypes());
             int asp = found.getModifiers() & ABSTRACT_STATIC_PRIVATE;
-            int visible = found.getModifiers() & VISIBILITY;
-            if (visible !=0 && asp == 0) return true;
-        } catch (NoSuchMethodException e) {/*ignore*/}
-        if (c==Object.class) return false;
+            int visible = found.getModifiers() & PUBLIC_OR_PROTECTED;
+            if (visible != 0 && asp == 0) return true;
+        } catch (NoSuchMethodException ignore) {
+        }
+        if (c == Object.class) return false;
         return hasUsableImplementation(c.getSuperclass(), m);
     }
 
@@ -145,9 +151,7 @@ public class CachedSAMClass extends CachedClass {
         if (current.size()==1) return current.get(0);
         Method m = current.remove(0);
         for (Method m2 : current) {
-            if (m.getName().equals(m2.getName()) && 
-                Arrays.equals(m.getParameterTypes(), m2.getParameterTypes()))
-            {
+            if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) {
                 continue;
             }
             return null;
@@ -156,53 +160,41 @@ public class CachedSAMClass extends CachedClass {
     }
 
     /**
-     * returns the abstract method from a SAM type, if it is a SAM type.
-     * @param c the SAM class
-     * @return null if nothing was found, the method otherwise
+     * Finds the abstract method of given class, if it is a SAM type.
      */
     public static Method getSAMMethod(Class<?> c) {
-      try {
-        return getSAMMethodImpl(c);
-      } catch (NoClassDefFoundError ignore) {
-        return null;
-      }
-    }
-
-    private static Method getSAMMethodImpl(Class<?> c) {
-        // SAM = single public abstract method
         // if the class is not abstract there is no abstract method
         if (!Modifier.isAbstract(c.getModifiers())) return null;
-        if (c.isInterface()) {
-            Method[] methods = c.getMethods();
-            // res stores the first found abstract method
-            Method res = null;
-            for (Method mi : methods) {
-                // ignore methods, that are not abstract and from Object
-                if (!Modifier.isAbstract(mi.getModifiers())) continue;
-                // ignore trait methods which have a default implementation
-                if (mi.getAnnotation(Traits.Implemented.class)!=null) continue;
-                try {
-                    Object.class.getMethod(mi.getName(), mi.getParameterTypes());
-                    continue;
-                } catch (NoSuchMethodException e) {/*ignore*/}
-
-                // we have two methods, so no SAM
-                if (res!=null) return null;
-                res = mi;
-            }
-            return res;
-
-        } else {
-
-            LinkedList<Method> methods = new LinkedList();
-            getAbstractMethods(c, methods);
-            if (methods.isEmpty()) return null;
-            ListIterator<Method> it = methods.listIterator();
-            while (it.hasNext()) {
-                Method m = it.next();
-                if (hasUsableImplementation(c, m)) it.remove();
+        try {
+            if (c.isInterface()) {
+                // res stores the first found abstract method
+                Method res = null;
+                for (Method mi : c.getMethods()) {
+                    // ignore methods that are not abstract
+                    if (!Modifier.isAbstract(mi.getModifiers())) continue;
+                    // ignore trait methods with a default implementation
+                    if (mi.getAnnotation(Traits.Implemented.class) != null) continue;
+                    try { // ignore methods that are from java.lang.Object
+                        Object.class.getMethod(mi.getName(), mi.getParameterTypes());
+                        continue;
+                    } catch (NoSuchMethodException ignore) {
+                    }
+                    // we have two methods, so no SAM
+                    if (res != null) return null;
+                    res = mi;
+                }
+                return res;
+            } else {
+                List<Method> methods = new LinkedList<>();
+                getAbstractMethods(c, methods);
+                if (methods.isEmpty()) return null;
+                for (ListIterator<Method> it = methods.listIterator(); it.hasNext(); ) {
+                    if (hasUsableImplementation(c, it.next())) it.remove();
+                }
+                return getSingleNonDuplicateMethod(methods);
             }
-            return getSingleNonDuplicateMethod(methods);
+        } catch (NoClassDefFoundError ignore) {
+            return null;
         }
     }
 }
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 51579b8989..59694fb0da 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -119,7 +119,6 @@ import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
 import java.math.BigDecimal;
@@ -11768,38 +11767,28 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Coerces the closure to an implementation of the given class.  The class
+     * Coerces the closure to an implementation of the given class. The class
      * is assumed to be an interface or class with a single method definition.
      * The closure is used as the implementation of that single method.
      *
-     * @param cl    the implementation of the single method
-     * @param clazz the target type
-     * @return a Proxy of the given type which wraps this closure.
+     * @param impl the implementation of the single method
+     * @param type the target type
+     * @return A proxy of the given type which wraps this closure.
+     *
      * @since 1.0
      */
-    @SuppressWarnings("unchecked")
-    public static <T> T asType(Closure cl, Class<T> clazz) {
-        if (clazz.isInterface() && !(clazz.isInstance(cl))) {
-            if (Traits.isTrait(clazz)) {
-                Method samMethod = CachedSAMClass.getSAMMethod(clazz);
-                if (samMethod!=null) {
-                    Map impl = Collections.singletonMap(samMethod.getName(),cl);
-                    return (T) ProxyGenerator.INSTANCE.instantiateAggregate(impl, Collections.<Class>singletonList(clazz));
-                }
-            }
-            return (T) Proxy.newProxyInstance(
-                    clazz.getClassLoader(),
-                    new Class[]{clazz},
-                    new ConvertedClosure(cl));
+    public static <T> T asType(final Closure impl, final Class<T> type) {
+        if (type.isInterface() && !type.isInstance(impl)) {
+            return (T) CachedSAMClass.coerceToSAM(impl, CachedSAMClass.getSAMMethod(type), type, true);
         }
+
         try {
-            return asType((Object) cl, clazz);
-        } catch (GroovyCastException ce) {
+            return asType((Object) impl, type);
+        } catch (GroovyCastException gce) {
             try {
-                return (T) ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(cl, clazz);
-            } catch (GroovyRuntimeException cause) {
-                throw new GroovyCastException("Error casting closure to " + clazz.getName() +
-                        ", Reason: " + cause.getMessage());
+                return (T) ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(impl, type);
+            } catch (GroovyRuntimeException gre) {
+                throw new GroovyCastException("Error casting closure to " + type.getName() + ", Reason: " + gre.getMessage());
             }
         }
     }
diff --git a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
index cd88448142..5ef57edc7c 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
@@ -23,7 +23,6 @@ import groovy.lang.GeneratedGroovyProxy;
 import groovy.lang.GroovyClassLoader;
 import groovy.lang.GroovyObject;
 import groovy.lang.GroovyRuntimeException;
-import groovy.transform.Trait;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
@@ -42,7 +41,6 @@ import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -90,19 +88,18 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
 
     private static final String CLOSURES_MAP_FIELD = "$closures$delegate$map";
     private static final String DELEGATE_OBJECT_FIELD = "$delegate";
-    private static List<Method> OBJECT_METHODS = getInheritedMethods(Object.class, new ArrayList<Method>());
-    private static List<Method> GROOVYOBJECT_METHODS = getInheritedMethods(GroovyObject.class, new ArrayList<Method>());
-
     private static final AtomicLong pxyCounter = new AtomicLong();
-    private static final Set<String> GROOVYOBJECT_METHOD_NAMESS;
     private static final Object[] EMPTY_ARGS = new Object[0];
 
+    private static List<Method> OBJECT_METHODS = getInheritedMethods(Object.class, new ArrayList<Method>());
+    private static List<Method> GROOVYOBJECT_METHODS = getInheritedMethods(GroovyObject.class, new ArrayList<Method>());
+    private static final Set<String> GROOVYOBJECT_METHOD_NAMES;
     static {
-        List<String> names = new ArrayList<String>();
+        Set<String> names = new HashSet<>();
         for (Method method : GroovyObject.class.getMethods()) {
             names.add(method.getName());
         }
-        GROOVYOBJECT_METHOD_NAMESS = new HashSet<String>(names);
+        GROOVYOBJECT_METHOD_NAMES = Collections.unmodifiableSet(names);
     }
 
     private final Class superClass;
@@ -199,20 +196,16 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
         cachedNoArgConstructor = constructor;
     }
 
-    private Class adjustSuperClass(Class superClass, final Class[] interfaces) {
-        boolean isSuperClassAnInterface = superClass.isInterface();
-        if (!isSuperClassAnInterface) {
+    private Class adjustSuperClass(final Class superClass, Class[] interfaces) {
+        if (!superClass.isInterface()) {
             return superClass;
         }
-        Class result = Object.class;
-        Set<ClassNode> traits = new LinkedHashSet<ClassNode>();
-        // check if it's a trait
-        collectTraits(superClass, traits);
-        if (interfaces != null) {
-            for (Class anInterface : interfaces) {
-                collectTraits(anInterface, traits);
-            }
+        if (interfaces == null || interfaces.length == 0) {
+            interfaces = new Class[]{superClass};
         }
+        assert Arrays.asList(interfaces).contains(superClass);
+
+        Set<ClassNode> traits = collectTraits(interfaces);
         if (!traits.isEmpty()) {
             String name = superClass.getName() + "$TraitAdapter";
             ClassNode cn = new ClassNode(name, ACC_PUBLIC | ACC_ABSTRACT, ClassHelper.OBJECT_TYPE, traits.toArray(ClassNode.EMPTY_ARRAY), null);
@@ -224,29 +217,35 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
             su.getAST().addClass(cn);
             cu.compile(Phases.CLASS_GENERATION);
             @SuppressWarnings("unchecked")
-            List<GroovyClass> classes = (List<GroovyClass>) cu.getClasses();
+            List<GroovyClass> classes = cu.getClasses();
             for (GroovyClass groovyClass : classes) {
                 if (groovyClass.getName().equals(name)) {
                     return loader.defineClass(name, groovyClass.getBytes());
                 }
             }
         }
-        return result;
+
+        return Object.class;
     }
 
-    private static void collectTraits(final Class clazz, final Set<ClassNode> traits) {
-        Annotation annotation = clazz.getAnnotation(Trait.class);
-        if (annotation != null) {
-            ClassNode trait = ClassHelper.make(clazz);
-            traits.add(trait.getPlainNodeReference());
-            LinkedHashSet<ClassNode> selfTypes = new LinkedHashSet<ClassNode>();
-            Traits.collectSelfTypes(trait, selfTypes, true, true);
-            for (ClassNode selfType : selfTypes) {
-                if (Traits.isTrait(selfType)) {
-                    traits.add(selfType.getPlainNodeReference());
+    private static Set<ClassNode> collectTraits(final Class[] interfaces) {
+        Set<ClassNode> traits = new LinkedHashSet<>();
+        for (Class face : interfaces) {
+            for (ClassNode node : ClassHelper.make(face).getAllInterfaces()) {
+                if (Traits.isTrait(node)) {
+                    traits.add(node.getPlainNodeReference());
+                    // check trait for @SelfType types that are / extend traits
+                    LinkedHashSet<ClassNode> selfTypes = new LinkedHashSet<>();
+                    Traits.collectSelfTypes(node, selfTypes, true, true);
+                    for (ClassNode selfType : selfTypes) {
+                        if (Traits.isTrait(selfType)) {
+                            traits.add(selfType.getPlainNodeReference());
+                        }
+                    }
                 }
             }
         }
+        return traits;
     }
 
     private static InnerLoader createInnerLoader(final ClassLoader parent, final Class[] interfaces) {
@@ -557,7 +556,7 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
         int accessFlags = access;
         visitedMethods.add(key);
         if ((objectDelegateMethods.contains(name + desc) || delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) && !Modifier.isStatic(access) && !Modifier.isFinal(access)) {
-            if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
+            if (!GROOVYOBJECT_METHOD_NAMES.contains(name)) {
                 if (Modifier.isAbstract(access)) {
                     // prevents the proxy from being abstract
                     accessFlags -= ACC_ABSTRACT;
@@ -576,7 +575,7 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
             return createGetProxyTargetMethod(access, name, desc, signature, exceptions);
         } else if ("<init>".equals(name) && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
             return createConstructor(access, name, desc, signature, exceptions);
-        } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
+        } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMES.contains(name)) {
             if (isImplemented(superClass, name, desc)) {
                 return null;
             }
diff --git a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index 1ac9f6febe..ae6223f019 100644
--- a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -18,12 +18,14 @@
  */
 package org.codehaus.groovy.transform.traitx
 
+import groovy.transform.NotYetImplemented
 import groovy.transform.SelfType
 import org.codehaus.groovy.ast.ClassHelper
 import org.codehaus.groovy.ast.expr.ClassExpression
 import org.codehaus.groovy.ast.expr.ListExpression
 
-class TraitASTTransformationTest extends GroovyTestCase {
+final class TraitASTTransformationTest extends GroovyTestCase {
+
     void testTraitOverrideAnnotation() {
         assertScript '''
         interface MyInterface {
@@ -1116,7 +1118,7 @@ assert phone.speak() == 'My name is Galaxy S3\''''
         '''
     }
 
-    void testSAMCoercionOfTraitOnAssignment() {
+    void testSAMCoercion1() {
         assertScript '''
             trait SAMTrait {
                 String foo() { bar()+bar() }
@@ -1139,7 +1141,7 @@ assert phone.speak() == 'My name is Galaxy S3\''''
         '''
     }
 
-    void testSAMCoercionOfTraitOnMethod() {
+    void testSAMCoercion2() {
         assertScript '''
             trait SAMTrait {
                 String foo() { bar()+bar() }
@@ -1150,6 +1152,7 @@ assert phone.speak() == 'My name is Galaxy S3\''''
             }
             test { 'hello' } // SAM coercion
         '''
+
         assertScript '''
             trait SAMTrait {
                 String foo() { bar()+bar() }
@@ -1166,26 +1169,60 @@ assert phone.speak() == 'My name is Galaxy S3\''''
         '''
     }
 
-    void testImplicitSAMCoercionBug() {
+    void testSAMCoercion3() {
         assertScript '''
-trait Greeter {
-    String greet() { "Hello $name" }
-    abstract String getName()
-}
-Greeter greeter = { 'Alice' }
-assert greeter.greet() == 'Hello Alice'
-'''
+            trait Greeter {
+                abstract String getName()
+                String greet() { "Hello $name" }
+            }
+            Greeter greeter = { 'Alice' }
+            assert greeter.greet() == 'Hello Alice'
+            assert greeter.getName().equals('Alice')
+        '''
     }
 
-    void testExplicitSAMCoercionBug() {
+    void testSAMCoercion4() {
         assertScript '''
-trait Greeter {
-    String greet() { "Hello $name" }
-    abstract String getName()
-}
-Greeter greeter = { 'Alice' } as Greeter
-assert greeter.greet() == 'Hello Alice'
-'''
+            trait Greeter {
+                abstract String getName()
+                String greet() { "Hello $name" }
+            }
+            def greeter = { 'Alice' } as Greeter
+            assert greeter.greet() == 'Hello Alice'
+            assert greeter.getName().equals('Alice')
+        '''
+    }
+
+    // GROOVY-8243
+    void testSAMCoercion5() {
+        assertScript '''
+            trait T {
+                abstract foo(int n)
+                def bar(double n) {
+                    "trait $n".toString()
+                }
+            }
+            interface I extends T {
+            }
+
+            I obj = { "proxy $it".toString() }
+            assert obj.foo(123) == 'proxy 123'
+            assert obj.bar(4.5) == 'trait 4.5'
+        '''
+    }
+
+    // GROOVY-8244
+    @NotYetImplemented
+    void testSAMCoercion6() {
+        assertScript '''
+            trait T {
+                abstract def foo(int a, int b = 2)
+            }
+            T t = { int a, int b ->
+                return a + b
+            }
+            assert t.foo(40) == 42
+        '''
     }
 
     void testMethodMissingInTrait() {