On Thu, 15 Oct 2020 09:04:09 GMT, Peter Levart <plev...@openjdk.org> 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