From 4695a353f82611bd0f3c156872cd414d1d90e8d5 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Thu, 29 Aug 2024 12:32:21 -0500 Subject: [PATCH] GROOVY-9873: use class loader of SAM type for proxy creation 3_0_X backport --- .../groovy/vmplugin/v8/TypeTransformers.java | 23 ++- src/test/groovy/bugs/Groovy9873.groovy | 137 ++++++++++++++++++ .../transform/stc/GenericsSTCTest.groovy | 10 +- 3 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 src/test/groovy/bugs/Groovy9873.groovy 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 e22b0dba507..5e1d4b913e3 100644 --- a/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeTransformers.java +++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/TypeTransformers.java @@ -142,10 +142,10 @@ protected static MethodHandle addTransformer(MethodHandle handle, int pos, Objec } /** - * 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! @@ -165,17 +165,14 @@ private static MethodHandle createSAMTransform(Object arg, Class parameter) { } // 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 + // input is the closure, the method name, the class loader and the + // 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: @@ -208,8 +205,8 @@ public static MethodHandle applyUnsharpFilter(MethodHandle handle, int pos, Meth } /** - * 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 00000000000..65afad43ed9 --- /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 { + private T t + C(T item) { + t = item + } + static C of(U item) { + new C(item) + } + def C map(F func) { + new C(func.apply(t)) + } + } + ''' + def d = new File(parentDir, 'D.groovy') + d.write ''' + class D { + static Set wrap(W o) { + Collections.singleton(o) + } + } + ''' + def f = new File(parentDir, 'F.groovy') + f.write ''' + interface F { + 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 68e6f23d7bb..0587de0d211 100644 --- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy +++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy @@ -2722,13 +2722,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()) """ } }