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