/*
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang.invoke;
import java.lang.reflect.*;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import sun.invoke.
WrapperInstance;
import java.util.
ArrayList;
import sun.reflect.
CallerSensitive;
import sun.reflect.
Reflection;
import sun.reflect.misc.
ReflectUtil;
import static java.lang.invoke.
MethodHandleStatics.*;
/**
* This class consists exclusively of static methods that help adapt
* method handles to other JVM types, such as interfaces.
*/
public class
MethodHandleProxies {
private
MethodHandleProxies() { } // do not instantiate
/**
* Produces an instance of the given single-method interface which redirects
* its calls to the given method handle.
* <p>
* A single-method interface is an interface which declares a uniquely named method.
* When determining the uniquely named method of a single-method interface,
* the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode})
* are disregarded. For example, {@link java.util.Comparator} is a single-method interface,
* even though it re-declares the {@code Object.equals} method.
* <p>
* The interface must be public. No additional access checks are performed.
* <p>
* The resulting instance of the required type will respond to
* invocation of the type's uniquely named method by calling
* the given target on the incoming arguments,
* and returning or throwing whatever the target
* returns or throws. The invocation will be as if by
* {@code target.invoke}.
* The target's type will be checked before the
* instance is created, as if by a call to {@code asType},
* which may result in a {@code WrongMethodTypeException}.
* <p>
* The uniquely named method is allowed to be multiply declared,
* with distinct type descriptors. (E.g., it can be overloaded,
* or can possess bridge methods.) All such declarations are
* connected directly to the target method handle.
* Argument and return types are adjusted by {@code asType}
* for each individual declaration.
* <p>
* The wrapper instance will implement the requested interface
* and its super-types, but no other single-method interfaces.
* This means that the instance will not unexpectedly
* pass an {@code instanceof} test for any unrequested type.
* <p style="font-size:smaller;">
* <em>Implementation Note:</em>
* Therefore, each instance must implement a unique single-method interface.
* Implementations may not bundle together
* multiple single-method interfaces onto single implementation classes
* in the style of {@link java.awt.AWTEventMulticaster}.
* <p>
* The method handle may throw an <em>undeclared exception</em>,
* which means any checked exception (or other checked throwable)
* not declared by the requested type's single abstract method.
* If this happens, the throwable will be wrapped in an instance of
* {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}
* and thrown in that wrapped form.
* <p>
* Like {@link java.lang.Integer#valueOf Integer.valueOf},
* {@code asInterfaceInstance} is a factory method whose results are defined
* by their behavior.
* It is not guaranteed to return a new instance for every call.
* <p>
* Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods}
* and other corner cases, the interface may also have several abstract methods
* with the same name but having distinct descriptors (types of returns and parameters).
* In this case, all the methods are bound in common to the one given target.
* The type check and effective {@code asType} conversion is applied to each
* method type descriptor, and all abstract methods are bound to the target in common.
* Beyond this type check, no further checks are made to determine that the
* abstract methods are related in any way.
* <p>
* Future versions of this API may accept additional types,
* such as abstract classes with single abstract methods.
* Future versions of this API may also equip wrapper instances
* with one or more additional public "marker" interfaces.
* <p>
* If a security manager is installed, this method is caller sensitive.
* During any invocation of the target method handle via the returned wrapper,
* the original creator of the wrapper (the caller) will be visible
* to context checks requested by the security manager.
*
* @param <T> the desired type of the wrapper, a single-method interface
* @param intfc a class object representing {@code T}
* @param target the method handle to invoke from the wrapper
* @return a correctly-typed wrapper for the given target
* @throws NullPointerException if either argument is null
* @throws IllegalArgumentException if the {@code intfc} is not a
* valid argument to this method
* @throws WrongMethodTypeException if the target cannot
* be converted to the type required by the requested interface
*/
// Other notes to implementors:
// <p>
// No stable mapping is promised between the single-method interface and
// the implementation class C. Over time, several implementation
// classes might be used for the same type.
// <p>
// If the implementation is able
// to prove that a wrapper of the required type
// has already been created for a given
// method handle, or for another method handle with the
// same behavior, the implementation may return that wrapper in place of
// a new wrapper.
// <p>
// This method is designed to apply to common use cases
// where a single method handle must interoperate with
// an interface that implements a function-like
// API. Additional variations, such as single-abstract-method classes with
// private constructors, or interfaces with multiple but related
// entry points, must be covered by hand-written or automatically
// generated adapter classes.
//
@
CallerSensitive
public static
<T> T
asInterfaceInstance(final
Class<T>
intfc, final
MethodHandle target) {
if (!
intfc.
isInterface() || !
Modifier.
isPublic(
intfc.
getModifiers()))
throw
newIllegalArgumentException("not a public interface",
intfc.
getName());
final
MethodHandle mh;
if (
System.
getSecurityManager() != null) {
final
Class<?>
caller =
Reflection.
getCallerClass();
final
ClassLoader ccl =
caller != null ?
caller.
getClassLoader() : null;
ReflectUtil.
checkProxyPackageAccess(
ccl,
intfc);
mh =
ccl != null ?
bindCaller(
target,
caller) :
target;
} else {
mh =
target;
}
ClassLoader proxyLoader =
intfc.
getClassLoader();
if (
proxyLoader == null) {
ClassLoader cl =
Thread.
currentThread().
getContextClassLoader(); // avoid use of BCP
proxyLoader =
cl != null ?
cl :
ClassLoader.
getSystemClassLoader();
}
final
Method[]
methods =
getSingleNameMethods(
intfc);
if (
methods == null)
throw
newIllegalArgumentException("not a single-method interface",
intfc.
getName());
final
MethodHandle[]
vaTargets = new
MethodHandle[
methods.length];
for (int
i = 0;
i <
methods.length;
i++) {
Method sm =
methods[
i];
MethodType smMT =
MethodType.
methodType(
sm.
getReturnType(),
sm.
getParameterTypes());
MethodHandle checkTarget =
mh.
asType(
smMT); // make throw WMT
checkTarget =
checkTarget.
asType(
checkTarget.
type().
changeReturnType(
Object.class));
vaTargets[
i] =
checkTarget.
asSpreader(
Object[].class,
smMT.
parameterCount());
}
final
InvocationHandler ih = new
InvocationHandler() {
private
Object getArg(
String name) {
if ((
Object)
name == "getWrapperInstanceTarget") return
target;
if ((
Object)
name == "getWrapperInstanceType") return
intfc;
throw new
AssertionError();
}
public
Object invoke(
Object proxy,
Method method,
Object[]
args) throws
Throwable {
for (int
i = 0;
i <
methods.length;
i++) {
if (
method.
equals(
methods[
i]))
return
vaTargets[
i].
invokeExact(
args);
}
if (
method.
getDeclaringClass() ==
WrapperInstance.class)
return
getArg(
method.
getName());
if (
isObjectMethod(
method))
return
callObjectMethod(
proxy,
method,
args);
throw
newInternalError("bad proxy method: "+
method);
}
};
final
Object proxy;
if (
System.
getSecurityManager() != null) {
// sun.invoke.WrapperInstance is a restricted interface not accessible
// by any non-null class loader.
final
ClassLoader loader =
proxyLoader;
proxy =
AccessController.
doPrivileged(new
PrivilegedAction<
Object>() {
public
Object run() {
return
Proxy.
newProxyInstance(
loader,
new
Class<?>[]{
intfc,
WrapperInstance.class },
ih);
}
});
} else {
proxy =
Proxy.
newProxyInstance(
proxyLoader,
new
Class<?>[]{
intfc,
WrapperInstance.class },
ih);
}
return
intfc.
cast(
proxy);
}
private static
MethodHandle bindCaller(
MethodHandle target,
Class<?>
hostClass) {
MethodHandle cbmh =
MethodHandleImpl.
bindCaller(
target,
hostClass);
if (
target.
isVarargsCollector()) {
MethodType type =
cbmh.
type();
int
arity =
type.
parameterCount();
return
cbmh.
asVarargsCollector(
type.
parameterType(
arity-1));
}
return
cbmh;
}
/**
* Determines if the given object was produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
* @param x any reference
* @return true if the reference is not null and points to an object produced by {@code asInterfaceInstance}
*/
public static
boolean
isWrapperInstance(
Object x) {
return
x instanceof
WrapperInstance;
}
private static
WrapperInstance asWrapperInstance(
Object x) {
try {
if (
x != null)
return (
WrapperInstance)
x;
} catch (
ClassCastException ex) {
}
throw
newIllegalArgumentException("not a wrapper instance");
}
/**
* Produces or recovers a target method handle which is behaviorally
* equivalent to the unique method of this wrapper instance.
* The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
* This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
* @param x any reference
* @return a method handle implementing the unique method
* @throws IllegalArgumentException if the reference x is not to a wrapper instance
*/
public static
MethodHandle wrapperInstanceTarget(
Object x) {
return
asWrapperInstance(
x).
getWrapperInstanceTarget();
}
/**
* Recovers the unique single-method interface type for which this wrapper instance was created.
* The object {@code x} must have been produced by a call to {@link #asInterfaceInstance asInterfaceInstance}.
* This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
* @param x any reference
* @return the single-method interface type for which the wrapper was created
* @throws IllegalArgumentException if the reference x is not to a wrapper instance
*/
public static
Class<?>
wrapperInstanceType(
Object x) {
return
asWrapperInstance(
x).
getWrapperInstanceType();
}
private static
boolean
isObjectMethod(
Method m) {
switch (
m.
getName()) {
case "toString":
return (
m.
getReturnType() ==
String.class
&&
m.
getParameterTypes().length == 0);
case "hashCode":
return (
m.
getReturnType() == int.class
&&
m.
getParameterTypes().length == 0);
case "equals":
return (
m.
getReturnType() == boolean.class
&&
m.
getParameterTypes().length == 1
&&
m.
getParameterTypes()[0] ==
Object.class);
}
return false;
}
private static
Object callObjectMethod(
Object self,
Method m,
Object[]
args) {
assert(
isObjectMethod(
m)) :
m;
switch (
m.
getName()) {
case "toString":
return
self.
getClass().
getName() + "@" +
Integer.
toHexString(
self.
hashCode());
case "hashCode":
return
System.
identityHashCode(
self);
case "equals":
return (
self ==
args[0]);
}
return null;
}
private static
Method[]
getSingleNameMethods(
Class<?>
intfc) {
ArrayList<
Method>
methods = new
ArrayList<
Method>();
String uniqueName = null;
for (
Method m :
intfc.
getMethods()) {
if (
isObjectMethod(
m)) continue;
if (!
Modifier.
isAbstract(
m.
getModifiers())) continue;
String mname =
m.
getName();
if (
uniqueName == null)
uniqueName =
mname;
else if (!
uniqueName.
equals(
mname))
return null; // too many abstract methods
methods.
add(
m);
}
if (
uniqueName == null) return null;
return
methods.
toArray(new
Method[
methods.
size()]);
}
}