Skip to content

Commit

Permalink
GROOVY-9873: use class loader of SAM type for proxy creation
Browse files Browse the repository at this point in the history
4_0_X backport
  • Loading branch information
eric-milles committed Aug 29, 2024
1 parent 1af63eb commit ec5903b
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,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!
Expand All @@ -164,17 +164,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
// 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:
Expand Down Expand Up @@ -207,8 +204,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);
Expand Down
137 changes: 137 additions & 0 deletions src/test/groovy/bugs/Groovy9873.groovy
Original file line number Diff line number Diff line change
@@ -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()
}
}
}
10 changes: 3 additions & 7 deletions src/test/groovy/transform/stc/GenericsSTCTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2622,13 +2622,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())
"""
}
}
Expand Down

0 comments on commit ec5903b

Please sign in to comment.