Avoiding sun.misc.Unsafe and embracing modules in Java libraries: missing links
Rafael Winterhalter
rafael.wth at gmail.com
Mon Apr 9 19:48:42 UTC 2018
Hei Alan,
maybe I am doing it wrong but this is my example. I created a module with
two interfaces bar.Bar and qux.Qux that both define a default method String
foo() { return "foo"; }. The module exports bar and exports and opens qux.
>From another module that reads that first module I run the following code:
package main;
import bar.Bar;
import qux.Qux;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Proxy;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
test(Foo.class, Foo::foo); // works: interface in same module
test(Bar.class, Bar::foo); // works not: interface in other
module, exported
test(Qux.class, Qux::foo); // works: interface in other
module, exported and opened
test(Bar.class, new Bar() { // works: explicit proxy
@Override
public String foo() {
return Bar.super.foo();
}
}, Bar::foo);
test(Bar.class, new Bar() { // works not: explicit proxy with
method handle
@Override
public String foo() {
try {
return (String) MethodHandles.lookup().findSpecial(
Bar.class,
"foo",
MethodType.methodType(String.class, new
Class<?>[0]),
Bar.class
).bindTo(this).invokeWithArguments();
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}, Bar::foo);
}
private static <T> void test(Class<? extends T> iface, Consumer<T>
consumer) {
Object instance = Proxy.newProxyInstance(
Bar.class.getClassLoader(),
new Class<?>[]{iface},
(proxy, method, arguments) ->
MethodHandles.privateLookupIn(iface,
MethodHandles.lookup()).findSpecial(
iface,
"foo",
MethodType.methodType(String.class, new Class<?>[0]),
iface
).bindTo(proxy).invokeWithArguments()
);
test(iface, iface.cast(instance), consumer);
}
private static <T> void test(Class<? extends T> iface, T instance,
Consumer<T> consumer) {
try {
consumer.accept(instance);
System.out.println("Could invoke special method for " + iface);
} catch (Throwable throwable) {
System.out.println("Could NOT invoke special method for " + iface);
}
}
}
>From the code comments, some approaches work and some do not.
What would I need to do differently?
Thank you and best regards, Rafael
2018-04-09 9:33 GMT+02:00 Alan Bateman <Alan.Bateman at oracle.com>:
> On 01/04/2018 22:02, Rafael Winterhalter wrote:
>
>> :
>>
>> 1. Java agents cannot define auxiliary classes.
>>
>> :
>> The reason for
>> using Unsafe is that many instrumentations need to define auxiliary
>> classes
>> to aid an instrumentation similar to javac which sometimes needs to define
>> anonymous classes or even synthetic classes. For example, if a Java agent
>> wants to register an event listener to some framework, such listeners
>> often
>> declare multiple methods what makes it impossible to fullfil the listener
>> contract using a lambda expression. Instead, one typically injects an
>> additional class into the same package as the instrumented class.
>>
> This seems a reasonable requirement. As you know, JSR-163 created this API
> (and JVM TI) for tools to instrument code in mostly benign ways where any
> additional agent provided helper classes are made visibility via the
> appendToXXXClassLoaderSearch methods. I don't think the use-case of
> dynamically generated helper classes came up, I don't recall it coming up
> on serviceability-dev in the intervening years either. In any case, I think
> there should be a way to support this scenario, it amounts to a
> ClassFileTransformer providing the class bytes of additional classes to be
> defined in the same runtime package as the class being loaded or
> transformed. There are a number of API choices and it's probably best if we
> bring proposals to serviceability-dev as that is where this API is
> maintained.
>
>
> :
>>
>> 2. Java proxies cannot invoke default methods of proxied interfaces
>>
>> The Java proxy API does not currently allow the invocation of an
>> overridden
>> default method since
>> the InvocationHandler API only supplies an instance of
>> java.lang.reflection.Method.
>>
> The issue of Proxies and default methods has come up on core-libs-dev a
> few times. In the mean-time, JEP 274 added support for MethodHandles that
> bind to non-abstract methods in interfaces. I just double checked and I can
> create a proxy where the invocation handler creates a method handle to the
> default method, binds it to proxy, and invokes it. I also checked the case
> where the interface is public in an exported package of a named module. Can
> you say a bit more, or provide an example, where you run into issues? I'm
> wondering if you are running into an accessibility issue or something else.
>
> -Alan
>
>
More information about the jigsaw-dev
mailing list