RFR: 8159746: (proxy) Support for default methods

Mandy Chung mchung at openjdk.java.net
Thu Oct 22 00:28:11 UTC 2020


On Tue, 20 Oct 2020 23:42:31 GMT, Mandy Chung <mchung at openjdk.org> wrote:

>> Hi Mandy,
>> 
>> You're right. I haven't thought of what one can do with a Lookup for one proxy class when other proxy classes are co-located in the same module and/or package. So instead of a Lookup, we could use some other more targeted "capability". Here's alternative API no.2 that uses special SuperInvoker callback interface passed as argument to invocation handler:
>> 
>> https://github.com/mlchung/jdk/pull/4
>> 
>> Each proxy class has its own SuperInvoker instance, assigned to static final field of generated proxy class like Method objects, which is passed to invocation handler in the same way as the selected Method object. Invocation handler can use this SuperInvoker instance to invoke the super default method on the proxy. SuperInvoker can verify that the passed-in proxy is of the correct class very quickly. Also the cache of transformed method handles per Method is embedded in the SuperInvoker itself instead of using ClassValue to hold it, so overhead for super invocations is further reduced:
>> 
>> Mandy's original:
>> 
>> Benchmark             Mode  Cnt    Score    Error  Units
>> ProxyBench.implClass  avgt    5    3.709 ±  0.026  ns/op
>> ProxyBench.implProxy  avgt    5  926.215 ± 11.835  ns/op
>> 
>> 
>> Peter's performance patch:
>> 
>> Benchmark             Mode  Cnt   Score   Error  Units
>> ProxyBench.implClass  avgt    5   3.777 ± 0.005  ns/op
>> ProxyBench.implProxy  avgt    5  27.579 ± 0.250  ns/op
>> 
>> 
>> Static method moved to InvocationHandler + added access check
>> 
>> Benchmark               Mode  Cnt    Score   Error  Units
>> ProxyBench.implClass    avgt    5    3.740 ± 0.004  ns/op
>> ProxyBench.implProxy    avgt    5   34.226 ± 0.125  ns/op
>> ProxyBench.ppImplClass  avgt    5    3.780 ± 0.004  ns/op
>> ProxyBench.ppImplProxy  avgt    5  147.318 ± 1.422  ns/op
>> 
>> 
>> Alternative API #1 with access check in newProxyInstance and Lookup parameter
>> 
>> Benchmark               Mode  Cnt   Score   Error  Units
>> ProxyBench.implClass    avgt    5   3.782 ± 0.013  ns/op
>> ProxyBench.implProxy    avgt    5  32.493 ± 0.192  ns/op
>> ProxyBench.ppImplClass  avgt    5   3.749 ± 0.002  ns/op
>> ProxyBench.ppImplProxy  avgt    5  30.565 ± 0.190  ns/op
>> 
>> 
>> Alternative API #2 with SuperInvoker parameter
>> 
>> Benchmark               Mode  Cnt   Score   Error  Units
>> ProxyBench.implClass    avgt    5   3.777 ± 0.003  ns/op
>> ProxyBench.implProxy    avgt    5  20.282 ± 0.585  ns/op
>> ProxyBench.ppImplClass  avgt    5   3.752 ± 0.002  ns/op
>> ProxyBench.ppImplProxy  avgt    5  19.790 ± 0.335  ns/op
>> 
>> So what do you think about this one?
>
> Hi Peter, thanks for coming with these alternatives.   The need for new `InvocationHandler2` and `InvocationHandler2.SuperInvoker` APIs and the complexity of `plevart:proxy-default-method-alt-api2` look unpleasing in order to keep the invocation of default methods in lambdas.  I prefer the `DelegatingInvocationHandler` abstract class approach without caller-sensitive method.

Hi Peter, Alan

I have prototyped two alternatives ([mlchung:proxy-default-method-3](https://github.com/mlchung/jdk/tree/proxy-default-method-3)).
1. `DelegatingInvocationHandler` abstract class with:
protected final Object invokeDefault(Object proxy, Method method, Object... params)

2. `NewInvocationHandler` functional interface
    public Object invoke(DefaultMethodInvoker invoker, Object proxy, Method method, Object... args) throws Throwable;

    /**
     * {@inheritDoc}
     */
    public default Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
         return invoke(DefaultMethodInvoker.getInvoker(proxy, this), proxy, method, args);
    }

   public final class DefaultMethodInvoker {
        public final Object invoke(Object proxy, Method method, Object... args)
                throws InvocationTargetException
   }

Similar idea as yours SuperInvoker.  But I think this is less complicated
and DefaultMethodInvoker is a final class rather than an interface.
The generated proxy class will keep invoking `InvocationHandler::invoke`
(no change in `ProxyGenerator`).  `NewInvocationHandler` allows the use of lambdas.

I like `DelegatingInvocationHandler` which is simple and clean but not
a functional interface.   `NewInvocationHandler` is a functional interface
with the need of `DefaultMethodInvoker` to bridge between an invocation
handler and an accessed check proxy object.

Thoughts?  I'm leaning toward `DelegatingInvocationHandler`.  

We need to decide on the exception if access checks fails on
`Proxy::newProxyInstance` and  `Proxy::getInvocationHandler` 
if the invocation handler needs the ability to invoke default
methods.

-------------

PR: https://git.openjdk.java.net/jdk/pull/313


More information about the core-libs-dev mailing list