7012650: implement JSR 292 EG adjustments through January 2010
Misc. EG changes and polishes (excluding 7013417) Reviewed-by: twisti
This commit is contained in:
parent
eedbedae93
commit
54e473f9f8
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -78,7 +78,7 @@ static {
|
||||
}
|
||||
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
|
||||
// ignore caller and name, but match the type:
|
||||
return new ConstantCallSite(MethodHandles.collectArguments(printArgs, type));
|
||||
return new ConstantCallSite(printArgs.asType(type));
|
||||
}
|
||||
</pre></blockquote>
|
||||
* @author John Rose, JSR 292 EG
|
||||
@ -86,6 +86,7 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam
|
||||
abstract
|
||||
public class CallSite {
|
||||
private static final Access IMPL_TOKEN = Access.getToken();
|
||||
static { MethodHandleImpl.initStatics(); }
|
||||
|
||||
// Fields used only by the JVM. Do not use or change.
|
||||
private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
|
||||
@ -125,8 +126,8 @@ public class CallSite {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the type of this call site's target.
|
||||
* Although targets may change, the call site's type can never change.
|
||||
* 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.
|
||||
* The {@code setTarget} method enforces this invariant by refusing any new target that does
|
||||
* not have the previous target's type.
|
||||
* @return the type of the current target, which is also the type of any future target
|
||||
@ -154,73 +155,40 @@ public class CallSite {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the current linkage state of the call site, a value which may change over time.
|
||||
* <p>
|
||||
* If a {@code CallSite} object is returned
|
||||
* from the bootstrap method of the {@code invokedynamic} instruction,
|
||||
* the {@code CallSite} is permanently bound to that instruction.
|
||||
* When the {@code invokedynamic} instruction is executed, the target method
|
||||
* of its associated call site object is invoked directly.
|
||||
* It is as if the instruction calls {@code getTarget} and then
|
||||
* calls {@link MethodHandle#invokeExact invokeExact} on the result.
|
||||
* <p>
|
||||
* Unless specified differently by a subclass,
|
||||
* the interactions of {@code getTarget} with memory are the same
|
||||
* as of a read from an ordinary variable, such as an array element or a
|
||||
* non-volatile, non-final field.
|
||||
* <p>
|
||||
* In particular, the current thread may choose to reuse the result
|
||||
* of a previous read of the target from memory, and may fail to see
|
||||
* a recent update to the target by another thread.
|
||||
* <p>
|
||||
* In a {@linkplain ConstantCallSite constant call site}, the {@code getTarget} method behaves
|
||||
* like a read from a {@code final} field of the {@code CallSite}.
|
||||
* <p>
|
||||
* In a {@linkplain VolatileCallSite volatile call site}, the {@code getTarget} method behaves
|
||||
* like a read from a {@code volatile} field of the {@code CallSite}.
|
||||
* <p>
|
||||
* This method may not be overridden by application code.
|
||||
* Returns the target method of the call site, according to the
|
||||
* behavior defined by this call site's specific class.
|
||||
* The immediate subclasses of {@code CallSite} document the
|
||||
* class-specific behaviors of this method.
|
||||
*
|
||||
* @return the current linkage state of the call site, its target method handle
|
||||
* @see ConstantCallSite
|
||||
* @see VolatileCallSite
|
||||
* @see #setTarget
|
||||
* @see ConstantCallSite#getTarget
|
||||
* @see MutableCallSite#getTarget
|
||||
* @see VolatileCallSite#getTarget
|
||||
*/
|
||||
public final MethodHandle getTarget() {
|
||||
return getTarget0();
|
||||
}
|
||||
public abstract MethodHandle getTarget();
|
||||
|
||||
/**
|
||||
* Privileged implementations can override this to force final or volatile semantics on getTarget.
|
||||
*/
|
||||
/*package-private*/
|
||||
MethodHandle getTarget0() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target method of this call site.
|
||||
* Updates the target method of this call site, according to the
|
||||
* behavior defined by this call site's specific class.
|
||||
* The immediate subclasses of {@code CallSite} document the
|
||||
* class-specific behaviors of this method.
|
||||
* <p>
|
||||
* Unless a subclass of CallSite documents otherwise,
|
||||
* the interactions of {@code setTarget} with memory are the same
|
||||
* as of a write to an ordinary variable, such as an array element or a
|
||||
* non-volatile, non-final field.
|
||||
* <p>
|
||||
* In particular, unrelated threads may fail to see the updated target
|
||||
* until they perform a read from memory.
|
||||
* Stronger guarantees can be created by putting appropriate operations
|
||||
* into the bootstrap method and/or the target methods used
|
||||
* at any given call site.
|
||||
* The type of the new target must be {@linkplain MethodType#equals equal to}
|
||||
* the type of the old target.
|
||||
*
|
||||
* @param newTarget the new target
|
||||
* @throws NullPointerException if the proposed new target is null
|
||||
* @throws WrongMethodTypeException if the proposed new target
|
||||
* has a method type that differs from the previous target
|
||||
* @throws UnsupportedOperationException if the call site is
|
||||
* in fact a {@link ConstantCallSite}
|
||||
* @see CallSite#getTarget
|
||||
* @see ConstantCallSite#setTarget
|
||||
* @see MutableCallSite#setTarget
|
||||
* @see VolatileCallSite#setTarget
|
||||
*/
|
||||
public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(this.target, newTarget);
|
||||
setTargetNormal(newTarget);
|
||||
}
|
||||
public abstract void setTarget(MethodHandle newTarget);
|
||||
|
||||
void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
|
||||
MethodType oldType = oldTarget.type();
|
||||
@ -236,25 +204,25 @@ public class CallSite {
|
||||
/**
|
||||
* Produce a method handle equivalent to an invokedynamic instruction
|
||||
* which has been linked to this call site.
|
||||
* <p>If this call site is a {@linkplain ConstantCallSite constant call site},
|
||||
* this method simply returns the call site's target, since that will never change.
|
||||
* <p>Otherwise, this method is equivalent to the following code:
|
||||
* <p><blockquote><pre>
|
||||
* <p>
|
||||
* This method is equivalent to the following code:
|
||||
* <blockquote><pre>
|
||||
* MethodHandle getTarget, invoker, result;
|
||||
* getTarget = MethodHandles.lookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
* getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
|
||||
* invoker = MethodHandles.exactInvoker(this.type());
|
||||
* result = MethodHandles.foldArguments(invoker, getTarget)
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return a method handle which always invokes this call site's current target
|
||||
*/
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
if (this instanceof ConstantCallSite) {
|
||||
return getTarget0(); // will not change dynamically
|
||||
}
|
||||
public abstract MethodHandle dynamicInvoker();
|
||||
|
||||
/*non-public*/ MethodHandle makeDynamicInvoker() {
|
||||
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
|
||||
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
|
||||
return MethodHandles.foldArguments(invoker, getTarget);
|
||||
}
|
||||
|
||||
private static final MethodHandle GET_TARGET;
|
||||
static {
|
||||
try {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 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
|
||||
@ -31,10 +31,14 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
/**
|
||||
* Lazily associate a computed value with (potentially) every class.
|
||||
* Lazily associate a computed value with (potentially) every type.
|
||||
* For example, if a dynamic language needs to construct a message dispatch
|
||||
* table for each class encountered at a message send call site,
|
||||
* it can use a {@code ClassValue} to cache information needed to
|
||||
* perform the message send quickly, for each class encountered.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class ClassValue<T> {
|
||||
public abstract class ClassValue<T> {
|
||||
/**
|
||||
* Compute the given class's derived value for this {@code ClassValue}.
|
||||
* <p>
|
||||
@ -45,61 +49,41 @@ public class ClassValue<T> {
|
||||
* but it may be invoked again if there has been a call to
|
||||
* {@link #remove remove}.
|
||||
* <p>
|
||||
* If there is no override from a subclass, this method returns
|
||||
* the result of applying the {@code ClassValue}'s {@code computeValue}
|
||||
* method handle, which was supplied at construction time.
|
||||
* If this method throws an exception, the corresponding call to {@code get}
|
||||
* will terminate abnormally with that exception, and no class value will be recorded.
|
||||
*
|
||||
* @param type the type whose class value must be computed
|
||||
* @return the newly computed value associated with this {@code ClassValue}, for the given class or interface
|
||||
* @throws UndeclaredThrowableException if the {@code computeValue} method handle invocation throws something other than a {@code RuntimeException} or {@code Error}
|
||||
* @throws UnsupportedOperationException if the {@code computeValue} method handle is null (subclasses must override)
|
||||
* @see #get
|
||||
* @see #remove
|
||||
*/
|
||||
protected T computeValue(Class<?> type) {
|
||||
if (computeValue == null)
|
||||
return null;
|
||||
try {
|
||||
return (T) (Object) computeValue.invokeGeneric(type);
|
||||
} catch (Throwable ex) {
|
||||
if (ex instanceof Error) throw (Error) ex;
|
||||
if (ex instanceof RuntimeException) throw (RuntimeException) ex;
|
||||
throw new UndeclaredThrowableException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final MethodHandle computeValue;
|
||||
|
||||
/**
|
||||
* Creates a new class value.
|
||||
* Subclasses which use this constructor must override
|
||||
* the {@link #computeValue computeValue} method,
|
||||
* since the default {@code computeValue} method requires a method handle,
|
||||
* which this constructor does not provide.
|
||||
*/
|
||||
protected ClassValue() {
|
||||
this.computeValue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new class value, whose {@link #computeValue computeValue} method
|
||||
* will return the result of {@code computeValue.invokeGeneric(type)}.
|
||||
* @throws NullPointerException if the method handle parameter is null
|
||||
*/
|
||||
public ClassValue(MethodHandle computeValue) {
|
||||
computeValue.getClass(); // trigger NPE if null
|
||||
this.computeValue = computeValue;
|
||||
}
|
||||
protected abstract T computeValue(Class<?> type);
|
||||
|
||||
/**
|
||||
* Returns the value for the given class.
|
||||
* If no value has yet been computed, it is obtained by
|
||||
* by an invocation of the {@link #computeValue computeValue} method.
|
||||
* an invocation of the {@link #computeValue computeValue} method.
|
||||
* <p>
|
||||
* The actual installation of the value on the class
|
||||
* is performed atomically.
|
||||
* At that point, if racing threads have
|
||||
* At that point, if several racing threads have
|
||||
* computed values, one is chosen, and returned to
|
||||
* all the racing threads.
|
||||
* <p>
|
||||
* The {@code type} parameter is typically a class, but it may be any type,
|
||||
* such as an interface, a primitive type (like {@code int.class}), or {@code void.class}.
|
||||
* <p>
|
||||
* In the absence of {@code remove} calls, a class value has a simple
|
||||
* state diagram: uninitialized and initialized.
|
||||
* When {@code remove} calls are made,
|
||||
* the rules for value observation are more complex.
|
||||
* See the documentation for {@link #remove remove} for more information.
|
||||
*
|
||||
* @param type the type whose class value must be computed or retrieved
|
||||
* @return the current value associated with this {@code ClassValue}, for the given class or interface
|
||||
* @throws NullPointerException if the argument is null
|
||||
* @see #remove
|
||||
* @see #computeValue
|
||||
*/
|
||||
public T get(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
@ -119,12 +103,51 @@ public class ClassValue<T> {
|
||||
* This may result in an additional invocation of the
|
||||
* {@code computeValue computeValue} method for the given class.
|
||||
* <p>
|
||||
* If racing threads perform a combination of {@code get} and {@code remove} calls,
|
||||
* the calls are serialized.
|
||||
* A value produced by a call to {@code computeValue} will be discarded, if
|
||||
* the corresponding {@code get} call was followed by a {@code remove} call
|
||||
* before the {@code computeValue} could complete.
|
||||
* In such a case, the {@code get} call will re-invoke {@code computeValue}.
|
||||
* 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
|
||||
* the alternation between uninitialized and initialized states.
|
||||
* To do this, number these states sequentially from zero, and note that
|
||||
* uninitialized (or removed) states are numbered with even numbers,
|
||||
* while initialized (or re-initialized) states have odd numbers.
|
||||
* <p>
|
||||
* When a thread {@code T} removes a class value in state {@code 2N},
|
||||
* nothing happens, since the class value is already uninitialized.
|
||||
* Otherwise, the state is advanced atomically to {@code 2N+1}.
|
||||
* <p>
|
||||
* When a thread {@code T} queries a class value in state {@code 2N},
|
||||
* the thread first attempts to initialize the class value to state {@code 2N+1}
|
||||
* by invoking {@code computeValue} and installing the resulting value.
|
||||
* <p>
|
||||
* When {@code T} attempts to install the newly computed value,
|
||||
* if the state is still at {@code 2N}, the class value will be initialized
|
||||
* with the computed value, advancing it to state {@code 2N+1}.
|
||||
* <p>
|
||||
* Otherwise, whether the new state is even or odd,
|
||||
* {@code T} will discard the newly computed value
|
||||
* and retry the {@code get} operation.
|
||||
* <p>
|
||||
* Discarding and retrying is an important proviso,
|
||||
* since otherwise {@code T} could potentially install
|
||||
* a disastrously stale value. For example:
|
||||
* <ul>
|
||||
* <li>{@code T} calls {@code CV.get(C)} and sees state {@code 2N}
|
||||
* <li>{@code T} quickly computes a time-dependent value {@code V0} and gets ready to install it
|
||||
* <li>{@code T} is hit by an unlucky paging or scheduling event, and goes to sleep for a long time
|
||||
* <li>...meanwhile, {@code T2} also calls {@code CV.get(C)} and sees state {@code 2N}
|
||||
* <li>{@code T2} quickly computes a similar time-dependent value {@code V1} and installs it on {@code CV.get(C)}
|
||||
* <li>{@code T2} (or a third thread) then calls {@code CV.remove(C)}, undoing {@code T2}'s work
|
||||
* <li> the previous actions of {@code T2} are repeated several times
|
||||
* <li> also, the relevant computed values change over time: {@code V1}, {@code V2}, ...
|
||||
* <li>...meanwhile, {@code T} wakes up and attempts to install {@code V0}; <em>this must fail</em>
|
||||
* </ul>
|
||||
* We can assume in the above scenario that {@code CV.computeValue} uses locks to properly
|
||||
* observe the time-dependent states as it computes {@code V1}, etc.
|
||||
* This does not remove the threat of a stale value, since there is a window of time
|
||||
* between the return of {@code computeValue} in {@code T} and the installation
|
||||
* of the the new value. No user synchronization is possible during this time.
|
||||
*
|
||||
* @param type the type whose class value must be removed
|
||||
* @throws NullPointerException if the argument is null
|
||||
*/
|
||||
public void remove(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
@ -137,9 +160,9 @@ public class ClassValue<T> {
|
||||
|
||||
/// Implementation...
|
||||
|
||||
/** The hash code for this type is based on the identity of the object,
|
||||
* and is well-dispersed for power-of-two tables.
|
||||
*/
|
||||
// The hash code for this type is based on the identity of the object,
|
||||
// and is well-dispersed for power-of-two tables.
|
||||
/** @deprecated This override, which is implementation-specific, will be removed for PFD. */
|
||||
public final int hashCode() { return hashCode; }
|
||||
private final int hashCode = HASH_CODES.getAndAdd(0x61c88647);
|
||||
private static final AtomicInteger HASH_CODES = new AtomicInteger();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 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
|
||||
@ -32,16 +32,46 @@ package java.dyn;
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class ConstantCallSite extends CallSite {
|
||||
/** Create a call site with a permanent target.
|
||||
/**
|
||||
* Creates a call site with a permanent target.
|
||||
* @param target the target to be permanently associated with this call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public ConstantCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an {@link UnsupportedOperationException}, because this kind of call site cannot change its target.
|
||||
* Returns the target method of the call site, which behaves
|
||||
* like a {@code final} field of the {@code ConstantCallSite}.
|
||||
* That is, the the target is always the original value passed
|
||||
* 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
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always throws an {@link UnsupportedOperationException}.
|
||||
* This kind of call site cannot change its target.
|
||||
* @param ignore a new target proposed for the call site, which is ignored
|
||||
* @throws UnsupportedOperationException because this kind of call site cannot change its target
|
||||
*/
|
||||
@Override public final void setTarget(MethodHandle ignore) {
|
||||
throw new UnsupportedOperationException("ConstantCallSite");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this call site's permanent target.
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
return getTarget();
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ package java.dyn;
|
||||
* {@linkplain BootstrapMethod bootstrap method},
|
||||
* or the bootstrap method has
|
||||
* failed to provide a
|
||||
* {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target}
|
||||
* of the correct {@linkplain MethodType method type}.
|
||||
* {@linkplain CallSite call site} with a {@linkplain CallSite#getTarget target}
|
||||
* of the correct {@linkplain MethodHandle#type method type}.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
|
@ -101,8 +101,9 @@ public class Linkage {
|
||||
/**
|
||||
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
|
||||
* Invalidate all <code>invokedynamic</code> call sites everywhere.
|
||||
* @deprecated Use {@linkplain CallSite#setTarget call site target setting}
|
||||
* and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
|
||||
* @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting},
|
||||
* {@link MutableCallSite#syncAll call site update pushing},
|
||||
* and {@link SwitchPoint#guardWithTest target switching} instead.
|
||||
*/
|
||||
public static
|
||||
Object invalidateAll() {
|
||||
@ -113,8 +114,9 @@ public class Linkage {
|
||||
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
|
||||
* Invalidate all {@code invokedynamic} call sites in the bytecodes
|
||||
* of any methods of the given class.
|
||||
* @deprecated Use {@linkplain CallSite#setTarget call site target setting}
|
||||
* and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
|
||||
* @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting},
|
||||
* {@link MutableCallSite#syncAll call site update pushing},
|
||||
* and {@link SwitchPoint#guardWithTest target switching} instead.
|
||||
*/
|
||||
public static
|
||||
Object invalidateCallerClass(Class<?> callerClass) {
|
||||
|
@ -34,7 +34,7 @@ import static java.dyn.MethodHandles.invokers; // package-private API
|
||||
import static sun.dyn.MemberName.newIllegalArgumentException; // utility
|
||||
|
||||
/**
|
||||
* A method handle is a typed, directly executable reference to a method,
|
||||
* A method handle is a typed, directly executable reference to an underlying method,
|
||||
* constructor, field, or similar low-level operation, with optional
|
||||
* transformations of arguments or return values.
|
||||
* These transformations are quite general, and include such patterns as
|
||||
@ -48,105 +48,183 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
|
||||
* will be removed before the Proposed Final Draft.
|
||||
* Also, the final version will not include any public or
|
||||
* protected constructors.</em>
|
||||
* <p>
|
||||
* Method handles are strongly typed according to signature.
|
||||
* They are not distinguished by method name or enclosing class.
|
||||
* A method handle must be invoked under a signature which matches
|
||||
* the method handle's own {@linkplain MethodType method type}.
|
||||
*
|
||||
* <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}.
|
||||
* <p>
|
||||
* Every method handle reports its type via the {@link #type type} accessor.
|
||||
* The structure of this type is a series of classes, one of which is
|
||||
* This type descriptor is a {@link java.dyn.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).
|
||||
* <p>
|
||||
* Every method handle appears as an object containing a method named
|
||||
* {@link #invokeExact invokeExact}, whose signature exactly matches
|
||||
* the method handle's type.
|
||||
* A Java method call expression, which compiles to an
|
||||
* {@code invokevirtual} instruction,
|
||||
* can invoke this method from Java source code.
|
||||
* A method handle's type controls the types of invocations it accepts,
|
||||
* and the kinds of transformations that apply to it.
|
||||
* <p>
|
||||
* Every call to a method handle specifies an intended method type,
|
||||
* which must exactly match the type of the method handle.
|
||||
* (The type is specified in the {@code invokevirtual} instruction,
|
||||
* via a {@code CONSTANT_NameAndType} constant pool entry.)
|
||||
* The call looks within the receiver object for a method
|
||||
* named {@code invokeExact} of the intended method type.
|
||||
* The call fails with a {@link WrongMethodTypeException}
|
||||
* if the method does not exist, even if there is an {@code invokeExact}
|
||||
* method of a closely similar signature.
|
||||
* As with other kinds
|
||||
* of methods in the JVM, signature matching during method linkage
|
||||
* is exact, and does not allow for language-level implicit conversions
|
||||
* such as {@code String} to {@code Object} or {@code short} to {@code int}.
|
||||
* A method handle contains a pair of special invoker methods
|
||||
* called {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}.
|
||||
* Both invoker methods provide direct access to the method handle's
|
||||
* underlying method, constructor, field, or other operation,
|
||||
* as modified by transformations of arguments and return values.
|
||||
* Both invokers accept calls which exactly match the method handle's own type.
|
||||
* The {@code invokeGeneric} invoker also accepts a range of other call types.
|
||||
* <p>
|
||||
* Method handles are immutable and have no visible state.
|
||||
* Of course, they can be bound to underlying methods or data which exhibit state.
|
||||
* With respect to the Java Memory Model, any method handle will behave
|
||||
* as if all of its (internal) fields are final variables. This means that any method
|
||||
* handle made visible to the application will always be fully formed.
|
||||
* This is true even if the method handle is published through a shared
|
||||
* variable in a data race.
|
||||
* <p>
|
||||
* Method handles cannot be subclassed by the user.
|
||||
* Implementations may (or may not) create internal subclasses of {@code MethodHandle}
|
||||
* which may be visible via the {@link java.lang.Object#getClass Object.getClass}
|
||||
* operation. The programmer should not draw conclusions about a method handle
|
||||
* from its specific class, as the method handle class hierarchy (if any)
|
||||
* may change from time to time or across implementations from different vendors.
|
||||
*
|
||||
* <h3>Method handle compilation</h3>
|
||||
* A Java method call expression naming {@code invokeExact} or {@code invokeGeneric}
|
||||
* can invoke a method handle from Java source code.
|
||||
* 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,
|
||||
* but they have an additional quality called "signature polymorphism"
|
||||
* which connects this freedom of invocation directly to the JVM execution stack.
|
||||
* <p>
|
||||
* As is usual with virtual methods, source-level calls to {@code invokeExact}
|
||||
* and {@code invokeGeneric} compile to an {@code invokevirtual} instruction.
|
||||
* More unusually, the compiler must record the actual argument types,
|
||||
* 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
|
||||
* describes the argument and return types.
|
||||
* <p>
|
||||
* To issue a complete 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}.
|
||||
* The ambiguity with the type {@code Void} is harmless, since there are no references of type
|
||||
* {@code Void} except the null reference.
|
||||
*
|
||||
* <h3>Method handle invocation</h3>
|
||||
* The first time a {@code invokevirtual} instruction is executed
|
||||
* 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 invokeGeneric}.
|
||||
* In this case, the 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
|
||||
* 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.
|
||||
* 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.
|
||||
* <p>
|
||||
* In the case of {@code invokeExact}, the type descriptor of the invocation
|
||||
* (after resolving symbolic type names) must exactly match the method type
|
||||
* of the receiving method handle.
|
||||
* In the case of {@code invokeGeneric}, the resolved type descriptor
|
||||
* must be a valid argument to the receiver's {@link #asType asType} method.
|
||||
* Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}.
|
||||
* <p>
|
||||
* After type matching, a call to {@code invokeExact} directly
|
||||
* and immediately invoke the method handle's underlying method
|
||||
* (or other behavior, as the case may be).
|
||||
* <p>
|
||||
* Each individual method handle also contains a method named
|
||||
* {@link #invokeGeneric invokeGeneric}, whose type is the same
|
||||
* as {@code invokeExact}, and is therefore also reported by
|
||||
* the {@link #type type} accessor.
|
||||
* A call to {@code invokeGeneric} works the same as a call to
|
||||
* {@code invokeExact}, if the signature specified by the caller
|
||||
* {@code invokeExact}, if the type descriptor specified by the caller
|
||||
* exactly matches the method handle's own type.
|
||||
* If there is a type mismatch, {@code invokeGeneric} attempts
|
||||
* to adjust the type of the target method handle
|
||||
* (as if by a call to {@link #asType asType})
|
||||
* to obtain an exactly invokable target.
|
||||
* to adjust the type of the receiving method handle,
|
||||
* as if by a call to {@link #asType asType},
|
||||
* to obtain an exactly invokable method handle {@code M2}.
|
||||
* This allows a more powerful negotiation of method type
|
||||
* between caller and callee.
|
||||
* <p>
|
||||
* A method handle is an unrestricted capability to call a method.
|
||||
* A method handle can be formed on a non-public method by a class
|
||||
* that has access to that method; the resulting handle can be used
|
||||
* in any place by any caller who receives a reference to it. Thus, access
|
||||
* checking is performed when the method handle is created, not
|
||||
* (as in reflection) every time it is called. Handles to non-public
|
||||
* methods, or in non-public classes, should generally be kept secret.
|
||||
* (Note: The adjusted method handle {@code M2} is not directly observable,
|
||||
* and implementations are therefore not required to materialize it.)
|
||||
*
|
||||
* <h3>Invocation checking</h3>
|
||||
* In typical programs, method handle type matching will usually succeed.
|
||||
* But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
|
||||
* either directly (in the case of {@code invokeExact}) or indirectly as if
|
||||
* by a failed call to {@code asType} (in the case of {@code invokeGeneric}).
|
||||
* <p>
|
||||
* Thus, a method type mismatch which might show up as a linkage error
|
||||
* in a statically typed program can show up as
|
||||
* a dynamic {@code WrongMethodTypeException}
|
||||
* in a program which uses method handles.
|
||||
* <p>
|
||||
* Because method types contain "live" {@code Class} objects,
|
||||
* 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
|
||||
* descriptor, as resolved in {@code L2},
|
||||
* is matched against the original callee method's 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
|
||||
* when the {@code invokevirtual} instruction is linked.
|
||||
* <p>
|
||||
* Apart from the checking of type descriptors,
|
||||
* a method handle's capability to call its underlying method is unrestricted.
|
||||
* If a method handle is formed on a non-public method by a class
|
||||
* that has access to that method, the resulting handle can be used
|
||||
* in any place by any caller who receives a reference to it.
|
||||
* <p>
|
||||
* Unlike with the Core Reflection API, where access is checked every time
|
||||
* a reflective method is invoked,
|
||||
* method handle access checking is performed
|
||||
* <a href="MethodHandles.Lookup.html#access">when the method handle is created</a>.
|
||||
* In the case of {@code ldc} (see below), access checking is performed as part of linking
|
||||
* the constant pool entry underlying the constant method handle.
|
||||
* <p>
|
||||
* Thus, handles to non-public methods, or to methods in non-public classes,
|
||||
* should generally be kept secret.
|
||||
* They should not be passed to untrusted code unless their use from
|
||||
* the untrusted code would be harmless.
|
||||
*
|
||||
* <h3>Method handle creation</h3>
|
||||
* Java code can create a method handle that directly accesses
|
||||
* any method, constructor, or field that is accessible to that code.
|
||||
* This is done via a reflective, capability-based API called
|
||||
* {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
|
||||
* For example, a static method handle can be obtained
|
||||
* from {@link java.dyn.MethodHandles.Lookup#findStatic Lookup.findStatic}.
|
||||
* There are also conversion methods from Core Reflection API objects,
|
||||
* such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}.
|
||||
* <p>
|
||||
* Bytecode in the JVM can directly call a method handle's
|
||||
* {@code invokeExact} method from an {@code invokevirtual} instruction.
|
||||
* The receiver class type must be {@code MethodHandle} and the method name
|
||||
* must be {@code invokeExact}. The signature of the invocation
|
||||
* (after resolving symbolic type names) must exactly match the method type
|
||||
* of the target method.
|
||||
* Similarly, bytecode can directly call a method handle's {@code invokeGeneric}
|
||||
* method. The signature of the invocation (after resolving symbolic type names)
|
||||
* must either exactly match the method type or be a valid argument to
|
||||
* the target's {@link #asType asType} method.
|
||||
* <p>
|
||||
* Every {@code invokeExact} and {@code invokeGeneric} method always
|
||||
* throws {@link java.lang.Throwable Throwable},
|
||||
* which is to say that there is no static restriction on what a method handle
|
||||
* can throw. Since the JVM does not distinguish between checked
|
||||
* and unchecked exceptions (other than by their class, of course),
|
||||
* 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
|
||||
* throwables locally, rethrowing only those which are legal in the context,
|
||||
* and wrapping ones which are illegal.
|
||||
* <p>
|
||||
* Bytecode in the JVM can directly obtain a method handle
|
||||
* for any accessible method from a {@code ldc} instruction
|
||||
* which refers to a {@code CONSTANT_MethodHandle} constant pool entry.
|
||||
* (Each such entry refers directly to a {@code CONSTANT_Methodref},
|
||||
* Like classes and strings, method handles that correspond to accessible
|
||||
* fields, methods, and constructors can also be represented directly
|
||||
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
|
||||
* A new type of constant pool entry, {@code CONSTANT_MethodHandle},
|
||||
* refers directly to an associated {@code CONSTANT_Methodref},
|
||||
* {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
|
||||
* constant pool entry.
|
||||
* For more details, see the <a href="package-summary.html#mhcon">package summary</a>.)
|
||||
* (For more details on method handle constants,
|
||||
* see the <a href="package-summary.html#mhcon">package summary</a>.)
|
||||
* <p>
|
||||
* Method handles produced by lookups or constant loads from methods or
|
||||
* constructors with the variable arity modifier bit ({@code 0x0080})
|
||||
* have a corresponding variable arity, as if they were defined with
|
||||
* the help of {@link #asVarargsCollector asVarargsCollector}.
|
||||
* <p>
|
||||
* Java code can also use a reflective API called
|
||||
* {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
|
||||
* for creating and calling method handles.
|
||||
* For example, a static method handle can be obtained
|
||||
* from {@link java.dyn.MethodHandles.Lookup#findStatic Lookup.findStatic}.
|
||||
* There are also bridge methods from Core Reflection API objects,
|
||||
* such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.ureflect}.
|
||||
* <p>
|
||||
* A method reference may refer either to a static or non-static method.
|
||||
* In the non-static case, the method handle type includes an explicit
|
||||
* receiver argument, prepended before any other arguments.
|
||||
@ -158,64 +236,141 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
|
||||
* When a method handle to a virtual method is invoked, the method is
|
||||
* always looked up in the receiver (that is, the first argument).
|
||||
* <p>
|
||||
* A non-virtual method handles to a specific virtual method implementation
|
||||
* A non-virtual method handle to a specific virtual method implementation
|
||||
* can also be created. These do not perform virtual lookup based on
|
||||
* receiver type. Such a method handle simulates the effect of
|
||||
* an {@code invokespecial} instruction to the same method.
|
||||
* <p>
|
||||
*
|
||||
* <h3>Usage examples</h3>
|
||||
* Here are some examples of usage:
|
||||
* <p><blockquote><pre>
|
||||
Object x, y; String s; int i;
|
||||
MethodType mt; MethodHandle mh;
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
// mt is {(char,char) => String}
|
||||
// mt is (char,char)String
|
||||
mt = MethodType.methodType(String.class, char.class, char.class);
|
||||
mh = lookup.findVirtual(String.class, "replace", mt);
|
||||
// (Ljava/lang/String;CC)Ljava/lang/String;
|
||||
s = (String) mh.invokeExact("daddy",'d','n');
|
||||
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
|
||||
assert(s.equals("nanny"));
|
||||
// weakly typed invocation (using MHs.invoke)
|
||||
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
|
||||
assert(s.equals("savvy"));
|
||||
// mt is {Object[] => List}
|
||||
// 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.invokeGeneric("one", "two");
|
||||
// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||
assert(x.equals(java.util.Arrays.asList("one","two")));
|
||||
// mt is {(Object,Object,Object) => Object}
|
||||
// mt is (Object,Object,Object)Object
|
||||
mt = MethodType.genericMethodType(3);
|
||||
mh = MethodHandles.collectArguments(mh, mt);
|
||||
// mt is {(Object,Object,Object) => Object}
|
||||
// (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
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}
|
||||
mt = MethodType.methodType(int.class);
|
||||
mh = lookup.findVirtual(java.util.List.class, "size", mt);
|
||||
// (Ljava/util/List;)I
|
||||
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
|
||||
// invokeExact(Ljava/util/List;)I
|
||||
assert(i == 3);
|
||||
mt = MethodType.methodType(void.class, String.class);
|
||||
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
|
||||
mh.invokeExact(System.out, "Hello, world.");
|
||||
// (Ljava/io/PrintStream;Ljava/lang/String;)V
|
||||
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
|
||||
* </pre></blockquote>
|
||||
* Each of the above calls generates a single invokevirtual instruction
|
||||
* with the name {@code invoke} and the type descriptors indicated in the comments.
|
||||
* The argument types are taken directly from the actual arguments,
|
||||
* while the return type is taken from the cast immediately applied to the call.
|
||||
* This cast may be to a primitive.
|
||||
* If it is missing, the type defaults to {@code Object} if the call
|
||||
* occurs in a context which uses the return value.
|
||||
* If the call occurs as a statement, a cast is impossible,
|
||||
* and there is no return type; the call is {@code void}.
|
||||
* Each of the above calls to {@code invokeExact} or {@code invokeGeneric}
|
||||
* generates a single invokevirtual instruction with
|
||||
* the type descriptor indicated in the following comment.
|
||||
*
|
||||
* <h3>Exceptions</h3>
|
||||
* The methods {@code invokeExact} and {@code invokeGeneric} are declared
|
||||
* to throw {@link java.lang.Throwable Throwable},
|
||||
* which is to say that there is no static restriction on what a method handle
|
||||
* can throw. Since the JVM does not distinguish between checked
|
||||
* and unchecked exceptions (other than by their class, of course),
|
||||
* 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
|
||||
* throwables locally, rethrowing only those which are legal in the context,
|
||||
* and wrapping ones which are illegal.
|
||||
*
|
||||
* <h3><a name="polysig"></a>Signature polymorphism</h3>
|
||||
* The unusual compilation and linkage behavior of
|
||||
* {@code invokeExact} and {@code invokeGeneric}
|
||||
* is referenced by the term <em>signature polymorphism</em>.
|
||||
* 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>
|
||||
* <em>A note on generic typing:</em> Method handles do not represent
|
||||
* their function types in terms of Java parameterized (generic) types,
|
||||
* because there are three mismatches between function types and parameterized
|
||||
* In source code, a call to a signature polymorphic method will
|
||||
* compile, regardless of the requested 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
|
||||
* 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.
|
||||
* (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.
|
||||
* Tools which determine symbolic linkage are required to accept such
|
||||
* untransformed descriptors, without reporting linkage errors.
|
||||
*
|
||||
* <h3>Interoperation between method handles and the Core Reflection API</h3>
|
||||
* Using factory methods in the {@link java.dyn.MethodHandles.Lookup Lookup} API,
|
||||
* any class member represented by a Core Reflection API object
|
||||
* can be converted to a behaviorally equivalent method handle.
|
||||
* For example, a reflective {@link java.lang.reflect.Method Method} can
|
||||
* be converted to a method handle using
|
||||
* {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}.
|
||||
* The resulting method handles generally provide more direct and efficient
|
||||
* access to the underlying class members.
|
||||
* <p>
|
||||
* As a special case,
|
||||
* when the Core Reflection API is used to view the signature polymorphic
|
||||
* methods {@code invokeExact} or {@code invokeGeneric} in this class,
|
||||
* they appear as single, non-polymorphic native methods.
|
||||
* Calls to these native methods do not result in method handle invocations.
|
||||
* Since {@code invokevirtual} instructions can natively
|
||||
* invoke method handles under any type descriptor, this reflective view conflicts
|
||||
* with the normal presentation via bytecodes.
|
||||
* Thus, these two native methods, as viewed by
|
||||
* {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
|
||||
* are placeholders only.
|
||||
* If invoked via {@link java.lang.reflect.Method#invoke Method.invoke},
|
||||
* they will throw {@code UnsupportedOperationException}.
|
||||
* <p>
|
||||
* In order to obtain an invoker method for a particular type descriptor,
|
||||
* use {@link java.dyn.MethodHandles#exactInvoker MethodHandles.exactInvoker},
|
||||
* or {@link java.dyn.MethodHandles#genericInvoker MethodHandles.genericInvoker}.
|
||||
* The {@link java.dyn.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
|
||||
* API is also able to return a method handle
|
||||
* to call {@code invokeExact} or {@code invokeGeneric},
|
||||
* for any specified type descriptor .
|
||||
*
|
||||
* <h3>Interoperation between method handles and Java generics</h3>
|
||||
* A method handle can be obtained on a method, constructor, or field
|
||||
* which is declared with Java generic types.
|
||||
* As with the Core Reflection API, the type of the method handle
|
||||
* will constructed from the erasure of the source-level type.
|
||||
* 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
|
||||
* for the {@code invokevirtual} instruction.
|
||||
* <p>
|
||||
* Method handles do not represent
|
||||
* their function-like types in terms of Java parameterized (generic) types,
|
||||
* because there are three mismatches between function-like types and parameterized
|
||||
* Java types.
|
||||
* <ol>
|
||||
* <ul>
|
||||
* <li>Method types range over all possible arities,
|
||||
* from no arguments to up to 255 of arguments (a limit imposed by the JVM).
|
||||
* Generics are not variadic, and so cannot represent this.</li>
|
||||
@ -225,29 +380,7 @@ mh.invokeExact(System.out, "Hello, world.");
|
||||
* often generic across a wide range of function types, including
|
||||
* those of multiple arities. It is impossible to represent such
|
||||
* genericity with a Java type parameter.</li>
|
||||
* </ol>
|
||||
* Signature polymorphic methods in this class appear to be documented
|
||||
* as having type parameters for return types and a parameter, but that is
|
||||
* merely a documentation convention. These type parameters do
|
||||
* not play a role in type-checking method handle invocations.
|
||||
* <p>
|
||||
* Like classes and strings, method handles that correspond to accessible
|
||||
* fields, methods, and constructors can be represented directly
|
||||
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
|
||||
* Loading such a constant causes the component classes of its type to be loaded as necessary.
|
||||
* <p>
|
||||
* Method handles cannot be subclassed by the user.
|
||||
* Implementations may (or may not) create internal subclasses of {@code MethodHandle}
|
||||
* which may be visible via the {@code java.lang.Object#getClass Object.getClass}
|
||||
* operation. The programmer should not draw conclusions about a method handle
|
||||
* from its specific class, as the method handle class hierarchy (if any)
|
||||
* may change from time to time or across implementations from different vendors.
|
||||
* <p>
|
||||
* With respect to the Java Memory Model, any method handle will behave
|
||||
* as if all of its fields are final variables. This means that any method
|
||||
* handle made visible to the application will always be fully formed.
|
||||
* This is true even if the method handle is published through a shared
|
||||
* variables in a data race.
|
||||
* </ul>
|
||||
*
|
||||
* @see MethodType
|
||||
* @see MethodHandles
|
||||
@ -259,6 +392,7 @@ public abstract class MethodHandle
|
||||
extends MethodHandleImpl
|
||||
{
|
||||
private static Access IMPL_TOKEN = Access.getToken();
|
||||
static { MethodHandleImpl.initStatics(); }
|
||||
|
||||
// interface MethodHandle<R throws X extends Exception,A...>
|
||||
// { MethodType<R throws X,A...> type(); public R invokeExact(A...) throws X; }
|
||||
@ -278,7 +412,7 @@ public abstract class MethodHandle
|
||||
* Every invocation of this method handle via {@code invokeExact} must exactly match this type.
|
||||
* @return the method handle type
|
||||
*/
|
||||
public final MethodType type() {
|
||||
public MethodType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -315,20 +449,27 @@ public abstract class MethodHandle
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method handle, allowing any caller signature, but requiring an exact signature match.
|
||||
* The signature at the call site of {@code invokeExact} must
|
||||
* Invoke 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
|
||||
* exactly match this method handle's {@link #type type}.
|
||||
* No conversions are allowed on arguments or return values.
|
||||
* @throws WrongMethodTypeException if the target's type is not identical with the caller's type signature
|
||||
* <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,
|
||||
* or indirectly via {@link java.dyn.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 Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
||||
*/
|
||||
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
* Invoke the method handle, allowing any caller signature,
|
||||
* Invoke the method handle, allowing any caller type descriptor,
|
||||
* and optionally performing conversions on arguments and return values.
|
||||
* <p>
|
||||
* If the call site signature exactly matches this method handle's {@link #type type},
|
||||
* If the call site 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
|
||||
@ -341,12 +482,19 @@ 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 signature at the call site of {@code invokeGeneric} must
|
||||
* The type descriptor at the call site of {@code invokeGeneric} 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,
|
||||
* if the callee is not a {@linkplain #asVarargsCollector variable arity collector}.
|
||||
* @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type signature
|
||||
* <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,
|
||||
* or indirectly via {@link java.dyn.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 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
|
||||
*/
|
||||
@ -390,13 +538,19 @@ public abstract class MethodHandle
|
||||
* MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
|
||||
* Object result = invoker.invokeExact(this, arguments);
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* Unlike the signature polymorphic methods {@code invokeExact} and {@code invokeGeneric},
|
||||
* {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
|
||||
* It can therefore be used as a bridge between native or reflective code and method handles.
|
||||
*
|
||||
* @param arguments the arguments to pass to the target
|
||||
* @return the result returned by the target
|
||||
* @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments
|
||||
* @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
|
||||
* @see MethodHandles#spreadInvoker
|
||||
*/
|
||||
public final Object invokeWithArguments(Object... arguments) throws Throwable {
|
||||
public Object invokeWithArguments(Object... arguments) throws Throwable {
|
||||
int argc = arguments == null ? 0 : arguments.length;
|
||||
MethodType type = type();
|
||||
if (type.parameterCount() != argc) {
|
||||
@ -404,7 +558,7 @@ public abstract class MethodHandle
|
||||
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
|
||||
}
|
||||
if (argc <= 10) {
|
||||
MethodHandle invoker = MethodHandles.invokers(type).genericInvoker();
|
||||
MethodHandle invoker = invokers(type).genericInvoker();
|
||||
switch (argc) {
|
||||
case 0: return invoker.invokeExact(this);
|
||||
case 1: return invoker.invokeExact(this,
|
||||
@ -447,15 +601,7 @@ public abstract class MethodHandle
|
||||
return invoker.invokeExact(this, arguments);
|
||||
}
|
||||
/** Equivalent to {@code invokeWithArguments(arguments.toArray())}. */
|
||||
public final Object invokeWithArguments(java.util.List<?> arguments) throws Throwable {
|
||||
return invokeWithArguments(arguments.toArray());
|
||||
}
|
||||
@Deprecated
|
||||
public final Object invokeVarargs(Object... arguments) throws Throwable {
|
||||
return invokeWithArguments(arguments);
|
||||
}
|
||||
@Deprecated
|
||||
public final Object invokeVarargs(java.util.List<?> arguments) throws Throwable {
|
||||
public Object invokeWithArguments(java.util.List<?> arguments) throws Throwable {
|
||||
return invokeWithArguments(arguments.toArray());
|
||||
}
|
||||
|
||||
@ -524,7 +670,7 @@ public abstract class MethodHandle
|
||||
* @throws WrongMethodTypeException if the implied {@code asType} call fails
|
||||
* @see #asCollector
|
||||
*/
|
||||
public final MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
|
||||
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
|
||||
MethodType oldType = type();
|
||||
@ -575,7 +721,7 @@ public abstract class MethodHandle
|
||||
* @see #asSpreader
|
||||
* @see #asVarargsCollector
|
||||
*/
|
||||
public final MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
|
||||
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
|
||||
MethodType oldType = type();
|
||||
@ -699,6 +845,7 @@ assertEquals(1, ls.size());
|
||||
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
* </pre></blockquote>
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* These rules are designed as a dynamically-typed variation
|
||||
* of the Java rules for variable arity methods.
|
||||
* In both cases, callers to a variable arity method or method handle
|
||||
@ -710,7 +857,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
* 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 signature of the call site and the type of the method handle.)
|
||||
* 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
|
||||
@ -719,9 +866,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
MethodHandle vamh = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
MethodHandle invokeExact = publicLookup()
|
||||
.findVirtual(MethodHandle.class, "invokeExact", vamh.type());
|
||||
MethodHandle mh = invokeExact.bindTo(vamh);
|
||||
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
||||
assert(vamh.type().equals(mh.type()));
|
||||
assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
|
||||
boolean failed = false;
|
||||
@ -731,12 +876,14 @@ assert(failed);
|
||||
* </pre></blockquote>
|
||||
* This transformation has no behavioral effect if the method handle is
|
||||
* not of variable arity.
|
||||
*
|
||||
* @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 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
|
||||
*/
|
||||
public MethodHandle asVarargsCollector(Class<?> arrayType) {
|
||||
Class<?> arrayElement = arrayType.getComponentType();
|
||||
@ -756,6 +903,7 @@ assert(failed);
|
||||
* which resolves to a variable arity Java method or constructor
|
||||
* </ul>
|
||||
* @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls
|
||||
* @see #asVarargsCollector
|
||||
*/
|
||||
public boolean isVarargsCollector() {
|
||||
return false;
|
||||
@ -785,7 +933,7 @@ assert(failed);
|
||||
* to the leading parameter type of the target
|
||||
* @see MethodHandles#insertArguments
|
||||
*/
|
||||
public final MethodHandle bindTo(Object x) {
|
||||
public MethodHandle bindTo(Object x) {
|
||||
return MethodHandles.insertArguments(this, 0, x);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -31,6 +31,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import sun.dyn.Access;
|
||||
import sun.dyn.Invokers;
|
||||
import sun.dyn.MethodHandleImpl;
|
||||
import sun.dyn.MethodTypeImpl;
|
||||
import sun.dyn.util.BytecodeDescriptor;
|
||||
import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
@ -41,8 +42,8 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
* and expected by a method handle caller. Method types must be properly
|
||||
* matched between a method handle and all its callers,
|
||||
* and the JVM's operations enforce this matching at, specifically
|
||||
* during calls to {@link MethodHandle#invokeExact}
|
||||
* and {@link MethodHandle#invokeGeneric}, and during execution
|
||||
* during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
|
||||
* and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution
|
||||
* of {@code invokedynamic} instructions.
|
||||
* <p>
|
||||
* The structure is a return type accompanied by any number of parameter types.
|
||||
@ -70,8 +71,9 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
* with the instructions in a class file's constant pool.
|
||||
* <p>
|
||||
* Like classes and strings, method types can also be represented directly
|
||||
* in a class file's constant pool as constants. The may be loaded by an {@code ldc}
|
||||
* instruction which refers to a suitable {@code CONSTANT_MethodType} constant pool entry.
|
||||
* in a class file's constant pool as constants.
|
||||
* A method type may be loaded by an {@code ldc} instruction which refers
|
||||
* to a suitable {@code CONSTANT_MethodType} constant pool entry.
|
||||
* The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string.
|
||||
* For more details, see the <a href="package-summary.html#mtcon">package summary</a>.
|
||||
* <p>
|
||||
@ -124,15 +126,32 @@ class MethodType {
|
||||
this.ptypes = ptypes;
|
||||
}
|
||||
|
||||
private void checkRtype(Class<?> rtype) {
|
||||
private static void checkRtype(Class<?> rtype) {
|
||||
rtype.equals(rtype); // null check
|
||||
}
|
||||
private void checkPtypes(Class<?>[] ptypes) {
|
||||
private static int checkPtype(Class<?> ptype) {
|
||||
ptype.getClass(); //NPE
|
||||
if (ptype == void.class)
|
||||
throw newIllegalArgumentException("parameter type cannot be void");
|
||||
if (ptype == double.class || ptype == long.class) return 1;
|
||||
return 0;
|
||||
}
|
||||
/** Return number of extra slots (count of long/double args). */
|
||||
private static int checkPtypes(Class<?>[] ptypes) {
|
||||
int slots = 0;
|
||||
for (Class<?> ptype : ptypes) {
|
||||
ptype.equals(ptype); // null check
|
||||
if (ptype == void.class)
|
||||
throw newIllegalArgumentException("parameter type cannot be void");
|
||||
slots += checkPtype(ptype);
|
||||
}
|
||||
checkSlotCount(ptypes.length + slots);
|
||||
return slots;
|
||||
}
|
||||
private static void checkSlotCount(int count) {
|
||||
if ((count & 0xFF) != count)
|
||||
throw newIllegalArgumentException("bad parameter count "+count);
|
||||
}
|
||||
private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
|
||||
if (num instanceof Integer) num = "bad index: "+num;
|
||||
return new IndexOutOfBoundsException(num.toString());
|
||||
}
|
||||
|
||||
static final HashMap<MethodType,MethodType> internTable
|
||||
@ -140,27 +159,39 @@ class MethodType {
|
||||
|
||||
static final Class<?>[] NO_PTYPES = {};
|
||||
|
||||
/** Find or create an instance of the given method type.
|
||||
/**
|
||||
* Find or create an instance of the given method type.
|
||||
* @param rtype the return type
|
||||
* @param ptypes the parameter types
|
||||
* @return a method type with the given parts
|
||||
* @throws NullPointerException if rtype or any ptype is null
|
||||
* @throws IllegalArgumentException if any of the ptypes is void
|
||||
* @return a method type with the given components
|
||||
* @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
|
||||
* @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
|
||||
*/
|
||||
public static
|
||||
MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
|
||||
return makeImpl(rtype, ptypes, false);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */
|
||||
/**
|
||||
* Finds or creates a method type with the given components.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @return a method type with the given components
|
||||
* @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
|
||||
* @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
|
||||
*/
|
||||
public static
|
||||
MethodType methodType(Class<?> rtype, List<? extends Class<?>> ptypes) {
|
||||
MethodType methodType(Class<?> rtype, List<Class<?>> ptypes) {
|
||||
boolean notrust = false; // random List impl. could return evil ptypes array
|
||||
return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* The leading parameter type is prepended to the remaining array.
|
||||
/**
|
||||
* Finds or creates a method type with the given components.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* The leading parameter type is prepended to the remaining array.
|
||||
* @return a method type with the given components
|
||||
* @throws NullPointerException if {@code rtype} or {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is null
|
||||
* @throws IllegalArgumentException if {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is {@code void.class}
|
||||
*/
|
||||
public static
|
||||
MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
|
||||
@ -170,25 +201,37 @@ class MethodType {
|
||||
return makeImpl(rtype, ptypes1, true);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* The resulting method has no parameter types.
|
||||
/**
|
||||
* Finds or creates a method type with the given components.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* The resulting method has no parameter types.
|
||||
* @return a method type with the given return value
|
||||
* @throws NullPointerException if {@code rtype} is null
|
||||
*/
|
||||
public static
|
||||
MethodType methodType(Class<?> rtype) {
|
||||
return makeImpl(rtype, NO_PTYPES, true);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* The resulting method has the single given parameter type.
|
||||
/**
|
||||
* Finds or creates a method type with the given components.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* The resulting method has the single given parameter type.
|
||||
* @return a method type with the given return value and parameter type
|
||||
* @throws NullPointerException if {@code rtype} or {@code ptype0} is null
|
||||
* @throws IllegalArgumentException if {@code ptype0} is {@code void.class}
|
||||
*/
|
||||
public static
|
||||
MethodType methodType(Class<?> rtype, Class<?> ptype0) {
|
||||
return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* The resulting method has the same parameter types as {@code ptypes},
|
||||
* and the specified return type.
|
||||
/**
|
||||
* Finds or creates a method type with the given components.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* The resulting method has the same parameter types as {@code ptypes},
|
||||
* and the specified return type.
|
||||
* @throws NullPointerException if {@code rtype} or {@code ptypes} is null
|
||||
*/
|
||||
public static
|
||||
MethodType methodType(Class<?> rtype, MethodType ptypes) {
|
||||
@ -237,17 +280,20 @@ class MethodType {
|
||||
private static final MethodType[] objectOnlyTypes = new MethodType[20];
|
||||
|
||||
/**
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* All parameters and the return type will be {@code Object},
|
||||
* except the final varargs parameter if any, which will be {@code Object[]}.
|
||||
* @param objectArgCount number of parameters (excluding the varargs parameter if any)
|
||||
* @param varargs whether there will be a varargs parameter, of type {@code Object[]}
|
||||
* @return a totally generic method type, given only its count of parameters and varargs
|
||||
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
|
||||
* @see #genericMethodType(int)
|
||||
*/
|
||||
public static
|
||||
MethodType genericMethodType(int objectArgCount, boolean varargs) {
|
||||
MethodType mt;
|
||||
checkSlotCount(objectArgCount);
|
||||
int ivarargs = (!varargs ? 0 : 1);
|
||||
int ootIndex = objectArgCount*2 + ivarargs;
|
||||
if (ootIndex < objectOnlyTypes.length) {
|
||||
@ -265,9 +311,12 @@ class MethodType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a method type whose components are all {@code Object}.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* All parameters and the return type will be Object.
|
||||
* @param objectArgCount number of parameters
|
||||
* @return a totally generic method type, given only its count of parameters
|
||||
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
|
||||
* @see #genericMethodType(int, boolean)
|
||||
*/
|
||||
public static
|
||||
@ -275,27 +324,41 @@ class MethodType {
|
||||
return genericMethodType(objectArgCount, false);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
/**
|
||||
* Finds or creates a method type with a single different parameter type.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param num the index (zero-based) of the parameter type to change
|
||||
* @param nptype a new parameter type to replace the old one with
|
||||
* @return the same type, except with the selected parameter changed
|
||||
* @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
|
||||
* @throws IllegalArgumentException if {@code nptype} is {@code void.class}
|
||||
* @throws NullPointerException if {@code nptype} is null
|
||||
*/
|
||||
public MethodType changeParameterType(int num, Class<?> nptype) {
|
||||
if (parameterType(num) == nptype) return this;
|
||||
checkPtype(nptype);
|
||||
Class<?>[] nptypes = ptypes.clone();
|
||||
nptypes[num] = nptype;
|
||||
return makeImpl(rtype, nptypes, true);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
/**
|
||||
* Finds or creates a method type with additional parameter types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param num the position (zero-based) of the inserted parameter type(s)
|
||||
* @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
|
||||
* @param ptypesToInsert zero or more new parameter types to insert into the parameter list
|
||||
* @return the same type, except with the selected parameter(s) inserted
|
||||
* @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
|
||||
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
|
||||
* or if the resulting method type would have more than 255 parameter slots
|
||||
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
|
||||
*/
|
||||
public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) {
|
||||
int len = ptypes.length;
|
||||
if (num < 0 || num > len)
|
||||
throw newIllegalArgumentException("num="+num); //SPECME
|
||||
throw newIndexOutOfBoundsException(num);
|
||||
int ins = checkPtypes(ptypesToInsert);
|
||||
checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
|
||||
int ilen = ptypesToInsert.length;
|
||||
if (ilen == 0) return this;
|
||||
Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
|
||||
@ -304,40 +367,61 @@ class MethodType {
|
||||
return makeImpl(rtype, nptypes, true);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
|
||||
/**
|
||||
* Finds or creates a method type with additional parameter types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
|
||||
* @return the same type, except with the selected parameter(s) appended
|
||||
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
|
||||
* or if the resulting method type would have more than 255 parameter slots
|
||||
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
|
||||
*/
|
||||
public MethodType appendParameterTypes(Class<?>... ptypesToInsert) {
|
||||
return insertParameterTypes(parameterCount(), ptypesToInsert);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
|
||||
* @return the same type, except with the selected parameter(s) appended
|
||||
*/
|
||||
public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
|
||||
return insertParameterTypes(parameterCount(), ptypesToInsert);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
/**
|
||||
* Finds or creates a method type with additional parameter types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param num the position (zero-based) of the inserted parameter type(s)
|
||||
* @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
|
||||
* @param ptypesToInsert zero or more new parameter types to insert into the parameter list
|
||||
* @return the same type, except with the selected parameter(s) inserted
|
||||
* @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
|
||||
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
|
||||
* or if the resulting method type would have more than 255 parameter slots
|
||||
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
|
||||
*/
|
||||
public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
|
||||
return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES));
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
/**
|
||||
* Finds or creates a method type with additional parameter types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
|
||||
* @return the same type, except with the selected parameter(s) appended
|
||||
* @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
|
||||
* or if the resulting method type would have more than 255 parameter slots
|
||||
* @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
|
||||
*/
|
||||
public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
|
||||
return insertParameterTypes(parameterCount(), ptypesToInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a method type with some parameter types omitted.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param start the index (zero-based) of the first parameter type to remove
|
||||
* @param end the index (greater than {@code start}) of the first parameter type after not to remove
|
||||
* @return the same type, except with the selected parameter(s) removed
|
||||
* @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
|
||||
* or if {@code end} is negative or greater than {@code parameterCount()}
|
||||
* or if {@code start} is greater than {@code end}
|
||||
*/
|
||||
public MethodType dropParameterTypes(int start, int end) {
|
||||
int len = ptypes.length;
|
||||
if (!(0 <= start && start <= end && end <= len))
|
||||
throw newIllegalArgumentException("start="+start+" end="+end); //SPECME
|
||||
throw newIndexOutOfBoundsException("start="+start+" end="+end);
|
||||
if (start == end) return this;
|
||||
Class<?>[] nptypes;
|
||||
if (start == 0) {
|
||||
@ -361,17 +445,20 @@ class MethodType {
|
||||
return makeImpl(rtype, nptypes, true);
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
/**
|
||||
* Finds or creates a method type with a different return type.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* @param nrtype a return parameter type to replace the old one with
|
||||
* @return the same type, except with the return type change
|
||||
* @throws NullPointerException if {@code nrtype} is null
|
||||
*/
|
||||
public MethodType changeReturnType(Class<?> nrtype) {
|
||||
if (returnType() == nrtype) return this;
|
||||
return makeImpl(nrtype, ptypes, true);
|
||||
}
|
||||
|
||||
/** Convenience method.
|
||||
* Report if this type contains a primitive argument or return value.
|
||||
/**
|
||||
* Reports if this type contains a primitive argument or return value.
|
||||
* The return type {@code void} counts as a primitive.
|
||||
* @return true if any of the types are primitives
|
||||
*/
|
||||
@ -379,8 +466,8 @@ class MethodType {
|
||||
return form.hasPrimitives();
|
||||
}
|
||||
|
||||
/** Convenience method.
|
||||
* Report if this type contains a wrapper argument or return value.
|
||||
/**
|
||||
* 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.
|
||||
* @return true if any of the types are wrappers
|
||||
@ -389,8 +476,9 @@ class MethodType {
|
||||
return unwrap() != this;
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* Erase all reference types to {@code Object}.
|
||||
/**
|
||||
* Erases all reference types to {@code Object}.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* All primitive types (including {@code void}) will remain unchanged.
|
||||
* @return a version of the original type with all reference types replaced
|
||||
*/
|
||||
@ -398,8 +486,9 @@ class MethodType {
|
||||
return form.erasedType();
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #genericMethodType(int)}.
|
||||
* Convert all types, both reference and primitive, to {@code Object}.
|
||||
/**
|
||||
* Converts all types, both reference and primitive, to {@code Object}.
|
||||
* Convenience method for {@link #genericMethodType(int) genericMethodType}.
|
||||
* The expression {@code type.wrap().erase()} produces the same value
|
||||
* as {@code type.generic()}.
|
||||
* @return a version of the original type with all types replaced
|
||||
@ -408,8 +497,9 @@ class MethodType {
|
||||
return genericMethodType(parameterCount());
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* Convert all primitive types to their corresponding wrapper types.
|
||||
/**
|
||||
* Converts all primitive types to their corresponding wrapper types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* All reference types (including wrapper types) will remain unchanged.
|
||||
* A {@code void} return type is changed to the type {@code java.lang.Void}.
|
||||
* The expression {@code type.wrap().erase()} produces the same value
|
||||
@ -420,8 +510,9 @@ class MethodType {
|
||||
return hasPrimitives() ? wrapWithPrims(this) : this;
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
/**
|
||||
* Convert all wrapper types to their corresponding primitive types.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* All primitive types (including {@code void}) will remain unchanged.
|
||||
* A return type of {@code java.lang.Void} is changed to {@code void}.
|
||||
* @return a version of the original type with all wrapper types replaced
|
||||
@ -456,23 +547,33 @@ class MethodType {
|
||||
return uwt;
|
||||
}
|
||||
|
||||
/** @param num the index (zero-based) of the desired parameter type
|
||||
* @return the selected parameter type
|
||||
/**
|
||||
* Returns the parameter type at the specified index, within this method type.
|
||||
* @param num the index (zero-based) of the desired parameter type
|
||||
* @return the selected parameter type
|
||||
* @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
|
||||
*/
|
||||
public Class<?> parameterType(int num) {
|
||||
return ptypes[num];
|
||||
}
|
||||
/** @return the number of parameter types */
|
||||
/**
|
||||
* Returns the number of parameter types in this method type.
|
||||
* @return the number of parameter types
|
||||
*/
|
||||
public int parameterCount() {
|
||||
return ptypes.length;
|
||||
}
|
||||
/** @return the return type */
|
||||
/**
|
||||
* Returns the return type of this method type.
|
||||
* @return the return type
|
||||
*/
|
||||
public Class<?> returnType() {
|
||||
return rtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to present the arguments as a list.
|
||||
* Presents the parameter types as a list (a convenience method).
|
||||
* The list will be immutable.
|
||||
* @return the parameter types (as an immutable list)
|
||||
*/
|
||||
public List<Class<?>> parameterList() {
|
||||
@ -480,7 +581,7 @@ class MethodType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to present the arguments as an array.
|
||||
* Presents the parameter types as an array (a convenience method).
|
||||
* Changes to the array will not result in changes to the type.
|
||||
* @return the parameter types (as a fresh copy if necessary)
|
||||
*/
|
||||
@ -524,14 +625,14 @@ class MethodType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the method type,
|
||||
* of the form {@code "(PT0,PT1...)RT"}.
|
||||
* The string representation of a method type is a
|
||||
* parenthesis enclosed, comma separated list of type names,
|
||||
* followed immediately by the return type.
|
||||
* <p>
|
||||
* Each type is represented by its
|
||||
* {@link java.lang.Class#getSimpleName simple name}.
|
||||
* If a type name name is array, it the base type followed
|
||||
* by [], rather than the Class.getName of the array type.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -548,21 +649,22 @@ class MethodType {
|
||||
|
||||
/// Queries which have to do with the bytecode architecture
|
||||
|
||||
/** The number of JVM stack slots required to invoke a method
|
||||
/** Reports the number of JVM stack slots required to invoke a method
|
||||
* of this type. Note that (for historic reasons) the JVM requires
|
||||
* a second stack slot to pass long and double arguments.
|
||||
* So this method returns {@link #parameterCount()} plus the
|
||||
* So this method returns {@link #parameterCount() parameterCount} plus the
|
||||
* number of long and double parameters (if any).
|
||||
* <p>
|
||||
* This method is included for the benfit of applications that must
|
||||
* generate bytecodes that process method handles and invokedynamic.
|
||||
* @return the number of JVM stack slots for this type's parameters
|
||||
* @deprecated Will be removed for PFD.
|
||||
*/
|
||||
public int parameterSlotCount() {
|
||||
return form.parameterSlotCount();
|
||||
}
|
||||
|
||||
/** Number of JVM stack slots which carry all parameters including and after
|
||||
/** Reports the number of JVM stack slots which carry all parameters including and after
|
||||
* the given position, which must be in the range of 0 to
|
||||
* {@code parameterCount} inclusive. Successive parameters are
|
||||
* more shallowly stacked, and parameters are indexed in the bytecodes
|
||||
@ -583,6 +685,8 @@ class MethodType {
|
||||
* @param num an index (zero-based, inclusive) within the parameter types
|
||||
* @return the index of the (shallowest) JVM stack slot transmitting the
|
||||
* given parameter
|
||||
* @throws IllegalArgumentException if {@code num} is negative or greater than {@code parameterCount()}
|
||||
* @deprecated Will be removed for PFD.
|
||||
*/
|
||||
public int parameterSlotDepth(int num) {
|
||||
if (num < 0 || num > ptypes.length)
|
||||
@ -590,7 +694,7 @@ class MethodType {
|
||||
return form.parameterToArgSlot(num-1);
|
||||
}
|
||||
|
||||
/** The number of JVM stack slots required to receive a return value
|
||||
/** Reports the number of JVM stack slots required to receive a return value
|
||||
* from a method of this type.
|
||||
* If the {@link #returnType() return type} is void, it will be zero,
|
||||
* else if the return type is long or double, it will be two, else one.
|
||||
@ -598,13 +702,15 @@ class MethodType {
|
||||
* This method is included for the benfit of applications that must
|
||||
* generate bytecodes that process method handles and invokedynamic.
|
||||
* @return the number of JVM stack slots (0, 1, or 2) for this type's return value
|
||||
* @deprecated Will be removed for PFD.
|
||||
*/
|
||||
public int returnSlotCount() {
|
||||
return form.returnSlotCount();
|
||||
}
|
||||
|
||||
/** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
|
||||
* Find or create an instance of the given method type.
|
||||
/**
|
||||
* Find or create an instance of a method type, given the spelling of its bytecode descriptor.
|
||||
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
|
||||
* Any class or interface name embedded in the descriptor string
|
||||
* will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
|
||||
* on the given loader (or if it is null, on the system class loader).
|
||||
@ -614,10 +720,10 @@ class MethodType {
|
||||
* not all reachable from a common class loader.
|
||||
* <p>
|
||||
* This method is included for the benfit of applications that must
|
||||
* generate bytecodes that process method handles and invokedynamic.
|
||||
* @param descriptor a bytecode-level signature string "(T...)T"
|
||||
* generate bytecodes that process method handles and {@code invokedynamic}.
|
||||
* @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 signature
|
||||
* @return a method type matching the bytecode-level type descriptor
|
||||
* @throws IllegalArgumentException if the string is not well-formed
|
||||
* @throws TypeNotPresentException if a named type cannot be found
|
||||
*/
|
||||
@ -631,17 +737,17 @@ class MethodType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bytecode descriptor representation of the method type.
|
||||
* Produces a bytecode descriptor representation of the method type.
|
||||
* <p>
|
||||
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString}.
|
||||
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
|
||||
* Two distinct classes which share a common name but have different class loaders
|
||||
* will appear identical when viewed within descriptor strings.
|
||||
* <p>
|
||||
* This method is included for the benfit of applications that must
|
||||
* generate bytecodes that process method handles and invokedynamic.
|
||||
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader)},
|
||||
* generate bytecodes that process method handles and {@code invokedynamic}.
|
||||
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
|
||||
* because the latter requires a suitable class loader argument.
|
||||
* @return the bytecode signature representation
|
||||
* @return the bytecode type descriptor representation
|
||||
*/
|
||||
public String toMethodDescriptorString() {
|
||||
return BytecodeDescriptor.unparse(this);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -73,7 +73,7 @@ assertEquals("Wilma, dear?", (String) worker2.invokeExact());
|
||||
* (This is a normal consequence of the Java Memory Model as applied
|
||||
* to object fields.)
|
||||
* <p>
|
||||
* The {@link #sync sync} operation provides a way to force threads
|
||||
* The {@link #syncAll syncAll} operation provides a way to force threads
|
||||
* to accept a new target value, even if there is no other synchronization.
|
||||
* <p>
|
||||
* For target values which will be frequently updated, consider using
|
||||
@ -82,13 +82,17 @@ assertEquals("Wilma, dear?", (String) worker2.invokeExact());
|
||||
*/
|
||||
public class MutableCallSite extends CallSite {
|
||||
/**
|
||||
* Make a blank call site object with the given method type.
|
||||
* An initial target method is supplied which will throw
|
||||
* an {@link IllegalStateException} if called.
|
||||
* Creates a blank call site object with the given method type.
|
||||
* The initial target is set to a method handle of the given type
|
||||
* which will throw an {@link IllegalStateException} if called.
|
||||
* <p>
|
||||
* The type of the call site is permanently set to the given type.
|
||||
* <p>
|
||||
* Before this {@code CallSite} object is returned from a bootstrap method,
|
||||
* or invoked in some other manner,
|
||||
* it is usually provided with a more useful target method,
|
||||
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
|
||||
* @param type the method type that this call site will have
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public MutableCallSite(MethodType type) {
|
||||
@ -96,8 +100,9 @@ public class MutableCallSite extends CallSite {
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a blank call site object, possibly equipped with an initial target method handle.
|
||||
* @param target the method handle which will be the initial target of the call site
|
||||
* Creates a call site object with an initial target method handle.
|
||||
* The type of the call site is permanently set to the initial target's type.
|
||||
* @param target the method handle that will be the initial target of the call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public MutableCallSite(MethodHandle target) {
|
||||
@ -105,7 +110,59 @@ public class MutableCallSite extends CallSite {
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a synchronization operation on each call site in the given array,
|
||||
* Returns the target method of the call site, which behaves
|
||||
* like a normal field of the {@code MutableCallSite}.
|
||||
* <p>
|
||||
* The interactions of {@code getTarget} with memory are the same
|
||||
* as of a read from an ordinary variable, such as an array element or a
|
||||
* non-volatile, non-final field.
|
||||
* <p>
|
||||
* In particular, the current thread may choose to reuse the result
|
||||
* of a previous read of the target from memory, and may fail to see
|
||||
* a recent update to the target by another thread.
|
||||
*
|
||||
* @return the linkage state of this call site, a method handle which can change over time
|
||||
* @see #setTarget
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target method of this call site, as a normal variable.
|
||||
* The type of the new target must agree with the type of the old target.
|
||||
* <p>
|
||||
* The interactions with memory are the same
|
||||
* as of a write to an ordinary variable, such as an array element or a
|
||||
* non-volatile, non-final field.
|
||||
* <p>
|
||||
* In particular, unrelated threads may fail to see the updated target
|
||||
* until they perform a read from memory.
|
||||
* Stronger guarantees can be created by putting appropriate operations
|
||||
* into the bootstrap method and/or the target methods used
|
||||
* at any given call site.
|
||||
*
|
||||
* @param newTarget the new target
|
||||
* @throws NullPointerException if the proposed new target is null
|
||||
* @throws WrongMethodTypeException if the proposed new target
|
||||
* has a method type that differs from the previous target
|
||||
* @see #getTarget
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(this.target, newTarget);
|
||||
setTargetNormal(newTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
return makeDynamicInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a synchronization operation on each call site in the given array,
|
||||
* forcing all other threads to throw away any cached values previously
|
||||
* loaded from the target of any of the call sites.
|
||||
* <p>
|
||||
@ -115,19 +172,29 @@ public class MutableCallSite extends CallSite {
|
||||
* <p>
|
||||
* The overall effect is to force all future readers of each call site's target
|
||||
* to accept the most recently stored value.
|
||||
* ("Most recently" is reckoned relative to the {@code sync} itself.)
|
||||
* Conversely, the {@code sync} call may block until all readers have
|
||||
* ("Most recently" is reckoned relative to the {@code syncAll} itself.)
|
||||
* Conversely, the {@code syncAll} call may block until all readers have
|
||||
* (somehow) decached all previous versions of each call site's target.
|
||||
* <p>
|
||||
* To avoid race conditions, calls to {@code setTarget} and {@code sync}
|
||||
* To avoid race conditions, calls to {@code setTarget} and {@code syncAll}
|
||||
* should generally be performed under some sort of mutual exclusion.
|
||||
* Note that reader threads may observe an updated target as early
|
||||
* as the {@code setTarget} call that install the value
|
||||
* (and before the {@code sync} that confirms the value).
|
||||
* (and before the {@code syncAll} that confirms the value).
|
||||
* On the other hand, reader threads may observe previous versions of
|
||||
* the target until the {@code sync} call returns
|
||||
* the target until the {@code syncAll} call returns
|
||||
* (and after the {@code setTarget} that attempts to convey the updated version).
|
||||
* <p>
|
||||
* This operation is likely to be expensive and should be used sparingly.
|
||||
* If possible, it should be buffered for batch processing on sets of call sites.
|
||||
* <p>
|
||||
* If {@code sites} contains a null element,
|
||||
* a {@code NullPointerException} will be raised.
|
||||
* In this case, some non-null elements in the array may be
|
||||
* processed before the method returns abnormally.
|
||||
* Which elements these are (if any) is implementation-dependent.
|
||||
*
|
||||
* <h3>Java Memory Model details</h3>
|
||||
* In terms of the Java Memory Model, this operation performs a synchronization
|
||||
* action which is comparable in effect to the writing of a volatile variable
|
||||
* by the current thread, and an eventual volatile read by every other thread
|
||||
@ -171,18 +238,17 @@ public class MutableCallSite extends CallSite {
|
||||
* thereby ensuring communication of the new target value.
|
||||
* <p>
|
||||
* As long as the constraints of the Java Memory Model are obeyed,
|
||||
* implementations may delay the completion of a {@code sync}
|
||||
* implementations may delay the completion of a {@code syncAll}
|
||||
* operation while other threads ({@code T} above) continue to
|
||||
* use previous values of {@code S}'s target.
|
||||
* However, implementations are (as always) encouraged to avoid
|
||||
* livelock, and to eventually require all threads to take account
|
||||
* of the updated target.
|
||||
* <p>
|
||||
* This operation is likely to be expensive and should be used sparingly.
|
||||
* If possible, it should be buffered for batch processing on sets of call sites.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* (This is a static method on a set of call sites, not a
|
||||
* virtual method on a single call site, for performance reasons.
|
||||
* <em>Discussion:</em>
|
||||
* For performance reasons, {@code syncAll} is not a virtual method
|
||||
* on a single call site, but rather applies to a set of call sites.
|
||||
* Some implementations may incur a large fixed overhead cost
|
||||
* for processing one or more synchronization operations,
|
||||
* but a small incremental cost for each additional call site.
|
||||
@ -191,15 +257,25 @@ public class MutableCallSite extends CallSite {
|
||||
* in order to make them notice the updated target value.
|
||||
* However, it may be observed that a single call to synchronize
|
||||
* several sites has the same formal effect as many calls,
|
||||
* each on just one of the sites.)
|
||||
* <p>
|
||||
* each on just one of the sites.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* Simple implementations of {@code MutableCallSite} may use
|
||||
* a volatile variable for the target of a mutable call site.
|
||||
* In such an implementation, the {@code sync} method can be a no-op,
|
||||
* In such an implementation, the {@code syncAll} method can be a no-op,
|
||||
* and yet it will conform to the JMM behavior documented above.
|
||||
*
|
||||
* @param sites an array of call sites to be synchronized
|
||||
* @throws NullPointerException if the {@code sites} array reference is null
|
||||
* or the array contains a null
|
||||
*/
|
||||
public static void sync(MutableCallSite[] sites) {
|
||||
public static void syncAll(MutableCallSite[] sites) {
|
||||
if (sites.length == 0) return;
|
||||
STORE_BARRIER.lazySet(0);
|
||||
for (int i = 0; i < sites.length; i++) {
|
||||
sites[i].getClass(); // trigger NPE on first null
|
||||
}
|
||||
// FIXME: NYI
|
||||
}
|
||||
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
|
||||
|
195
jdk/src/share/classes/java/dyn/SwitchPoint.java
Normal file
195
jdk/src/share/classes/java/dyn/SwitchPoint.java
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 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.dyn;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code SwitchPoint} is an object which can publish state transitions to other threads.
|
||||
* A switch point is initially in the <em>valid</em> state, but may at any time be
|
||||
* changed to the <em>invalid</em> state. Invalidation cannot be reversed.
|
||||
* A switch point can combine a <em>guarded pair</em> of method handles into a
|
||||
* <em>guarded delegator</em>.
|
||||
* The guarded delegator is a method handle which delegates to one of the old method handles.
|
||||
* The state of the switch point determines which of the two gets the delegation.
|
||||
* <p>
|
||||
* A single switch point may be used to control any number of method handles.
|
||||
* (Indirectly, therefore, it can control any number of call sites.)
|
||||
* This is done by using the single switch point as a factory for combining
|
||||
* any number of guarded method handle pairs into guarded delegators.
|
||||
* <p>
|
||||
* When a guarded delegator is created from a guarded pair, the pair
|
||||
* is wrapped in a new method handle {@code M},
|
||||
* which is permanently associated with the switch point that created it.
|
||||
* Each pair consists of a target {@code T} and a fallback {@code F}.
|
||||
* While the switch point is valid, invocations to {@code M} are delegated to {@code T}.
|
||||
* After it is invalidated, invocations are delegated to {@code F}.
|
||||
* <p>
|
||||
* Invalidation is global and immediate, as if the switch point contained a
|
||||
* volatile boolean variable consulted on every call to {@code M}.
|
||||
* The invalidation is also permanent, which means the switch point
|
||||
* can change state only once.
|
||||
* The switch point will always delegate to {@code F} after being invalidated.
|
||||
* At that point {@code guardWithTest} may ignore {@code T} and return {@code F}.
|
||||
* <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);
|
||||
SwitchPoint spt = new SwitchPoint();
|
||||
// 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 worker = spt.guardWithTest(worker1, worker2);
|
||||
assertEquals("method", (String) worker.invokeExact("met", "hod"));
|
||||
SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
|
||||
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
|
||||
* </pre></blockquote>
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* Switch points are useful without subclassing. They may also be subclassed.
|
||||
* This may be useful in order to associate application-specific invalidation logic
|
||||
* with the switch point.
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* A switch point behaves as if implemented on top of {@link MutableCallSite},
|
||||
* approximately as follows:
|
||||
* <blockquote><pre>
|
||||
public class SwitchPoint {
|
||||
private static final MethodHandle
|
||||
K_true = MethodHandles.constant(boolean.class, true),
|
||||
K_false = MethodHandles.constant(boolean.class, false);
|
||||
private final MutableCallSite mcs;
|
||||
private final MethodHandle mcsInvoker;
|
||||
public SwitchPoint() {
|
||||
this.mcs = new MutableCallSite(K_true);
|
||||
this.mcsInvoker = mcs.dynamicInvoker();
|
||||
}
|
||||
public MethodHandle guardWithTest(
|
||||
MethodHandle target, MethodHandle fallback) {
|
||||
// Note: mcsInvoker is of type ()boolean.
|
||||
// Target and fallback may take any arguments, but must have the same type.
|
||||
return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
|
||||
}
|
||||
public static void invalidateAll(SwitchPoint[] spts) {
|
||||
List<MutableCallSite> mcss = new ArrayList<>();
|
||||
for (SwitchPoint spt : spts) mcss.add(spt.mcs);
|
||||
for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
|
||||
MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
|
||||
}
|
||||
}
|
||||
* </pre></blockquote>
|
||||
* @author Remi Forax, JSR 292 EG
|
||||
*/
|
||||
public class SwitchPoint {
|
||||
private static final MethodHandle
|
||||
K_true = MethodHandles.constant(boolean.class, true),
|
||||
K_false = MethodHandles.constant(boolean.class, false);
|
||||
|
||||
private final MutableCallSite mcs;
|
||||
private final MethodHandle mcsInvoker;
|
||||
|
||||
/**
|
||||
* Creates a new switch point.
|
||||
*/
|
||||
public SwitchPoint() {
|
||||
this.mcs = new MutableCallSite(K_true);
|
||||
this.mcsInvoker = mcs.dynamicInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* After that, it will permanently delegate to the fallback.
|
||||
* <p>
|
||||
* The target and fallback must be of exactly the same method type,
|
||||
* and the resulting combined method handle will also be of this type.
|
||||
*
|
||||
* @param target the method handle selected by the switch point as long as it is valid
|
||||
* @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
|
||||
* @see MethodHandles#guardWithTest
|
||||
*/
|
||||
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
|
||||
if (mcs.getTarget() == K_false)
|
||||
return fallback; // already invalid
|
||||
return MethodHandles.guardWithTest(mcsInvoker, target, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all of the given switch points into the invalid state.
|
||||
* After this call executes, no thread will observe any of the
|
||||
* switch points to be in a valid state.
|
||||
* <p>
|
||||
* This operation is likely to be expensive and should be used sparingly.
|
||||
* If possible, it should be buffered for batch processing on sets of switch points.
|
||||
* <p>
|
||||
* If {@code switchPoints} contains a null element,
|
||||
* a {@code NullPointerException} will be raised.
|
||||
* In this case, some non-null elements in the array may be
|
||||
* processed before the method returns abnormally.
|
||||
* Which elements these are (if any) is implementation-dependent.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* For performance reasons, {@code invalidateAll} is not a virtual method
|
||||
* on a single switch point, but rather applies to a set of switch points.
|
||||
* Some implementations may incur a large fixed overhead cost
|
||||
* for processing one or more invalidation operations,
|
||||
* but a small incremental cost for each additional invalidation.
|
||||
* In any case, this operation is likely to be costly, since
|
||||
* other threads may have to be somehow interrupted
|
||||
* in order to make them notice the updated switch point state.
|
||||
* However, it may be observed that a single call to invalidate
|
||||
* several switch points has the same formal effect as many calls,
|
||||
* each on just one of the switch points.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Implementation Note:</em>
|
||||
* Simple implementations of {@code SwitchPoint} may use
|
||||
* a private {@link MutableCallSite} to publish the state of a switch point.
|
||||
* In such an implementation, the {@code invalidateAll} method can
|
||||
* simply change the call site's target, and issue one call to
|
||||
* {@linkplain MutableCallSite#syncAll synchronize} all the
|
||||
* private call sites.
|
||||
*
|
||||
* @param switchPoints an array of call sites to be synchronized
|
||||
* @throws NullPointerException if the {@code switchPoints} array reference is null
|
||||
* or the array contains a null
|
||||
*/
|
||||
public static void invalidateAll(SwitchPoint[] switchPoints) {
|
||||
if (switchPoints.length == 0) return;
|
||||
MutableCallSite[] sites = new MutableCallSite[switchPoints.length];
|
||||
for (int i = 0; i < switchPoints.length; i++) {
|
||||
SwitchPoint spt = switchPoints[i];
|
||||
if (spt == null) break; // MSC.syncAll will trigger a NPE
|
||||
sites[i] = spt.mcs;
|
||||
spt.mcs.setTarget(K_false);
|
||||
}
|
||||
MutableCallSite.syncAll(sites);
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 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.dyn;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code Switcher} is an object which can publish state transitions to other threads.
|
||||
* A switcher is initially in the <em>valid</em> state, but may at any time be
|
||||
* changed to the <em>invalid</em> state. Invalidation cannot be reversed.
|
||||
* <p>
|
||||
* A single switcher may be used to create any number of guarded method handle pairs.
|
||||
* Each guarded pair is wrapped in a new method handle {@code M},
|
||||
* which is permanently associated with the switcher that created it.
|
||||
* Each pair consists of a target {@code T} and a fallback {@code F}.
|
||||
* While the switcher is valid, invocations to {@code M} are delegated to {@code T}.
|
||||
* After it is invalidated, invocations are delegated to {@code F}.
|
||||
* <p>
|
||||
* Invalidation is global and immediate, as if the switcher contained a
|
||||
* volatile boolean variable consulted on every call to {@code M}.
|
||||
* The invalidation is also permanent, which means the switcher
|
||||
* can change state only once.
|
||||
* <p>
|
||||
* Here is an example of a switcher in action:
|
||||
* <blockquote><pre>
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodHandle MH_strcat = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MT_str2);
|
||||
Switcher switcher = new Switcher();
|
||||
// the following steps may be repeated to re-use the same switcher:
|
||||
MethodHandle worker1 = strcat;
|
||||
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
|
||||
MethodHandle worker = switcher.guardWithTest(worker1, worker2);
|
||||
assertEquals("method", (String) worker.invokeExact("met", "hod"));
|
||||
switcher.invalidate();
|
||||
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* <em>Implementation Note:</em>
|
||||
* A switcher behaves as if implemented on top of {@link MutableCallSite},
|
||||
* approximately as follows:
|
||||
* <blockquote><pre>
|
||||
public class Switcher {
|
||||
private static final MethodHandle
|
||||
K_true = MethodHandles.constant(boolean.class, true),
|
||||
K_false = MethodHandles.constant(boolean.class, false);
|
||||
private final MutableCallSite mcs;
|
||||
private final MethodHandle mcsInvoker;
|
||||
public Switcher() {
|
||||
this.mcs = new MutableCallSite(K_true);
|
||||
this.mcsInvoker = mcs.dynamicInvoker();
|
||||
}
|
||||
public MethodHandle guardWithTest(
|
||||
MethodHandle target, MethodHandle fallback) {
|
||||
// Note: mcsInvoker is of type boolean().
|
||||
// Target and fallback may take any arguments, but must have the same type.
|
||||
return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
|
||||
}
|
||||
public static void invalidateAll(Switcher[] switchers) {
|
||||
List<MutableCallSite> mcss = new ArrayList<>();
|
||||
for (Switcher s : switchers) mcss.add(s.mcs);
|
||||
for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
|
||||
MutableCallSite.sync(mcss.toArray(new MutableCallSite[0]));
|
||||
}
|
||||
}
|
||||
* </pre></blockquote>
|
||||
* @author Remi Forax, JSR 292 EG
|
||||
*/
|
||||
public class Switcher {
|
||||
private static final MethodHandle
|
||||
K_true = MethodHandles.constant(boolean.class, true),
|
||||
K_false = MethodHandles.constant(boolean.class, false);
|
||||
|
||||
private final MutableCallSite mcs;
|
||||
private final MethodHandle mcsInvoker;
|
||||
|
||||
/** Create a switcher. */
|
||||
public Switcher() {
|
||||
this.mcs = new MutableCallSite(K_true);
|
||||
this.mcsInvoker = mcs.dynamicInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return 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 switcher is valid.
|
||||
* After that, it will permanently delegate to the fallback.
|
||||
* <p>
|
||||
* The target and fallback must be of exactly the same method type,
|
||||
* and the resulting combined method handle will also be of this type.
|
||||
* @see MethodHandles#guardWithTest
|
||||
*/
|
||||
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
|
||||
if (mcs.getTarget() == K_false)
|
||||
return fallback; // already invalid
|
||||
return MethodHandles.guardWithTest(mcsInvoker, target, fallback);
|
||||
}
|
||||
|
||||
/** Set all of the given switchers into the invalid state. */
|
||||
public static void invalidateAll(Switcher[] switchers) {
|
||||
MutableCallSite[] sites = new MutableCallSite[switchers.length];
|
||||
int fillp = 0;
|
||||
for (Switcher switcher : switchers) {
|
||||
sites[fillp++] = switcher.mcs;
|
||||
switcher.mcs.setTarget(K_false);
|
||||
}
|
||||
MutableCallSite.sync(sites);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 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
|
||||
@ -34,7 +34,7 @@ import java.util.List;
|
||||
* There may be a performance penalty for such tight coupling between threads.
|
||||
* <p>
|
||||
* Unlike {@code MutableCallSite}, there is no
|
||||
* {@linkplain MutableCallSite#sync sync operation} on volatile
|
||||
* {@linkplain MutableCallSite#syncAll syncAll operation} on volatile
|
||||
* call sites, since every write to a volatile variable is implicitly
|
||||
* synchronized with reader threads.
|
||||
* <p>
|
||||
@ -44,36 +44,68 @@ import java.util.List;
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class VolatileCallSite extends CallSite {
|
||||
/** Create a call site with a volatile target.
|
||||
* The initial target is set to a method handle
|
||||
* of the given type which will throw {@code IllegalStateException}.
|
||||
/**
|
||||
* Creates a call site with a volatile binding to its target.
|
||||
* The initial target is set to a method handle
|
||||
* of the given type which will throw an {@code IllegalStateException} if called.
|
||||
* @param type the method type that this call site will have
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public VolatileCallSite(MethodType type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
/** Create a call site with a volatile target.
|
||||
* The target is set to the given value.
|
||||
/**
|
||||
* Creates a call site with a volatile binding to its target.
|
||||
* The target is set to the given value.
|
||||
* @param target the method handle that will be the initial target of the call site
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public VolatileCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/** Internal override to nominally final getTarget. */
|
||||
@Override
|
||||
MethodHandle getTarget0() {
|
||||
/**
|
||||
* Returns the target method of the call site, which behaves
|
||||
* like a {@code volatile} field of the {@code VolatileCallSite}.
|
||||
* <p>
|
||||
* The interactions of {@code getTarget} with memory are the same
|
||||
* as of a read from a {@code volatile} field.
|
||||
* <p>
|
||||
* In particular, the current thread is required to issue a fresh
|
||||
* read of the target from memory, and must not fail to see
|
||||
* a recent update to the target by another thread.
|
||||
*
|
||||
* @return the linkage state of this call site, a method handle which can change over time
|
||||
* @see #setTarget
|
||||
*/
|
||||
@Override public final MethodHandle getTarget() {
|
||||
return getTargetVolatile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target method of this call site, as a volatile variable.
|
||||
* Has the same effect as {@link CallSite#setTarget CallSite.setTarget}, with the additional
|
||||
* effects associated with volatiles, in the Java Memory Model.
|
||||
* Updates the target method of this call site, as a volatile variable.
|
||||
* The type of the new target must agree with the type of the old target.
|
||||
* <p>
|
||||
* The interactions with memory are the same as of a write to a volatile field.
|
||||
* In particular, any threads is guaranteed to see the updated target
|
||||
* the next time it calls {@code getTarget}.
|
||||
* @param newTarget the new target
|
||||
* @throws NullPointerException if the proposed new target is null
|
||||
* @throws WrongMethodTypeException if the proposed new target
|
||||
* has a method type that differs from the previous target
|
||||
* @see #getTarget
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(getTargetVolatile(), newTarget);
|
||||
setTargetVolatile(newTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
return makeDynamicInvoker();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ package java.dyn;
|
||||
* Thrown to indicate that code has attempted to call a method handle
|
||||
* via the wrong method type. As with the bytecode representation of
|
||||
* normal Java method calls, method handle calls are strongly typed
|
||||
* to a specific signature associated with a call site.
|
||||
* to a specific type descriptor associated with a call site.
|
||||
* <p>
|
||||
* This exception may also be thrown when two method handles are
|
||||
* composed, and the system detects that their types cannot be
|
||||
|
@ -24,21 +24,20 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package contains dynamic language support provided directly by
|
||||
* The {@code java.lang.invoke} package contains dynamic language support provided directly by
|
||||
* the Java core class libraries and virtual machine.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Historic Note:</em> In some early versions of Java SE 7,
|
||||
* the name of this package is {@code java.dyn}.
|
||||
* <p>
|
||||
* Certain types in this package have special relations to dynamic
|
||||
* language support in the virtual machine:
|
||||
* <ul>
|
||||
* <li>In source code, a call to
|
||||
* {@link java.dyn.MethodHandle#invokeExact MethodHandle.invokeExact} or
|
||||
* {@link java.dyn.MethodHandle#invokeGeneric MethodHandle.invokeGeneric}
|
||||
* will compile and link, regardless of the requested type signature.
|
||||
* As usual, the Java compiler emits an {@code invokevirtual}
|
||||
* instruction with the given signature against the named method.
|
||||
* The JVM links any such call (regardless of signature) to a dynamically
|
||||
* typed method handle invocation. In the case of {@code invokeGeneric},
|
||||
* argument and return value conversions are applied.
|
||||
* <li>The class {@link java.dyn.MethodHandle MethodHandle} contains
|
||||
* <a href="MethodHandle.html#sigpoly">signature polymorphic methods</a>
|
||||
* which can be linked regardless of their type descriptor.
|
||||
* Normally, method linkage requires exact matching of type descriptors.
|
||||
* </li>
|
||||
*
|
||||
* <li>The JVM bytecode format supports immediate constants of
|
||||
@ -58,12 +57,11 @@
|
||||
* 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 tag value 17 is also temporarily allowed. See below.)
|
||||
* The entry specifies the following information:
|
||||
* <ul>
|
||||
* <li>a bootstrap method (a {@link java.dyn.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 signature in 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>
|
||||
@ -78,9 +76,9 @@
|
||||
* as <a href="#bsm">described below</a>.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* (Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
|
||||
* <em>Historic Note:</em> Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
|
||||
* instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the
|
||||
* bootstrap method was specified dynamically, in a per-class basis, during class initialization.)
|
||||
* bootstrap method was specified dynamically, in a per-class basis, during class initialization.
|
||||
*
|
||||
* <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),
|
||||
@ -90,33 +88,21 @@
|
||||
* <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}.
|
||||
* This table is not part of the constant pool. Instead, it is stored
|
||||
* in a class attribute named {@code BootstrapMethods}, described below.
|
||||
* <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.
|
||||
* <p>
|
||||
* Some older JVMs may allow an older constant pool entry tag of decimal 17.
|
||||
* The format and behavior of a constant pool entry with this tag is identical to
|
||||
* an entry with a tag of decimal 18, except that the first index refers directly
|
||||
* to a {@code CONSTANT_MethodHandle} to use as the bootstrap method.
|
||||
* This format does not require the bootstrap method table.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>(Note: The Proposed Final Draft of this specification is likely to support
|
||||
* only the tag 18, not the tag 17.)</em>
|
||||
*
|
||||
* <h3><a name="mtcon"></a>constant pool entries for {@linkplain java.dyn.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 signature.
|
||||
* 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.dyn.MethodType MethodType}
|
||||
* will be created which represents the signature.
|
||||
* 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
|
||||
@ -144,24 +130,58 @@
|
||||
* 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>MH behavior</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></tr>
|
||||
* <tr><td>2</td><td>REF_getStatic</td><td>C.f:T</td><td>( )T</td><td>getstatic C.f:T</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></tr>
|
||||
* <tr><td>4</td><td>REF_putStatic</td><td>C.f:T</td><td>(T)void</td><td>putstatic C.f:T</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></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></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></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></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></tr>
|
||||
* <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.dyn.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.dyn.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.dyn.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.dyn.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.dyn.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.dyn.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.dyn.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.dyn.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.dyn.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.dyn.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. In particular, method handles to
|
||||
* 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>
|
||||
@ -186,14 +206,14 @@
|
||||
* 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 resoultion succeeds, the same object reference is produced
|
||||
* 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 {@link CONSTANT_Class} and {@link CONSTANT_String} entries,
|
||||
* 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.
|
||||
*
|
||||
@ -207,31 +227,31 @@
|
||||
* <p>
|
||||
* Each {@code invokedynamic} instruction statically specifies its own
|
||||
* bootstrap method as a constant pool reference.
|
||||
* The constant pool reference also specifies the call site's name and type signature,
|
||||
* The constant pool reference also specifies the call site's name and type descriptor,
|
||||
* just like {@code invokevirtual} and the other invoke instructions.
|
||||
* <p>
|
||||
* Linking starts with resolving the constant pool entry for the
|
||||
* bootstrap method, and resolving a {@link java.dyn.MethodType MethodType} object for
|
||||
* the type signature of the dynamic call site.
|
||||
* the type descriptor of the dynamic call site.
|
||||
* This resolution process may trigger class loading.
|
||||
* It may therefore throw an error if a class fails to load.
|
||||
* This error becomes the abnormal termination of the dynamic
|
||||
* call site execution.
|
||||
* Linkage does not trigger class initialization.
|
||||
* <p>
|
||||
* Next, the bootstrap method call is started, with four or five values being stacked:
|
||||
* Next, the bootstrap method call is started, with at least four values being stacked:
|
||||
* <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 signature of the call </li>
|
||||
* <li>optionally, a single object representing one or more <a href="#args">additional static arguments</a> </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>
|
||||
* </ul>
|
||||
* The method handle is then applied to the other values as if by
|
||||
* {@link java.dyn.MethodHandle#invokeGeneric invokeGeneric}.
|
||||
* The returned result must be a {@link java.dyn.CallSite CallSite} (or a subclass).
|
||||
* The type of the call site's target must be exactly equal to the type
|
||||
* derived from the dynamic call site signature and passed to
|
||||
* derived from the dynamic call site's type descriptor and passed to
|
||||
* the bootstrap method.
|
||||
* The call site then becomes permanently linked to the dynamic call site.
|
||||
* <p>
|
||||
@ -299,11 +319,11 @@
|
||||
* chosen target object.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* (Historic Note: Unlike some previous versions of this specification,
|
||||
* <em>Historic Note:</em> Unlike some previous versions of this specification,
|
||||
* these rules do not enable the JVM to duplicate dynamic call sites,
|
||||
* or to issue “causeless” bootstrap method calls.
|
||||
* Every dynamic call site transitions at most once from unlinked to linked,
|
||||
* just before its first invocation.)
|
||||
* just before its first invocation.
|
||||
*
|
||||
* <h3><a name="bsmattr">the {@code BootstrapMethods} attribute </h3>
|
||||
* Each {@code CONSTANT_InvokeDynamic} entry contains an index which references
|
||||
@ -349,7 +369,6 @@
|
||||
* 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.
|
||||
* If the value is a primitive type, it is converted to a reference by boxing conversion.
|
||||
* The valid constant pool entries are listed in this table:
|
||||
* <code>
|
||||
* <table border=1 cellpadding=5 summary="Static argument types">
|
||||
@ -374,6 +393,9 @@
|
||||
* at most 252 extra arguments can be supplied.)
|
||||
* The bootstrap method will be invoked as if by either {@code invokeGeneric}
|
||||
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
|
||||
* <p>
|
||||
* The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments.
|
||||
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
|
||||
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
|
||||
* 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
|
||||
@ -430,11 +452,6 @@ struct CONSTANT_MethodType_info {
|
||||
u1 tag = 16;
|
||||
u2 descriptor_index; // index to CONSTANT_Utf8, as in NameAndType
|
||||
}
|
||||
struct CONSTANT_InvokeDynamic_17_info {
|
||||
u1 tag = 17;
|
||||
u2 bootstrap_method_index; // index to CONSTANT_MethodHandle
|
||||
u2 name_and_type_index; // same as for CONSTANT_Methodref, etc.
|
||||
}
|
||||
struct CONSTANT_InvokeDynamic_info {
|
||||
u1 tag = 18;
|
||||
u2 bootstrap_method_attr_index; // index into BootstrapMethods_attr
|
||||
@ -451,9 +468,6 @@ struct BootstrapMethods_attr {
|
||||
} bootstrap_methods[bootstrap_method_count];
|
||||
}
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* <em>Note: The Proposed Final Draft of JSR 292 may remove the constant tag 17,
|
||||
* for the sake of simplicity.</em>
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
|
@ -962,7 +962,7 @@ public class AdapterMethodHandle extends BoundMethodHandle {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return nonAdapter((MethodHandle)vmtarget).toString();
|
||||
return MethodHandleImpl.getNameString(IMPL_TOKEN, nonAdapter((MethodHandle)vmtarget), this);
|
||||
}
|
||||
|
||||
private static MethodHandle nonAdapter(MethodHandle mh) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -28,6 +28,7 @@ package sun.dyn;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.*;
|
||||
import sun.dyn.util.*;
|
||||
import static sun.dyn.MethodTypeImpl.invokers;
|
||||
|
||||
/**
|
||||
* Adapters which mediate between incoming calls which are generic
|
||||
@ -128,7 +129,7 @@ class FromGeneric {
|
||||
MethodType targetType, MethodType internalType) {
|
||||
// All the adapters we have here have reference-untyped internal calls.
|
||||
assert(internalType == internalType.erase());
|
||||
MethodHandle invoker = MethodHandles.exactInvoker(targetType);
|
||||
MethodHandle invoker = invokers(targetType).exactInvoker();
|
||||
// cast all narrow reference types, unbox all primitive arguments:
|
||||
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
|
||||
MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN,
|
||||
|
@ -28,6 +28,7 @@ package sun.dyn;
|
||||
import java.dyn.*;
|
||||
import java.lang.reflect.*;
|
||||
import sun.dyn.util.*;
|
||||
import static sun.dyn.MethodTypeImpl.invokers;
|
||||
|
||||
/**
|
||||
* Adapters which manage MethodHanndle.invokeGeneric calls.
|
||||
@ -87,7 +88,7 @@ class InvokeGeneric {
|
||||
private MethodHandle makePostDispatchInvoker() {
|
||||
// Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...).
|
||||
MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS);
|
||||
return MethodHandles.exactInvoker(invokerType);
|
||||
return invokers(invokerType).exactInvoker();
|
||||
}
|
||||
private MethodHandle dropDispatchArguments(MethodHandle targetInvoker) {
|
||||
assert(targetInvoker.type().parameterType(0) == MethodHandle.class);
|
||||
|
@ -104,8 +104,7 @@ public class Invokers {
|
||||
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
|
||||
if (vaInvoker != null) return vaInvoker;
|
||||
MethodHandle gInvoker = genericInvoker();
|
||||
MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
|
||||
vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType));
|
||||
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
|
||||
spreadInvokers[objectArgCount] = vaInvoker;
|
||||
return vaInvoker;
|
||||
}
|
||||
|
@ -136,6 +136,8 @@ public abstract class MethodHandleImpl {
|
||||
}
|
||||
|
||||
static {
|
||||
if (!MethodHandleNatives.JVM_SUPPORT) // force init of native API
|
||||
throw new InternalError("No JVM support for JSR 292");
|
||||
// Force initialization of Lookup, so it calls us back as initLookup:
|
||||
MethodHandles.publicLookup();
|
||||
if (IMPL_LOOKUP_INIT == null)
|
||||
@ -1216,14 +1218,24 @@ public abstract class MethodHandleImpl {
|
||||
}
|
||||
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
|
||||
|
||||
public static String getNameString(Access token, MethodHandle target) {
|
||||
public static String getNameString(Access token, MethodHandle target, Object type) {
|
||||
Access.check(token);
|
||||
if (!(type instanceof MethodType)) {
|
||||
if (type == null)
|
||||
type = target.type();
|
||||
else if (type instanceof MethodHandle)
|
||||
type = ((MethodHandle)type).type();
|
||||
}
|
||||
MemberName name = null;
|
||||
if (target != null)
|
||||
name = MethodHandleNatives.getMethodName(target);
|
||||
if (name == null)
|
||||
return "invoke" + target.type();
|
||||
return name.getName() + target.type();
|
||||
return "invoke" + type;
|
||||
return name.getName() + type;
|
||||
}
|
||||
|
||||
public static String getNameString(Access token, MethodHandle target) {
|
||||
return getNameString(token, target, null);
|
||||
}
|
||||
|
||||
static String addTypeString(Object obj, MethodHandle target) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -31,6 +31,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import sun.dyn.util.ValueConversions;
|
||||
import sun.dyn.util.Wrapper;
|
||||
import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
import static sun.dyn.MethodTypeImpl.invokers;
|
||||
|
||||
/**
|
||||
* Adapters which mediate between incoming calls which are not generic
|
||||
@ -72,7 +73,7 @@ class ToGeneric {
|
||||
assert(entryType.erase() == entryType); // for now
|
||||
// incoming call will first "forget" all reference types except Object
|
||||
this.entryType = entryType;
|
||||
MethodHandle invoker0 = MethodHandles.exactInvoker(entryType.generic());
|
||||
MethodHandle invoker0 = invokers(entryType.generic()).exactInvoker();
|
||||
MethodType rawEntryTypeInit;
|
||||
Adapter ad = findAdapter(rawEntryTypeInit = entryType);
|
||||
if (ad != null) {
|
||||
|
48
jdk/src/share/classes/sun/dyn/WrapperInstance.java
Normal file
48
jdk/src/share/classes/sun/dyn/WrapperInstance.java
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 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 sun.dyn;
|
||||
|
||||
import java.dyn.MethodHandle;
|
||||
|
||||
/**
|
||||
* Private API used inside of java.dyn.MethodHandles.
|
||||
* Interface implemented by every object which is produced by
|
||||
* {@link java.dyn.MethodHandles#asInstance MethodHandles.asInstance}.
|
||||
* The methods of this interface allow a caller to recover the parameters
|
||||
* to {@code asInstance}.
|
||||
* This allows applications to repeatedly convert between method handles
|
||||
* and SAM objects, without the risk of creating unbounded delegation chains.
|
||||
*/
|
||||
public interface WrapperInstance {
|
||||
/** Produce or recover a target method handle which is behaviorally
|
||||
* equivalent to the SAM method of this object.
|
||||
*/
|
||||
public MethodHandle getWrapperInstanceTarget();
|
||||
/** Recover the SAM type for which this object was created.
|
||||
*/
|
||||
public Class<?> getWrapperInstanceType();
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ public class InvokeGenericTest {
|
||||
MethodHandle callable(List<Class<?>> params) {
|
||||
MethodHandle mh = CALLABLES.get(params);
|
||||
if (mh == null) {
|
||||
mh = collectArguments(collector_MH, methodType(Object.class, params));
|
||||
mh = collector_MH.asType(methodType(Object.class, params));
|
||||
CALLABLES.put(params, mh);
|
||||
}
|
||||
return mh;
|
||||
|
@ -151,8 +151,8 @@ MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
// mt is (char,char)String
|
||||
mt = MethodType.methodType(String.class, char.class, char.class);
|
||||
mh = lookup.findVirtual(String.class, "replace", mt);
|
||||
// (Ljava/lang/String;CC)Ljava/lang/String;
|
||||
s = (String) mh.invokeExact("daddy",'d','n');
|
||||
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
|
||||
assert(s.equals("nanny"));
|
||||
// weakly typed invocation (using MHs.invoke)
|
||||
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
|
||||
@ -162,23 +162,24 @@ mt = MethodType.methodType(java.util.List.class, Object[].class);
|
||||
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
|
||||
assert(mh.isVarargsCollector());
|
||||
x = mh.invokeGeneric("one", "two");
|
||||
// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||
assert(x.equals(java.util.Arrays.asList("one","two")));
|
||||
// mt is (Object,Object,Object)Object
|
||||
mt = MethodType.genericMethodType(3);
|
||||
mh = MethodHandles.collectArguments(mh, mt);
|
||||
// mt is (Object,Object,Object)Object
|
||||
// (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
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}
|
||||
mt = MethodType.methodType(int.class);
|
||||
mh = lookup.findVirtual(java.util.List.class, "size", mt);
|
||||
// (Ljava/util/List;)I
|
||||
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
|
||||
// invokeExact(Ljava/util/List;)I
|
||||
assert(i == 3);
|
||||
mt = MethodType.methodType(void.class, String.class);
|
||||
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
|
||||
mh.invokeExact(System.out, "Hello, world.");
|
||||
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
|
||||
{}
|
||||
}}
|
||||
}
|
||||
@ -206,9 +207,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
|
||||
MethodHandle vamh = publicLookup()
|
||||
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
|
||||
.asVarargsCollector(Object[].class);
|
||||
MethodHandle invokeExact = publicLookup()
|
||||
.findVirtual(MethodHandle.class, "invokeExact", vamh.type());
|
||||
MethodHandle mh = invokeExact.bindTo(vamh);
|
||||
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
|
||||
assert(vamh.type().equals(mh.type()));
|
||||
assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
|
||||
boolean failed = false;
|
||||
|
@ -323,6 +323,44 @@ public class MethodHandlesTest {
|
||||
return x.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
/** Return lambda(arg...[arity]) { new Object[]{ arg... } } */
|
||||
static MethodHandle varargsList(int arity) {
|
||||
return ValueConversions.varargsList(arity);
|
||||
}
|
||||
/** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */
|
||||
static MethodHandle varargsArray(int arity) {
|
||||
return ValueConversions.varargsArray(arity);
|
||||
}
|
||||
/** Variation of varargsList, but with the given rtype. */
|
||||
static MethodHandle varargsList(int arity, Class<?> rtype) {
|
||||
MethodHandle list = varargsList(arity);
|
||||
MethodType listType = list.type().changeReturnType(rtype);
|
||||
if (List.class.isAssignableFrom(rtype) || rtype == void.class || rtype == Object.class) {
|
||||
// OK
|
||||
} else if (rtype.isAssignableFrom(String.class)) {
|
||||
if (LIST_TO_STRING == null)
|
||||
try {
|
||||
LIST_TO_STRING = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToString",
|
||||
MethodType.methodType(String.class, List.class));
|
||||
} catch (Exception ex) { throw new RuntimeException(ex); }
|
||||
list = MethodHandles.filterReturnValue(list, LIST_TO_STRING);
|
||||
} else if (rtype.isPrimitive()) {
|
||||
if (LIST_TO_INT == null)
|
||||
try {
|
||||
LIST_TO_INT = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToInt",
|
||||
MethodType.methodType(int.class, List.class));
|
||||
} catch (Exception ex) { throw new RuntimeException(ex); }
|
||||
list = MethodHandles.filterReturnValue(list, LIST_TO_INT);
|
||||
list = MethodHandles.explicitCastArguments(list, listType);
|
||||
} else {
|
||||
throw new RuntimeException("varargsList: "+rtype);
|
||||
}
|
||||
return list.asType(listType);
|
||||
}
|
||||
private static MethodHandle LIST_TO_STRING, LIST_TO_INT;
|
||||
private static String listToString(List x) { return x.toString(); }
|
||||
private static int listToInt(List x) { return x.toString().hashCode(); }
|
||||
|
||||
static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
|
||||
return changeArgTypes(target, 0, 999, argType);
|
||||
}
|
||||
@ -1302,7 +1340,7 @@ public class MethodHandlesTest {
|
||||
}
|
||||
MethodType inType = MethodType.methodType(Object.class, types);
|
||||
MethodType outType = MethodType.methodType(Object.class, permTypes);
|
||||
MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType);
|
||||
MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType);
|
||||
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
|
||||
Object result = newTarget.invokeWithArguments(args);
|
||||
Object expected = Arrays.asList(permArgs);
|
||||
@ -1329,7 +1367,7 @@ public class MethodHandlesTest {
|
||||
}
|
||||
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
|
||||
countTest();
|
||||
MethodHandle target = ValueConversions.varargsArray(nargs);
|
||||
MethodHandle target = varargsArray(nargs);
|
||||
MethodHandle target2 = changeArgTypes(target, argType);
|
||||
if (verbosity >= 3)
|
||||
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
|
||||
@ -1359,7 +1397,7 @@ public class MethodHandlesTest {
|
||||
spreadParams.clear(); spreadParams.add(Object[].class);
|
||||
}
|
||||
MethodType newType = MethodType.methodType(Object.class, newParams);
|
||||
MethodHandle result = MethodHandles.spreadArguments(target2, newType);
|
||||
MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
|
||||
Object[] returnValue;
|
||||
if (pos == 0) {
|
||||
// In the following line, the first cast implies
|
||||
@ -1393,7 +1431,7 @@ public class MethodHandlesTest {
|
||||
public void testCollectArguments(Class<?> argType, int pos, int nargs) throws Throwable {
|
||||
countTest();
|
||||
// fake up a MH with the same type as the desired adapter:
|
||||
MethodHandle fake = ValueConversions.varargsArray(nargs);
|
||||
MethodHandle fake = varargsArray(nargs);
|
||||
fake = changeArgTypes(fake, argType);
|
||||
MethodType newType = fake.type();
|
||||
Object[] args = randomArgs(newType.parameterArray());
|
||||
@ -1401,12 +1439,12 @@ public class MethodHandlesTest {
|
||||
Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1);
|
||||
collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length);
|
||||
// here is the MH which will witness the collected argument tail:
|
||||
MethodHandle target = ValueConversions.varargsArray(pos+1);
|
||||
MethodHandle target = varargsArray(pos+1);
|
||||
target = changeArgTypes(target, 0, pos, argType);
|
||||
target = changeArgTypes(target, pos, pos+1, Object[].class);
|
||||
if (verbosity >= 3)
|
||||
System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
|
||||
MethodHandle result = MethodHandles.collectArguments(target, newType);
|
||||
MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType);
|
||||
Object[] returnValue = (Object[]) result.invokeWithArguments(args);
|
||||
// assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]);
|
||||
// returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]);
|
||||
@ -1430,7 +1468,7 @@ public class MethodHandlesTest {
|
||||
|
||||
void testInsertArguments(int nargs, int pos, int ins) throws Throwable {
|
||||
countTest();
|
||||
MethodHandle target = ValueConversions.varargsArray(nargs + ins);
|
||||
MethodHandle target = varargsArray(nargs + ins);
|
||||
Object[] args = randomArgs(target.type().parameterArray());
|
||||
List<Object> resList = Arrays.asList(args);
|
||||
List<Object> argsToPass = new ArrayList<Object>(resList);
|
||||
@ -1449,6 +1487,55 @@ public class MethodHandlesTest {
|
||||
assertEquals(resList, res2List);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterReturnValue() throws Throwable {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
startTest("filterReturnValue");
|
||||
Class<?> classOfVCList = varargsList(1).invokeWithArguments(0).getClass();
|
||||
assertTrue(List.class.isAssignableFrom(classOfVCList));
|
||||
for (int nargs = 0; nargs <= 3; nargs++) {
|
||||
for (Class<?> rtype : new Class[] { Object.class,
|
||||
List.class,
|
||||
int.class,
|
||||
//byte.class, //FIXME: add this
|
||||
//long.class, //FIXME: add this
|
||||
CharSequence.class,
|
||||
String.class }) {
|
||||
testFilterReturnValue(nargs, rtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testFilterReturnValue(int nargs, Class<?> rtype) throws Throwable {
|
||||
countTest();
|
||||
MethodHandle target = varargsList(nargs, rtype);
|
||||
MethodHandle filter;
|
||||
if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class))
|
||||
filter = varargsList(1); // add another layer of list-ness
|
||||
else
|
||||
filter = MethodHandles.identity(rtype);
|
||||
filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype));
|
||||
Object[] argsToPass = randomArgs(nargs, Object.class);
|
||||
if (verbosity >= 3)
|
||||
System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter);
|
||||
MethodHandle target2 = MethodHandles.filterReturnValue(target, filter);
|
||||
if (verbosity >= 4)
|
||||
System.out.println("filtered target: "+target2);
|
||||
// Simulate expected effect of filter on return value:
|
||||
Object unfiltered = target.invokeWithArguments(argsToPass);
|
||||
Object expected = filter.invokeWithArguments(unfiltered);
|
||||
if (verbosity >= 4)
|
||||
System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName());
|
||||
if (verbosity >= 4)
|
||||
System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName());
|
||||
Object result = target2.invokeWithArguments(argsToPass);
|
||||
if (verbosity >= 3)
|
||||
System.out.println("result: "+result+" : "+result.getClass().getSimpleName());
|
||||
if (!expected.equals(result))
|
||||
System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterArguments() throws Throwable {
|
||||
if (CAN_SKIP_WORKING) return;
|
||||
@ -1462,8 +1549,8 @@ public class MethodHandlesTest {
|
||||
|
||||
void testFilterArguments(int nargs, int pos) throws Throwable {
|
||||
countTest();
|
||||
MethodHandle target = ValueConversions.varargsList(nargs);
|
||||
MethodHandle filter = ValueConversions.varargsList(1);
|
||||
MethodHandle target = varargsList(nargs);
|
||||
MethodHandle filter = varargsList(1);
|
||||
filter = MethodHandles.convertArguments(filter, filter.type().generic());
|
||||
Object[] argsToPass = randomArgs(nargs, Object.class);
|
||||
if (verbosity >= 3)
|
||||
@ -1477,7 +1564,7 @@ public class MethodHandlesTest {
|
||||
if (verbosity >= 3)
|
||||
System.out.println("result: "+result);
|
||||
if (!expected.equals(result))
|
||||
System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+argsToPass+" => "+result);
|
||||
System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@ -1497,8 +1584,8 @@ public class MethodHandlesTest {
|
||||
void testFoldArguments(int nargs, int pos, int fold) throws Throwable {
|
||||
if (pos != 0) return; // can fold only at pos=0 for now
|
||||
countTest();
|
||||
MethodHandle target = ValueConversions.varargsList(1 + nargs);
|
||||
MethodHandle combine = ValueConversions.varargsList(fold).asType(MethodType.genericMethodType(fold));
|
||||
MethodHandle target = varargsList(1 + nargs);
|
||||
MethodHandle combine = varargsList(fold).asType(MethodType.genericMethodType(fold));
|
||||
List<Object> argsToPass = Arrays.asList(randomArgs(nargs, Object.class));
|
||||
if (verbosity >= 3)
|
||||
System.out.println("fold "+target+" with "+combine);
|
||||
@ -1514,7 +1601,7 @@ public class MethodHandlesTest {
|
||||
if (verbosity >= 3)
|
||||
System.out.println("result: "+result);
|
||||
if (!expected.equals(result))
|
||||
System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result);
|
||||
System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@ -1533,7 +1620,7 @@ public class MethodHandlesTest {
|
||||
|
||||
void testDropArguments(int nargs, int pos, int drop) throws Throwable {
|
||||
countTest();
|
||||
MethodHandle target = ValueConversions.varargsArray(nargs);
|
||||
MethodHandle target = varargsArray(nargs);
|
||||
Object[] args = randomArgs(target.type().parameterArray());
|
||||
MethodHandle target2 = MethodHandles.dropArguments(target, pos,
|
||||
Collections.nCopies(drop, Object.class).toArray(new Class[0]));
|
||||
@ -1584,7 +1671,8 @@ public class MethodHandlesTest {
|
||||
boolean testRetCode = type.returnType() != void.class;
|
||||
MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee",
|
||||
MethodType.genericMethodType(0, true));
|
||||
target = MethodHandles.collectArguments(target, type);
|
||||
assertTrue(target.isVarargsCollector());
|
||||
target = target.asType(type);
|
||||
Object[] args = randomArgs(type.parameterArray());
|
||||
List<Object> targetPlusArgs = new ArrayList<Object>(Arrays.asList(args));
|
||||
targetPlusArgs.add(0, target);
|
||||
@ -1808,7 +1896,7 @@ public class MethodHandlesTest {
|
||||
MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
|
||||
while (thrower.type().parameterCount() < nargs)
|
||||
thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class);
|
||||
MethodHandle catcher = ValueConversions.varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs));
|
||||
MethodHandle catcher = varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs));
|
||||
MethodHandle target = MethodHandles.catchException(thrower,
|
||||
thrown.getClass(), catcher);
|
||||
assertEquals(thrower.type(), target.type());
|
||||
@ -2079,7 +2167,7 @@ public class MethodHandlesTest {
|
||||
CharSequence.class,
|
||||
Example.class }) {
|
||||
try {
|
||||
MethodHandles.asInstance(ValueConversions.varargsArray(0), nonSAM);
|
||||
MethodHandles.asInstance(varargsArray(0), nonSAM);
|
||||
System.out.println("Failed to throw");
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
@ -2183,22 +2271,22 @@ class ValueConversions {
|
||||
Object a8, Object a9)
|
||||
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
||||
static MethodHandle[] makeLists() {
|
||||
ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
|
||||
ArrayList<MethodHandle> lists = new ArrayList<MethodHandle>();
|
||||
MethodHandles.Lookup lookup = IMPL_LOOKUP;
|
||||
for (;;) {
|
||||
int nargs = arrays.size();
|
||||
int nargs = lists.size();
|
||||
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
|
||||
String name = "list";
|
||||
MethodHandle array = null;
|
||||
MethodHandle list = null;
|
||||
try {
|
||||
array = lookup.findStatic(ValueConversions.class, name, type);
|
||||
list = lookup.findStatic(ValueConversions.class, name, type);
|
||||
} catch (NoAccessException ex) {
|
||||
}
|
||||
if (array == null) break;
|
||||
arrays.add(array);
|
||||
if (list == null) break;
|
||||
lists.add(list);
|
||||
}
|
||||
assert(arrays.size() == 11); // current number of methods
|
||||
return arrays.toArray(new MethodHandle[0]);
|
||||
assert(lists.size() == 11); // current number of methods
|
||||
return lists.toArray(new MethodHandle[0]);
|
||||
}
|
||||
static final MethodHandle[] LISTS = makeLists();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user