mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-15 12:55:07 +00:00
Merge
This commit is contained in:
commit
93196c8b51
@ -39,14 +39,14 @@ public class BootstrapMethodError extends LinkageError {
|
||||
private static final long serialVersionUID = 292L;
|
||||
|
||||
/**
|
||||
* Constructs an {@code BootstrapMethodError} with no detail message.
|
||||
* Constructs a {@code BootstrapMethodError} with no detail message.
|
||||
*/
|
||||
public BootstrapMethodError() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code BootstrapMethodError} with the specified
|
||||
* Constructs a {@code BootstrapMethodError} with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param s the detail message.
|
||||
|
||||
@ -38,6 +38,13 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* @since 1.7
|
||||
*/
|
||||
public abstract class ClassValue<T> {
|
||||
/**
|
||||
* Sole constructor. (For invocation by subclass constructors, typically
|
||||
* implicit.)
|
||||
*/
|
||||
protected ClassValue() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the given class's derived value for this {@code ClassValue}.
|
||||
* <p>
|
||||
@ -100,7 +107,7 @@ public abstract class ClassValue<T> {
|
||||
* If this value is subsequently {@linkplain #get read} for the same class,
|
||||
* its value will be reinitialized by invoking its {@link #computeValue computeValue} method.
|
||||
* This may result in an additional invocation of the
|
||||
* {@code computeValue computeValue} method for the given class.
|
||||
* {@code computeValue} method for the given class.
|
||||
* <p>
|
||||
* In order to explain the interaction between {@code get} and {@code remove} calls,
|
||||
* we must model the state transitions of a class value to take into account
|
||||
@ -193,6 +200,7 @@ public abstract class ClassValue<T> {
|
||||
= new WeakHashMap<Class<?>, ClassValueMap>();
|
||||
|
||||
private static ClassValueMap getMap(Class<?> type) {
|
||||
type.getClass(); // test for null
|
||||
return ROOT.get(type);
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,8 @@ import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
@ -62,7 +64,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
// the target and change its type, instead of adding another layer.
|
||||
|
||||
/** Can a JVM-level adapter directly implement the proposed
|
||||
* argument conversions, as if by MethodHandles.convertArguments?
|
||||
* argument conversions, as if by fixed-arity MethodHandle.asType?
|
||||
*/
|
||||
static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
|
||||
// same number of args, of course
|
||||
@ -92,7 +94,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
}
|
||||
|
||||
/** Can a JVM-level adapter directly implement the proposed
|
||||
* argument conversion, as if by MethodHandles.convertArguments?
|
||||
* argument conversion, as if by fixed-arity MethodHandle.asType?
|
||||
*/
|
||||
static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
|
||||
// ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
|
||||
@ -550,6 +552,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
int last = type.parameterCount() - 1;
|
||||
if (type.parameterType(last) != arrayType)
|
||||
target = target.asType(type.changeParameterType(last, arrayType));
|
||||
target = target.asFixedArity(); // make sure this attribute is turned off
|
||||
return new AsVarargsCollector(target, arrayType);
|
||||
}
|
||||
|
||||
@ -570,6 +573,11 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle asFixedArity() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle asType(MethodType newType) {
|
||||
MethodType type = this.type();
|
||||
@ -594,14 +602,6 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
cache = collector;
|
||||
return collector.asType(newType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle asVarargsCollector(Class<?> arrayType) {
|
||||
MethodType type = this.type();
|
||||
if (type.parameterType(type.parameterCount()-1) == arrayType)
|
||||
return this;
|
||||
return super.asVarargsCollector(arrayType);
|
||||
}
|
||||
}
|
||||
|
||||
/** Can a checkcast adapter validly convert the target to newType?
|
||||
@ -747,8 +747,31 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
if (!canUnboxArgument(newType, oldType, arg, convType, level))
|
||||
return null;
|
||||
MethodType castDone = newType;
|
||||
if (!VerifyType.isNullConversion(src, boxType))
|
||||
if (!VerifyType.isNullConversion(src, boxType)) {
|
||||
// Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int
|
||||
if (level != 0) {
|
||||
// must include additional conversions
|
||||
if (src == Object.class || !Wrapper.isWrapperType(src)) {
|
||||
// src must be examined at runtime, to detect Byte, Character, etc.
|
||||
MethodHandle unboxMethod = (level == 1
|
||||
? ValueConversions.unbox(dst)
|
||||
: ValueConversions.unboxCast(dst));
|
||||
long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
|
||||
return new AdapterMethodHandle(target, newType, conv, unboxMethod);
|
||||
}
|
||||
// Example: Byte->int
|
||||
// Do this by reformulating the problem to Byte->byte.
|
||||
Class<?> srcPrim = Wrapper.forWrapperType(src).primitiveType();
|
||||
MethodType midType = newType.changeParameterType(arg, srcPrim);
|
||||
MethodHandle fixPrim; // makePairwiseConvert(midType, target, 0);
|
||||
if (canPrimCast(midType, oldType, arg, dst))
|
||||
fixPrim = makePrimCast(midType, target, arg, dst);
|
||||
else
|
||||
fixPrim = target;
|
||||
return makeUnboxArgument(newType, fixPrim, arg, srcPrim, 0);
|
||||
}
|
||||
castDone = newType.changeParameterType(arg, boxType);
|
||||
}
|
||||
long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType));
|
||||
MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType);
|
||||
if (castDone == newType)
|
||||
@ -917,6 +940,20 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
if (swapArg1 == swapArg2)
|
||||
return target;
|
||||
if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; }
|
||||
if (type2size(newType.parameterType(swapArg1)) !=
|
||||
type2size(newType.parameterType(swapArg2))) {
|
||||
// turn a swap into a pair of rotates:
|
||||
// [x a b c y] => [a b c y x] => [y a b c x]
|
||||
int argc = swapArg2 - swapArg1 + 1;
|
||||
final int ROT = 1;
|
||||
ArrayList<Class<?>> rot1Params = new ArrayList<Class<?>>(target.type().parameterList());
|
||||
Collections.rotate(rot1Params.subList(swapArg1, swapArg1 + argc), -ROT);
|
||||
MethodType rot1Type = MethodType.methodType(target.type().returnType(), rot1Params);
|
||||
MethodHandle rot1 = makeRotateArguments(rot1Type, target, swapArg1, argc, +ROT);
|
||||
if (argc == 2) return rot1;
|
||||
MethodHandle rot2 = makeRotateArguments(newType, rot1, swapArg1, argc-1, -ROT);
|
||||
return rot2;
|
||||
}
|
||||
if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2))
|
||||
return null;
|
||||
Class<?> swapType = newType.parameterType(swapArg1);
|
||||
@ -946,7 +983,6 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
static boolean canRotateArguments(MethodType newType, MethodType targetType,
|
||||
int firstArg, int argCount, int rotateBy) {
|
||||
if (!convOpSupported(OP_ROT_ARGS)) return false;
|
||||
if (argCount <= 2) return false; // must be a swap, not a rotate
|
||||
rotateBy = positiveRotation(argCount, rotateBy);
|
||||
if (rotateBy == 0) return false; // no rotation
|
||||
if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION)
|
||||
@ -992,6 +1028,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
// From here on out, it assumes a single-argument shift.
|
||||
assert(MAX_ARG_ROTATION == 1);
|
||||
int srcArg, dstArg;
|
||||
int dstSlot;
|
||||
byte basicType;
|
||||
if (chunk2Slots <= chunk1Slots) {
|
||||
// Rotate right/down N (rotateBy = +N, N small, c2 small):
|
||||
@ -999,6 +1036,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
// out arglist: [0: ...keep1 | arg1: c2 | arg1+N: c1... | limit: keep2... ]
|
||||
srcArg = limit-1;
|
||||
dstArg = firstArg;
|
||||
dstSlot = depth0 - chunk2Slots;
|
||||
basicType = basicType(newType.parameterType(srcArg));
|
||||
assert(chunk2Slots == type2size(basicType));
|
||||
} else {
|
||||
@ -1007,10 +1045,10 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||
// out arglist: [0: ...keep1 | arg1: c2 ... | limit-N: c1 | limit: keep2... ]
|
||||
srcArg = firstArg;
|
||||
dstArg = limit-1;
|
||||
dstSlot = depth2;
|
||||
basicType = basicType(newType.parameterType(srcArg));
|
||||
assert(chunk1Slots == type2size(basicType));
|
||||
}
|
||||
int dstSlot = newType.parameterSlotDepth(dstArg + 1);
|
||||
long conv = makeSwapConv(OP_ROT_ARGS, srcArg, basicType, dstSlot);
|
||||
return new AdapterMethodHandle(target, newType, conv);
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ class BoundMethodHandle extends MethodHandle {
|
||||
|
||||
final static RuntimeException badBoundArgumentException(Object argument, MethodHandle mh, int argnum) {
|
||||
String atype = (argument == null) ? "null" : argument.getClass().toString();
|
||||
return new WrongMethodTypeException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
|
||||
return new ClassCastException("cannot bind "+atype+" argument to parameter #"+argnum+" of "+mh.type());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -111,7 +111,7 @@ public class CallSite {
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a blank call site object, possibly equipped with an initial target method handle.
|
||||
* Make a call site object equipped with an initial target method handle.
|
||||
* @param target the method handle which will be the initial target of the call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
@ -121,6 +121,25 @@ public class CallSite {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a call site object equipped with an initial target method handle.
|
||||
* @param targetType the desired type of the call site
|
||||
* @param createTargetHook a hook which will bind the call site to the target method handle
|
||||
* @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
|
||||
* or if the target returned by the hook is not of the given {@code targetType}
|
||||
* @throws NullPointerException if the hook returns a null value
|
||||
* @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
|
||||
* @throws Throwable anything else thrown by the the hook function
|
||||
*/
|
||||
/*package-private*/
|
||||
CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
|
||||
this(targetType);
|
||||
ConstantCallSite selfCCS = (ConstantCallSite) this;
|
||||
MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
|
||||
checkTargetChange(this.target, boundTarget);
|
||||
this.target = boundTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this call site's target.
|
||||
* Although targets may change, any call site's type is permanent, and can never change to an unequal type.
|
||||
@ -129,6 +148,7 @@ public class CallSite {
|
||||
* @return the type of the current target, which is also the type of any future target
|
||||
*/
|
||||
public MethodType type() {
|
||||
// warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException
|
||||
return target.type();
|
||||
}
|
||||
|
||||
@ -294,8 +314,8 @@ public class CallSite {
|
||||
} else {
|
||||
throw new ClassCastException("bootstrap method failed to produce a CallSite");
|
||||
}
|
||||
assert(site.getTarget() != null);
|
||||
assert(site.getTarget().type().equals(type));
|
||||
if (!site.getTarget().type().equals(type))
|
||||
throw new WrongMethodTypeException("wrong type: "+site.getTarget());
|
||||
} catch (Throwable ex) {
|
||||
BootstrapMethodError bex;
|
||||
if (ex instanceof BootstrapMethodError)
|
||||
|
||||
@ -32,6 +32,8 @@ package java.lang.invoke;
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class ConstantCallSite extends CallSite {
|
||||
private final boolean isFrozen;
|
||||
|
||||
/**
|
||||
* Creates a call site with a permanent target.
|
||||
* @param target the target to be permanently associated with this call site
|
||||
@ -39,6 +41,45 @@ public class ConstantCallSite extends CallSite {
|
||||
*/
|
||||
public ConstantCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
isFrozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a call site with a permanent target, possibly bound to the call site itself.
|
||||
* <p>
|
||||
* During construction of the call site, the {@code createTargetHook} is invoked to
|
||||
* produce the actual target, as if by a call of the form
|
||||
* {@code (MethodHandle) createTargetHook.invoke(this)}.
|
||||
* <p>
|
||||
* Note that user code cannot perform such an action directly in a subclass constructor,
|
||||
* since the target must be fixed before the {@code ConstantCallSite} constructor returns.
|
||||
* <p>
|
||||
* The hook is said to bind the call site to a target method handle,
|
||||
* and a typical action would be {@code someTarget.bindTo(this)}.
|
||||
* However, the hook is free to take any action whatever,
|
||||
* including ignoring the call site and returning a constant target.
|
||||
* <p>
|
||||
* The result returned by the hook must be a method handle of exactly
|
||||
* the same type as the call site.
|
||||
* <p>
|
||||
* While the hook is being called, the new {@code ConstantCallSite}
|
||||
* object is in a partially constructed state.
|
||||
* In this state,
|
||||
* a call to {@code getTarget}, or any other attempt to use the target,
|
||||
* will result in an {@code IllegalStateException}.
|
||||
* It is legal at all times to obtain the call site's type using the {@code type} method.
|
||||
*
|
||||
* @param targetType the type of the method handle to be permanently associated with this call site
|
||||
* @param createTargetHook a method handle to invoke (on the call site) to produce the call site's target
|
||||
* @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
|
||||
* or if the target returned by the hook is not of the given {@code targetType}
|
||||
* @throws NullPointerException if the hook returns a null value
|
||||
* @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
|
||||
* @throws Throwable anything else thrown by the the hook function
|
||||
*/
|
||||
protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
|
||||
super(targetType, createTargetHook);
|
||||
isFrozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,9 +89,10 @@ public class ConstantCallSite extends CallSite {
|
||||
* to the constructor call which created this instance.
|
||||
*
|
||||
* @return the immutable linkage state of this call site, a constant method handle
|
||||
* @throws UnsupportedOperationException because this kind of call site cannot change its target
|
||||
* @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
if (!isFrozen) throw new IllegalStateException();
|
||||
return target;
|
||||
}
|
||||
|
||||
@ -61,7 +103,7 @@ public class ConstantCallSite extends CallSite {
|
||||
* @throws UnsupportedOperationException because this kind of call site cannot change its target
|
||||
*/
|
||||
@Override public final void setTarget(MethodHandle ignore) {
|
||||
throw new UnsupportedOperationException("ConstantCallSite");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,6 +111,7 @@ public class ConstantCallSite extends CallSite {
|
||||
* Since that target will never change, this is a correct implementation
|
||||
* of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
|
||||
* @return the immutable linkage state of this call site, a constant method handle
|
||||
* @throws IllegalStateException if the {@code ConstantCallSite} constructor has not completed
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
|
||||
@ -46,7 +46,10 @@ class Invokers {
|
||||
// general invoker for the outgoing call
|
||||
private /*lazy*/ MethodHandle generalInvoker;
|
||||
|
||||
// general invoker for the outgoing call; accepts a single Object[]
|
||||
// general invoker for the outgoing call, uses varargs
|
||||
private /*lazy*/ MethodHandle varargsInvoker;
|
||||
|
||||
// general invoker for the outgoing call; accepts a trailing Object[]
|
||||
private final /*lazy*/ MethodHandle[] spreadInvokers;
|
||||
|
||||
// invoker for an unbound callsite
|
||||
@ -67,45 +70,56 @@ class Invokers {
|
||||
/*non-public*/ MethodHandle exactInvoker() {
|
||||
MethodHandle invoker = exactInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
try {
|
||||
invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new InternalError("JVM cannot find invoker for "+targetType);
|
||||
}
|
||||
assert(invokerType(targetType) == invoker.type());
|
||||
invoker = lookupInvoker("invokeExact");
|
||||
exactInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle generalInvoker() {
|
||||
MethodHandle invoker1 = exactInvoker();
|
||||
MethodHandle invoker = generalInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
MethodType generalType = targetType.generic();
|
||||
invoker = invoker1.asType(invokerType(generalType));
|
||||
invoker = lookupInvoker("invoke");
|
||||
generalInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
private MethodHandle lookupInvoker(String name) {
|
||||
MethodHandle invoker;
|
||||
try {
|
||||
invoker = IMPL_LOOKUP.findVirtual(MethodHandle.class, name, targetType);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new InternalError("JVM cannot find invoker for "+targetType);
|
||||
}
|
||||
assert(invokerType(targetType) == invoker.type());
|
||||
assert(!invoker.isVarargsCollector());
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle erasedInvoker() {
|
||||
MethodHandle invoker1 = exactInvoker();
|
||||
MethodHandle xinvoker = exactInvoker();
|
||||
MethodHandle invoker = erasedInvoker;
|
||||
if (invoker != null) return invoker;
|
||||
MethodType erasedType = targetType.erase();
|
||||
if (erasedType == targetType.generic())
|
||||
invoker = generalInvoker();
|
||||
else
|
||||
invoker = invoker1.asType(invokerType(erasedType));
|
||||
invoker = xinvoker.asType(invokerType(erasedType));
|
||||
erasedInvoker = invoker;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle spreadInvoker(int objectArgCount) {
|
||||
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
|
||||
/*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) {
|
||||
MethodHandle vaInvoker = spreadInvokers[leadingArgCount];
|
||||
if (vaInvoker != null) return vaInvoker;
|
||||
MethodHandle gInvoker = generalInvoker();
|
||||
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
|
||||
spreadInvokers[objectArgCount] = vaInvoker;
|
||||
int spreadArgCount = targetType.parameterCount() - leadingArgCount;
|
||||
vaInvoker = gInvoker.asSpreader(Object[].class, spreadArgCount);
|
||||
spreadInvokers[leadingArgCount] = vaInvoker;
|
||||
return vaInvoker;
|
||||
}
|
||||
|
||||
/*non-public*/ MethodHandle varargsInvoker() {
|
||||
MethodHandle vaInvoker = varargsInvoker;
|
||||
if (vaInvoker != null) return vaInvoker;
|
||||
vaInvoker = spreadInvoker(0).asType(invokerType(MethodType.genericMethodType(0, true)));
|
||||
varargsInvoker = vaInvoker;
|
||||
return vaInvoker;
|
||||
}
|
||||
|
||||
|
||||
@ -506,8 +506,18 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
if (from != null) message += ", from " + from;
|
||||
return new IllegalAccessException(message);
|
||||
}
|
||||
public ReflectiveOperationException makeAccessException(String message) {
|
||||
message = message + ": "+ toString();
|
||||
private String message() {
|
||||
if (isResolved())
|
||||
return "no access";
|
||||
else if (isConstructor())
|
||||
return "no such constructor";
|
||||
else if (isMethod())
|
||||
return "no such method";
|
||||
else
|
||||
return "no such field";
|
||||
}
|
||||
public ReflectiveOperationException makeAccessException() {
|
||||
String message = message() + ": "+ toString();
|
||||
if (isResolved())
|
||||
return new IllegalAccessException(message);
|
||||
else if (isConstructor())
|
||||
@ -641,7 +651,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
MemberName result = resolveOrNull(m, searchSupers, lookupClass);
|
||||
if (result != null)
|
||||
return result;
|
||||
ReflectiveOperationException ex = m.makeAccessException("no access");
|
||||
ReflectiveOperationException ex = m.makeAccessException();
|
||||
if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex;
|
||||
throw nsmClass.cast(ex);
|
||||
}
|
||||
|
||||
@ -41,12 +41,12 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* and {@linkplain java.lang.invoke.MethodHandles#filterArguments substitution}.
|
||||
*
|
||||
* <h3>Method handle contents</h3>
|
||||
* Method handles are dynamically and strongly typed according to type descriptor.
|
||||
* They are not distinguished by the name or defining class of their underlying methods.
|
||||
* A method handle must be invoked using type descriptor which matches
|
||||
* the method handle's own {@linkplain #type method type}.
|
||||
* Method handles are dynamically and strongly typed according to their parameter and return types.
|
||||
* They are not distinguished by the name or the defining class of their underlying methods.
|
||||
* A method handle must be invoked using a symbolic type descriptor which matches
|
||||
* the method handle's own {@linkplain #type type descriptor}.
|
||||
* <p>
|
||||
* Every method handle reports its type via the {@link #type type} accessor.
|
||||
* Every method handle reports its type descriptor via the {@link #type type} accessor.
|
||||
* This type descriptor is a {@link java.lang.invoke.MethodType MethodType} object,
|
||||
* whose structure is a series of classes, one of which is
|
||||
* the return type of the method (or {@code void.class} if none).
|
||||
@ -83,7 +83,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* From the viewpoint of source code, these methods can take any arguments
|
||||
* and their result can be cast to any return type.
|
||||
* Formally this is accomplished by giving the invoker methods
|
||||
* {@code Object} return types and variable-arity {@code Object} arguments,
|
||||
* {@code Object} return types and variable arity {@code Object} arguments,
|
||||
* but they have an additional quality called <em>signature polymorphism</em>
|
||||
* which connects this freedom of invocation directly to the JVM execution stack.
|
||||
* <p>
|
||||
@ -93,17 +93,17 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* and may not perform method invocation conversions on the arguments.
|
||||
* Instead, it must push them on the stack according to their own unconverted types.
|
||||
* The method handle object itself is pushed on the stack before the arguments.
|
||||
* The compiler then calls the method handle with a type descriptor which
|
||||
* The compiler then calls the method handle with a symbolic type descriptor which
|
||||
* describes the argument and return types.
|
||||
* <p>
|
||||
* To issue a complete type descriptor, the compiler must also determine
|
||||
* To issue a complete symbolic type descriptor, the compiler must also determine
|
||||
* the return type. This is based on a cast on the method invocation expression,
|
||||
* if there is one, or else {@code Object} if the invocation is an expression
|
||||
* or else {@code void} if the invocation is a statement.
|
||||
* The cast may be to a primitive type (but not {@code void}).
|
||||
* <p>
|
||||
* As a corner case, an uncasted {@code null} argument is given
|
||||
* a type descriptor of {@code java.lang.Void}.
|
||||
* a symbolic type descriptor of {@code java.lang.Void}.
|
||||
* The ambiguity with the type {@code Void} is harmless, since there are no references of type
|
||||
* {@code Void} except the null reference.
|
||||
*
|
||||
@ -112,16 +112,16 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* it is linked, by symbolically resolving the names in the instruction
|
||||
* and verifying that the method call is statically legal.
|
||||
* This is true of calls to {@code invokeExact} and {@code invoke}.
|
||||
* In this case, the type descriptor emitted by the compiler is checked for
|
||||
* In this case, the symbolic type descriptor emitted by the compiler is checked for
|
||||
* correct syntax and names it contains are resolved.
|
||||
* Thus, an {@code invokevirtual} instruction which invokes
|
||||
* a method handle will always link, as long
|
||||
* as the type descriptor is syntactically well-formed
|
||||
* as the symbolic type descriptor is syntactically well-formed
|
||||
* and the types exist.
|
||||
* <p>
|
||||
* When the {@code invokevirtual} is executed after linking,
|
||||
* the receiving method handle's type is first checked by the JVM
|
||||
* to ensure that it matches the descriptor.
|
||||
* to ensure that it matches the symbolic type descriptor.
|
||||
* If the type match fails, it means that the method which the
|
||||
* caller is invoking is not present on the individual
|
||||
* method handle being invoked.
|
||||
@ -138,7 +138,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* (or other behavior, as the case may be).
|
||||
* <p>
|
||||
* A call to plain {@code invoke} works the same as a call to
|
||||
* {@code invokeExact}, if the type descriptor specified by the caller
|
||||
* {@code invokeExact}, if the symbolic type descriptor specified by the caller
|
||||
* exactly matches the method handle's own type.
|
||||
* If there is a type mismatch, {@code invoke} attempts
|
||||
* to adjust the type of the receiving method handle,
|
||||
@ -165,9 +165,9 @@ import static java.lang.invoke.MethodHandleStatics.*;
|
||||
* method type matching takes into account both types names and class loaders.
|
||||
* Thus, even if a method handle {@code M} is created in one
|
||||
* class loader {@code L1} and used in another {@code L2},
|
||||
* method handle calls are type-safe, because the caller's type
|
||||
* method handle calls are type-safe, because the caller's symbolic type
|
||||
* descriptor, as resolved in {@code L2},
|
||||
* is matched against the original callee method's type descriptor,
|
||||
* is matched against the original callee method's symbolic type descriptor,
|
||||
* as resolved in {@code L1}.
|
||||
* The resolution in {@code L1} happens when {@code M} is created
|
||||
* and its type is assigned, while the resolution in {@code L2} happens
|
||||
@ -243,24 +243,24 @@ mt = MethodType.methodType(String.class, char.class, char.class);
|
||||
mh = lookup.findVirtual(String.class, "replace", mt);
|
||||
s = (String) mh.invokeExact("daddy",'d','n');
|
||||
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
|
||||
assert(s.equals("nanny"));
|
||||
assertEquals(s, "nanny");
|
||||
// weakly typed invocation (using MHs.invoke)
|
||||
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
|
||||
assert(s.equals("savvy"));
|
||||
assertEquals(s, "savvy");
|
||||
// mt is (Object[])List
|
||||
mt = MethodType.methodType(java.util.List.class, Object[].class);
|
||||
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
||||
assert(mh.isVarargsCollector());
|
||||
x = mh.invoke("one", "two");
|
||||
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||
assert(x.equals(java.util.Arrays.asList("one","two")));
|
||||
assertEquals(x, java.util.Arrays.asList("one","two"));
|
||||
// mt is (Object,Object,Object)Object
|
||||
mt = MethodType.genericMethodType(3);
|
||||
mh = mh.asType(mt);
|
||||
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
|
||||
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
assert(x.equals(java.util.Arrays.asList(1,2,3)));
|
||||
// mt is int()
|
||||
assertEquals(x, java.util.Arrays.asList(1,2,3));
|
||||
// mt is ()int
|
||||
mt = MethodType.methodType(int.class);
|
||||
mh = lookup.findVirtual(java.util.List.class, "size", mt);
|
||||
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
|
||||
@ -273,7 +273,10 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* </pre></blockquote>
|
||||
* Each of the above calls to {@code invokeExact} or plain {@code invoke}
|
||||
* generates a single invokevirtual instruction with
|
||||
* the type descriptor indicated in the following comment.
|
||||
* the symbolic type descriptor indicated in the following comment.
|
||||
* In these examples, the helper method {@code assertEquals} is assumed to
|
||||
* be a method which calls {@link Objects.equals java.util.Objects#equals}
|
||||
* on its arguments, and asserts that the result is true.
|
||||
*
|
||||
* <h3>Exceptions</h3>
|
||||
* The methods {@code invokeExact} and {@code invoke} are declared
|
||||
@ -284,7 +287,7 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* there is no particular effect on bytecode shape from ascribing
|
||||
* checked exceptions to method handle invocations. But in Java source
|
||||
* code, methods which perform method handle calls must either explicitly
|
||||
* throw {@code java.lang.Throwable Throwable}, or else must catch all
|
||||
* throw {@code Throwable}, or else must catch all
|
||||
* throwables locally, rethrowing only those which are legal in the context,
|
||||
* and wrapping ones which are illegal.
|
||||
*
|
||||
@ -292,77 +295,26 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* The unusual compilation and linkage behavior of
|
||||
* {@code invokeExact} and plain {@code invoke}
|
||||
* is referenced by the term <em>signature polymorphism</em>.
|
||||
* A signature polymorphic method is one which can operate with
|
||||
* As defined in the Java Language Specification,
|
||||
* a signature polymorphic method is one which can operate with
|
||||
* any of a wide range of call signatures and return types.
|
||||
* In order to make this work, both the Java compiler and the JVM must
|
||||
* give special treatment to signature polymorphic methods.
|
||||
* <p>
|
||||
* In source code, a call to a signature polymorphic method will
|
||||
* compile, regardless of the requested type descriptor.
|
||||
* compile, regardless of the requested symbolic type descriptor.
|
||||
* As usual, the Java compiler emits an {@code invokevirtual}
|
||||
* instruction with the given type descriptor against the named method.
|
||||
* The unusual part is that the type descriptor is derived from
|
||||
* instruction with the given symbolic type descriptor against the named method.
|
||||
* The unusual part is that the symbolic type descriptor is derived from
|
||||
* the actual argument and return types, not from the method declaration.
|
||||
* <p>
|
||||
* When the JVM processes bytecode containing signature polymorphic calls,
|
||||
* it will successfully link any such call, regardless of its type descriptor.
|
||||
* it will successfully link any such call, regardless of its symbolic type descriptor.
|
||||
* (In order to retain type safety, the JVM will guard such calls with suitable
|
||||
* dynamic type checks, as described elsewhere.)
|
||||
* <p>
|
||||
* Bytecode generators, including the compiler back end, are required to emit
|
||||
* untransformed type descriptors for these methods.
|
||||
* untransformed symbolic type descriptors for these methods.
|
||||
* Tools which determine symbolic linkage are required to accept such
|
||||
* untransformed descriptors, without reporting linkage errors.
|
||||
* <p>
|
||||
* For the sake of tools (but not as a programming API), the signature polymorphic
|
||||
* methods are marked with a private yet standard annotation,
|
||||
* {@code @java.lang.invoke.MethodHandle.PolymorphicSignature}.
|
||||
* The annotation's retention is {@code RUNTIME}, so that all tools can see it.
|
||||
*
|
||||
* <h3>Formal rules for processing signature polymorphic methods</h3>
|
||||
* <p>
|
||||
* The following methods (and no others) are signature polymorphic:
|
||||
* <ul>
|
||||
* <li>{@link java.lang.invoke.MethodHandle#invokeExact MethodHandle.invokeExact}
|
||||
* <li>{@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}
|
||||
* </ul>
|
||||
* <p>
|
||||
* A signature polymorphic method will be declared with the following properties:
|
||||
* <ul>
|
||||
* <li>It must be native.
|
||||
* <li>It must take a single varargs parameter of the form {@code Object...}.
|
||||
* <li>It must produce a return value of type {@code Object}.
|
||||
* <li>It must be contained within the {@code java.lang.invoke} package.
|
||||
* </ul>
|
||||
* Because of these requirements, a signature polymorphic method is able to accept
|
||||
* any number and type of actual arguments, and can, with a cast, produce a value of any type.
|
||||
* However, the JVM will treat these declaration features as a documentation convention,
|
||||
* rather than a description of the actual structure of the methods as executed.
|
||||
* <p>
|
||||
* When a call to a signature polymorphic method is compiled, the associated linkage information for
|
||||
* its arguments is not array of {@code Object} (as for other similar varargs methods)
|
||||
* but rather the erasure of the static types of all the arguments.
|
||||
* <p>
|
||||
* In an argument position of a method invocation on a signature polymorphic method,
|
||||
* a null literal has type {@code java.lang.Void}, unless cast to a reference type.
|
||||
* (<em>Note:</em> This typing rule allows the null type to have its own encoding in linkage information
|
||||
* distinct from other types.
|
||||
* <p>
|
||||
* The linkage information for the return type is derived from a context-dependent target typing convention.
|
||||
* The return type for a signature polymorphic method invocation is determined as follows:
|
||||
* <ul>
|
||||
* <li>If the method invocation expression is an expression statement, the method is {@code void}.
|
||||
* <li>Otherwise, if the method invocation expression is the immediate operand of a cast,
|
||||
* the return type is the erasure of the cast type.
|
||||
* <li>Otherwise, the return type is the method's nominal return type, {@code Object}.
|
||||
* </ul>
|
||||
* (Programmers are encouraged to use explicit casts unless it is clear that a signature polymorphic
|
||||
* call will be used as a plain {@code Object} expression.)
|
||||
* <p>
|
||||
* The linkage information for argument and return types is stored in the descriptor for the
|
||||
* compiled (bytecode) call site. As for any invocation instruction, the arguments and return value
|
||||
* will be passed directly on the JVM stack, in accordance with the descriptor,
|
||||
* and without implicit boxing or unboxing.
|
||||
*
|
||||
* <h3>Interoperation between method handles and the Core Reflection API</h3>
|
||||
* Using factory methods in the {@link java.lang.invoke.MethodHandles.Lookup Lookup} API,
|
||||
@ -386,14 +338,14 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* declared method, including in this case {@code native} and {@code varargs} bits.
|
||||
* <p>
|
||||
* As with any reflected method, these methods (when reflected) may be
|
||||
* invoked via {@link java.lang.reflect.Method#invoke Method.invoke}.
|
||||
* invoked via {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}.
|
||||
* However, such reflective calls do not result in method handle invocations.
|
||||
* Such a call, if passed the required argument
|
||||
* (a single one, of type {@code Object[]}), will ignore the argument and
|
||||
* will throw an {@code UnsupportedOperationException}.
|
||||
* <p>
|
||||
* Since {@code invokevirtual} instructions can natively
|
||||
* invoke method handles under any type descriptor, this reflective view conflicts
|
||||
* invoke method handles under any symbolic type descriptor, this reflective view conflicts
|
||||
* with the normal presentation of these methods via bytecodes.
|
||||
* Thus, these two native methods, when reflectively viewed by
|
||||
* {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
|
||||
@ -414,7 +366,7 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* When a method handle is invoked, the types of its arguments
|
||||
* or the return value cast type may be generic types or type instances.
|
||||
* If this occurs, the compiler will replace those
|
||||
* types by their erasures when when it constructs the type descriptor
|
||||
* types by their erasures when it constructs the symbolic type descriptor
|
||||
* for the {@code invokevirtual} instruction.
|
||||
* <p>
|
||||
* Method handles do not represent
|
||||
@ -503,17 +455,17 @@ public abstract class MethodHandle {
|
||||
|
||||
/**
|
||||
* Invokes the method handle, allowing any caller type descriptor, but requiring an exact type match.
|
||||
* The type descriptor at the call site of {@code invokeExact} must
|
||||
* The symbolic type descriptor at the call site of {@code invokeExact} must
|
||||
* exactly match this method handle's {@link #type type}.
|
||||
* No conversions are allowed on arguments or return values.
|
||||
* <p>
|
||||
* When this method is observed via the Core Reflection API,
|
||||
* it will appear as a single native method, taking an object array and returning an object.
|
||||
* If this native method is invoked directly via
|
||||
* {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI,
|
||||
* {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI,
|
||||
* or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect},
|
||||
* it will throw an {@code UnsupportedOperationException}.
|
||||
* @throws WrongMethodTypeException if the target's type is not identical with the caller's type descriptor
|
||||
* @throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor
|
||||
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
||||
*/
|
||||
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
|
||||
@ -522,7 +474,7 @@ public abstract class MethodHandle {
|
||||
* Invokes the method handle, allowing any caller type descriptor,
|
||||
* and optionally performing conversions on arguments and return values.
|
||||
* <p>
|
||||
* If the call site type descriptor exactly matches this method handle's {@link #type type},
|
||||
* If the call site's symbolic type descriptor exactly matches this method handle's {@link #type type},
|
||||
* the call proceeds as if by {@link #invokeExact invokeExact}.
|
||||
* <p>
|
||||
* Otherwise, the call proceeds as if this method handle were first
|
||||
@ -535,7 +487,7 @@ public abstract class MethodHandle {
|
||||
* adaptations directly on the caller's arguments,
|
||||
* and call the target method handle according to its own exact type.
|
||||
* <p>
|
||||
* The type descriptor at the call site of {@code invoke} must
|
||||
* The resolved type descriptor at the call site of {@code invoke} must
|
||||
* be a valid argument to the receivers {@code asType} method.
|
||||
* In particular, the caller must specify the same argument arity
|
||||
* as the callee's type,
|
||||
@ -544,24 +496,17 @@ public abstract class MethodHandle {
|
||||
* When this method is observed via the Core Reflection API,
|
||||
* it will appear as a single native method, taking an object array and returning an object.
|
||||
* If this native method is invoked directly via
|
||||
* {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI,
|
||||
* {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI,
|
||||
* or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect},
|
||||
* it will throw an {@code UnsupportedOperationException}.
|
||||
* @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type descriptor
|
||||
* @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's symbolic type descriptor
|
||||
* @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
|
||||
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
||||
*/
|
||||
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
* <em>Temporary alias</em> for {@link #invoke}, for backward compatibility with some versions of JSR 292.
|
||||
* On some JVMs, support can be excluded by the flags {@code -XX:+UnlockExperimentalVMOptions -XX:-AllowInvokeGeneric}.
|
||||
* @deprecated Will be removed for JSR 292 Proposed Final Draft.
|
||||
*/
|
||||
public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
* Performs a varargs invocation, passing the arguments in the given array
|
||||
* Performs a variable arity invocation, passing the arguments in the given array
|
||||
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
|
||||
* which mentions only the type {@code Object}, and whose arity is the length
|
||||
* of the argument array.
|
||||
@ -613,56 +558,16 @@ public abstract class MethodHandle {
|
||||
public Object invokeWithArguments(Object... arguments) throws Throwable {
|
||||
int argc = arguments == null ? 0 : arguments.length;
|
||||
MethodType type = type();
|
||||
if (type.parameterCount() != argc) {
|
||||
if (type.parameterCount() != argc || isVarargsCollector()) {
|
||||
// simulate invoke
|
||||
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
|
||||
}
|
||||
if (argc <= 10) {
|
||||
MethodHandle invoker = type.invokers().generalInvoker();
|
||||
switch (argc) {
|
||||
case 0: return invoker.invokeExact(this);
|
||||
case 1: return invoker.invokeExact(this,
|
||||
arguments[0]);
|
||||
case 2: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1]);
|
||||
case 3: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2]);
|
||||
case 4: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3]);
|
||||
case 5: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3], arguments[4]);
|
||||
case 6: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3], arguments[4], arguments[5]);
|
||||
case 7: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3], arguments[4], arguments[5],
|
||||
arguments[6]);
|
||||
case 8: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3], arguments[4], arguments[5],
|
||||
arguments[6], arguments[7]);
|
||||
case 9: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3], arguments[4], arguments[5],
|
||||
arguments[6], arguments[7], arguments[8]);
|
||||
case 10: return invoker.invokeExact(this,
|
||||
arguments[0], arguments[1], arguments[2],
|
||||
arguments[3], arguments[4], arguments[5],
|
||||
arguments[6], arguments[7], arguments[8],
|
||||
arguments[9]);
|
||||
}
|
||||
}
|
||||
|
||||
// more than ten arguments get boxed in a varargs list:
|
||||
MethodHandle invoker = type.invokers().spreadInvoker(0);
|
||||
MethodHandle invoker = type.invokers().varargsInvoker();
|
||||
return invoker.invokeExact(this, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a varargs invocation, passing the arguments in the given array
|
||||
* Performs a variable arity invocation, passing the arguments in the given array
|
||||
* to the method handle, as if via an inexact {@link #invoke invoke} from a call site
|
||||
* which mentions only the type {@code Object}, and whose arity is the length
|
||||
* of the argument array.
|
||||
@ -674,6 +579,7 @@ public abstract class MethodHandle {
|
||||
*
|
||||
* @param arguments the arguments to pass to the target
|
||||
* @return the result returned by the target
|
||||
* @throws NullPointerException if {@code arguments} is a null reference
|
||||
* @throws ClassCastException if an argument cannot be converted by reference casting
|
||||
* @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments
|
||||
* @throws Throwable anything thrown by the target method invocation
|
||||
@ -690,22 +596,95 @@ public abstract class MethodHandle {
|
||||
* <p>
|
||||
* If the original type and new type are equal, returns {@code this}.
|
||||
* <p>
|
||||
* The new method handle, when invoked, will perform the following
|
||||
* steps:
|
||||
* <ul>
|
||||
* <li>Convert the incoming argument list to match the original
|
||||
* method handle's argument list.
|
||||
* <li>Invoke the original method handle on the converted argument list.
|
||||
* <li>Convert any result returned by the original method handle
|
||||
* to the return type of new method handle.
|
||||
* </ul>
|
||||
* <p>
|
||||
* This method provides the crucial behavioral difference between
|
||||
* {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}. The two methods
|
||||
* perform the same steps when the caller's type descriptor is identical
|
||||
* with the callee's, but when the types differ, plain {@link #invoke invoke}
|
||||
* {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}.
|
||||
* The two methods
|
||||
* perform the same steps when the caller's type descriptor exactly m atches
|
||||
* the callee's, but when the types differ, plain {@link #invoke invoke}
|
||||
* also calls {@code asType} (or some internal equivalent) in order
|
||||
* to match up the caller's and callee's types.
|
||||
* <p>
|
||||
* This method is equivalent to {@link MethodHandles#convertArguments convertArguments},
|
||||
* except for variable arity method handles produced by {@link #asVarargsCollector asVarargsCollector}.
|
||||
* If the current method is a variable arity method handle
|
||||
* argument list conversion may involve the conversion and collection
|
||||
* of several arguments into an array, as
|
||||
* {@linkplain #asVarargsCollector described elsewhere}.
|
||||
* In every other case, all conversions are applied <em>pairwise</em>,
|
||||
* which means that each argument or return value is converted to
|
||||
* exactly one argument or return value (or no return value).
|
||||
* The applied conversions are defined by consulting the
|
||||
* the corresponding component types of the old and new
|
||||
* method handle types.
|
||||
* <p>
|
||||
* Let <em>T0</em> and <em>T1</em> be corresponding new and old parameter types,
|
||||
* or old and new return types. Specifically, for some valid index {@code i}, let
|
||||
* <em>T0</em>{@code =newType.parameterType(i)} and <em>T1</em>{@code =this.type().parameterType(i)}.
|
||||
* Or else, going the other way for return values, let
|
||||
* <em>T0</em>{@code =this.type().returnType()} and <em>T1</em>{@code =newType.returnType()}.
|
||||
* If the types are the same, the new method handle makes no change
|
||||
* to the corresponding argument or return value (if any).
|
||||
* Otherwise, one of the following conversions is applied
|
||||
* if possible:
|
||||
* <ul>
|
||||
* <li>If <em>T0</em> and <em>T1</em> are references, then a cast to <em>T1</em> is applied.
|
||||
* (The types do not need to be related in any particular way.
|
||||
* This is because a dynamic value of null can convert to any reference type.)
|
||||
* <li>If <em>T0</em> and <em>T1</em> are primitives, then a Java method invocation
|
||||
* conversion (JLS 5.3) is applied, if one exists.
|
||||
* (Specifically, <em>T0</em> must convert to <em>T1</em> by a widening primitive conversion.)
|
||||
* <li>If <em>T0</em> is a primitive and <em>T1</em> a reference,
|
||||
* a Java casting conversion (JLS 5.5) is applied if one exists.
|
||||
* (Specifically, the value is boxed from <em>T0</em> to its wrapper class,
|
||||
* which is then widened as needed to <em>T1</em>.)
|
||||
* <li>If <em>T0</em> is a reference and <em>T1</em> a primitive, an unboxing
|
||||
* conversion will be applied at runtime, possibly followed
|
||||
* by a Java method invocation conversion (JLS 5.3)
|
||||
* on the primitive value. (These are the primitive widening conversions.)
|
||||
* <em>T0</em> must be a wrapper class or a supertype of one.
|
||||
* (In the case where <em>T0</em> is Object, these are the conversions
|
||||
* allowed by {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}.)
|
||||
* The unboxing conversion must have a possibility of success, which means that
|
||||
* if <em>T0</em> is not itself a wrapper class, there must exist at least one
|
||||
* wrapper class <em>TW</em> which is a subtype of <em>T0</em> and whose unboxed
|
||||
* primitive value can be widened to <em>T1</em>.
|
||||
* <li>If the return type <em>T1</em> is marked as void, any returned value is discarded
|
||||
* <li>If the return type <em>T0</em> is void and <em>T1</em> a reference, a null value is introduced.
|
||||
* <li>If the return type <em>T0</em> is void and <em>T1</em> a primitive,
|
||||
* a zero value is introduced.
|
||||
* </ul>
|
||||
* (<em>Note:</em> Both <em>T0</em> and <em>T1</em> may be regarded as static types,
|
||||
* because neither corresponds specifically to the <em>dynamic type</em> of any
|
||||
* actual argument or return value.)
|
||||
* <p>
|
||||
* The method handle conversion cannot be made if any one of the required
|
||||
* pairwise conversions cannot be made.
|
||||
* <p>
|
||||
* At runtime, the conversions applied to reference arguments
|
||||
* or return values may require additional runtime checks which can fail.
|
||||
* An unboxing operation may fail because the original reference is null,
|
||||
* causing a {@link java.lang.NullPointerException NullPointerException}.
|
||||
* An unboxing operation or a reference cast may also fail on a reference
|
||||
* to an object of the wrong type,
|
||||
* causing a {@link java.lang.ClassCastException ClassCastException}.
|
||||
* Although an unboxing operation may accept several kinds of wrappers,
|
||||
* if none are available, a {@code ClassCastException} will be thrown.
|
||||
*
|
||||
* @param newType the expected type of the new method handle
|
||||
* @return a method handle which delegates to {@code this} after performing
|
||||
* any necessary argument conversions, and arranges for any
|
||||
* necessary return value conversions
|
||||
* @throws NullPointerException if {@code newType} is a null reference
|
||||
* @throws WrongMethodTypeException if the conversion cannot be made
|
||||
* @see MethodHandles#convertArguments
|
||||
* @see MethodHandles#explicitCastArguments
|
||||
*/
|
||||
public MethodHandle asType(MethodType newType) {
|
||||
if (!type.isConvertibleTo(newType)) {
|
||||
@ -715,7 +694,7 @@ public abstract class MethodHandle {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an adapter which accepts a trailing array argument
|
||||
* Makes an <em>array-spreading</em> method handle, which accepts a trailing array argument
|
||||
* and spreads its elements as positional arguments.
|
||||
* The new method handle adapts, as its <i>target</i>,
|
||||
* the current method handle. The type of the adapter will be
|
||||
@ -740,13 +719,54 @@ public abstract class MethodHandle {
|
||||
* contains exactly enough elements to provide a correct argument count
|
||||
* to the target method handle.
|
||||
* (The array may also be null when zero elements are required.)
|
||||
* <p>
|
||||
* Here are some simple examples of array-spreading method handles:
|
||||
* <blockquote><pre>
|
||||
MethodHandle equals = publicLookup()
|
||||
.findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
|
||||
assert( (boolean) equals.invokeExact("me", (Object)"me"));
|
||||
assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
|
||||
// spread both arguments from a 2-array:
|
||||
MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
|
||||
assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
|
||||
assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
|
||||
// spread both arguments from a String array:
|
||||
MethodHandle eq2s = equals.asSpreader(String[].class, 2);
|
||||
assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
|
||||
assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
|
||||
// spread second arguments from a 1-array:
|
||||
MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
|
||||
assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
|
||||
assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
|
||||
// spread no arguments from a 0-array or null:
|
||||
MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
|
||||
assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
|
||||
assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
|
||||
// asSpreader and asCollector are approximate inverses:
|
||||
for (int n = 0; n <= 2; n++) {
|
||||
for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
|
||||
MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
|
||||
assert( (boolean) equals2.invokeWithArguments("me", "me"));
|
||||
assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
|
||||
}
|
||||
}
|
||||
MethodHandle caToString = publicLookup()
|
||||
.findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
|
||||
assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
|
||||
MethodHandle caString3 = caToString.asCollector(char[].class, 3);
|
||||
assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
|
||||
MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
|
||||
assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
|
||||
* </pre></blockquote>
|
||||
* @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments
|
||||
* @param arrayLength the number of arguments to spread from an incoming array argument
|
||||
* @return a new method handle which spreads its final array argument,
|
||||
* before calling the original method handle
|
||||
* @throws NullPointerException if {@code arrayType} is a null reference
|
||||
* @throws IllegalArgumentException if {@code arrayType} is not an array type
|
||||
* @throws IllegalArgumentException if target does not have at least
|
||||
* {@code arrayLength} parameter types
|
||||
* {@code arrayLength} parameter types,
|
||||
* or if {@code arrayLength} is negative
|
||||
* @throws WrongMethodTypeException if the implied {@code asType} call fails
|
||||
* @see #asCollector
|
||||
*/
|
||||
@ -758,7 +778,8 @@ public abstract class MethodHandle {
|
||||
private void asSpreaderChecks(Class<?> arrayType, int arrayLength) {
|
||||
spreadArrayChecks(arrayType, arrayLength);
|
||||
int nargs = type().parameterCount();
|
||||
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
|
||||
if (nargs < arrayLength || arrayLength < 0)
|
||||
throw newIllegalArgumentException("bad spread array length");
|
||||
if (arrayType != Object[].class && arrayLength != 0) {
|
||||
boolean sawProblem = false;
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
@ -794,7 +815,7 @@ public abstract class MethodHandle {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an adapter which accepts a given number of trailing
|
||||
* Makes an <em>array-collecting</em> method handle, which accepts a given number of trailing
|
||||
* positional arguments and collects them into an array argument.
|
||||
* The new method handle adapts, as its <i>target</i>,
|
||||
* the current method handle. The type of the adapter will be
|
||||
@ -821,10 +842,40 @@ public abstract class MethodHandle {
|
||||
* <p>
|
||||
* In order to create a collecting adapter which is not restricted to a particular
|
||||
* number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead.
|
||||
* <p>
|
||||
* Here are some examples of array-collecting method handles:
|
||||
* <blockquote><pre>
|
||||
MethodHandle deepToString = publicLookup()
|
||||
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
|
||||
assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"}));
|
||||
MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
|
||||
assertEquals(methodType(String.class, Object.class), ts1.type());
|
||||
//assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL
|
||||
assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
|
||||
// arrayType can be a subtype of Object[]
|
||||
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
|
||||
assertEquals(methodType(String.class, String.class, String.class), ts2.type());
|
||||
assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
|
||||
MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
|
||||
assertEquals("[]", (String) ts0.invokeExact());
|
||||
// collectors can be nested, Lisp-style
|
||||
MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
|
||||
assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
|
||||
// arrayType can be any primitive array type
|
||||
MethodHandle bytesToString = publicLookup()
|
||||
.findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
|
||||
.asCollector(byte[].class, 3);
|
||||
assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
|
||||
MethodHandle longsToString = publicLookup()
|
||||
.findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
|
||||
.asCollector(long[].class, 1);
|
||||
assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
* </pre></blockquote>
|
||||
* @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
|
||||
* @param arrayLength the number of arguments to collect into a new array argument
|
||||
* @return a new method handle which collects some trailing argument
|
||||
* into an array, before calling the original method handle
|
||||
* @throws NullPointerException if {@code arrayType} is a null reference
|
||||
* @throws IllegalArgumentException if {@code arrayType} is not an array type
|
||||
* or {@code arrayType} is not assignable to this method handle's trailing parameter type,
|
||||
* or {@code arrayLength} is not a legal array size
|
||||
@ -838,11 +889,16 @@ public abstract class MethodHandle {
|
||||
return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
|
||||
}
|
||||
|
||||
private void asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||
// private API: return true if last param exactly matches arrayType
|
||||
private boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
|
||||
spreadArrayChecks(arrayType, arrayLength);
|
||||
int nargs = type().parameterCount();
|
||||
if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
|
||||
throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
|
||||
if (nargs != 0) {
|
||||
Class<?> lastParam = type().parameterType(nargs-1);
|
||||
if (lastParam == arrayType) return true;
|
||||
if (lastParam.isAssignableFrom(arrayType)) return false;
|
||||
}
|
||||
throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -859,6 +915,10 @@ public abstract class MethodHandle {
|
||||
* {@code arrayType}, even if the target has a different
|
||||
* last parameter type.
|
||||
* <p>
|
||||
* This transformation may return {@code this} if the method handle is
|
||||
* already of variable arity and its trailing parameter type
|
||||
* is identical to {@code arrayType}.
|
||||
* <p>
|
||||
* When called with {@link #invokeExact invokeExact}, the adapter invokes
|
||||
* the target with no argument changes.
|
||||
* (<em>Note:</em> This behavior is different from a
|
||||
@ -875,8 +935,8 @@ public abstract class MethodHandle {
|
||||
* trailing parameter type of the caller is a reference type identical to
|
||||
* or assignable to the trailing parameter type of the adapter,
|
||||
* the arguments and return values are converted pairwise,
|
||||
* as if by {@link MethodHandles#convertArguments convertArguments}.
|
||||
* (This is also normal behavior for {@code invoke} in such a case.)
|
||||
* as if by {@link #asType asType} on a fixed arity
|
||||
* method handle.
|
||||
* <p>
|
||||
* Otherwise, the arities differ, or the adapter's trailing parameter
|
||||
* type is not assignable from the corresponding caller type.
|
||||
@ -944,14 +1004,24 @@ public abstract class MethodHandle {
|
||||
* <p>
|
||||
* Here is an example, of a list-making variable arity method handle:
|
||||
* <blockquote><pre>
|
||||
MethodHandle deepToString = publicLookup()
|
||||
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
|
||||
MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
|
||||
assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"}));
|
||||
assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"}));
|
||||
assertEquals("[won]", (String) ts1.invoke( "won" ));
|
||||
assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
|
||||
// findStatic of Arrays.asList(...) produces a variable arity method handle:
|
||||
MethodHandle asList = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
|
||||
assertEquals(methodType(List.class, Object[].class), asList.type());
|
||||
assert(asList.isVarargsCollector());
|
||||
assertEquals("[]", asList.invoke().toString());
|
||||
assertEquals("[1]", asList.invoke(1).toString());
|
||||
assertEquals("[two, too]", asList.invoke("two", "too").toString());
|
||||
Object[] argv = { "three", "thee", "tee" };
|
||||
String[] argv = { "three", "thee", "tee" };
|
||||
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
|
||||
assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
|
||||
List ls = (List) asList.invoke((Object)argv);
|
||||
assertEquals(1, ls.size());
|
||||
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
@ -968,38 +1038,24 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
* or not a single trailing argument is interpreted as a whole
|
||||
* array or a single element of an array to be collected.
|
||||
* Note that the dynamic type of the trailing argument has no
|
||||
* effect on this decision, only a comparison between the static
|
||||
* type descriptor of the call site and the type of the method handle.)
|
||||
* <p style="font-size:smaller;">
|
||||
* As a result of the previously stated rules, the variable arity behavior
|
||||
* of a method handle may be suppressed, by binding it to the exact invoker
|
||||
* of its own type, as follows:
|
||||
* <blockquote><pre>
|
||||
MethodHandle vamh = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
||||
assert(vamh.type().equals(mh.type()));
|
||||
assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
|
||||
boolean failed = false;
|
||||
try { mh.invoke(1,2,3); }
|
||||
catch (WrongMethodTypeException ex) { failed = true; }
|
||||
assert(failed);
|
||||
* </pre></blockquote>
|
||||
* This transformation has no behavioral effect if the method handle is
|
||||
* not of variable arity.
|
||||
* effect on this decision, only a comparison between the symbolic
|
||||
* type descriptor of the call site and the type descriptor of the method handle.)
|
||||
*
|
||||
* @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
|
||||
* @return a new method handle which can collect any number of trailing arguments
|
||||
* into an array, before calling the original method handle
|
||||
* @throws NullPointerException if {@code arrayType} is a null reference
|
||||
* @throws IllegalArgumentException if {@code arrayType} is not an array type
|
||||
* or {@code arrayType} is not assignable to this method handle's trailing parameter type
|
||||
* @see #asCollector
|
||||
* @see #isVarargsCollector
|
||||
* @see #asFixedArity
|
||||
*/
|
||||
public MethodHandle asVarargsCollector(Class<?> arrayType) {
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
asCollectorChecks(arrayType, 0);
|
||||
boolean lastMatch = asCollectorChecks(arrayType, 0);
|
||||
if (isVarargsCollector() && lastMatch)
|
||||
return this;
|
||||
return AdapterMethodHandle.makeVarargsCollector(this, arrayType);
|
||||
}
|
||||
|
||||
@ -1016,11 +1072,60 @@ assert(failed);
|
||||
* </ul>
|
||||
* @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls
|
||||
* @see #asVarargsCollector
|
||||
* @see #asFixedArity
|
||||
*/
|
||||
public boolean isVarargsCollector() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a <em>fixed arity</em> method handle which is otherwise
|
||||
* equivalent to the the current method handle.
|
||||
* <p>
|
||||
* If the current method handle is not of
|
||||
* {@linkplain #asVarargsCollector variable arity},
|
||||
* the current method handle is returned.
|
||||
* This is true even if the current method handle
|
||||
* could not be a valid input to {@code asVarargsCollector}.
|
||||
* <p>
|
||||
* Otherwise, the resulting fixed-arity method handle has the same
|
||||
* type and behavior of the current method handle,
|
||||
* except that {@link #isVarargsCollector isVarargsCollector}
|
||||
* will be false.
|
||||
* The fixed-arity method handle may (or may not) be the
|
||||
* a previous argument to {@code asVarargsCollector}.
|
||||
* <p>
|
||||
* Here is an example, of a list-making variable arity method handle:
|
||||
* <blockquote><pre>
|
||||
MethodHandle asListVar = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
MethodHandle asListFix = asListVar.asFixedArity();
|
||||
assertEquals("[1]", asListVar.invoke(1).toString());
|
||||
Exception caught = null;
|
||||
try { asListFix.invoke((Object)1); }
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof ClassCastException);
|
||||
assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
|
||||
try { asListFix.invoke("two", "too"); }
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof WrongMethodTypeException);
|
||||
Object[] argv = { "three", "thee", "tee" };
|
||||
assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
|
||||
assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
|
||||
assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
|
||||
assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return a new method handle which accepts only a fixed number of arguments
|
||||
* @see #asVarargsCollector
|
||||
* @see #isVarargsCollector
|
||||
*/
|
||||
public MethodHandle asFixedArity() {
|
||||
assert(!isVarargsCollector());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a value {@code x} to the first argument of a method handle, without invoking it.
|
||||
* The new method handle adapts, as its <i>target</i>,
|
||||
|
||||
@ -82,12 +82,17 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, lookupClass);
|
||||
if (!mh.isValid())
|
||||
throw method.makeAccessException("no access", lookupClass);
|
||||
throw method.makeAccessException("no direct method handle", lookupClass);
|
||||
assert(mh.type() == mtype);
|
||||
if (!method.isVarargs())
|
||||
return mh;
|
||||
else
|
||||
return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1));
|
||||
int argc = mtype.parameterCount();
|
||||
if (argc != 0) {
|
||||
Class<?> arrayType = mtype.parameterType(argc-1);
|
||||
if (arrayType.isArray())
|
||||
return AdapterMethodHandle.makeVarargsCollector(mh, arrayType);
|
||||
}
|
||||
throw method.makeAccessException("cannot make variable arity", null);
|
||||
}
|
||||
|
||||
static
|
||||
@ -485,14 +490,14 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
*/
|
||||
static
|
||||
MethodHandle bindReceiver(MethodHandle target, Object receiver) {
|
||||
if (receiver == null) return null;
|
||||
if (target instanceof AdapterMethodHandle &&
|
||||
((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY
|
||||
) {
|
||||
Object info = MethodHandleNatives.getTargetInfo(target);
|
||||
if (info instanceof DirectMethodHandle) {
|
||||
DirectMethodHandle dmh = (DirectMethodHandle) info;
|
||||
if (receiver == null ||
|
||||
dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
|
||||
if (dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
|
||||
MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
|
||||
MethodType newType = target.type().dropParameterTypes(0, 1);
|
||||
return convertArguments(bmh, newType, bmh.type(), 0);
|
||||
@ -698,7 +703,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
if (target == null) throw newIllegalArgumentException("cannot drop");
|
||||
oldType = target.type();
|
||||
}
|
||||
return convertArguments(target, newType, oldType, 0);
|
||||
target = convertArguments(target, newType, oldType, 0);
|
||||
assert(target != null);
|
||||
return target;
|
||||
}
|
||||
|
||||
/*non-public*/ static
|
||||
@ -907,14 +914,16 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
this.test = test;
|
||||
this.target = target;
|
||||
this.fallback = fallback;
|
||||
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||
}
|
||||
// FIXME: Build the control flow out of foldArguments.
|
||||
static boolean preferRicochetFrame(MethodType type) {
|
||||
return (type.parameterCount() >= INVOKES.length || type.hasPrimitives());
|
||||
}
|
||||
static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) {
|
||||
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||
MethodType type = target.type();
|
||||
int nargs = type.parameterCount();
|
||||
if (nargs < INVOKES.length) {
|
||||
if (preferRicochetFrame(type))
|
||||
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||
MethodHandle invoke = INVOKES[nargs];
|
||||
MethodType gtype = type.generic();
|
||||
assert(invoke.type().dropParameterTypes(0,1) == gtype);
|
||||
@ -925,6 +934,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
|
||||
return convertArguments(gguard, type, gtype, 0);
|
||||
} else {
|
||||
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||
MethodHandle invoke = VARARGS_INVOKE;
|
||||
MethodType gtype = MethodType.genericMethodType(1);
|
||||
assert(invoke.type().dropParameterTypes(0,1) == gtype);
|
||||
@ -1048,8 +1058,10 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
// where select(z) = select(z, t, f).bindTo(t, f) => z ? t f
|
||||
// [tailcall]=> tf(arg...)
|
||||
assert(test.type().returnType() == boolean.class);
|
||||
MethodType foldTargetType = target.type().insertParameterTypes(0, boolean.class);
|
||||
if (AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true)) {
|
||||
MethodType targetType = target.type();
|
||||
MethodType foldTargetType = targetType.insertParameterTypes(0, boolean.class);
|
||||
if (AdapterMethodHandle.canCollectArguments(foldTargetType, test.type(), 0, true)
|
||||
&& GuardWithTest.preferRicochetFrame(targetType)) {
|
||||
// working backwards, as usual:
|
||||
assert(target.type().equals(fallback.type()));
|
||||
MethodHandle tailcall = MethodHandles.exactInvoker(target.type());
|
||||
@ -1062,7 +1074,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
MethodHandle fold = foldArguments(filter, filter.type().dropParameterTypes(0, 1), 0, test);
|
||||
return fold;
|
||||
}
|
||||
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||
return GuardWithTest.make(test, target, fallback);
|
||||
}
|
||||
|
||||
|
||||
@ -400,7 +400,7 @@ class MethodHandleNatives {
|
||||
case REF_newInvokeSpecial: return lookup.findConstructor( defc, (MethodType) type );
|
||||
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
|
||||
}
|
||||
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
|
||||
throw new InternalError("bad MethodHandle constant "+name+" : "+type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
Error err = new IncompatibleClassChangeError();
|
||||
err.initCause(ex);
|
||||
|
||||
257
jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java
Normal file
257
jdk/src/share/classes/java/lang/invoke/MethodHandleProxies.java
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2011, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import sun.invoke.WrapperInstance;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param target the method handle to invoke from the wrapper
|
||||
* @param intfc the desired type of the wrapper, a single-method interface
|
||||
* @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.
|
||||
//
|
||||
public static
|
||||
<T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) {
|
||||
// POC implementation only; violates the above contract several ways
|
||||
final Method sm = getSingleMethod(intfc);
|
||||
if (sm == null)
|
||||
throw new IllegalArgumentException("not a single-method interface: "+intfc.getName());
|
||||
MethodType smMT = MethodType.methodType(sm.getReturnType(), sm.getParameterTypes());
|
||||
MethodHandle checkTarget = target.asType(smMT); // make throw WMT
|
||||
checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
|
||||
final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, smMT.parameterCount());
|
||||
return intfc.cast(Proxy.newProxyInstance(
|
||||
intfc.getClassLoader(),
|
||||
new Class[]{ intfc, WrapperInstance.class },
|
||||
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 {
|
||||
if (method.getDeclaringClass() == WrapperInstance.class)
|
||||
return getArg(method.getName());
|
||||
if (method.equals(sm))
|
||||
return vaTarget.invokeExact(args);
|
||||
if (isObjectMethod(method))
|
||||
return callObjectMethod(this, method, args);
|
||||
throw new InternalError();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 new IllegalArgumentException("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 getSingleMethod(Class<?> intfc) {
|
||||
if (!intfc.isInterface()) return null;
|
||||
Method sm = null;
|
||||
for (Method m : intfc.getMethods()) {
|
||||
int mod = m.getModifiers();
|
||||
if (Modifier.isAbstract(mod)) {
|
||||
if (sm != null && !isObjectMethod(sm))
|
||||
return null; // too many abstract methods
|
||||
sm = m;
|
||||
}
|
||||
}
|
||||
return sm;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -273,7 +273,7 @@ class MethodType implements java.io.Serializable {
|
||||
* @param objectArgCount number of parameters (excluding the final array parameter if any)
|
||||
* @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
|
||||
* @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
|
||||
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray})
|
||||
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray} is true)
|
||||
* @see #genericMethodType(int)
|
||||
*/
|
||||
public static
|
||||
@ -455,7 +455,8 @@ class MethodType implements java.io.Serializable {
|
||||
/**
|
||||
* Reports if this type contains a wrapper argument or return value.
|
||||
* Wrappers are types which box primitive values, such as {@link Integer}.
|
||||
* The reference type {@code java.lang.Void} counts as a wrapper.
|
||||
* The reference type {@code java.lang.Void} counts as a wrapper,
|
||||
* if it occurs as a return type.
|
||||
* @return true if any of the types are wrappers
|
||||
*/
|
||||
public boolean hasWrappers() {
|
||||
@ -649,13 +650,55 @@ class MethodType implements java.io.Serializable {
|
||||
}
|
||||
/*non-public*/
|
||||
static boolean canConvert(Class<?> src, Class<?> dst) {
|
||||
if (src == dst || dst == void.class) return true;
|
||||
if (src.isPrimitive() && dst.isPrimitive()) {
|
||||
if (!Wrapper.forPrimitiveType(dst)
|
||||
.isConvertibleFrom(Wrapper.forPrimitiveType(src)))
|
||||
// short-circuit a few cases:
|
||||
if (src == dst || dst == Object.class) return true;
|
||||
// the remainder of this logic is documented in MethodHandle.asType
|
||||
if (src.isPrimitive()) {
|
||||
// can force void to an explicit null, a la reflect.Method.invoke
|
||||
// can also force void to a primitive zero, by analogy
|
||||
if (src == void.class) return true; //or !dst.isPrimitive()?
|
||||
Wrapper sw = Wrapper.forPrimitiveType(src);
|
||||
if (dst.isPrimitive()) {
|
||||
// P->P must widen
|
||||
return Wrapper.forPrimitiveType(dst).isConvertibleFrom(sw);
|
||||
} else {
|
||||
// P->R must box and widen
|
||||
return dst.isAssignableFrom(sw.wrapperType());
|
||||
}
|
||||
} else if (dst.isPrimitive()) {
|
||||
// any value can be dropped
|
||||
if (dst == void.class) return true;
|
||||
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||
// R->P must be able to unbox (from a dynamically chosen type) and widen
|
||||
// For example:
|
||||
// Byte/Number/Comparable/Object -> dw:Byte -> byte.
|
||||
// Character/Comparable/Object -> dw:Character -> char
|
||||
// Boolean/Comparable/Object -> dw:Boolean -> boolean
|
||||
// This means that dw must be cast-compatible with src.
|
||||
if (src.isAssignableFrom(dw.wrapperType())) {
|
||||
return true;
|
||||
}
|
||||
// The above does not work if the source reference is strongly typed
|
||||
// to a wrapper whose primitive must be widened. For example:
|
||||
// Byte -> unbox:byte -> short/int/long/float/double
|
||||
// Character -> unbox:char -> int/long/float/double
|
||||
if (Wrapper.isWrapperType(src) &&
|
||||
dw.isConvertibleFrom(Wrapper.forWrapperType(src))) {
|
||||
// can unbox from src and then widen to dst
|
||||
return true;
|
||||
}
|
||||
// We have already covered cases which arise due to runtime unboxing
|
||||
// of a reference type which covers several wrapper types:
|
||||
// Object -> cast:Integer -> unbox:int -> long/float/double
|
||||
// Serializable -> cast:Byte -> unbox:byte -> byte/short/int/long/float/double
|
||||
// An marginal case is Number -> dw:Character -> char, which would be OK if there were a
|
||||
// subclass of Number which wraps a value that can convert to char.
|
||||
// Since there is none, we don't need an extra check here to cover char or boolean.
|
||||
return false;
|
||||
} else {
|
||||
// R->R always works, since null is always valid dynamically
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Queries which have to do with the bytecode architecture
|
||||
@ -740,6 +783,7 @@ class MethodType implements java.io.Serializable {
|
||||
* @param descriptor a bytecode-level type descriptor string "(T...)T"
|
||||
* @param loader the class loader in which to look up the types
|
||||
* @return a method type matching the bytecode-level type descriptor
|
||||
* @throws NullPointerException if the string is null
|
||||
* @throws IllegalArgumentException if the string is not well-formed
|
||||
* @throws TypeNotPresentException if a named type cannot be found
|
||||
*/
|
||||
|
||||
@ -37,12 +37,13 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* <p>
|
||||
* Here is an example of a mutable call site which introduces a
|
||||
* state variable into a method handle chain.
|
||||
* <!-- JavaDocExamplesTest.testMutableCallSite -->
|
||||
* <blockquote><pre>
|
||||
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
|
||||
MethodHandle MH_name = name.dynamicInvoker();
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodType MT_str1 = MethodType.methodType(String.class);
|
||||
MethodHandle MH_upcase = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "toUpperCase", MT_str2);
|
||||
.findVirtual(String.class, "toUpperCase", MT_str1);
|
||||
MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
|
||||
name.setTarget(MethodHandles.constant(String.class, "Rocky"));
|
||||
assertEquals("ROCKY", (String) worker1.invokeExact());
|
||||
@ -53,8 +54,10 @@ assertEquals("FRED", (String) worker1.invokeExact());
|
||||
* <p>
|
||||
* The same call site may be used in several places at once.
|
||||
* <blockquote><pre>
|
||||
MethodHandle MH_dear = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MT_str2).bindTo(", dear?");
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodHandle MH_cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
|
||||
MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
|
||||
assertEquals("Fred, dear?", (String) worker2.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Wilma"));
|
||||
|
||||
@ -56,16 +56,17 @@ package java.lang.invoke;
|
||||
* <p>
|
||||
* Here is an example of a switch point in action:
|
||||
* <blockquote><pre>
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodHandle MH_strcat = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MT_str2);
|
||||
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
|
||||
SwitchPoint spt = new SwitchPoint();
|
||||
assert(spt.isValid());
|
||||
// the following steps may be repeated to re-use the same switch point:
|
||||
MethodHandle worker1 = strcat;
|
||||
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
|
||||
MethodHandle worker1 = MH_strcat;
|
||||
MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
|
||||
MethodHandle worker = spt.guardWithTest(worker1, worker2);
|
||||
assertEquals("method", (String) worker.invokeExact("met", "hod"));
|
||||
SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
|
||||
assert(!spt.isValid());
|
||||
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
|
||||
* </pre></blockquote>
|
||||
* <p style="font-size:smaller;">
|
||||
@ -124,6 +125,19 @@ public class SwitchPoint {
|
||||
this.mcsInvoker = mcs.dynamicInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this switch point is still valid.
|
||||
* <p>
|
||||
* Since invalidation is a global and immediate operation,
|
||||
* this query must be sequenced with any
|
||||
* other threads that could invalidate this switch point.
|
||||
* It may therefore be expensive.
|
||||
* @return true if this switch point has never been invalidated
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return (mcs.getTarget() == K_true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a method handle which always delegates either to the target or the fallback.
|
||||
* The method handle will delegate to the target exactly as long as the switch point is valid.
|
||||
@ -136,6 +150,7 @@ public class SwitchPoint {
|
||||
* @param fallback the method handle selected by the switch point after it is invalidated
|
||||
* @return a combined method handle which always calls either the target or fallback
|
||||
* @throws NullPointerException if either argument is null
|
||||
* @throws IllegalArgumentException if the two method types do not match
|
||||
* @see MethodHandles#guardWithTest
|
||||
*/
|
||||
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
|
||||
|
||||
@ -28,7 +28,8 @@
|
||||
* the Java core class libraries and virtual machine.
|
||||
*
|
||||
* <p>
|
||||
* Certain types in this package have special relations to dynamic
|
||||
* As described in the Java Virtual Machine Specification,
|
||||
* certain types in this package have special relations to dynamic
|
||||
* language support in the virtual machine:
|
||||
* <ul>
|
||||
* <li>The class {@link java.lang.invoke.MethodHandle MethodHandle} contains
|
||||
@ -42,177 +43,16 @@
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2><a name="jvm_mods"></a>Corresponding JVM bytecode format changes</h2>
|
||||
* <em>The following low-level information is presented here as a preview of
|
||||
* changes being made to the Java Virtual Machine specification for JSR 292.
|
||||
* This information will be incorporated in a future version of the JVM specification.</em>
|
||||
* <h2><a name="jvm_mods"></a>Summary of relevant Java Virtual Machine changes</h2>
|
||||
* The following low-level information summarizes relevant parts of the
|
||||
* Java Virtual Machine specification. For full details, please see the
|
||||
* current version of that specification.
|
||||
*
|
||||
* <h3><a name="indyinsn"></a>{@code invokedynamic} instruction format</h3>
|
||||
* In bytecode, an {@code invokedynamic} instruction is formatted as five bytes.
|
||||
* The first byte is the opcode 186 (hexadecimal {@code BA}).
|
||||
* The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions).
|
||||
* The final two bytes are reserved for future use and required to be zero.
|
||||
* The constant pool reference of an {@code invokedynamic} instruction is to a entry
|
||||
* with tag {@code CONSTANT_InvokeDynamic} (decimal 18). See below for its format.
|
||||
* The entry specifies the following information:
|
||||
* <ul>
|
||||
* <li>a bootstrap method (a {@link java.lang.invoke.MethodHandle MethodHandle} constant)</li>
|
||||
* <li>the dynamic invocation name (a UTF8 string)</li>
|
||||
* <li>the argument and return types of the call (encoded as a type descriptor in a UTF8 string)</li>
|
||||
* <li>optionally, a sequence of additional <em>static arguments</em> to the bootstrap method ({@code ldc}-type constants)</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Each instance of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
|
||||
* Multiple instances of an {@code invokedynamic} instruction can share a single
|
||||
* {@code CONSTANT_InvokeDynamic} entry.
|
||||
* In any case, distinct call sites always have distinct linkage state.
|
||||
* <p>
|
||||
* Each occurrence of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
|
||||
* <h3><a name="indyinsn"></a>{@code invokedynamic} instructions</h3>
|
||||
* A dynamic call site is originally in an unlinked state. In this state, there is
|
||||
* no target method for the call site to invoke.
|
||||
* A dynamic call site is linked by means of a bootstrap method,
|
||||
* as <a href="#bsm">described below</a>.
|
||||
*
|
||||
* <h3><a name="indycon"></a>constant pool entries for {@code invokedynamic} instructions</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18),
|
||||
* it must contain exactly four more bytes after the tag.
|
||||
* These bytes are interpreted as two 16-bit indexes, in the usual {@code u2} format.
|
||||
* The first pair of bytes after the tag must be an index into a side table called the
|
||||
* <em>bootstrap method table</em>, which is stored in the {@code BootstrapMethods}
|
||||
* attribute as <a href="#bsmattr">described below</a>.
|
||||
* The second pair of bytes must be an index to a {@code CONSTANT_NameAndType}.
|
||||
* <p>
|
||||
* The first index specifies a bootstrap method used by the associated dynamic call sites.
|
||||
* The second index specifies the method name, argument types, and return type of the dynamic call site.
|
||||
* The structure of such an entry is therefore analogous to a {@code CONSTANT_Methodref},
|
||||
* except that the bootstrap method specifier reference replaces
|
||||
* the {@code CONSTANT_Class} reference of a {@code CONSTANT_Methodref} entry.
|
||||
*
|
||||
* <h3><a name="mtcon"></a>constant pool entries for {@linkplain java.lang.invoke.MethodType method types}</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16),
|
||||
* it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8}
|
||||
* entry which represents a method type descriptor.
|
||||
* <p>
|
||||
* The JVM will ensure that on first
|
||||
* execution of an {@code ldc} instruction for this entry, a {@link java.lang.invoke.MethodType MethodType}
|
||||
* will be created which represents the type descriptor.
|
||||
* Any classes mentioned in the {@code MethodType} will be loaded if necessary,
|
||||
* but not initialized.
|
||||
* Access checking and error reporting is performed exactly as it is for
|
||||
* references by {@code ldc} instructions to {@code CONSTANT_Class} constants.
|
||||
*
|
||||
* <h3><a name="mhcon"></a>constant pool entries for {@linkplain java.lang.invoke.MethodHandle method handles}</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15),
|
||||
* it must contain exactly three more bytes. The first byte after the tag is a subtag
|
||||
* value which must be in the range 1 through 9, and the last two must be an index to a
|
||||
* {@code CONSTANT_Fieldref}, {@code CONSTANT_Methodref}, or
|
||||
* {@code CONSTANT_InterfaceMethodref} entry which represents a field or method
|
||||
* for which a method handle is to be created.
|
||||
* Furthermore, the subtag value and the type of the constant index value
|
||||
* must agree according to the table below.
|
||||
* <p>
|
||||
* The JVM will ensure that on first execution of an {@code ldc} instruction
|
||||
* for this entry, a {@link java.lang.invoke.MethodHandle MethodHandle} will be created which represents
|
||||
* the field or method reference, according to the specific mode implied by the subtag.
|
||||
* <p>
|
||||
* As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants,
|
||||
* the {@code Class} or {@code MethodType} object which reifies the field or method's
|
||||
* type is created. Any classes mentioned in this reification will be loaded if necessary,
|
||||
* but not initialized, and access checking and error reporting performed as usual.
|
||||
* <p>
|
||||
* Unlike the reflective {@code Lookup} API, there are no security manager calls made
|
||||
* when these constants are resolved.
|
||||
* <p>
|
||||
* The method handle itself will have a type and behavior determined by the subtag as follows:
|
||||
* <code>
|
||||
* <table border=1 cellpadding=5 summary="CONSTANT_MethodHandle subtypes">
|
||||
* <tr><th>N</th><th>subtag name</th><th>member</th><th>MH type</th><th>bytecode behavior</th><th>lookup expression</th></tr>
|
||||
* <tr><td>1</td><td>REF_getField</td><td>C.f:T</td><td>(C)T</td><td>getfield C.f:T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findGetter findGetter(C.class,"f",T.class)}</td></tr>
|
||||
* <tr><td>2</td><td>REF_getStatic</td><td>C.f:T</td><td>( )T</td><td>getstatic C.f:T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findStaticGetter findStaticGetter(C.class,"f",T.class)}</td></tr>
|
||||
* <tr><td>3</td><td>REF_putField</td><td>C.f:T</td><td>(C,T)void</td><td>putfield C.f:T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findSetter findSetter(C.class,"f",T.class)}</td></tr>
|
||||
* <tr><td>4</td><td>REF_putStatic</td><td>C.f:T</td><td>(T)void</td><td>putstatic C.f:T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findStaticSetter findStaticSetter(C.class,"f",T.class)}</td></tr>
|
||||
* <tr><td>5</td><td>REF_invokeVirtual</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokevirtual C.m(A*)T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}</td></tr>
|
||||
* <tr><td>6</td><td>REF_invokeStatic</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokestatic C.m(A*)T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findStatic findStatic(C.class,"m",MT)}</td></tr>
|
||||
* <tr><td>7</td><td>REF_invokeSpecial</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokespecial C.m(A*)T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findSpecial findSpecial(C.class,"m",MT,this.class)}</td></tr>
|
||||
* <tr><td>8</td><td>REF_newInvokeSpecial</td><td>C.<init>(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.<init>(A*)void</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findConstructor findConstructor(C.class,MT)}</td></tr>
|
||||
* <tr><td>9</td><td>REF_invokeInterface</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokeinterface C.m(A*)T</td>
|
||||
* <td>{@linkplain java.lang.invoke.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}</td></tr>
|
||||
* </table>
|
||||
* </code>
|
||||
* Here, the type {@code C} is taken from the {@code CONSTANT_Class} reference associated
|
||||
* with the {@code CONSTANT_NameAndType} descriptor.
|
||||
* The field name {@code f} or method name {@code m} is taken from the {@code CONSTANT_NameAndType}
|
||||
* as is the result type {@code T} and (in the case of a method or constructor) the argument type sequence
|
||||
* {@code A*}.
|
||||
* <p>
|
||||
* Each method handle constant has an equivalent instruction sequence called its <em>bytecode behavior</em>.
|
||||
* In general, creating a method handle constant can be done in exactly the same circumstances that
|
||||
* the JVM would successfully resolve the symbolic references in the bytecode behavior.
|
||||
* Also, the type of a method handle constant is such that a valid {@code invokeExact} call
|
||||
* on the method handle has exactly the same JVM stack effects as the <em>bytecode behavior</em>.
|
||||
* Finally, calling a method handle constant on a valid set of arguments has exactly the same effect
|
||||
* and returns the same result (if any) as the corresponding <em>bytecode behavior</em>.
|
||||
* <p>
|
||||
* Each method handle constant also has an equivalent reflective <em>lookup expression</em>,
|
||||
* which is a query to a method in {@link java.lang.invoke.MethodHandles.Lookup}.
|
||||
* In the example lookup method expression given in the table above, the name {@code MT}
|
||||
* stands for a {@code MethodType} built from {@code T} and the sequence of argument types {@code A*}.
|
||||
* (Note that the type {@code C} is not prepended to the query type {@code MT} even if the member is non-static.)
|
||||
* In the case of {@code findSpecial}, the name {@code this.class} refers to the class containing
|
||||
* the bytecodes.
|
||||
* <p>
|
||||
* The special name {@code <clinit>} is not allowed.
|
||||
* The special name {@code <init>} is not allowed except for subtag 8 as shown.
|
||||
* <p>
|
||||
* The JVM verifier and linker apply the same access checks and restrictions for these references as for the hypothetical
|
||||
* bytecode instructions specified in the last column of the table.
|
||||
* A method handle constant will successfully resolve to a method handle if the symbolic references
|
||||
* of the corresponding bytecode instruction(s) would also resolve successfully.
|
||||
* Otherwise, an attempt to resolve the constant will throw equivalent linkage errors.
|
||||
* In particular, method handles to
|
||||
* private and protected members can be created in exactly those classes for which the corresponding
|
||||
* normal accesses are legal.
|
||||
* <p>
|
||||
* A constant may refer to a method or constructor with the {@code varargs}
|
||||
* bit (hexadecimal {@code 0x0080}) set in its modifier bitmask.
|
||||
* The method handle constant produced for such a method behaves as if
|
||||
* it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}.
|
||||
* In other words, the constant method handle will exhibit variable arity,
|
||||
* when invoked via {@code MethodHandle.invoke}.
|
||||
* On the other hand, its behavior with respect to {@code invokeExact} will be the same
|
||||
* as if the {@code varargs} bit were not set.
|
||||
* <p>
|
||||
* Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types
|
||||
* resolve class names, they do not force class initialization.
|
||||
* Method handle constants for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic}
|
||||
* may force class initialization on their first invocation, just like the corresponding bytecodes.
|
||||
* <p>
|
||||
* The rules of section 5.4.3 of
|
||||
* <cite>The Java™ Virtual Machine Specification</cite>
|
||||
* apply to the resolution of {@code CONSTANT_MethodType}, {@code CONSTANT_MethodHandle},
|
||||
* and {@code CONSTANT_InvokeDynamic} constants,
|
||||
* by the execution of {@code invokedynamic} and {@code ldc} instructions.
|
||||
* (Roughly speaking, this means that every use of a constant pool entry
|
||||
* must lead to the same outcome.
|
||||
* If the resolution succeeds, the same object reference is produced
|
||||
* by every subsequent execution of the same instruction.
|
||||
* If the resolution of the constant causes an error to occur,
|
||||
* the same error will be re-thrown on every subsequent attempt
|
||||
* to use this particular constant.)
|
||||
* <p>
|
||||
* Constants created by the resolution of these constant pool types are not necessarily
|
||||
* interned. Except for {@code CONSTANT_Class} and {@code CONSTANT_String} entries,
|
||||
* two distinct constant pool entries might not resolve to the same reference
|
||||
* even if they contain the same symbolic reference.
|
||||
*
|
||||
* <h2><a name="bsm"></a>Bootstrap Methods</h2>
|
||||
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
|
||||
* the call site must first be <em>linked</em>.
|
||||
* Linking is accomplished by calling a <em>bootstrap method</em>
|
||||
@ -234,15 +74,14 @@
|
||||
* call site execution.
|
||||
* Linkage does not trigger class initialization.
|
||||
* <p>
|
||||
* Next, the bootstrap method call is started, with at least four values being stacked:
|
||||
* The bootstrap method is invoked on at least three values:
|
||||
* <ul>
|
||||
* <li>a {@code MethodHandle}, the resolved bootstrap method itself </li>
|
||||
* <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em> in which dynamic call site occurs </li>
|
||||
* <li>a {@code String}, the method name mentioned in the call site </li>
|
||||
* <li>a {@code MethodType}, the resolved type descriptor of the call </li>
|
||||
* <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
|
||||
* <li>optionally, between 1 and 251 additional static arguments taken from the constant pool </li>
|
||||
* </ul>
|
||||
* The method handle is then applied to the other values as if by
|
||||
* Invocation is as if by
|
||||
* {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
|
||||
* The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
|
||||
* The type of the call site's target must be exactly equal to the type
|
||||
@ -250,38 +89,15 @@
|
||||
* the bootstrap method.
|
||||
* The call site then becomes permanently linked to the dynamic call site.
|
||||
* <p>
|
||||
* As long as each bootstrap method can be correctly invoked
|
||||
* by <code>MethodHandle.invoke</code>, its detailed type is arbitrary.
|
||||
* For example, the first argument could be {@code Object}
|
||||
* instead of {@code MethodHandles.Lookup}, and the return type
|
||||
* could also be {@code Object} instead of {@code CallSite}.
|
||||
* (Note that the types and number of the stacked arguments limit
|
||||
* the legal kinds of bootstrap methods to appropriately typed
|
||||
* static methods and constructors of {@code CallSite} subclasses.)
|
||||
* <p>
|
||||
* After resolution, the linkage process may fail in a variety of ways.
|
||||
* All failures are reported by a {@link java.lang.BootstrapMethodError BootstrapMethodError},
|
||||
* As documented in the JVM specification, all failures arising from
|
||||
* the linkage of a dynamic call site are reported
|
||||
* by a {@link java.lang.BootstrapMethodError BootstrapMethodError},
|
||||
* which is thrown as the abnormal termination of the dynamic call
|
||||
* site execution.
|
||||
* The following circumstances will cause this:
|
||||
* <ul>
|
||||
* <li>the index to the bootstrap method specifier is out of range </li>
|
||||
* <li>the bootstrap method cannot be resolved </li>
|
||||
* <li>the {@code MethodType} to pass to the bootstrap method cannot be resolved </li>
|
||||
* <li>a static argument to the bootstrap method cannot be resolved
|
||||
* (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType},
|
||||
* or {@code CONSTANT_MethodHandle} argument cannot be linked) </li>
|
||||
* <li>the bootstrap method has the wrong arity,
|
||||
* causing {@code MethodHandle.invoke} to throw {@code WrongMethodTypeException} </li>
|
||||
* <li>the bootstrap method has a wrong argument or return type </li>
|
||||
* <li>the bootstrap method invocation completes abnormally </li>
|
||||
* <li>the result from the bootstrap invocation is not a reference to
|
||||
* an object of type {@link java.lang.invoke.CallSite CallSite} </li>
|
||||
* <li>the target of the {@code CallSite} does not have a target of
|
||||
* the expected {@code MethodType} </li>
|
||||
* </ul>
|
||||
* If this happens, the same error will the thrown for all subsequent
|
||||
* attempts to execute the dynamic call site.
|
||||
*
|
||||
* <h3><a name="linktime"></a>timing of linkage</h3>
|
||||
* <h3>timing of linkage</h3>
|
||||
* A dynamic call site is linked just before its first execution.
|
||||
* The bootstrap method call implementing the linkage occurs within
|
||||
* a thread that is attempting a first execution.
|
||||
@ -306,7 +122,7 @@
|
||||
* all threads. Any other bootstrap method calls are allowed to complete, but their
|
||||
* results are ignored, and their dynamic call site invocations proceed with the originally
|
||||
* chosen target object.
|
||||
*
|
||||
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* These rules do not enable the JVM to duplicate dynamic call sites,
|
||||
@ -315,64 +131,15 @@
|
||||
* just before its first invocation.
|
||||
* There is no way to undo the effect of a completed bootstrap method call.
|
||||
*
|
||||
* <h3><a name="bsmattr">the {@code BootstrapMethods} attribute </h3>
|
||||
* Each {@code CONSTANT_InvokeDynamic} entry contains an index which references
|
||||
* a bootstrap method specifier; all such specifiers are contained in a separate array.
|
||||
* This array is defined by a class attribute named {@code BootstrapMethods}.
|
||||
* The body of this attribute consists of a sequence of byte pairs, all interpreted as
|
||||
* as 16-bit counts or constant pool indexes, in the {@code u2} format.
|
||||
* The attribute body starts with a count of bootstrap method specifiers,
|
||||
* which is immediately followed by the sequence of specifiers.
|
||||
* <p>
|
||||
* Each bootstrap method specifier contains an index to a
|
||||
* {@code CONSTANT_MethodHandle} constant, which is the bootstrap
|
||||
* method itself.
|
||||
* This is followed by a count, and then a sequence (perhaps empty) of
|
||||
* indexes to <a href="#args">additional static arguments</a>
|
||||
* for the bootstrap method.
|
||||
* <p>
|
||||
* During class loading, the verifier must check the structure of the
|
||||
* {@code BootstrapMethods} attribute. In particular, each constant
|
||||
* pool index must be of the correct type. A bootstrap method index
|
||||
* must refer to a {@code CONSTANT_MethodHandle} (tag 15).
|
||||
* Every other index must refer to a valid operand of an
|
||||
* {@code ldc_w} or {@code ldc2_w} instruction (tag 3..8 or 15..16).
|
||||
*
|
||||
* <h3><a name="args">static arguments to the bootstrap method</h3>
|
||||
* An {@code invokedynamic} instruction specifies at least three arguments
|
||||
* to pass to its bootstrap method:
|
||||
* The caller class (expressed as a {@link java.lang.invoke.MethodHandles.Lookup Lookup object},
|
||||
* the name (extracted from the {@code CONSTANT_NameAndType} entry),
|
||||
* and the type (also extracted from the {@code CONSTANT_NameAndType} entry).
|
||||
* The {@code invokedynamic} instruction may specify additional metadata values
|
||||
* to pass to its bootstrap method.
|
||||
* Collectively, these values are called <em>static arguments</em> to the
|
||||
* {@code invokedynamic} instruction, because they are used once at link
|
||||
* time to determine the instruction's behavior on subsequent sets of
|
||||
* <em>dynamic arguments</em>.
|
||||
* <p>
|
||||
* Static arguments are used to communicate application-specific meta-data
|
||||
* to the bootstrap method.
|
||||
* Drawn from the constant pool, they may include references to classes, method handles,
|
||||
* strings, or numeric data that may be relevant to the task of linking that particular call site.
|
||||
* <p>
|
||||
* Static arguments are specified constant pool indexes stored in the {@code BootstrapMethods} attribute.
|
||||
* Before the bootstrap method is invoked, each index is used to compute an {@code Object}
|
||||
* reference to the indexed value in the constant pool.
|
||||
* The valid constant pool entries are listed in this table:
|
||||
* <code>
|
||||
* <table border=1 cellpadding=5 summary="Static argument types">
|
||||
* <tr><th>entry type</th><th>argument type</th><th>argument value</th></tr>
|
||||
* <tr><td>CONSTANT_String</td><td><code>java.lang.String</code></td><td>the indexed string literal</td></tr>
|
||||
* <tr><td>CONSTANT_Class</td><td><code>java.lang.Class</code></td><td>the indexed class, resolved</td></tr>
|
||||
* <tr><td>CONSTANT_Integer</td><td><code>java.lang.Integer</code></td><td>the indexed int value</td></tr>
|
||||
* <tr><td>CONSTANT_Long</td><td><code>java.lang.Long</code></td><td>the indexed long value</td></tr>
|
||||
* <tr><td>CONSTANT_Float</td><td><code>java.lang.Float</code></td><td>the indexed float value</td></tr>
|
||||
* <tr><td>CONSTANT_Double</td><td><code>java.lang.Double</code></td><td>the indexed double value</td></tr>
|
||||
* <tr><td>CONSTANT_MethodHandle</td><td><code>java.lang.invoke.MethodHandle</code></td><td>the indexed method handle constant</td></tr>
|
||||
* <tr><td>CONSTANT_MethodType</td><td><code>java.lang.invoke.MethodType</code></td><td>the indexed method type constant</td></tr>
|
||||
* </table>
|
||||
* </code>
|
||||
* <h3>types of bootstrap methods</h3>
|
||||
* As long as each bootstrap method can be correctly invoked
|
||||
* by {@code MethodHandle.invoke}, its detailed type is arbitrary.
|
||||
* For example, the first argument could be {@code Object}
|
||||
* instead of {@code MethodHandles.Lookup}, and the return type
|
||||
* could also be {@code Object} instead of {@code CallSite}.
|
||||
* (Note that the types and number of the stacked arguments limit
|
||||
* the legal kinds of bootstrap methods to appropriately typed
|
||||
* static methods and constructors of {@code CallSite} subclasses.)
|
||||
* <p>
|
||||
* If a given {@code invokedynamic} instruction specifies no static arguments,
|
||||
* the instruction's bootstrap method will be invoked on three arguments,
|
||||
@ -380,7 +147,8 @@
|
||||
* If the {@code invokedynamic} instruction specifies one or more static arguments,
|
||||
* those values will be passed as additional arguments to the method handle.
|
||||
* (Note that because there is a limit of 255 arguments to any method,
|
||||
* at most 252 extra arguments can be supplied.)
|
||||
* at most 251 extra arguments can be supplied, since the bootstrap method
|
||||
* handle itself and its first three arguments must also be stacked.)
|
||||
* The bootstrap method will be invoked as if by either {@code MethodHandle.invoke}
|
||||
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
|
||||
* <p>
|
||||
@ -390,12 +158,11 @@
|
||||
* then some or all of the arguments specified here may be collected into a trailing array parameter.
|
||||
* (This is not a special rule, but rather a useful consequence of the interaction
|
||||
* between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
|
||||
* and the {@code java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
|
||||
* and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
|
||||
* <p>
|
||||
* Given these rules, here are examples of legal bootstrap method declarations,
|
||||
* given various numbers {@code N} of extra arguments.
|
||||
* The first rows (marked {@code *}) will work for any number of extra arguments.
|
||||
* <code>
|
||||
* <table border=1 cellpadding=5 summary="Static argument types">
|
||||
* <tr><th>N</th><th>sample bootstrap method</th></tr>
|
||||
* <tr><td>*</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
|
||||
@ -408,7 +175,6 @@
|
||||
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)</code></td></tr>
|
||||
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)</code></td></tr>
|
||||
* </table>
|
||||
* </code>
|
||||
* The last example assumes that the extra arguments are of type
|
||||
* {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively.
|
||||
* The second-to-last example assumes that all extra arguments are of type
|
||||
@ -431,34 +197,6 @@
|
||||
* since each call site could be given its own unique bootstrap method.
|
||||
* Such a practice is likely to produce large class files and constant pools.
|
||||
*
|
||||
* <h2><a name="structs"></a>Structure Summary</h2>
|
||||
* <blockquote><pre>// summary of constant and attribute structures
|
||||
struct CONSTANT_MethodHandle_info {
|
||||
u1 tag = 15;
|
||||
u1 reference_kind; // 1..8 (one of REF_invokeVirtual, etc.)
|
||||
u2 reference_index; // index to CONSTANT_Fieldref or *Methodref
|
||||
}
|
||||
struct CONSTANT_MethodType_info {
|
||||
u1 tag = 16;
|
||||
u2 descriptor_index; // index to CONSTANT_Utf8, as in NameAndType
|
||||
}
|
||||
struct CONSTANT_InvokeDynamic_info {
|
||||
u1 tag = 18;
|
||||
u2 bootstrap_method_attr_index; // index into BootstrapMethods_attr
|
||||
u2 name_and_type_index; // index to CONSTANT_NameAndType, as in Methodref
|
||||
}
|
||||
struct BootstrapMethods_attr {
|
||||
u2 name; // CONSTANT_Utf8 = "BootstrapMethods"
|
||||
u4 size;
|
||||
u2 bootstrap_method_count;
|
||||
struct bootstrap_method_specifier {
|
||||
u2 bootstrap_method_ref; // index to CONSTANT_MethodHandle
|
||||
u2 bootstrap_argument_count;
|
||||
u2 bootstrap_arguments[bootstrap_argument_count]; // constant pool indexes
|
||||
} bootstrap_methods[bootstrap_method_count];
|
||||
}
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
@ -198,27 +198,30 @@ public class ValueConversions {
|
||||
return unbox(Wrapper.forPrimitiveType(type), true, false);
|
||||
}
|
||||
|
||||
static private final Integer ZERO_INT = 0, ONE_INT = 1;
|
||||
|
||||
/// Primitive conversions
|
||||
public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
|
||||
// Maybe merge this code with Wrapper.convert/cast.
|
||||
Number res = null;
|
||||
if (x == null) {
|
||||
if (!cast) return null;
|
||||
x = wrap.zero();
|
||||
return ZERO_INT;
|
||||
}
|
||||
if (x instanceof Number) {
|
||||
res = (Number) x;
|
||||
} else if (x instanceof Boolean) {
|
||||
res = ((boolean)x ? 1 : 0);
|
||||
res = ((boolean)x ? ONE_INT : ZERO_INT);
|
||||
} else if (x instanceof Character) {
|
||||
res = (int)(char)x;
|
||||
} else {
|
||||
// this will fail with the required ClassCastException:
|
||||
res = (Number) x;
|
||||
}
|
||||
if (!cast && !wrap.isConvertibleFrom(Wrapper.forWrapperType(x.getClass())))
|
||||
Wrapper xwrap = Wrapper.findWrapperType(x.getClass());
|
||||
if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap))
|
||||
// this will fail with the required ClassCastException:
|
||||
res = (Number) wrap.wrapperType().cast(x);
|
||||
return (Number) wrap.wrapperType().cast(x);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -154,9 +154,10 @@ public class VerifyAccess {
|
||||
* @return whether they are in the same package
|
||||
*/
|
||||
public static boolean isSamePackage(Class<?> class1, Class<?> class2) {
|
||||
assert(!class1.isArray() && !class2.isArray());
|
||||
if (class1 == class2)
|
||||
return true;
|
||||
if (!loadersAreRelated(class1.getClassLoader(), class2.getClassLoader()))
|
||||
if (!loadersAreRelated(class1.getClassLoader(), class2.getClassLoader(), false))
|
||||
return false;
|
||||
String name1 = class1.getName(), name2 = class2.getName();
|
||||
int dot = name1.lastIndexOf('.');
|
||||
@ -169,6 +170,16 @@ public class VerifyAccess {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return the package name for this class.
|
||||
*/
|
||||
public static String getPackageName(Class<?> cls) {
|
||||
assert(!cls.isArray());
|
||||
String name = cls.getName();
|
||||
int dot = name.lastIndexOf('.');
|
||||
if (dot < 0) return "";
|
||||
return name.substring(0, dot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if two classes are defined as part of the same package member (top-level class).
|
||||
* If this is true, they can share private access with each other.
|
||||
@ -193,18 +204,33 @@ public class VerifyAccess {
|
||||
return pkgmem;
|
||||
}
|
||||
|
||||
private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2) {
|
||||
if (loader1 == loader2 || loader1 == null || loader2 == null) {
|
||||
private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2,
|
||||
boolean loader1MustBeParent) {
|
||||
if (loader1 == loader2 || loader1 == null
|
||||
|| (loader2 == null && !loader1MustBeParent)) {
|
||||
return true;
|
||||
}
|
||||
for (ClassLoader scan1 = loader1;
|
||||
scan1 != null; scan1 = scan1.getParent()) {
|
||||
if (scan1 == loader2) return true;
|
||||
}
|
||||
for (ClassLoader scan2 = loader2;
|
||||
scan2 != null; scan2 = scan2.getParent()) {
|
||||
if (scan2 == loader1) return true;
|
||||
}
|
||||
if (loader1MustBeParent) return false;
|
||||
// see if loader2 is a parent of loader1:
|
||||
for (ClassLoader scan1 = loader1;
|
||||
scan1 != null; scan1 = scan1.getParent()) {
|
||||
if (scan1 == loader2) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the class loader of parentClass identical to, or an ancestor of,
|
||||
* the class loader of childClass?
|
||||
* @param parentClass
|
||||
* @param childClass
|
||||
* @return whether parentClass precedes or equals childClass in class loader order
|
||||
*/
|
||||
public static boolean classLoaderIsAncestor(Class<?> parentClass, Class<?> childClass) {
|
||||
return loadersAreRelated(parentClass.getClassLoader(), childClass.getClassLoader(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ public enum Wrapper {
|
||||
* <li>any type converted to {@code void} (i.e., dropping a method call's value)
|
||||
* <li>boxing conversion followed by widening reference conversion to {@code Object}
|
||||
* </ul>
|
||||
* These are the cases allowed by MethodHandle.asType and convertArguments.
|
||||
* These are the cases allowed by MethodHandle.asType.
|
||||
*/
|
||||
public boolean isConvertibleFrom(Wrapper source) {
|
||||
if (this == source) return true;
|
||||
|
||||
@ -164,6 +164,7 @@ public class Test6998541 {
|
||||
private static boolean canDoAsType(Class<?> src, Class<?> dst) {
|
||||
if (src == dst) return true;
|
||||
if (dst == void.class) return true;
|
||||
if (src == void.class) return true; // allow void->zero
|
||||
if (!src.isPrimitive() || !dst.isPrimitive()) return true;
|
||||
// primitive conversion works for asType only when it's widening
|
||||
if (src == boolean.class || dst == boolean.class) return false;
|
||||
@ -451,7 +452,6 @@ public class Test6998541 {
|
||||
private final static MethodHandle mh_dv = mh(double.class );
|
||||
|
||||
private static void void2prim(int i) throws Throwable {
|
||||
if (!DO_CASTS) return;
|
||||
assertEquals( false, (boolean) mh_zv.invokeExact()); // void -> boolean
|
||||
assertEquals((byte) 0, (byte) mh_bv.invokeExact()); // void -> byte
|
||||
assertEquals((char) 0, (char) mh_cv.invokeExact()); // void -> char
|
||||
@ -463,15 +463,7 @@ public class Test6998541 {
|
||||
}
|
||||
|
||||
private static void void2prim_invalid(double x) throws Throwable {
|
||||
if (DO_CASTS) return;
|
||||
try { assertEquals( false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> boolean
|
||||
try { assertEquals((byte) 0, (byte) mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> byte
|
||||
try { assertEquals((char) 0, (char) mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> char
|
||||
try { assertEquals((short) 0, (short) mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> short
|
||||
try { assertEquals( 0, (int) mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> int
|
||||
try { assertEquals( 0L, (long) mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> long
|
||||
try { assertEquals( 0.0f, (float) mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> float
|
||||
try { assertEquals( 0.0d, (double) mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> double
|
||||
// no cases
|
||||
}
|
||||
|
||||
private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); }
|
||||
|
||||
@ -106,8 +106,10 @@ public class InvokeDynamicPrintArgs {
|
||||
"Done printing argument lists."
|
||||
};
|
||||
|
||||
private static boolean doPrint = true;
|
||||
private static void printArgs(Object bsmInfo, Object... args) {
|
||||
System.out.println(bsmInfo+Arrays.deepToString(args));
|
||||
String message = bsmInfo+Arrays.deepToString(args);
|
||||
if (doPrint) System.out.println(message);
|
||||
}
|
||||
private static MethodHandle MH_printArgs() throws ReflectiveOperationException {
|
||||
shouldNotCallThis();
|
||||
@ -129,11 +131,48 @@ public class InvokeDynamicPrintArgs {
|
||||
return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
|
||||
}
|
||||
|
||||
private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException {
|
||||
/* Example of a constant call site with user-data.
|
||||
* In this case, the user data is exactly the BSM data.
|
||||
* Note that a CCS with user data must use the "hooked" constructor
|
||||
* to bind the CCS itself into the resulting target.
|
||||
* A normal constructor would not allow a circular relation
|
||||
* between the CCS and its target.
|
||||
*/
|
||||
public static class PrintingCallSite extends ConstantCallSite {
|
||||
final Lookup caller;
|
||||
final String name;
|
||||
final Object[] staticArgs;
|
||||
|
||||
PrintingCallSite(Lookup caller, String name, MethodType type, Object... staticArgs) throws Throwable {
|
||||
super(type, MH_createTarget());
|
||||
this.caller = caller;
|
||||
this.name = name;
|
||||
this.staticArgs = staticArgs;
|
||||
}
|
||||
|
||||
public MethodHandle createTarget() {
|
||||
try {
|
||||
return lookup().bind(this, "runTarget", genericMethodType(0, true)).asType(type());
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Object runTarget(Object... dynamicArgs) {
|
||||
List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type()));
|
||||
bsmInfo.addAll(Arrays.asList(staticArgs));
|
||||
printArgs(bsmInfo, dynamicArgs);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MethodHandle MH_createTarget() throws ReflectiveOperationException {
|
||||
shouldNotCallThis();
|
||||
return lookup().findVirtual(lookup().lookupClass(), "createTarget", methodType(MethodHandle.class));
|
||||
}
|
||||
}
|
||||
private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws Throwable {
|
||||
// ignore caller and name, but match the type:
|
||||
List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type));
|
||||
bsmInfo.addAll(Arrays.asList((Object[])arg));
|
||||
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
|
||||
return new PrintingCallSite(caller, name, type, arg);
|
||||
}
|
||||
private static MethodType MT_bsm2() {
|
||||
shouldNotCallThis();
|
||||
@ -146,33 +185,33 @@ public class InvokeDynamicPrintArgs {
|
||||
|
||||
private static MethodHandle INDY_nothing() throws Throwable {
|
||||
shouldNotCallThis();
|
||||
return ((CallSite) MH_bsm().invokeGeneric(lookup(),
|
||||
return ((CallSite) MH_bsm().invoke(lookup(),
|
||||
"nothing", methodType(void.class)
|
||||
)).dynamicInvoker();
|
||||
}
|
||||
private static MethodHandle INDY_foo() throws Throwable {
|
||||
shouldNotCallThis();
|
||||
return ((CallSite) MH_bsm().invokeGeneric(lookup(),
|
||||
return ((CallSite) MH_bsm().invoke(lookup(),
|
||||
"foo", methodType(void.class, String.class)
|
||||
)).dynamicInvoker();
|
||||
}
|
||||
private static MethodHandle INDY_bar() throws Throwable {
|
||||
shouldNotCallThis();
|
||||
return ((CallSite) MH_bsm2().invokeGeneric(lookup(),
|
||||
return ((CallSite) MH_bsm2().invoke(lookup(),
|
||||
"bar", methodType(void.class, String.class, int.class)
|
||||
, Void.class, "void type!", 1, 234.5F, 67.5, (long)89
|
||||
)).dynamicInvoker();
|
||||
}
|
||||
private static MethodHandle INDY_bar2() throws Throwable {
|
||||
shouldNotCallThis();
|
||||
return ((CallSite) MH_bsm2().invokeGeneric(lookup(),
|
||||
return ((CallSite) MH_bsm2().invoke(lookup(),
|
||||
"bar2", methodType(void.class, String.class, int.class)
|
||||
, Void.class, "void type!", 1, 234.5F, 67.5, (long)89
|
||||
)).dynamicInvoker();
|
||||
}
|
||||
private static MethodHandle INDY_baz() throws Throwable {
|
||||
shouldNotCallThis();
|
||||
return ((CallSite) MH_bsm2().invokeGeneric(lookup(),
|
||||
return ((CallSite) MH_bsm2().invoke(lookup(),
|
||||
"baz", methodType(void.class, String.class, int.class, double.class)
|
||||
, 1234.5
|
||||
)).dynamicInvoker();
|
||||
|
||||
@ -314,7 +314,7 @@ public class InvokeGenericTest {
|
||||
ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList());
|
||||
Collections.fill(argTypes.subList(beg, end), argType);
|
||||
MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
|
||||
return MethodHandles.convertArguments(target, ttype2);
|
||||
return target.asType(ttype2);
|
||||
}
|
||||
|
||||
// This lookup is good for all members in and under InvokeGenericTest.
|
||||
@ -378,7 +378,7 @@ public class InvokeGenericTest {
|
||||
String[] args = { "one", "two" };
|
||||
MethodHandle mh = callable(Object.class, String.class);
|
||||
Object res; List resl;
|
||||
res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]);
|
||||
res = resl = (List) mh.invoke((String)args[0], (Object)args[1]);
|
||||
//System.out.println(res);
|
||||
assertEquals(Arrays.asList(args), res);
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
$ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \
|
||||
$DAVINCI/sources/jdk/test/java/lang/invoke/JavaDocExamplesTest.java
|
||||
$ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \
|
||||
-Dtest.java.lang.invoke.JavaDocExamplesTest.verbosity=1 \
|
||||
-DJavaDocExamplesTest.verbosity=1 \
|
||||
test.java.lang.invoke.JavaDocExamplesTest
|
||||
----
|
||||
*/
|
||||
@ -45,12 +45,10 @@ import java.lang.invoke.*;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
|
||||
/**
|
||||
@ -60,11 +58,29 @@ public class JavaDocExamplesTest {
|
||||
/** Wrapper for running the JUnit tests in this module.
|
||||
* Put JUnit on the classpath!
|
||||
*/
|
||||
public static void main(String... ignore) {
|
||||
org.junit.runner.JUnitCore.runClasses(JavaDocExamplesTest.class);
|
||||
public static void main(String... ignore) throws Throwable {
|
||||
System.out.println("can run this as:");
|
||||
System.out.println("$ java org.junit.runner.JUnitCore "+JavaDocExamplesTest.class.getName());
|
||||
new JavaDocExamplesTest().run();
|
||||
}
|
||||
public void run() throws Throwable {
|
||||
testFindVirtual();
|
||||
testPermuteArguments();
|
||||
testDropArguments();
|
||||
testFilterArguments();
|
||||
testFoldArguments();
|
||||
testMethodHandlesSummary();
|
||||
testAsSpreader();
|
||||
testAsCollector();
|
||||
testAsVarargsCollector();
|
||||
testAsFixedArity();
|
||||
testAsTypeCornerCases();
|
||||
testMutableCallSite();
|
||||
}
|
||||
// How much output?
|
||||
static int verbosity = Integer.getInteger("test.java.lang.invoke.JavaDocExamplesTest.verbosity", 0);
|
||||
static final Class<?> THIS_CLASS = JavaDocExamplesTest.class;
|
||||
static int verbosity = Integer.getInteger(THIS_CLASS.getSimpleName()+".verbosity", 0);
|
||||
|
||||
|
||||
{}
|
||||
static final private Lookup LOOKUP = lookup();
|
||||
@ -74,17 +90,23 @@ static final private Lookup LOOKUP = lookup();
|
||||
// "hashCode", methodType(int.class));
|
||||
|
||||
// form required if ReflectiveOperationException is intercepted:
|
||||
static final private MethodHandle CONCAT_2, HASHCODE_2;
|
||||
static final private MethodHandle CONCAT_2, HASHCODE_2, ADD_2, SUB_2;
|
||||
static {
|
||||
try {
|
||||
Class<?> THIS_CLASS = LOOKUP.lookupClass();
|
||||
CONCAT_2 = LOOKUP.findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
HASHCODE_2 = LOOKUP.findVirtual(Object.class,
|
||||
"hashCode", methodType(int.class));
|
||||
ADD_2 = LOOKUP.findStatic(THIS_CLASS, "add", methodType(int.class, int.class, int.class));
|
||||
SUB_2 = LOOKUP.findStatic(THIS_CLASS, "sub", methodType(int.class, int.class, int.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
static int add(int x, int y) { return x + y; }
|
||||
static int sub(int x, int y) { return x - y; }
|
||||
|
||||
{}
|
||||
|
||||
@Test public void testFindVirtual() throws Throwable {
|
||||
@ -101,6 +123,39 @@ assertEquals("xy".hashCode(), (int) HASHCODE_2.invokeExact((Object)"xy"));
|
||||
assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy"));
|
||||
{}
|
||||
}
|
||||
|
||||
@Test public void testPermuteArguments() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodType intfn1 = methodType(int.class, int.class);
|
||||
MethodType intfn2 = methodType(int.class, int.class, int.class);
|
||||
MethodHandle sub = SUB_2;// ... {int x, int y => x-y} ...;
|
||||
assert(sub.type().equals(intfn2));
|
||||
MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
|
||||
MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
|
||||
assert((int)rsub.invokeExact(1, 100) == 99);
|
||||
MethodHandle add = ADD_2;// ... {int x, int y => x+y} ...;
|
||||
assert(add.type().equals(intfn2));
|
||||
MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
|
||||
assert(twice.type().equals(intfn1));
|
||||
assert((int)twice.invokeExact(21) == 42);
|
||||
}}
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
assertEquals("xy", (String) cat.invokeExact("x", "y"));
|
||||
MethodHandle d0 = dropArguments(cat, 0, String.class);
|
||||
assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
|
||||
MethodHandle d1 = dropArguments(cat, 1, String.class);
|
||||
assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
|
||||
MethodHandle d2 = dropArguments(cat, 2, String.class);
|
||||
assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
|
||||
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
|
||||
assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testDropArguments() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
@ -145,6 +200,21 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testFoldArguments() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
|
||||
"println", methodType(void.class, String.class))
|
||||
.bindTo(System.out);
|
||||
MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
|
||||
MethodHandle catTrace = foldArguments(cat, trace);
|
||||
// also prints "boo":
|
||||
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
}}
|
||||
}
|
||||
|
||||
static void assertEquals(Object exp, Object act) {
|
||||
if (verbosity > 0)
|
||||
System.out.println("result: "+act);
|
||||
@ -162,24 +232,24 @@ mt = MethodType.methodType(String.class, char.class, char.class);
|
||||
mh = lookup.findVirtual(String.class, "replace", mt);
|
||||
s = (String) mh.invokeExact("daddy",'d','n');
|
||||
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
|
||||
assert(s.equals("nanny"));
|
||||
assertEquals(s, "nanny");
|
||||
// weakly typed invocation (using MHs.invoke)
|
||||
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
|
||||
assert(s.equals("savvy"));
|
||||
assertEquals(s, "savvy");
|
||||
// mt is (Object[])List
|
||||
mt = MethodType.methodType(java.util.List.class, Object[].class);
|
||||
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
||||
assert(mh.isVarargsCollector());
|
||||
x = mh.invoke("one", "two");
|
||||
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||
assert(x.equals(java.util.Arrays.asList("one","two")));
|
||||
assertEquals(x, java.util.Arrays.asList("one","two"));
|
||||
// mt is (Object,Object,Object)Object
|
||||
mt = MethodType.genericMethodType(3);
|
||||
mh = mh.asType(mt);
|
||||
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
|
||||
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
assert(x.equals(java.util.Arrays.asList(1,2,3)));
|
||||
// mt is { => int}
|
||||
assertEquals(x, java.util.Arrays.asList(1,2,3));
|
||||
// mt is ()int
|
||||
mt = MethodType.methodType(int.class);
|
||||
mh = lookup.findVirtual(java.util.List.class, "size", mt);
|
||||
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
|
||||
@ -193,37 +263,239 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testAsSpreader() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle equals = publicLookup()
|
||||
.findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
|
||||
assert( (boolean) equals.invokeExact("me", (Object)"me"));
|
||||
assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
|
||||
// spread both arguments from a 2-array:
|
||||
MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
|
||||
assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
|
||||
assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
|
||||
// spread both arguments from a String array:
|
||||
MethodHandle eq2s = equals.asSpreader(String[].class, 2);
|
||||
assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
|
||||
assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
|
||||
// spread second arguments from a 1-array:
|
||||
MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
|
||||
assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
|
||||
assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
|
||||
// spread no arguments from a 0-array or null:
|
||||
MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
|
||||
assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
|
||||
assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
|
||||
// asSpreader and asCollector are approximate inverses:
|
||||
for (int n = 0; n <= 2; n++) {
|
||||
for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
|
||||
MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
|
||||
assert( (boolean) equals2.invokeWithArguments("me", "me"));
|
||||
assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
|
||||
}
|
||||
}
|
||||
MethodHandle caToString = publicLookup()
|
||||
.findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
|
||||
assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
|
||||
MethodHandle caString3 = caToString.asCollector(char[].class, 3);
|
||||
assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
|
||||
MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
|
||||
assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testAsCollector() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle deepToString = publicLookup()
|
||||
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
|
||||
assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"}));
|
||||
MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
|
||||
assertEquals(methodType(String.class, Object.class), ts1.type());
|
||||
//assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL
|
||||
assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
|
||||
// arrayType can be a subtype of Object[]
|
||||
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
|
||||
assertEquals(methodType(String.class, String.class, String.class), ts2.type());
|
||||
assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
|
||||
MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
|
||||
assertEquals("[]", (String) ts0.invokeExact());
|
||||
// collectors can be nested, Lisp-style
|
||||
MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
|
||||
assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
|
||||
// arrayType can be any primitive array type
|
||||
MethodHandle bytesToString = publicLookup()
|
||||
.findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
|
||||
.asCollector(byte[].class, 3);
|
||||
assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
|
||||
MethodHandle longsToString = publicLookup()
|
||||
.findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
|
||||
.asCollector(long[].class, 1);
|
||||
assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testAsVarargsCollector() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle deepToString = publicLookup()
|
||||
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
|
||||
MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
|
||||
assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"}));
|
||||
assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"}));
|
||||
assertEquals("[won]", (String) ts1.invoke( "won" ));
|
||||
assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
|
||||
// findStatic of Arrays.asList(...) produces a variable arity method handle:
|
||||
MethodHandle asList = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
|
||||
assertEquals(methodType(List.class, Object[].class), asList.type());
|
||||
assert(asList.isVarargsCollector());
|
||||
assertEquals("[]", asList.invoke().toString());
|
||||
assertEquals("[1]", asList.invoke(1).toString());
|
||||
assertEquals("[two, too]", asList.invoke("two", "too").toString());
|
||||
Object[] argv = { "three", "thee", "tee" };
|
||||
String[] argv = { "three", "thee", "tee" };
|
||||
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
|
||||
assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
|
||||
List ls = (List) asList.invoke((Object)argv);
|
||||
assertEquals(1, ls.size());
|
||||
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testVarargsCollectorSuppression() throws Throwable {
|
||||
@Test public void testAsFixedArity() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle vamh = publicLookup()
|
||||
MethodHandle asListVar = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
||||
assert(vamh.type().equals(mh.type()));
|
||||
assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
|
||||
boolean failed = false;
|
||||
try { mh.invoke(1,2,3); }
|
||||
catch (WrongMethodTypeException ex) { failed = true; }
|
||||
assert(failed);
|
||||
MethodHandle asListFix = asListVar.asFixedArity();
|
||||
assertEquals("[1]", asListVar.invoke(1).toString());
|
||||
Exception caught = null;
|
||||
try { asListFix.invoke((Object)1); }
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof ClassCastException);
|
||||
assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
|
||||
try { asListFix.invoke("two", "too"); }
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof WrongMethodTypeException);
|
||||
Object[] argv = { "three", "thee", "tee" };
|
||||
assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
|
||||
assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
|
||||
assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
|
||||
assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testAsTypeCornerCases() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle i2s = publicLookup()
|
||||
.findVirtual(Integer.class, "toString", methodType(String.class));
|
||||
i2s = i2s.asType(i2s.type().unwrap());
|
||||
MethodHandle l2s = publicLookup()
|
||||
.findVirtual(Long.class, "toString", methodType(String.class));
|
||||
l2s = l2s.asType(l2s.type().unwrap());
|
||||
|
||||
Exception caught = null;
|
||||
try { i2s.asType(methodType(String.class, String.class)); }
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof WrongMethodTypeException);
|
||||
|
||||
i2s.asType(methodType(String.class, byte.class));
|
||||
i2s.asType(methodType(String.class, Byte.class));
|
||||
i2s.asType(methodType(String.class, Character.class));
|
||||
i2s.asType(methodType(String.class, Integer.class));
|
||||
l2s.asType(methodType(String.class, byte.class));
|
||||
l2s.asType(methodType(String.class, Byte.class));
|
||||
l2s.asType(methodType(String.class, Character.class));
|
||||
l2s.asType(methodType(String.class, Integer.class));
|
||||
l2s.asType(methodType(String.class, Long.class));
|
||||
|
||||
caught = null;
|
||||
try { i2s.asType(methodType(String.class, Long.class)); }
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof WrongMethodTypeException);
|
||||
|
||||
MethodHandle i2sGen = i2s.asType(methodType(String.class, Object.class));
|
||||
MethodHandle l2sGen = l2s.asType(methodType(String.class, Object.class));
|
||||
|
||||
i2sGen.invoke(42); // int -> Integer -> Object -> Integer -> int
|
||||
i2sGen.invoke((byte)4); // byte -> Byte -> Object -> Byte -> byte -> int
|
||||
l2sGen.invoke(42); // int -> Integer -> Object -> Integer -> int
|
||||
l2sGen.invoke((byte)4); // byte -> Byte -> Object -> Byte -> byte -> int
|
||||
l2sGen.invoke(0x420000000L);
|
||||
|
||||
caught = null;
|
||||
try { i2sGen.invoke(0x420000000L); } // long -> Long -> Object -> Integer CCE
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof ClassCastException);
|
||||
|
||||
caught = null;
|
||||
try { i2sGen.invoke("asdf"); } // String -> Object -> Integer CCE
|
||||
catch (Exception ex) { caught = ex; }
|
||||
assert(caught instanceof ClassCastException);
|
||||
{}
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testMutableCallSite() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
|
||||
MethodHandle MH_name = name.dynamicInvoker();
|
||||
MethodType MT_str1 = MethodType.methodType(String.class);
|
||||
MethodHandle MH_upcase = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "toUpperCase", MT_str1);
|
||||
MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
|
||||
name.setTarget(MethodHandles.constant(String.class, "Rocky"));
|
||||
assertEquals("ROCKY", (String) worker1.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Fred"));
|
||||
assertEquals("FRED", (String) worker1.invokeExact());
|
||||
// (mutation can be continued indefinitely)
|
||||
/*
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* The same call site may be used in several places at once.
|
||||
* <blockquote><pre>
|
||||
*/
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodHandle MH_cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
|
||||
MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
|
||||
assertEquals("Fred, dear?", (String) worker2.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Wilma"));
|
||||
assertEquals("WILMA", (String) worker1.invokeExact());
|
||||
assertEquals("Wilma, dear?", (String) worker2.invokeExact());
|
||||
{}
|
||||
}}
|
||||
}
|
||||
|
||||
@Test public void testSwitchPoint() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle MH_strcat = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
|
||||
SwitchPoint spt = new SwitchPoint();
|
||||
assert(spt.isValid());
|
||||
// the following steps may be repeated to re-use the same switch point:
|
||||
MethodHandle worker1 = MH_strcat;
|
||||
MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
|
||||
MethodHandle worker = spt.guardWithTest(worker1, worker2);
|
||||
assertEquals("method", (String) worker.invokeExact("met", "hod"));
|
||||
SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
|
||||
assert(!spt.isValid());
|
||||
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
|
||||
{}
|
||||
}}
|
||||
}
|
||||
|
||||
/* ---- TEMPLATE ----
|
||||
@Test public void testFoo() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
{}
|
||||
}}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@ -100,6 +100,31 @@ public class MethodHandlesTest {
|
||||
// ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13
|
||||
testInsertArguments(0, 0, MAX_ARG_INCREASE+10);
|
||||
}
|
||||
@Test @Ignore("permuteArguments has trouble with double slots")
|
||||
public void testFail_7() throws Throwable {
|
||||
testPermuteArguments(new Object[]{10, 200L},
|
||||
new Class<?>[]{Integer.class, long.class},
|
||||
new int[]{1,0});
|
||||
testPermuteArguments(new Object[]{10, 200L, 5000L},
|
||||
new Class<?>[]{Integer.class, long.class, long.class},
|
||||
new int[]{2,0,1}); //rot
|
||||
testPermuteArguments(new Object[]{10, 200L, 5000L},
|
||||
new Class<?>[]{Integer.class, long.class, long.class},
|
||||
new int[]{1,2,0}); //rot
|
||||
testPermuteArguments(new Object[]{10, 200L, 5000L},
|
||||
new Class<?>[]{Integer.class, long.class, long.class},
|
||||
new int[]{2,1,0}); //swap
|
||||
testPermuteArguments(new Object[]{10, 200L, 5000L},
|
||||
new Class<?>[]{Integer.class, long.class, long.class},
|
||||
new int[]{0,1,2,2}); //dup
|
||||
testPermuteArguments(new Object[]{10, 200L, 5000L},
|
||||
new Class<?>[]{Integer.class, long.class, long.class},
|
||||
new int[]{2,0,1,2});
|
||||
testPermuteArguments(new Object[]{10, 200L, 5000L},
|
||||
new Class<?>[]{Integer.class, long.class, long.class},
|
||||
new int[]{2,2,0,1});
|
||||
testPermuteArguments(4, Integer.class, 2, long.class, 6);
|
||||
}
|
||||
static final int MAX_ARG_INCREASE = 3;
|
||||
|
||||
public MethodHandlesTest() {
|
||||
@ -356,7 +381,7 @@ public class MethodHandlesTest {
|
||||
ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList());
|
||||
Collections.fill(argTypes.subList(beg, end), argType);
|
||||
MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
|
||||
return MethodHandles.convertArguments(target, ttype2);
|
||||
return target.asType(ttype2);
|
||||
}
|
||||
|
||||
// This lookup is good for all members in and under MethodHandlesTest.
|
||||
@ -1005,13 +1030,13 @@ public class MethodHandlesTest {
|
||||
Class<?> vtype = ftype;
|
||||
if (ftype != int.class) vtype = Object.class;
|
||||
if (isGetter) {
|
||||
mh = MethodHandles.convertArguments(mh, mh.type().generic()
|
||||
.changeReturnType(vtype));
|
||||
mh = mh.asType(mh.type().generic()
|
||||
.changeReturnType(vtype));
|
||||
} else {
|
||||
int last = mh.type().parameterCount() - 1;
|
||||
mh = MethodHandles.convertArguments(mh, mh.type().generic()
|
||||
.changeReturnType(void.class)
|
||||
.changeParameterType(last, vtype));
|
||||
mh = mh.asType(mh.type().generic()
|
||||
.changeReturnType(void.class)
|
||||
.changeParameterType(last, vtype));
|
||||
}
|
||||
if (f != null && f.getDeclaringClass() == HasFields.class) {
|
||||
assertEquals(f.get(fields), value); // clean to start with
|
||||
@ -1139,7 +1164,7 @@ public class MethodHandlesTest {
|
||||
// FIXME: change Integer.class and (Integer) below to int.class and (int) below.
|
||||
MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
|
||||
if (testSetter) gtype = gtype.changeReturnType(void.class);
|
||||
mh = MethodHandles.convertArguments(mh, gtype);
|
||||
mh = mh.asType(gtype);
|
||||
}
|
||||
Object sawValue, expValue;
|
||||
List<Object> model = array2list(array);
|
||||
@ -1233,11 +1258,10 @@ public class MethodHandlesTest {
|
||||
}
|
||||
|
||||
void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
|
||||
testConvert(true, false, id, rtype, name, params);
|
||||
testConvert(true, true, id, rtype, name, params);
|
||||
testConvert(true, id, rtype, name, params);
|
||||
}
|
||||
|
||||
void testConvert(boolean positive, boolean useAsType,
|
||||
void testConvert(boolean positive,
|
||||
MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
|
||||
countTest(positive);
|
||||
MethodType idType = id.type();
|
||||
@ -1265,10 +1289,7 @@ public class MethodHandlesTest {
|
||||
MethodHandle target = null;
|
||||
RuntimeException error = null;
|
||||
try {
|
||||
if (useAsType)
|
||||
target = id.asType(newType);
|
||||
else
|
||||
target = MethodHandles.convertArguments(id, newType);
|
||||
target = id.asType(newType);
|
||||
} catch (RuntimeException ex) {
|
||||
error = ex;
|
||||
}
|
||||
@ -1293,11 +1314,11 @@ public class MethodHandlesTest {
|
||||
MethodType.methodType(Object.class, String.class, Object[].class));
|
||||
vac0 = vac0.bindTo("vac");
|
||||
MethodHandle vac = vac0.asVarargsCollector(Object[].class);
|
||||
testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
|
||||
testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
|
||||
testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
|
||||
testConvert(true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
|
||||
for (Class<?> at : new Class[] { Object.class, String.class, Integer.class }) {
|
||||
testConvert(true, true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
|
||||
testConvert(true, true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
|
||||
testConvert(true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
|
||||
testConvert(true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1306,8 +1327,8 @@ public class MethodHandlesTest {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
startTest("permuteArguments");
|
||||
testPermuteArguments(4, Integer.class, 2, String.class, 0);
|
||||
//testPermuteArguments(6, Integer.class, 0, null, 30);
|
||||
//testPermuteArguments(4, Integer.class, 1, int.class, 6);
|
||||
testPermuteArguments(6, Integer.class, 0, null, 30);
|
||||
//testPermuteArguments(4, Integer.class, 2, long.class, 6); // FIXME Fail_7
|
||||
}
|
||||
public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable {
|
||||
if (verbosity >= 2)
|
||||
@ -1421,8 +1442,9 @@ public class MethodHandlesTest {
|
||||
}
|
||||
MethodType inType = MethodType.methodType(Object.class, types);
|
||||
MethodType outType = MethodType.methodType(Object.class, permTypes);
|
||||
MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType);
|
||||
MethodHandle target = varargsList(outargs).asType(outType);
|
||||
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
|
||||
if (verbosity >= 5) System.out.println("newTarget = "+newTarget);
|
||||
Object result = newTarget.invokeWithArguments(args);
|
||||
Object expected = Arrays.asList(permArgs);
|
||||
if (!expected.equals(result)) {
|
||||
@ -1666,7 +1688,7 @@ public class MethodHandlesTest {
|
||||
countTest();
|
||||
MethodHandle target = varargsList(nargs);
|
||||
MethodHandle filter = varargsList(1);
|
||||
filter = MethodHandles.convertArguments(filter, filter.type().generic());
|
||||
filter = filter.asType(filter.type().generic());
|
||||
Object[] argsToPass = randomArgs(nargs, Object.class);
|
||||
if (verbosity >= 3)
|
||||
System.out.println("filter "+target+" at "+pos+" with "+filter);
|
||||
@ -1807,7 +1829,7 @@ public class MethodHandlesTest {
|
||||
// generic invoker
|
||||
countTest();
|
||||
inv = MethodHandles.invoker(type);
|
||||
if (nargs <= 3) {
|
||||
if (nargs <= 3 && type == type.generic()) {
|
||||
calledLog.clear();
|
||||
switch (nargs) {
|
||||
case 0:
|
||||
@ -1833,10 +1855,16 @@ public class MethodHandlesTest {
|
||||
// varargs invoker #0
|
||||
calledLog.clear();
|
||||
inv = MethodHandles.spreadInvoker(type, 0);
|
||||
result = inv.invokeExact(target, args);
|
||||
if (type.returnType() == Object.class) {
|
||||
result = inv.invokeExact(target, args);
|
||||
} else if (type.returnType() == void.class) {
|
||||
result = null; inv.invokeExact(target, args);
|
||||
} else {
|
||||
result = inv.invokeWithArguments(target, (Object) args);
|
||||
}
|
||||
if (testRetCode) assertEquals(code, result);
|
||||
assertCalled("invokee", args);
|
||||
if (nargs >= 1) {
|
||||
if (nargs >= 1 && type == type.generic()) {
|
||||
// varargs invoker #1
|
||||
calledLog.clear();
|
||||
inv = MethodHandles.spreadInvoker(type, 1);
|
||||
@ -1844,7 +1872,7 @@ public class MethodHandlesTest {
|
||||
if (testRetCode) assertEquals(code, result);
|
||||
assertCalled("invokee", args);
|
||||
}
|
||||
if (nargs >= 2) {
|
||||
if (nargs >= 2 && type == type.generic()) {
|
||||
// varargs invoker #2
|
||||
calledLog.clear();
|
||||
inv = MethodHandles.spreadInvoker(type, 2);
|
||||
@ -1852,7 +1880,7 @@ public class MethodHandlesTest {
|
||||
if (testRetCode) assertEquals(code, result);
|
||||
assertCalled("invokee", args);
|
||||
}
|
||||
if (nargs >= 3) {
|
||||
if (nargs >= 3 && type == type.generic()) {
|
||||
// varargs invoker #3
|
||||
calledLog.clear();
|
||||
inv = MethodHandles.spreadInvoker(type, 3);
|
||||
@ -1865,6 +1893,10 @@ public class MethodHandlesTest {
|
||||
countTest();
|
||||
calledLog.clear();
|
||||
inv = MethodHandles.spreadInvoker(type, k);
|
||||
MethodType expType = (type.dropParameterTypes(k, nargs)
|
||||
.appendParameterTypes(Object[].class)
|
||||
.insertParameterTypes(0, MethodHandle.class));
|
||||
assertEquals(expType, inv.type());
|
||||
List<Object> targetPlusVarArgs = new ArrayList<Object>(targetPlusArgs);
|
||||
List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
|
||||
Object[] tail = tailList.toArray();
|
||||
@ -2045,7 +2077,7 @@ public class MethodHandlesTest {
|
||||
//System.out.println("throwing with "+target+" : "+thrown);
|
||||
MethodType expectedType = MethodType.methodType(returnType, exType);
|
||||
assertEquals(expectedType, target.type());
|
||||
target = MethodHandles.convertArguments(target, target.type().generic());
|
||||
target = target.asType(target.type().generic());
|
||||
Throwable caught = null;
|
||||
try {
|
||||
Object res = target.invokeExact((Object) thrown);
|
||||
@ -2117,12 +2149,12 @@ public class MethodHandlesTest {
|
||||
if (mode.endsWith("/return")) {
|
||||
if (mode.equals("unbox/return")) {
|
||||
// fail on return to ((Integer)surprise).intValue
|
||||
surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(int.class, Object.class));
|
||||
identity = MethodHandles.convertArguments(identity, MethodType.methodType(int.class, Object.class));
|
||||
surprise = surprise.asType(MethodType.methodType(int.class, Object.class));
|
||||
identity = identity.asType(MethodType.methodType(int.class, Object.class));
|
||||
} else if (mode.equals("cast/return")) {
|
||||
// fail on return to (Integer)surprise
|
||||
surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(Integer.class, Object.class));
|
||||
identity = MethodHandles.convertArguments(identity, MethodType.methodType(Integer.class, Object.class));
|
||||
surprise = surprise.asType(MethodType.methodType(Integer.class, Object.class));
|
||||
identity = identity.asType(MethodType.methodType(Integer.class, Object.class));
|
||||
}
|
||||
} else if (mode.endsWith("/argument")) {
|
||||
MethodHandle callee = null;
|
||||
@ -2134,14 +2166,14 @@ public class MethodHandlesTest {
|
||||
callee = Surprise.BOX_IDENTITY;
|
||||
}
|
||||
if (callee != null) {
|
||||
callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1));
|
||||
callee = callee.asType(MethodType.genericMethodType(1));
|
||||
surprise = MethodHandles.filterArguments(callee, 0, surprise);
|
||||
identity = MethodHandles.filterArguments(callee, 0, identity);
|
||||
}
|
||||
}
|
||||
assertNotSame(mode, surprise, surprise0);
|
||||
identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1));
|
||||
surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1));
|
||||
identity = identity.asType(MethodType.genericMethodType(1));
|
||||
surprise = surprise.asType(MethodType.genericMethodType(1));
|
||||
Object x = 42;
|
||||
for (int i = 0; i < okCount; i++) {
|
||||
Object y = identity.invokeExact(x);
|
||||
@ -2230,14 +2262,14 @@ public class MethodHandlesTest {
|
||||
{
|
||||
MethodType mt = MethodType.methodType(void.class);
|
||||
MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt);
|
||||
Runnable proxy = MethodHandles.asInstance(mh, Runnable.class);
|
||||
Runnable proxy = MethodHandleProxies.asInterfaceInstance(Runnable.class, mh);
|
||||
proxy.run();
|
||||
assertCalled("runForRunnable");
|
||||
}
|
||||
{
|
||||
MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class);
|
||||
MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt);
|
||||
Fooable proxy = MethodHandles.asInstance(mh, Fooable.class);
|
||||
Fooable proxy = MethodHandleProxies.asInterfaceInstance(Fooable.class, mh);
|
||||
Object[] args = randomArgs(mt.parameterArray());
|
||||
Object result = proxy.foo((Fooable) args[0], args[1]);
|
||||
assertCalled("fooForFooable", args);
|
||||
@ -2251,7 +2283,7 @@ public class MethodHandlesTest {
|
||||
}) {
|
||||
MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class);
|
||||
mh = MethodHandles.insertArguments(mh, 0, ex);
|
||||
WillThrow proxy = MethodHandles.asInstance(mh, WillThrow.class);
|
||||
WillThrow proxy = MethodHandleProxies.asInterfaceInstance(WillThrow.class, mh);
|
||||
try {
|
||||
proxy.willThrow();
|
||||
System.out.println("Failed to throw: "+ex);
|
||||
@ -2279,7 +2311,7 @@ public class MethodHandlesTest {
|
||||
CharSequence.class,
|
||||
Example.class }) {
|
||||
try {
|
||||
MethodHandles.asInstance(varargsArray(0), nonSAM);
|
||||
MethodHandleProxies.asInterfaceInstance(nonSAM, varargsArray(0));
|
||||
System.out.println("Failed to throw");
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
|
||||
@ -524,6 +524,8 @@ public class Indify {
|
||||
if (verifySpecifierCount >= 0) {
|
||||
List<Object[]> specs = bootstrapMethodSpecifiers(false);
|
||||
int specsLen = (specs == null ? 0 : specs.size());
|
||||
// Pass by specsLen == 0, to help with associated (inner) classes.
|
||||
if (specsLen == 0) specsLen = verifySpecifierCount;
|
||||
if (specsLen != verifySpecifierCount) {
|
||||
throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user