Skip to content
Permalink
Browse files
8159746: (proxy) Support for default methods
Co-authored-by: Peter Levart <plevart@openjdk.org>
Reviewed-by: darcy, alanb, plevart
  • Loading branch information
Mandy Chung and Peter Levart committed Dec 1, 2020
1 parent 1433baf commit 56b15fbbcc7c04252f2712d859ff7b820b7c79ad
Showing with 1,376 additions and 144 deletions.
  1. +3 −0 src/java.base/share/classes/java/lang/System.java
  2. +200 −1 src/java.base/share/classes/java/lang/reflect/InvocationHandler.java
  3. +3 −0 src/java.base/share/classes/java/lang/reflect/Method.java
  4. +279 −59 src/java.base/share/classes/java/lang/reflect/Proxy.java
  5. +48 −2 src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java
  6. +5 −0 src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
  7. +7 −0 src/java.base/share/classes/jdk/internal/module/Modules.java
  8. +1 −2 src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java
  9. +386 −0 test/jdk/java/lang/reflect/Proxy/DefaultMethods.java
  10. +2 −2 test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java
  11. +4 −2 test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java
  12. +5 −3 test/jdk/java/lang/reflect/Proxy/ProxyModuleMapping.java
  13. +16 −0 test/jdk/java/lang/reflect/Proxy/ProxyTest.java
  14. +129 −0 test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java
  15. +1 −1 test/jdk/java/lang/reflect/Proxy/nonPublicProxy/NonPublicProxyClass.java
  16. +2 −1 test/jdk/java/lang/reflect/Proxy/nonPublicProxy/p/Bar.java
  17. +2 −1 test/jdk/java/lang/reflect/Proxy/nonPublicProxy/p/Foo.java
  18. +37 −0 test/jdk/java/lang/reflect/Proxy/nonPublicProxy/p/ProxyMaker.java
  19. +3 −1 test/jdk/java/lang/reflect/Proxy/src/m1/p/one/I.java
  20. +2 −1 test/jdk/java/lang/reflect/Proxy/src/m2/p/two/internal/C.java
  21. +4 −1 test/jdk/java/lang/reflect/Proxy/src/m3/p/three/internal/Q.java
  22. +97 −0 test/jdk/java/lang/reflect/Proxy/src/test/jdk/test/DefaultMethods.java
  23. +12 −14 test/jdk/java/lang/reflect/Proxy/src/test/jdk/test/Main.java
  24. +5 −1 test/jdk/java/lang/reflect/Proxy/src/test/jdk/test/NP.java
  25. +5 −2 test/jdk/java/lang/reflect/Proxy/src/test/jdk/test/ProxyTest.java
  26. +33 −0 test/jdk/java/lang/reflect/Proxy/src/test/jdk/test/internal/NP.java
  27. +3 −1 test/jdk/java/lang/reflect/Proxy/src/test/jdk/test/internal/R.java
  28. +2 −2 test/langtools/jdk/jshell/ExceptionsTest.java
  29. +80 −47 test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java
@@ -2221,6 +2221,9 @@ public void addReads(Module m1, Module m2) {
public void addReadsAllUnnamed(Module m) {
m.implAddReadsAllUnnamed();
}
public void addExports(Module m, String pn) {
m.implAddExports(pn);
}
public void addExports(Module m, String pn, Module other) {
m.implAddExports(pn, other);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,12 @@

package java.lang.reflect;

import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;

import java.lang.invoke.MethodHandle;
import java.util.Objects;

/**
* {@code InvocationHandler} is the interface implemented by
* the <i>invocation handler</i> of a proxy instance.
@@ -92,4 +98,197 @@
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;

/**
* 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 {
Objects.requireNonNull(proxy);
Objects.requireNonNull(method);

// verify that the object is actually a proxy instance
if (!Proxy.isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("'proxy' is not a proxy instance");
}
if (!method.isDefault()) {
throw new IllegalArgumentException("\"" + method + "\" is not a default method");
}
@SuppressWarnings("unchecked")
Class<? extends Proxy> proxyClass = (Class<? extends Proxy>)proxy.getClass();

Class<?> intf = method.getDeclaringClass();
// access check on the default method
method.checkAccess(Reflection.getCallerClass(), intf, proxyClass, method.getModifiers());

MethodHandle mh = Proxy.defaultMethodHandle(proxyClass, method);
// invoke the super method
try {
// the args array can be null if the number of formal parameters required by
// the method is zero (consistent with Method::invoke)
Object[] params = args != null ? args : Proxy.EMPTY_ARGS;
return mh.invokeExact(proxy, params);
} catch (ClassCastException | NullPointerException e) {
throw new IllegalArgumentException(e.getMessage(), e);
} catch (Proxy.InvocationException e) {
// unwrap and throw the exception thrown by the default method
throw e.getCause();
}
}
}
@@ -31,6 +31,7 @@
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
import sun.reflect.annotation.ExceptionProxy;
import sun.reflect.annotation.TypeNotPresentExceptionProxy;
import sun.reflect.generics.repository.MethodRepository;
@@ -66,6 +67,7 @@
* @since 1.1
*/
public final class Method extends Executable {
@Stable
private Class<?> clazz;
private int slot;
// This is guaranteed to be interned by the VM in the 1.4
@@ -74,6 +76,7 @@
private Class<?> returnType;
private Class<?>[] parameterTypes;
private Class<?>[] exceptionTypes;
@Stable
private int modifiers;
// Generics and annotations support
private transient String signature;

1 comment on commit 56b15fb

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 56b15fb Dec 1, 2020

Please sign in to comment.