|
1 | 1 | /*
|
2 |
| - * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
25 | 25 |
|
26 | 26 | package java.lang.reflect;
|
27 | 27 |
|
| 28 | +import jdk.internal.reflect.CallerSensitive; |
| 29 | +import jdk.internal.reflect.Reflection; |
| 30 | + |
| 31 | +import java.lang.invoke.MethodHandle; |
| 32 | +import java.util.Objects; |
| 33 | + |
28 | 34 | /**
|
29 | 35 | * {@code InvocationHandler} is the interface implemented by
|
30 | 36 | * the <i>invocation handler</i> of a proxy instance.
|
@@ -92,4 +98,197 @@ public interface InvocationHandler {
|
92 | 98 | */
|
93 | 99 | public Object invoke(Object proxy, Method method, Object[] args)
|
94 | 100 | throws Throwable;
|
| 101 | + |
| 102 | + /** |
| 103 | + * Invokes the specified default method on the given {@code proxy} instance with |
| 104 | + * the given parameters. The given {@code method} must be a default method |
| 105 | + * declared in a proxy interface of the {@code proxy}'s class or inherited |
| 106 | + * from its superinterface directly or indirectly. |
| 107 | + * <p> |
| 108 | + * Invoking this method behaves as if {@code invokespecial} instruction executed |
| 109 | + * from the proxy class, targeting the default method in a proxy interface. |
| 110 | + * This is equivalent to the invocation: |
| 111 | + * {@code X.super.m(A* a)} where {@code X} is a proxy interface and the call to |
| 112 | + * {@code X.super::m(A*)} is resolved to the given {@code method}. |
| 113 | + * <p> |
| 114 | + * Examples: interface {@code A} and {@code B} both declare a default |
| 115 | + * implementation of method {@code m}. Interface {@code C} extends {@code A} |
| 116 | + * and inherits the default method {@code m} from its superinterface {@code A}. |
| 117 | + * |
| 118 | + * <blockquote><pre>{@code |
| 119 | + * interface A { |
| 120 | + * default T m(A a) { return t1; } |
| 121 | + * } |
| 122 | + * interface B { |
| 123 | + * default T m(A a) { return t2; } |
| 124 | + * } |
| 125 | + * interface C extends A {} |
| 126 | + * }</pre></blockquote> |
| 127 | + * |
| 128 | + * The following creates a proxy instance that implements {@code A} |
| 129 | + * and invokes the default method {@code A::m}. |
| 130 | + * |
| 131 | + * <blockquote><pre>{@code |
| 132 | + * Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class }, |
| 133 | + * (o, m, params) -> { |
| 134 | + * if (m.isDefault()) { |
| 135 | + * // if it's a default method, invoke it |
| 136 | + * return InvocationHandler.invokeDefault(o, m, params); |
| 137 | + * } |
| 138 | + * }); |
| 139 | + * }</pre></blockquote> |
| 140 | + * |
| 141 | + * If a proxy instance implements both {@code A} and {@code B}, both |
| 142 | + * of which provides the default implementation of method {@code m}, |
| 143 | + * the invocation handler can dispatch the method invocation to |
| 144 | + * {@code A::m} or {@code B::m} via the {@code invokeDefault} method. |
| 145 | + * For example, the following code delegates the method invocation |
| 146 | + * to {@code B::m}. |
| 147 | + * |
| 148 | + * <blockquote><pre>{@code |
| 149 | + * Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class, B.class }, |
| 150 | + * (o, m, params) -> { |
| 151 | + * if (m.getName().equals("m")) { |
| 152 | + * // invoke B::m instead of A::m |
| 153 | + * Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes()); |
| 154 | + * return InvocationHandler.invokeDefault(o, bMethod, params); |
| 155 | + * } |
| 156 | + * }); |
| 157 | + * }</pre></blockquote> |
| 158 | + * |
| 159 | + * If a proxy instance implements {@code C} that inherits the default |
| 160 | + * method {@code m} from its superinterface {@code A}, then |
| 161 | + * the interface method invocation on {@code "m"} is dispatched to |
| 162 | + * the invocation handler's {@link #invoke(Object, Method, Object[]) invoke} |
| 163 | + * method with the {@code Method} object argument representing the |
| 164 | + * default method {@code A::m}. |
| 165 | + * |
| 166 | + * <blockquote><pre>{@code |
| 167 | + * Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { C.class }, |
| 168 | + * (o, m, params) -> { |
| 169 | + * if (m.isDefault()) { |
| 170 | + * // behaves as if calling C.super.m(params) |
| 171 | + * return InvocationHandler.invokeDefault(o, m, params); |
| 172 | + * } |
| 173 | + * }); |
| 174 | + * }</pre></blockquote> |
| 175 | + * |
| 176 | + * The invocation of method {@code "m"} on this {@code proxy} will behave |
| 177 | + * as if {@code C.super::m} is called and that is resolved to invoking |
| 178 | + * {@code A::m}. |
| 179 | + * <p> |
| 180 | + * Adding a default method, or changing a method from abstract to default |
| 181 | + * may cause an exception if an existing code attempts to call {@code invokeDefault} |
| 182 | + * to invoke a default method. |
| 183 | + * |
| 184 | + * For example, if {@code C} is modified to implement a default method |
| 185 | + * {@code m}: |
| 186 | + * |
| 187 | + * <blockquote><pre>{@code |
| 188 | + * interface C extends A { |
| 189 | + * default T m(A a) { return t3; } |
| 190 | + * } |
| 191 | + * }</pre></blockquote> |
| 192 | + * |
| 193 | + * The code above that creates proxy instance {@code proxy} with |
| 194 | + * the modified {@code C} will run with no exception and it will result in |
| 195 | + * calling {@code C::m} instead of {@code A::m}. |
| 196 | + * <p> |
| 197 | + * The following is another example that creates a proxy instance of {@code C} |
| 198 | + * and the invocation handler calls the {@code invokeDefault} method |
| 199 | + * to invoke {@code A::m}: |
| 200 | + * |
| 201 | + * <blockquote><pre>{@code |
| 202 | + * C c = (C) Proxy.newProxyInstance(loader, new Class<?>[] { C.class }, |
| 203 | + * (o, m, params) -> { |
| 204 | + * if (m.getName().equals("m")) { |
| 205 | + * // IllegalArgumentException thrown as {@code A::m} is not a method |
| 206 | + * // inherited from its proxy interface C |
| 207 | + * Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes()); |
| 208 | + * return InvocationHandler.invokeDefault(o, aMethod params); |
| 209 | + * } |
| 210 | + * }); |
| 211 | + * c.m(...); |
| 212 | + * }</pre></blockquote> |
| 213 | + * |
| 214 | + * The above code runs successfully with the old version of {@code C} and |
| 215 | + * {@code A::m} is invoked. When running with the new version of {@code C}, |
| 216 | + * the above code will fail with {@code IllegalArgumentException} because |
| 217 | + * {@code C} overrides the implementation of the same method and |
| 218 | + * {@code A::m} is not accessible by a proxy instance. |
| 219 | + * |
| 220 | + * @apiNote |
| 221 | + * The {@code proxy} parameter is of type {@code Object} rather than {@code Proxy} |
| 222 | + * to make it easy for {@link InvocationHandler#invoke(Object, Method, Object[]) |
| 223 | + * InvocationHandler::invoke} implementation to call directly without the need |
| 224 | + * of casting. |
| 225 | + * |
| 226 | + * @param proxy the {@code Proxy} instance on which the default method to be invoked |
| 227 | + * @param method the {@code Method} instance corresponding to a default method |
| 228 | + * declared in a proxy interface of the proxy class or inherited |
| 229 | + * from its superinterface directly or indirectly |
| 230 | + * @param args the parameters used for the method invocation; can be {@code null} |
| 231 | + * if the number of formal parameters required by the method is zero. |
| 232 | + * @return the value returned from the method invocation |
| 233 | + * |
| 234 | + * @throws IllegalArgumentException if any of the following conditions is {@code true}: |
| 235 | + * <ul> |
| 236 | + * <li>{@code proxy} is not {@linkplain Proxy#isProxyClass(Class) |
| 237 | + * a proxy instance}; or</li> |
| 238 | + * <li>the given {@code method} is not a default method declared |
| 239 | + * in a proxy interface of the proxy class and not inherited from |
| 240 | + * any of its superinterfaces; or</li> |
| 241 | + * <li>the given {@code method} is overridden directly or indirectly by |
| 242 | + * the proxy interfaces and the method reference to the named |
| 243 | + * method never resolves to the given {@code method}; or</li> |
| 244 | + * <li>the length of the given {@code args} array does not match the |
| 245 | + * number of parameters of the method to be invoked; or</li> |
| 246 | + * <li>any of the {@code args} elements fails the unboxing |
| 247 | + * conversion if the corresponding method parameter type is |
| 248 | + * a primitive type; or if, after possible unboxing, any of the |
| 249 | + * {@code args} elements cannot be assigned to the corresponding |
| 250 | + * method parameter type.</li> |
| 251 | + * </ul> |
| 252 | + * @throws IllegalAccessException if the declaring class of the specified |
| 253 | + * default method is inaccessible to the caller class |
| 254 | + * @throws NullPointerException if {@code proxy} or {@code method} is {@code null} |
| 255 | + * @throws Throwable anything thrown by the default method |
| 256 | +
|
| 257 | + * @since 16 |
| 258 | + * @jvms 5.4.3. Method Resolution |
| 259 | + */ |
| 260 | + @CallerSensitive |
| 261 | + public static Object invokeDefault(Object proxy, Method method, Object... args) |
| 262 | + throws Throwable { |
| 263 | + Objects.requireNonNull(proxy); |
| 264 | + Objects.requireNonNull(method); |
| 265 | + |
| 266 | + // verify that the object is actually a proxy instance |
| 267 | + if (!Proxy.isProxyClass(proxy.getClass())) { |
| 268 | + throw new IllegalArgumentException("'proxy' is not a proxy instance"); |
| 269 | + } |
| 270 | + if (!method.isDefault()) { |
| 271 | + throw new IllegalArgumentException("\"" + method + "\" is not a default method"); |
| 272 | + } |
| 273 | + @SuppressWarnings("unchecked") |
| 274 | + Class<? extends Proxy> proxyClass = (Class<? extends Proxy>)proxy.getClass(); |
| 275 | + |
| 276 | + Class<?> intf = method.getDeclaringClass(); |
| 277 | + // access check on the default method |
| 278 | + method.checkAccess(Reflection.getCallerClass(), intf, proxyClass, method.getModifiers()); |
| 279 | + |
| 280 | + MethodHandle mh = Proxy.defaultMethodHandle(proxyClass, method); |
| 281 | + // invoke the super method |
| 282 | + try { |
| 283 | + // the args array can be null if the number of formal parameters required by |
| 284 | + // the method is zero (consistent with Method::invoke) |
| 285 | + Object[] params = args != null ? args : Proxy.EMPTY_ARGS; |
| 286 | + return mh.invokeExact(proxy, params); |
| 287 | + } catch (ClassCastException | NullPointerException e) { |
| 288 | + throw new IllegalArgumentException(e.getMessage(), e); |
| 289 | + } catch (Proxy.InvocationException e) { |
| 290 | + // unwrap and throw the exception thrown by the default method |
| 291 | + throw e.getCause(); |
| 292 | + } |
| 293 | + } |
95 | 294 | }
|
0 commit comments