On Thu, 15 Oct 2020 09:04:09 GMT, Peter Levart <[email protected]> wrote:
>> `invokeDefaultMethod` is moved to a static method in `InvocationHandler`.
>> I also add `@throws IllegalAccessException` if the specified default method
>> is
>> inaccessible to the caller (access check was missing in the previous
>> version).
>> The declaring interface of the default method must be exported to the caller
>> to
>> use. If it's a non-public interface, the caller class must be in the same
>> runtime
>> package as the interface. The example in javadoc includes the case when
>> the proxy interface is modified.
>
> Hi Mandy,
>
> I'm beginning to think that we somewhat wondered away from the original
> intent which was to mimic the bytecode
> behaviour as much as possible. Bytecode behaviour for super calls
> (invokespecial) is very special and what you do in
> the last patch is guarding invocation to a super default method via
> @CallerSensitive method which checks the access of
> the caller as though this was a normal call. Similar access check when a
> default method might reside in a
> package-private class or when this class might not be exported is already
> performed when the code creates a Proxy
> class/instance (Proxy::getProxyClass/newProxyInstance). This code also
> designates an InvocationHandler for the proxy
> instance. From that point on, the designated invocation handler should have
> full access to invoking super default
> methods on the proxy instance regardless of where it resides because it has
> been chosen as the delegate of the Proxy
> class with full powers. Since we are exposing the invokeDefaultMethod
> functionality in a public static method, it is
> hard (or not quite possible) to check that the code doing the invocation is
> in fact the designated invocation handler
> code. One approximation might be the following check:
> `Proxy.getInvocationHandler(proxy).getClass() == callerClass`
> ...but that would not work for code residing in the invocation handler
> superclass even though such code might be
> eligible to call the super default method. OTOH such check is over permissive
> as it allows calling the super method
> from static context too. So I was thinking about an alternative approach. To
> expose the method as a protected final
> instance method in an abstract class implementing InvocationHandler: public
> interface InvocationHandler {
> ...
> abstract class Support implements InvocationHandler {
> protected final Object invokeDefaultMethod(Object proxy, Method method,
> Object... args) throws
> InvocationTargetException {
> if (this != Proxy.getInvocationHandler(proxy)) throw ...
> ...
> }
> }
> }
>
> Users would have to extend `InvocationHandler.Support` to access this
> feature. This might be OK for most users, but
> those wanting to extend some other class will not be able to do that.
> So WDYT? In case you still prefer your approach of re-checking the
> permissions in invokeDefaultMethod, then I might
> have some ideas of how to speed-up theses checks. As currently stands,
> invoking public exported interface default
> methods does exhibit a little more penalty, but for non-public or
> non-exported interfaces, the penalty is bigger:
> 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
>
> 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
>
> The updated benchmark code is here:
> https://gist.github.com/plevart/f6e0a5224c3ffbf14b28b47755599226
> (the ppImpl... benchmarks are against package-private interface)
Hi Peter,
The current `newProxyInstance` API does not perform access check against the
proxy interfaces. I want to avoid
adding new caller-sensitive method where possible. I did consider any
alternative to make it non-static method which
only allows a proxy's invocation handler to call `invokeDefaultMethod` if
`proxy.getInvocationHandler() == this`. If
access check done at invocation time, `invokeDefaultMethod` still needs to be
caller-sensitive which must be a final
method (can't be on a default method) In addition, an invocation handler to
call `invokeDefaultMethod` can't be
implemented in a lambda because it can't self-reference itself. So the
invocation handler that wants to invoke default
methods must be a concrete or anonymous class which is a limitation.
Alan and I also discussed other alternative earlier. I agree to prototype a
slightly different alternative that will
perform access check at proxy instance creation time: 1. define an abstract
`DelegatingInvocationHandler` class that
defines a protected final `invokeDefault` method (shortened method name). 2.
`Proxy::newProxyClass` will perform access
check if the given `InvocationHandler` is an instance of
`DelegatingInvocationHandler` 3. `Proxy(InvocationHandler h)`
constructor should throw an exception if the given `h` is an instance of
`DelegatingInvocationHandler`.
`Proxy::getProxyClass` is deprecated and it's recommended to call
`newProxyInstance` instead. It's strongly
discouraged to invoke the Proxy constructor and so I think not supporting
`DelegatingInvocationHandler` may be
reasonable. 4. `Proxy::getInvocationHandler` needs to check if the caller class
has access to the proxy interfaces if
it's a `DelegatingInvocationHandler`.
So one can't get a proxy instance whose invocation handler can invoke default
methods if the caller has no access to
the proxy interfaces; similarly one can't get a `DelegatingInvocationHandler`
from a proxy instance if the caller has
no access.
This alternative avoids `invokeDefault` being caller-sensitive but the
limitation of `DelegatingInvocationHandler`
implementation in a concrete/anonymous class.
Thought/feedback?
-------------
PR: https://git.openjdk.java.net/jdk/pull/313