/*
* Copyright 2008-2009 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.dyn;
import java.lang.reflect.Constructor;
import sun.dyn.Access;
import sun.dyn.MemberName;
import sun.dyn.MethodHandleImpl;
import sun.dyn.util.VerifyAccess;
import sun.dyn.util.Wrapper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import sun.dyn.Invokers;
import sun.dyn.MethodTypeImpl;
import sun.reflect.Reflection;
import static sun.dyn.MemberName.newIllegalArgumentException;
import static sun.dyn.MemberName.newNoAccessException;
/**
* Fundamental operations and utilities for MethodHandle.
*
* API Note: The matching of method types in this API cannot
* be completely checked by Java's generic type system for three reasons:
*
*
Method types range over all possible arities,
* from no arguments to an arbitrary number of arguments.
* Generics are not variadic, and so cannot represent this.
*
Method types can specify arguments of primitive types,
* which Java generic types cannot range over.
*
Method types can optionally specify varargs (ellipsis).
*
* @author John Rose, JSR 292 EG
*/
public class MethodHandles {
private MethodHandles() { } // do not instantiate
private static final Access IMPL_TOKEN = Access.getToken();
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(IMPL_TOKEN);
static { MethodHandleImpl.initStatics(); }
// See IMPL_LOOKUP below.
//// Method handle creation from ordinary methods.
public static Lookup lookup() {
return new Lookup();
}
/**
* PROVISIONAL API, WORK IN PROGRESS:
* A factory object for creating method handles, when the creation
* requires access checking. Method handles do not perform
* access checks when they are called; this is a major difference
* from reflective {@link Method}, which performs access checking
* against every caller, on every call. Method handle access
* restrictions are enforced when a method handle is created.
* The caller class against which those restrictions are enforced
* is known as the "lookup class". {@link Lookup} embodies an
* authenticated lookup class, and can be used to create any number
* of access-checked method handles, all checked against a single
* lookup class.
*
* A class which needs to create method handles will call
* {@code MethodHandles.lookup()} to create a factory for itself.
* It may then use this factory to create method handles on
* all of its methods, including private ones.
* It may also delegate the lookup (e.g., to a metaobject protocol)
* by passing the {@code Lookup} object to other code.
* If this other code creates method handles, they will be access
* checked against the original lookup class, and not with any higher
* privileges.
*
* Note that access checks only apply to named and reflected methods.
* Other method handle creation methods, such as {@link #convertArguments},
* do not require any access checks, and can be done independently
* of any lookup class.
*
* A note about error conditions: A lookup can fail, because
* the containing class is not accessible to the lookup class, or
* because the desired class member is missing, or because the
* desired class member is not accessible to the lookup class.
* It can also fail if a security manager is installed and refuses
* access. In any of these cases, an exception will be
* thrown from the attempted lookup.
* In general, the conditions under which a method handle may be
* created for a method {@code M} are exactly as restrictive as the conditions
* under which the lookup class could have compiled a call to {@code M}.
* At least some of these error conditions are likely to be
* represented by checked exceptions in the final version of this API.
*/
public static final
class Lookup {
private final Class> lookupClass;
/** Which class is performing the lookup? It is this class against
* which checks are performed for visibility and access permissions.
*
* This value is null if and only if this lookup is {@link #PUBLIC_LOOKUP}.
*/
public Class> lookupClass() {
return lookupClass;
}
/** Embody the current class (the lookupClass) as a lookup class
* for method handle creation.
* Must be called by from a method in this package,
* which in turn is called by a method not in this package.
* Also, don't make it private, lest javac interpose
* an access$N method.
*/
Lookup() {
Class caller = getCallerClassAtEntryPoint();
// make sure we haven't accidentally picked up this class:
checkUnprivilegedlookupClass(caller);
this.lookupClass = caller;
}
private Lookup(Class> lookupClass) {
this.lookupClass = lookupClass;
}
private static final Class> PUBLIC_ONLY = sun.dyn.empty.Empty.class;
/** Version of lookup which is trusted minimally.
* It can only be used to create method handles to
* publicly accessible members.
*/
public static final Lookup PUBLIC_LOOKUP = new Lookup(PUBLIC_ONLY);
/** Package-private version of lookup which is trusted. */
static final Lookup IMPL_LOOKUP = new Lookup(null);
static { MethodHandleImpl.initLookup(IMPL_TOKEN, IMPL_LOOKUP); }
private static void checkUnprivilegedlookupClass(Class> lookupClass) {
String name = lookupClass.getName();
if (name.startsWith("java.dyn.") || name.startsWith("sun.dyn."))
throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
}
@Override
public String toString() {
if (lookupClass == PUBLIC_ONLY)
return "public";
if (lookupClass == null)
return "privileged";
return lookupClass.getName();
}
// call this from an entry point method in Lookup with extraFrames=0.
private static Class> getCallerClassAtEntryPoint() {
final int CALLER_DEPTH = 4;
// 0: Reflection.getCC, 1: getCallerClassAtEntryPoint,
// 2: Lookup., 3: MethodHandles.*, 4: caller
// Note: This should be the only use of getCallerClass in this file.
return Reflection.getCallerClass(CALLER_DEPTH);
}
/**
* Produce a method handle for a static method.
* The type of the method handle will be that of the method.
* The method and all its argument types must be accessible to the lookup class.
* If the method's class has not yet been initialized, that is done
* immediately, before the method handle is returned.
* @param defc the class from which the method is accessed
* @param name the name of the method
* @param type the type of the method
* @return the desired method handle
* @exception SecurityException TBD
* @exception NoAccessException if the method does not exist or access checking fails
*/
public
MethodHandle findStatic(Class> defc, String name, MethodType type) throws NoAccessException {
MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
checkStatic(true, method, lookupClass);
//throw NoSuchMethodException
return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
}
/**
* Produce a method handle for a virtual method.
* The type of the method handle will be that of the method,
* with the receiver type ({@code defc}) prepended.
* The method and all its argument types must be accessible to the lookup class.
*
* (BUG NOTE: The type {@code Object} may be prepended instead
* of the receiver type, if the receiver type is not on the boot class path.
* This is due to a temporary JVM limitation, in which MethodHandle
* claims to be unable to access such classes. To work around this
* bug, use {@code convertArguments} to normalize the type of the leading
* argument to a type on the boot class path, such as {@code Object}.)
*
* When called, the handle will treat the first argument as a receiver
* and dispatch on the receiver's type to determine which method
* implementation to enter.
* (The dispatching action is identical with that performed by an
* {@code invokevirtual} or {@code invokeinterface} instruction.)
* @param defc the class or interface from which the method is accessed
* @param name the name of the method
* @param type the type of the method, with the receiver argument omitted
* @return the desired method handle
* @exception SecurityException TBD
* @exception NoAccessException if the method does not exist or access checking fails
*/
public MethodHandle findVirtual(Class> defc, String name, MethodType type) throws NoAccessException {
MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), true, lookupClass);
checkStatic(false, method, lookupClass);
return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
}
/**
* Produce an early-bound method handle for a virtual method,
* as if called from an {@code invokespecial}
* instruction from {@code caller}.
* The type of the method handle will be that of the method,
* with a suitably restricted receiver type (such as {@code caller}) prepended.
* The method and all its argument types must be accessible
* to the caller.
*
* When called, the handle will treat the first argument as a receiver,
* but will not dispatch on the receiver's type.
* (This direct invocation action is identical with that performed by an
* {@code invokespecial} instruction.)
*
* If the explicitly specified caller class is not identical with the
* lookup class, a security check TBD is performed.
* @param defc the class or interface from which the method is accessed
* @param name the name of the method, or "" for a constructor
* @param type the type of the method, with the receiver argument omitted
* @param specialCaller the proposed calling class to perform the {@code invokespecial}
* @return the desired method handle
* @exception SecurityException TBD
* @exception NoAccessException if the method does not exist or access checking fails
*/
public MethodHandle findSpecial(Class> defc, String name, MethodType type,
Class> specialCaller) throws NoAccessException {
checkSpecialCaller(specialCaller, lookupClass);
MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type), false, specialCaller);
checkStatic(false, method, lookupClass);
if (name.equals("")) {
throw newNoAccessException("cannot directly invoke a constructor", method, null);
} else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) {
throw newNoAccessException("method must be in a superclass of lookup class", method, lookupClass);
}
return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller);
}
/**
* Produce an early-bound method handle for a non-static method.
* The receiver must have a supertype {@code defc} in which a method
* of the given name and type is accessible to the lookup class.
* The method and all its argument types must be accessible to the lookup class.
* The type of the method handle will be that of the method.
* The given receiver will be bound into the method handle.
*
* Equivalent to the following expression:
*
* {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver)
*
* @param receiver the object from which the method is accessed
* @param name the name of the method
* @param type the type of the method, with the receiver argument omitted
* @return the desired method handle
* @exception SecurityException TBD
* @exception NoAccessException if the method does not exist or access checking fails
*/
public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
Class extends Object> rcvc = receiver.getClass(); // may get NPE
MemberName reference = new MemberName(rcvc, name, type);
MemberName method = IMPL_NAMES.resolveOrFail(reference, true, lookupClass);
checkStatic(false, method, lookupClass);
MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClass);
MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
if (bmh == null)
throw newNoAccessException(method, lookupClass);
return bmh;
}
/**
* Make a direct method handle to m, if the lookup class has permission.
* If m is non-static, the receiver argument is treated as an initial argument.
* If m is virtual, overriding is respected on every call.
* Unlike the Core Reflection API, exceptions are not wrapped.
* The type of the method handle will be that of the method,
* with the receiver type prepended (but only if it is non-static).
* If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class.
* If m is not public, do not share the resulting handle with untrusted parties.
* @param m the reflected method
* @return a method handle which can invoke the reflected method
* @exception NoAccessException if access checking fails
*/
public MethodHandle unreflect(Method m) throws NoAccessException {
return unreflectImpl(new MemberName(m), m.isAccessible(), true, lookupClass);
}
/**
* Produce a method handle for a reflected method.
* It will bypass checks for overriding methods on the receiver,
* as if by the {@code invokespecial} instruction.
* The type of the method handle will be that of the method,
* with the receiver type prepended.
* If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class,
* as if {@code invokespecial} instruction were being linked.
* @param m the reflected method
* @return a method handle which can invoke the reflected method
* @exception NoAccessException if access checking fails
*/
public MethodHandle unreflectSpecial(Method m, Class> specialCaller) throws NoAccessException {
checkSpecialCaller(specialCaller, lookupClass);
MemberName mname = new MemberName(m);
checkStatic(false, mname, lookupClass);
return unreflectImpl(mname, m.isAccessible(), false, specialCaller);
}
/**
* Produce a method handle for a reflected constructor.
* The type of the method handle will be that of the constructor.
* The method handle will perform a {@code newInstance} operation,
* creating a new instance of the constructor's class on the
* arguments passed to the method handle.
*
* If the constructor's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class,
* as if {@code invokespecial} instruction were being linked.
* @param ctor the reflected constructor
* @return a method handle which can invoke the reflected constructor
* @exception NoAccessException if access checking fails
*/
public MethodHandle unreflectConstructor(Constructor ctor) throws NoAccessException {
MemberName m = new MemberName(ctor);
return unreflectImpl(m, ctor.isAccessible(), false, lookupClass);
}
/**
* PROVISIONAL API, WORK IN PROGRESS:
* Produce a method handle giving read access to a reflected field.
* The type of the method handle will have a return type of the field's
* value type. Its sole argument will be the field's containing class
* (but only if it is non-static).
* If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class.
* @param f the reflected field
* @return a method handle which can load values from the reflected field
* @exception NoAccessException if access checking fails
*/
public MethodHandle unreflectGetter(Field f) throws NoAccessException {
return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, lookupClass);
}
/**
* PROVISIONAL API, WORK IN PROGRESS:
* Produce a method handle giving write access to a reflected field.
* The type of the method handle will have a void return type.
* Its last argument will be the field's value type.
* Its other argument will be the field's containing class
* (but only if it is non-static).
* If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class.
* @param f the reflected field
* @return a method handle which can store values into the reflected field
* @exception NoAccessException if access checking fails
*/
public MethodHandle unreflectSetter(Field f) throws NoAccessException {
return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, lookupClass);
}
}
static /*must not be public*/
MethodHandle findStaticFrom(Class> lookupClass,
Class> defc, String name, MethodType type) throws NoAccessException {
MemberName method = IMPL_NAMES.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, lookupClass);
checkStatic(true, method, lookupClass);
return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClass);
}
static void checkStatic(boolean wantStatic, MemberName m, Class> lookupClass) {
if (wantStatic != m.isStatic()) {
String message = wantStatic ? "expected a static method" : "expected a non-static method";
throw newNoAccessException(message, m, lookupClass);
}
}
static void checkSpecialCaller(Class> specialCaller, Class> lookupClass) {
if (lookupClass == Lookup.IMPL_LOOKUP.lookupClass())
return; // privileged action
if (lookupClass == null || // public-only access
!VerifyAccess.isSamePackageMember(specialCaller, lookupClass))
throw newNoAccessException("no private access", new MemberName(specialCaller), lookupClass);
}
// Helper for creating handles on reflected methods and constructors.
static MethodHandle unreflectImpl(MemberName m, boolean isAccessible,
boolean doDispatch, Class> lookupClass) {
MethodType mtype = m.getInvocationType();
Class> defc = m.getDeclaringClass();
int mods = m.getModifiers();
if (m.isStatic()) {
if (!isAccessible &&
VerifyAccess.isAccessible(defc, mods, false, lookupClass) == null)
throw newNoAccessException(m, lookupClass);
} else {
Class> constraint;
if (isAccessible) {
// abbreviated access check for "unlocked" method
constraint = doDispatch ? defc : lookupClass;
} else {
constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, lookupClass);
}
if (constraint != defc && !constraint.isAssignableFrom(defc)) {
if (!defc.isAssignableFrom(constraint))
throw newNoAccessException("receiver must be in caller class", m, lookupClass);
mtype = mtype.changeParameterType(0, constraint);
}
}
return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, lookupClass);
}
/**
* PROVISIONAL API, WORK IN PROGRESS:
* Produce a method handle giving read access to elements of an array.
* The type of the method handle will have a return type of the array's
* element type. Its first argument will be the array type,
* and the second will be {@code int}.
* @param arrayClass an array type
* @return a method handle which can load values from the given array type
* @throws IllegalArgumentException if arrayClass is not an array type
*/
public static
MethodHandle arrayElementGetter(Class> arrayClass) throws IllegalArgumentException {
return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false);
}
/**
* PROVISIONAL API, WORK IN PROGRESS:
* Produce a method handle giving write access to elements of an array.
* The type of the method handle will have a void return type.
* Its last argument will be the array's element type.
* The first and second arguments will be the array type and int.
* @return a method handle which can store values into the array type
* @throws IllegalArgumentException if arrayClass is not an array type
*/
public static
MethodHandle arrayElementSetter(Class> arrayClass) throws IllegalArgumentException {
return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true);
}
/// method handle invocation (reflective style)
/**
* PROVISIONAL API, WORK IN PROGRESS:
* Call the {@code invoke} method of a given method handle,
* with arguments that exactly match the parameter types of the method handle.
* The length of the arguments array must equal the parameter count
* of the target's type.
* The arguments array is spread into separate arguments, and
* basic reference and unboxing conversions are applied.
*
* In order to match the type of the target, the following argument
* conversions are applied as necessary:
*
*
reference casting
*
unboxing
*
* The following conversions are not applied:
*
*
primitive conversions (e.g., {@code byte} to {@code int}
*
varargs conversions other than the initial spread
*
any application-specific conversions (e.g., string to number)
*
* The result returned by the call is boxed if it is a primitive,
* or forced to null if the return type is void.
*
* This call is a convenience method for the following code:
*
* @param target the method handle to invoke
* @param arguments the arguments to pass to the target
* @return the result returned by the target
*/
public static
Object invoke(MethodHandle target, Object... arguments) {
int argc = arguments == null ? 0 : arguments.length;
MethodType type = target.type();
if (argc <= 4) {
MethodHandle invoker = invokers(type).genericInvoker();
switch (argc) {
case 0: return invoker.