RFR: 8159746: (proxy) Support for default methods

Mandy Chung mchung at openjdk.java.net
Thu Oct 8 06:52:14 UTC 2020


On Wed, 7 Oct 2020 07:29:51 GMT, Peter Levart <plevart at openjdk.org> wrote:

>> Joe has given some feedback in the CSR review.
>>> should this functionality be housed in the InvocationHandler interface rather than the Proxy class? If I understand the
>>> intended usage of the new method, it should only be done in an InvocationHandler.
>> 
>> The primary usage of this new method is `InvocationHandler`.  I agree that the new API should be defined in
>> `InvocationHandler`.
>> @plevart @AlanBateman any feedback or thought on moving the new API to `InvocationHandler`?
>> 
>>> Does the current spec handle a case like interface A declares a default method m, interface B extends A and separately
>>> interface B declare a default method m.
>> 
>> Yes
>> 
>>> Would invokeDefaultMethod on A::m do-what-is-wanted by call the default method inherited from A? Indirectly, this is
>>> also a question if the operation that are binary compatible with respect to moving around default methods and
>>> behaviorally compatible with the semantics of the new method.
>> 
>> If a proxy instance implements both A and B, `invokeDefaultMethod(o, A.class.getMethod("m"), params)` will invoke
>> `A::m`.
>> If a proxy instance implements B only, `invokeDefaultMethod(o, A.class.getMethod("m"), params)` will get IAE thrown.
>> This is consistent with a concrete class implements B that can only call `B.super::m` but not `A::m`.
>> I update the javadoc to clarify this.
>> 
>>> Any special cases for hidden classes?
>> 
>> No because proxy cannot implement a hidden proxy interface as specified in `Proxy::newProxyInstance`
>> 
>> IllegalArgumentException will be thrown if any of the following restrictions is violated:
>> 
>> - All of Class objects in the given interfaces array must represent non-hidden interfaces, not classes or primitive types.
>
>> Joe has given some feedback in the CSR review.
>> 
>> >    should this functionality be housed in the InvocationHandler interface rather than the Proxy class? If I understand the
>> >    intended usage of the new method, it should only be done in an InvocationHandler.
>> 
>> The primary usage of this new method is InvocationHandler. I agree that the new API should be defined in
>> InvocationHandler.
> 
> If it is a public static method, then Proxy or InvocationHandler are just a namespace for it. From discoverability
> perspective, either one is a good place I think. One point for moving the method into the InvocationHandler is the fact
> that Proxy is a class and classes "inherit" static methods too while interfaces don't. It would be impossible to create
> a dynamic proxy for an interface with a method having the same signature as the Proxy.invokeDefaultMethod static method
> since such proxy class would need to implement an instance method having the same signature as its superclass static
> method.  If interfaces supported protected default methods, then it would make sense to be a protected default method
> in InvocationHandler. Such instance method could additionally enforce two things:
> - that it is called from code comprising InvocationHandler
> - that the proxy instance passed to that method is in fact backed by the invocation handler instance that is the target
>   of that call by checking that in the method.
> 
> But we don't have protected default methods in interfaces (yet). So a static method it must be.
> 
> I was thinking about what such public static method allows and whether that presents a problem (perhaps security
> related) and came to the conclusion that it does not allow anything that normal Java code would not allow except for
> one thing (see below). From the 1st glance it would seem that it allows any code to call super default method on any
> Proxy instance, but what does that mean exactly? The same effect can be achieved by creating a special hand-made proxy
> class implementing all the interfaces that the dynamic Proxy class implements:  class HandmadeProxy implements I1, I2,
> ... {
>     private final Proxy targetProxy;
>     HandmadeProxy(Proxy targetProxy) { this.targetProxy = targetProxy }
>     // implement all non-default methods of I1, I2, ... by delegating to targetProxy
>     // example for I1.m()
>     public void m() {
>         ((I1)targetProxy).m();
>     }
> }
> 
> ...and then using such class to invoke some default method on an instance of the Proxy:
> 
> Proxy proxy = ...
> new HandmadeProxy(proxy).someDefaultMethod();
> 
> So such apparently powerful capability does not represent a problem. One additional thing is possible though:
> 
>>> Does the current spec handle a case like interface A declares a default method m, interface B extends A and separately
>>> interface B declare a default method m.
>>
>> If a proxy instance implements both A and B, `invokeDefaultMethod(o, A.class.getMethod("m"), params)` will invoke
>> `A::m`.
> 
> This is true since lookup.findSpecial() allows such thing (and I assume equivalent bytecode does too). The Java
> language (compiler) OTOH does not allow calling A.super.m() from a class implementing both A and B when B extends A and
> both A and B declare default method m():
>     interface A {
>         default void m() { }
>     }
> 
>     interface B extends A {
>         default void m() { }
>     }
> 
>     static class C implements A, B {
>         public void m() {
>             A.super.m();
>         }
>     }
> 
> java: bad type qualifier A in default super call
>   redundant interface A is extended by B
> 
> I'm in favour of following the bytecode (method handles) behaviour here even though it allows more than Java language
> allows.

I'm thinking a static method `InvocationHandler::invokeDefaultMethod` and no new method in `Proxy` class.   Proxy class
will still have the package-private methods for `InvocationHandler::invokeDefaultMethod` implementation to use.

It may seem to be cleaner to be a default method at the use site called from InvocationHandler.  However, it's not
really a method meant to be overridden.

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

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


More information about the core-libs-dev mailing list