Summary
-------
Provide a new static `InvocationHandler::invokeDefault` API to allow a default method
defined in a proxy interface directly or indirectly to be invoked.
Problem
-------
Default methods were introduced in Java SE 8. It has been lacking of
support in `java.lang.reflect.Proxy` API of invocation of the default methods
defined in proxy interfaces. A proxy class overrides all instance methods
defined in the specified proxy interfaces and the implementation forwards to
`InvocationHandler::invoke`. It needs a mechanism equivalent to calling
`X.super::m` to select a default method to be invoked.
Solution
--------
Define a static `InvocationHandler::invokeDefault(Object proxy, Method method, Object... args)` method that allows an invocation handler to specify a default method and invoke it with the specified proxy instance and arguments.
Specification
-------------
1) New static `InvocationHandler::invokeDefault` method
```
/**
* Invokes the specified default method on the given {@code proxy} instance with
* the given parameters. The given {@code method} must be a default method
* declared in a proxy interface of the {@code proxy}'s class or inherited
* from its superinterface directly or indirectly.
* <p>
* Invoking this method behaves as if {@code invokespecial} instruction executed
* from the proxy class, targeting the default method in a proxy interface.
* This is equivalent to the invocation:
* {@code X.super.m(A* a)} where {@code X} is a proxy interface and the call to
* {@code X.super::m(A*)} is resolved to the given {@code method}.
* <p>
* Examples: interface {@code A} and {@code B} both declare a default
* implementation of method {@code m}. Interface {@code C} extends {@code A}
* and inherits the default method {@code m} from its superinterface {@code A}.
*
* <blockquote><pre>{@code
* interface A {
* default T m(A a) { return t1; }
* }
* interface B {
* default T m(A a) { return t2; }
* }
* interface C extends A {}
* }</pre></blockquote>
*
* The following creates a proxy instance that implements {@code A}
* and invokes the default method {@code A::m}.
*
* <blockquote><pre>{@code
* Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class },
* (o, m, params) -> {
* if (m.isDefault()) {
* // if it's a default method, invoke it
* return InvocationHandler.invokeDefault(o, m, params);
* }
* });
* }</pre></blockquote>
*
* If a proxy instance implements both {@code A} and {@code B}, both
* of which provides the default implementation of method {@code m},
* the invocation handler can dispatch the method invocation to
* {@code A::m} or {@code B::m} via the {@code invokeDefault} method.
* For example, the following code delegates the method invocation
* to {@code B::m}.
*
* <blockquote><pre>{@code
* Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class, B.class },
* (o, m, params) -> {
* if (m.getName().equals("m")) {
* // invoke B::m instead of A::m
* Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes());
* return InvocationHandler.invokeDefault(o, bMethod, params);
* }
* });
* }</pre></blockquote>
*
* If a proxy instance implements {@code C} that inherits the default
* method {@code m} from its superinterface {@code A}, then
* the interface method invocation on {@code "m"} is dispatched to
* the invocation handler's {@link #invoke(Object, Method, Object[]) invoke}
* method with the {@code Method} object argument representing the
* default method {@code A::m}.
*
* <blockquote><pre>{@code
* Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
* (o, m, params) -> {
* if (m.isDefault()) {
* // behaves as if calling C.super.m(params)
* return InvocationHandler.invokeDefault(o, m, params);
* }
* });
* }</pre></blockquote>
*
* The invocation of method {@code "m"} on this {@code proxy} will behave
* as if {@code C.super::m} is called and that is resolved to invoking
* {@code A::m}.
* <p>
* Adding a default method, or changing a method from abstract to default
* may cause an exception if an existing code attempts to call {@code invokeDefault}
* to invoke a default method.
*
* For example, if {@code C} is modified to implement a default method
* {@code m}:
*
* <blockquote><pre>{@code
* interface C extends A {
* default T m(A a) { return t3; }
* }
* }</pre></blockquote>
*
* The code above that creates proxy instance {@code proxy} with
* the modified {@code C} will run with no exception and it will result in
* calling {@code C::m} instead of {@code A::m}.
* <p>
* The following is another example that creates a proxy instance of {@code C}
* and the invocation handler calls the {@code invokeDefault} method
* to invoke {@code A::m}:
*
* <blockquote><pre>{@code
* C c = (C) Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
* (o, m, params) -> {
* if (m.getName().equals("m")) {
* // IllegalArgumentException thrown as {@code A::m} is not a method
* // inherited from its proxy interface C
* Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes());
* return InvocationHandler.invokeDefault(o, aMethod params);
* }
* });
* c.m(...);
* }</pre></blockquote>
*
* The above code runs successfully with the old version of {@code C} and
* {@code A::m} is invoked. When running with the new version of {@code C},
* the above code will fail with {@code IllegalArgumentException} because
* {@code C} overrides the implementation of the same method and
* {@code A::m} is not accessible by a proxy instance.
*
* @apiNote
* The {@code proxy} parameter is of type {@code Object} rather than {@code Proxy}
* to make it easy for {@link InvocationHandler#invoke(Object, Method, Object[])
* InvocationHandler::invoke} implementation to call directly without the need
* of casting.
*
* @param proxy the {@code Proxy} instance on which the default method to be invoked
* @param method the {@code Method} instance corresponding to a default method
* declared in a proxy interface of the proxy class or inherited
* from its superinterface directly or indirectly
* @param args the parameters used for the method invocation; can be {@code null}
* if the number of formal parameters required by the method is zero.
* @return the value returned from the method invocation
*
* @throws IllegalArgumentException if any of the following conditions is {@code true}:
* <ul>
* <li>{@code proxy} is not {@linkplain Proxy#isProxyClass(Class)
* a proxy instance}; or</li>
* <li>the given {@code method} is not a default method declared
* in a proxy interface of the proxy class and not inherited from
* any of its superinterfaces; or</li>
* <li>the given {@code method} is overridden directly or indirectly by
* the proxy interfaces and the method reference to the named
* method never resolves to the given {@code method}; or</li>
* <li>the length of the given {@code args} array does not match the
* number of parameters of the method to be invoked; or</li>
* <li>any of the {@code args} elements fails the unboxing
* conversion if the corresponding method parameter type is
* a primitive type; or if, after possible unboxing, any of the
* {@code args} elements cannot be assigned to the corresponding
* method parameter type.</li>
* </ul>
* @throws IllegalAccessException if the declaring class of the specified
* default method is inaccessible to the caller class
* @throws NullPointerException if {@code proxy} or {@code method} is {@code null}
* @throws Throwable anything thrown by the default method
* @since 16
* @jvms 5.4.3. Method Resolution
*/
@CallerSensitive
public static Object invokeDefault(Object proxy, Method method, Object... args)
throws Throwable
```
2) Proxy class spec diff:
```
--- a/src/java.base/share/classes/java/lang/reflect/Proxy.java
+++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java
@@ -144,6 +151,12 @@
* InvocationHandler#invoke invoke} method as described in the
* documentation for that method.
* * * <li>A proxy interface may define a default method or inherit
+ * <li>A proxy interface may define a default method or inherit
+ * a default method from its superinterface directly or indirectly.
+ * An invocation handler can invoke a default method of a proxy interface
+ * by calling {@link InvocationHandler#invokeDefault(Object, Method, Object...)
+ * InvocationHandler::invokeDefault}.
+ *
* <li>An invocation of the {@code hashCode},
* {@code equals}, or {@code toString} methods declared in
* {@code java.lang.Object} on a proxy instance will be encoded and
@@ -172,9 +185,8 @@ import static java.lang.module.ModuleDescriptor.Modifier.SYNTHETIC;
* packages:
* <ol type="a">
* <li>if all the proxy interfaces are <em>public</em>, then the proxy class is
- * <em>public</em> in a package exported by the
- * {@linkplain ClassLoader#getUnnamedModule() unnamed module} of the specified
- * loader. The name of the package is unspecified.</li>
+ * <em>public</em> in an unconditionally exported but non-open package.
+ * The name of the package and the module are unspecified.</li>
*
* <li>if at least one of all the proxy interfaces is <em>non-public</em>, then
* the proxy class is <em>non-public</em> in the package and module of the
```
A proxy class, used to be defined in an unnamed module i.e. in a exported
and open package, is changed to be defined in an unconditionally
exported but non-open package. The module is unspecified.
Programs that assume it to be open unconditionally may be affected and
cannot do deep reflection on such proxy classes.
Defining a proxy class in an unconditionally exported but non-open package
will strengthen encapsulation such that private members of the proxy class
cannot be accessed via core reflection. This only applies to the case if all
the proxy interfaces are public and in a package that is exported or open.
This spec change is for defense-in-depth as the proxy support for default
method requires to access the `Lookup` of a proxy class for the proxy
machinery so that it can invoke the default method on behalf of the proxy
class.
Implementation-specific: one dynamic module is created for each class
loader that defines proxies. The implementation changes the dynamic
module to contain another package (same name as the module) that is
unconditionally exported and is qualifiedly opened to `java.base`.
There is no change to the package and module of the proxy class for
the following cases:
- if at least one proxy interface is non-public, then the proxy class is defined
in the package and module of the non-public interfaces.
- if at least one proxy is in a package that is non-exported and non-open,
if all proxy interfaces are public, then the proxy class is defined in
a non-exported, non-open package of a dynamic module.