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 ef3fdc5b21 GROOVY-9873: use class loader of SAM type for proxy creation
ef3fdc5b21 is described below

commit ef3fdc5b2169c19e934dc958b8fb61c902abaac1
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Thu Aug 29 12:32:21 2024 -0500

    GROOVY-9873: use class loader of SAM type for proxy creation
---
 src/main/java/groovy/lang/MetaClassImpl.java       |  21 ++--
 .../groovy/vmplugin/v8/TypeTransformers.java       |  21 ++--
 src/test/groovy/bugs/Groovy9873.groovy             | 137 +++++++++++++++++++++
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  10 +-
 4 files changed, 162 insertions(+), 27 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java 
b/src/main/java/groovy/lang/MetaClassImpl.java
index 480c550980..4308fa9883 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -2676,11 +2676,13 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
     }
 
     /**
-     * <p>Retrieves a property on the given receiver for the specified 
arguments. The sender is the class that is requesting the property from the 
object.
-     * The MetaClass will attempt to establish the method to invoke based on 
the name and arguments provided.
-     *
-     * <p>The useSuper and fromInsideClass help the Groovy runtime perform 
optimisations on the call to go directly
-     * to the super class if necessary
+     * Writes a property on the given receiver for the specified arguments. The
+     * sender is the class that is requesting the property from the object. The
+     * MetaClass will attempt to establish the method to invoke based on the 
name
+     * and arguments provided.
+     * <p>
+     * The useSuper and fromInsideClass help the runtime perform optimisations
+     * on the call to go directly to the super class if necessary
      *
      * @param sender          The java.lang.Class instance that is mutating 
the property
      * @param object          The Object which the property is being set on
@@ -2751,10 +2753,13 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
             method = listeners.get(name);
             ambiguousListener = (method == AMBIGUOUS_LISTENER_METHOD);
             if (method != null && !ambiguousListener && newValue instanceof 
Closure) {
+                // bean.name = { -> } is short for bean.addSomeListener({ -> 
});
+                // where "name" derives from the SomeListener interface's 
method
+                var listener = method.getParameterTypes()[0].getTheClass();
                 Object proxy = Proxy.newProxyInstance(
-                        theClass.getClassLoader(),
-                        new 
Class[]{method.getParameterTypes()[0].getTheClass()},
-                        new ConvertedClosure((Closure) newValue, name));
+                        listener.getClassLoader(),
+                        new Class[]{listener},
+                        new ConvertedClosure((Closure<?>) newValue, name));
                 arguments = new Object[]{proxy};
                 newValue = proxy;
             } else {
diff --git 
a/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeTransformers.java 
b/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeTransformers.java
index 55c77b65bd..673cc756c0 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeTransformers.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeTransformers.java
@@ -141,10 +141,10 @@ public class TypeTransformers {
     }
 
     /**
-     * creates a method handle able to transform the given Closure into a SAM 
type
-     * if the given parameter is a SAM type
+     * Creates a method handle that transforms the given Closure into the given
+     * parameter type, if it is a SAM type.
      */
-    private static MethodHandle createSAMTransform(Object arg, Class<?> 
parameter) {
+    private static MethodHandle createSAMTransform(Object closure, Class<?> 
parameter) {
         Method method = CachedSAMClass.getSAMMethod(parameter);
         if (method == null) return null;
         // TODO: have to think about how to optimize this!
@@ -164,17 +164,14 @@ public class TypeTransformers {
             }
             // the following code will basically do this:
             // return Proxy.newProxyInstance(
-            //        arg.getClass().getClassLoader(),
+            //        parameter.getClassLoader(),
             //        new Class[]{parameter},
-            //        new ConvertedClosure((Closure) arg));
+            //        new ConvertedClosure((Closure)closure, 
method.getName()));
             // TO_REFLECTIVE_PROXY will do that for us, though
             // input is the closure, the method name, the class loader and the
-            // class[]. All of that but the closure must be provided here
+            // class array. All of that but the closure must be provided here.
             MethodHandle ret = TO_REFLECTIVE_PROXY;
-            ret = MethodHandles.insertArguments(ret, 1,
-                    method.getName(),
-                    arg.getClass().getClassLoader(),
-                    new Class[]{parameter});
+            ret = MethodHandles.insertArguments(ret, 1, method.getName(), 
parameter.getClassLoader(), new Class[]{parameter});
             return ret;
         } else {
             // the following code will basically do this:
@@ -207,8 +204,8 @@ public class TypeTransformers {
     }
 
     /**
-     * returns a transformer later applied as filter to transform one
-     * number into another
+     * Returns a transformer later applied as filter to transform one
+     * number into another.
      */
     private static MethodHandle selectNumberTransformer(Class<?> param, Object 
arg) {
         param = TypeHelper.getWrapperClass(param);
diff --git a/src/test/groovy/bugs/Groovy9873.groovy 
b/src/test/groovy/bugs/Groovy9873.groovy
new file mode 100644
index 0000000000..65afad43ed
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9873.groovy
@@ -0,0 +1,137 @@
+/*
+ *  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.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy9873 {
+
+    @Test
+    void testCoerceClosure1() {
+        assertScript '''
+            @Grab('io.vavr:vavr:0.10.4;transitive=false')
+            import io.vavr.control.Try
+            class C { }
+            C resolve() {new C()}
+            Try.of(this::resolve)
+        '''
+    }
+
+    @Test
+    void testCoerceClosure2() {
+        def config = new CompilerConfiguration().tap {
+            jointCompilationOptions = [memStub: true]
+            targetDirectory = File.createTempDir()
+        }
+        File parentDir = File.createTempDir()
+        try {
+            def c = new File(parentDir, 'C.groovy')
+            c.write '''
+                class C<T> {
+                    private T t
+                    C(T item) {
+                        t = item
+                    }
+                    static <U> C<U> of(U item) {
+                        new C<U>(item)
+                    }
+                    def <V> C<V> map(F<? super T, ? super V> func) {
+                        new C<V>(func.apply(t))
+                    }
+                }
+            '''
+            def d = new File(parentDir, 'D.groovy')
+            d.write '''
+                class D {
+                    static <W> Set<W> wrap(W o) {
+                        Collections.singleton(o)
+                    }
+                }
+            '''
+            def f = new File(parentDir, 'F.groovy')
+            f.write '''
+                interface F<X,Y> {
+                    Y apply(X x)
+                }
+            '''
+            def g = new File(parentDir, 'G.groovy')
+            g.write '''
+                def c = C.of(123)
+                def d = c.map(D.&wrap)
+                def e = d.map(x -> x.first().intValue())
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(c, d, f, g)
+            cu.compile()
+
+            loader.loadClass('G').main()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    @Test
+    void testCoerceClosure3() {
+        def config = new CompilerConfiguration().tap {
+            jointCompilationOptions = [memStub: true]
+            targetDirectory = File.createTempDir()
+        }
+        File parentDir = File.createTempDir()
+        try {
+            def f = new File(parentDir, 'F.groovy')
+            f.write '''
+                class FInfo extends EventObject {
+                    FInfo() { super(null) }
+                }
+                interface FListener extends EventListener {
+                    void somethingHappened(FInfo i)
+                }
+            '''
+            def g = new File(parentDir, 'G.groovy')
+            g.write '''
+                class H {
+                    void addFListener(FListener f) {
+                        f.somethingHappened(null)
+                    }
+                    void removeFListener(FListener f) {
+                    }
+                }
+
+                new H().somethingHappened = { info -> }
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(f, g)
+            cu.compile()
+
+            loader.loadClass('G').main()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy 
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index faf5e8809c..2d41d771a6 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2653,13 +2653,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase 
{
                     }
                 }
 
-                void test() {
-                    def c = C.of(42)
-                    def d = c.map($toSet)
-                    def e = d.map(x -> x.first().intValue())
-                }
-
-                test()
+                def c = C.of(42)
+                def d = c.map($toSet)
+                def e = d.map(x -> x.first().intValue())
             """
         }
     }

Reply via email to