mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-29 06:42:31 +00:00
7001424: implement JSR 292 EG adjustments, November 2010
Reviewed-by: twisti
This commit is contained in:
parent
494c1f5c75
commit
34cd7bc224
@ -35,14 +35,25 @@ import java.util.Collection;
|
||||
* which is called its {@code target}.
|
||||
* An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
|
||||
* all calls to the site's current target.
|
||||
* A {@code CallSite} may be associated with several {@code invokedynamic}
|
||||
* instructions, or it may be "free floating", associated with none.
|
||||
* In any case, it may be invoked through an associated method handle
|
||||
* called its {@linkplain #dynamicInvoker dynamic invoker}.
|
||||
* <p>
|
||||
* If a mutable target is not required, the {@code invokedynamic} instruction
|
||||
* should be linked to a {@linkplain ConstantCallSite constant call site}.
|
||||
* If a volatile target is required, because updates to the target must be
|
||||
* reliably witnessed by other threads, the {@code invokedynamic} instruction
|
||||
* should be linked to a {@linkplain VolatileCallSite volatile call site}.
|
||||
* {@code CallSite} is an abstract class which does not allow
|
||||
* direct subclassing by users. It has three immediate,
|
||||
* concrete subclasses that may be either instantiated or subclassed.
|
||||
* <ul>
|
||||
* <li>If a mutable target is not required, an {@code invokedynamic} instruction
|
||||
* may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
|
||||
* <li>If a mutable target is required which has volatile variable semantics,
|
||||
* because updates to the target must be immediately and reliably witnessed by other threads,
|
||||
* a {@linkplain VolatileCallSite volatile call site} may be used.
|
||||
* <li>Otherwise, if a mutable target is required,
|
||||
* a {@linkplain MutableCallSite mutable call site} may be used.
|
||||
* </ul>
|
||||
* <p>
|
||||
* A call site may be <em>relinked</em> by changing its target.
|
||||
* A non-constant call site may be <em>relinked</em> by changing its target.
|
||||
* The new target must have the same {@linkplain MethodHandle#type() type}
|
||||
* as the previous target.
|
||||
* Thus, though a call site can be relinked to a series of
|
||||
@ -72,6 +83,7 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam
|
||||
</pre></blockquote>
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
abstract
|
||||
public class CallSite {
|
||||
private static final Access IMPL_TOKEN = Access.getToken();
|
||||
|
||||
@ -87,7 +99,6 @@ public class CallSite {
|
||||
private MemberName calleeNameRemoveForPFD;
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* 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.
|
||||
@ -95,16 +106,20 @@ public class CallSite {
|
||||
* Before this {@code CallSite} object is returned from a bootstrap method,
|
||||
* it is usually provided with a more useful target method,
|
||||
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public CallSite(MethodType type) {
|
||||
/*package-private*/
|
||||
CallSite(MethodType type) {
|
||||
target = MethodHandles.invokers(type).uninitializedCallSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public CallSite(MethodHandle target) {
|
||||
/*package-private*/
|
||||
CallSite(MethodHandle target) {
|
||||
target.type(); // null check
|
||||
this.target = target;
|
||||
}
|
||||
@ -199,6 +214,8 @@ public class CallSite {
|
||||
* @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}
|
||||
*/
|
||||
public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(this.target, newTarget);
|
||||
@ -216,14 +233,6 @@ public class CallSite {
|
||||
return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
|
||||
}
|
||||
|
||||
/** Produce a printed representation that displays information about this call site
|
||||
* that may be useful to the human reader.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a method handle equivalent to an invokedynamic instruction
|
||||
* which has been linked to this call site.
|
||||
@ -240,7 +249,7 @@ public class CallSite {
|
||||
*/
|
||||
public final MethodHandle dynamicInvoker() {
|
||||
if (this instanceof ConstantCallSite) {
|
||||
return target; // will not change dynamically
|
||||
return getTarget0(); // will not change dynamically
|
||||
}
|
||||
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
|
||||
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
|
||||
|
||||
@ -28,44 +28,78 @@ package java.dyn;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
/**
|
||||
* Lazily associate a computed value with (potentially) every class.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public abstract class ClassValue<T> {
|
||||
public class ClassValue<T> {
|
||||
/**
|
||||
* Compute the given class's derived value for this {@code ClassValue}.
|
||||
* <p>
|
||||
* This method will be invoked within the first thread that accesses
|
||||
* the value with the {@link #get}.
|
||||
* the value with the {@link #get get} method.
|
||||
* <p>
|
||||
* Normally, this method is invoked at most once per class,
|
||||
* but it may be invoked again in case of subsequent invocations
|
||||
* of {@link #remove} followed by {@link #get}.
|
||||
* 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.
|
||||
*
|
||||
* @return the computed value for this thread-local
|
||||
* @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)
|
||||
*/
|
||||
protected abstract T computeValue(Class<?> type);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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} method.
|
||||
* by an invocation of the {@link #computeValue computeValue} method.
|
||||
* <p>
|
||||
* The actual installation of the value on the class
|
||||
* is performed while the class's synchronization lock
|
||||
* is held. At that point, if racing threads have
|
||||
* is performed atomically.
|
||||
* At that point, if racing threads have
|
||||
* computed values, one is chosen, and returned to
|
||||
* all the racing threads.
|
||||
*
|
||||
* @return the current thread's value of this thread-local
|
||||
* @return the current value associated with this {@code ClassValue}, for the given class or interface
|
||||
*/
|
||||
public T get(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
@ -81,9 +115,16 @@ public abstract class ClassValue<T> {
|
||||
/**
|
||||
* Removes the associated value for the given class.
|
||||
* If this value is subsequently {@linkplain #get read} for the same class,
|
||||
* its value will be reinitialized by invoking its {@link #computeValue} method.
|
||||
* its value will be reinitialized by invoking its {@link #computeValue computeValue} method.
|
||||
* This may result in an additional invocation of the
|
||||
* {@code computeValue} method for the given class.
|
||||
* {@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}.
|
||||
*/
|
||||
public void remove(Class<?> type) {
|
||||
ClassValueMap map = getMap(type);
|
||||
@ -118,6 +159,7 @@ public abstract class ClassValue<T> {
|
||||
// Warm up the table with a null entry.
|
||||
map.preInitializeEntry(this);
|
||||
}
|
||||
STORE_BARRIER.lazySet(0);
|
||||
// All stores pending from table expansion are completed.
|
||||
synchronized (map) {
|
||||
value = (T) map.initializeEntry(this, value);
|
||||
|
||||
@ -32,14 +32,16 @@ package java.dyn;
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class ConstantCallSite extends CallSite {
|
||||
/** Create a call site with a permanent target. */
|
||||
/** Create a call site with a permanent target.
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public ConstantCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
/**
|
||||
* Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target.
|
||||
* Throw an {@link UnsupportedOperationException}, because this kind of call site cannot change its target.
|
||||
*/
|
||||
@Override public final void setTarget(MethodHandle ignore) {
|
||||
throw new IllegalArgumentException("ConstantCallSite");
|
||||
throw new UnsupportedOperationException("ConstantCallSite");
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,4 +67,16 @@ public class InvokeDynamicBootstrapError extends LinkageError {
|
||||
public InvokeDynamicBootstrapError(String s, Throwable cause) {
|
||||
super(s, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code InvokeDynamicBootstrapError} with the specified
|
||||
* cause.
|
||||
*
|
||||
* @param cause the cause, may be {@code null}.
|
||||
*/
|
||||
public InvokeDynamicBootstrapError(Throwable cause) {
|
||||
// cf. Throwable(Throwable cause) constructor.
|
||||
super(cause == null ? null : cause.toString());
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,8 +128,11 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
|
||||
* <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_Methodref} or
|
||||
* {@code CONSTANT_InterfaceMethodref} constant pool entry.
|
||||
* which refers to a {@code CONSTANT_MethodHandle} constant pool entry.
|
||||
* (Each such entry refers directly to a {@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>.)
|
||||
* <p>
|
||||
* Java code can also use a reflective API called
|
||||
* {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
|
||||
@ -185,12 +188,20 @@ mh = lookup.findVirtual(java.util.List.class, "size", mt);
|
||||
// (Ljava/util/List;)I
|
||||
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
|
||||
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
|
||||
* </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 type parameter.
|
||||
* (This type parameter may be a primitive, and it defaults to {@code Object}.)
|
||||
* 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}.
|
||||
* <p>
|
||||
* <em>A note on generic typing:</em> Method handles do not represent
|
||||
* their function types in terms of Java parameterized (generic) types,
|
||||
@ -216,6 +227,19 @@ assert(i == 3);
|
||||
* 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.
|
||||
*
|
||||
* @see MethodType
|
||||
* @see MethodHandles
|
||||
@ -282,8 +306,20 @@ public abstract class MethodHandle
|
||||
});
|
||||
}
|
||||
|
||||
/** Produce a printed representation that displays information about this call site
|
||||
* that may be useful to the human reader.
|
||||
/**
|
||||
* Returns a string representation of the method handle,
|
||||
* starting with the string {@code "MethodHandle"} and
|
||||
* ending with the string representation of the method handle's type.
|
||||
* In other words, this method returns a string equal to the value of:
|
||||
* <blockquote><pre>
|
||||
* "MethodHandle" + type().toString()
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* Note: Future releases of this API may add further information
|
||||
* to the string representation.
|
||||
* Therefore, the present syntax should not be parsed by applications.
|
||||
*
|
||||
* @return a string representation of the method handle
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -512,7 +548,7 @@ public abstract class MethodHandle
|
||||
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
|
||||
int keepPosArgs = nargs - arrayLength;
|
||||
MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
|
||||
newType = newType.insertParameterTypes(keepPosArgs, arrayElement);
|
||||
newType = newType.insertParameterTypes(keepPosArgs, arrayType);
|
||||
return MethodHandles.spreadArguments(this, newType);
|
||||
}
|
||||
|
||||
@ -610,6 +646,14 @@ public abstract class MethodHandle
|
||||
* else if the type of the target does not exactly match
|
||||
* the requested type, a {@link WrongMethodTypeException} is thrown.
|
||||
* <p>
|
||||
* A method handle's type handler is not guaranteed to be called every
|
||||
* time its {@code asType} or {@code invokeGeneric} method is called.
|
||||
* If the implementation is faced is able to prove that an equivalent
|
||||
* type handler call has already occurred (on the same two arguments),
|
||||
* it may substitute the result of that previous invocation, without
|
||||
* making a new invocation. Thus, type handlers should not (in general)
|
||||
* perform significant side effects.
|
||||
* <p>
|
||||
* Therefore, the type handler is invoked as if by this code:
|
||||
* <blockquote><pre>
|
||||
* MethodHandle target = this; // original method handle
|
||||
@ -637,9 +681,9 @@ MethodHandle collectingTypeHandler = lookup()
|
||||
methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
|
||||
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
|
||||
|
||||
System.out.println(makeAnyList.invokeGeneric()); // prints []
|
||||
System.out.println(makeAnyList.invokeGeneric(1)); // prints [1]
|
||||
System.out.println(makeAnyList.invokeGeneric("two", "too")); // prints [two, too]
|
||||
assertEquals("[]", makeAnyList.invokeGeneric().toString());
|
||||
assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
|
||||
assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
|
||||
* <pre><blockquote>
|
||||
*/
|
||||
public MethodHandle withTypeHandler(MethodHandle typeHandler) {
|
||||
|
||||
@ -155,16 +155,39 @@ public class MethodHandles {
|
||||
/** The class on behalf of whom the lookup is being performed. */
|
||||
private final Class<?> lookupClass;
|
||||
|
||||
/** The allowed sorts of members which may be looked up (public, etc.), with STATIC for package. */
|
||||
/** The allowed sorts of members which may be looked up (PUBLIC, etc.). */
|
||||
private final int allowedModes;
|
||||
|
||||
private static final int
|
||||
PUBLIC = Modifier.PUBLIC,
|
||||
PACKAGE = Modifier.STATIC,
|
||||
PROTECTED = Modifier.PROTECTED,
|
||||
PRIVATE = Modifier.PRIVATE,
|
||||
ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE),
|
||||
TRUSTED = -1;
|
||||
/** A single-bit mask representing {@code public} access,
|
||||
* which may contribute to the result of {@link #lookupModes lookupModes}.
|
||||
* The value, {@code 0x01}, happens to be the same as the value of the
|
||||
* {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}.
|
||||
*/
|
||||
public static final int PUBLIC = Modifier.PUBLIC;
|
||||
|
||||
/** A single-bit mask representing {@code private} access,
|
||||
* which may contribute to the result of {@link #lookupModes lookupModes}.
|
||||
* The value, {@code 0x02}, happens to be the same as the value of the
|
||||
* {@code private} {@linkplain java.lang.reflect.Modifier#PRIVATE modifier bit}.
|
||||
*/
|
||||
public static final int PRIVATE = Modifier.PRIVATE;
|
||||
|
||||
/** A single-bit mask representing {@code protected} access,
|
||||
* which may contribute to the result of {@link #lookupModes lookupModes}.
|
||||
* The value, {@code 0x04}, happens to be the same as the value of the
|
||||
* {@code protected} {@linkplain java.lang.reflect.Modifier#PROTECTED modifier bit}.
|
||||
*/
|
||||
public static final int PROTECTED = Modifier.PROTECTED;
|
||||
|
||||
/** A single-bit mask representing {@code package} access (default access),
|
||||
* which may contribute to the result of {@link #lookupModes lookupModes}.
|
||||
* The value is {@code 0x08}, which does not correspond meaningfully to
|
||||
* any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
|
||||
*/
|
||||
public static final int PACKAGE = Modifier.STATIC;
|
||||
|
||||
private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE);
|
||||
private static final int TRUSTED = -1;
|
||||
|
||||
private static int fixmods(int mods) {
|
||||
mods &= (ALL_MODES - PACKAGE);
|
||||
@ -189,13 +212,11 @@ public class MethodHandles {
|
||||
}
|
||||
|
||||
/** Which types of members can this lookup object produce?
|
||||
* The result is a bit-mask of the {@link java.lang.reflect.Modifier Modifier} bits
|
||||
* {@linkplain java.lang.reflect.Modifier#PUBLIC PUBLIC (0x01)},
|
||||
* {@linkplain java.lang.reflect.Modifier#PROTECTED PROTECTED (0x02)},
|
||||
* {@linkplain java.lang.reflect.Modifier#PRIVATE PRIVATE (0x04)},
|
||||
* and {@linkplain java.lang.reflect.Modifier#STATIC STATIC (0x08)}.
|
||||
* The modifier bit {@code STATIC} stands in for the package protection mode,
|
||||
* which does not have an explicit modifier bit.
|
||||
* The result is a bit-mask of the bits
|
||||
* {@linkplain #PUBLIC PUBLIC (0x01)},
|
||||
* {@linkplain #PRIVATE PRIVATE (0x02)},
|
||||
* {@linkplain #PROTECTED PROTECTED (0x04)},
|
||||
* and {@linkplain #PACKAGE PACKAGE (0x08)}.
|
||||
* <p>
|
||||
* A freshly-created lookup object
|
||||
* on the {@linkplain java.dyn.MethodHandles#lookup() caller's class}
|
||||
@ -238,7 +259,7 @@ public class MethodHandles {
|
||||
/**
|
||||
* Create a lookup on the specified new lookup class.
|
||||
* The resulting object will report the specified
|
||||
* class as its own {@link #lookupClass}.
|
||||
* class as its own {@link #lookupClass lookupClass}.
|
||||
* <p>
|
||||
* However, the resulting {@code Lookup} object is guaranteed
|
||||
* to have no more access capabilities than the original.
|
||||
@ -300,35 +321,43 @@ public class MethodHandles {
|
||||
throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
|
||||
}
|
||||
|
||||
/** Display the name of the class.
|
||||
* If there are restrictions on the access permitted to this lookup,
|
||||
* display those also.
|
||||
/**
|
||||
* Display the name of the class from which lookups are to be made.
|
||||
* (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.)
|
||||
* If there are restrictions on the access permitted to this lookup,
|
||||
* this is indicated by adding a suffix to the class name, consisting
|
||||
* of a slash and a keyword. The keyword is chosen as follows:
|
||||
* <ul>
|
||||
* <li>If no access is allowed, the suffix is "/noaccess".
|
||||
* <li>If only public access is allowed, the suffix is "/public".
|
||||
* <li>If only public and package access are allowed, the suffix is "/package".
|
||||
* <li>If only public, package, and private access are allowed, the suffix is "/private".
|
||||
* </ul>
|
||||
* If none of the above cases apply, it is the case that full
|
||||
* access (public, package, private, and protected) is allowed.
|
||||
* In this case, no suffix is added.
|
||||
* This is true only of an object obtained originally from
|
||||
* {@link java.dyn.MethodHandles#lookup() MethodHandles.lookup}.
|
||||
* Objects created by {@link java.dyn.MethodHandles.Lookup#in() Lookup#in}
|
||||
* always have restricted access, and will display a suffix.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String modestr;
|
||||
String cname = lookupClass.getName();
|
||||
switch (allowedModes) {
|
||||
case TRUSTED:
|
||||
return "/trusted";
|
||||
return "/trusted"; // internal only
|
||||
case PUBLIC:
|
||||
modestr = "/public";
|
||||
if (lookupClass == Object.class)
|
||||
return modestr;
|
||||
break;
|
||||
return cname + "/public";
|
||||
case PUBLIC|PACKAGE:
|
||||
return cname + "/package";
|
||||
case 0: // no privileges
|
||||
return cname + "/noaccess";
|
||||
case ALL_MODES:
|
||||
return cname;
|
||||
default:
|
||||
return cname + "/private";
|
||||
}
|
||||
StringBuilder buf = new StringBuilder(cname);
|
||||
if ((allowedModes & PUBLIC) != 0) buf.append("/public");
|
||||
if ((allowedModes & PACKAGE) != 0) buf.append("/package");
|
||||
if ((allowedModes & PROTECTED) != 0) buf.append("/protected");
|
||||
if ((allowedModes & PRIVATE) != 0) buf.append("/private");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
// call this from an entry point method in Lookup with extraFrames=0.
|
||||
@ -369,13 +398,6 @@ public class MethodHandles {
|
||||
* with the receiver type (usually {@code refc}) prepended.
|
||||
* The method and all its argument types must be accessible to the lookup class.
|
||||
* <p>
|
||||
* (<em>BUG NOTE:</em> The type {@code Object} may be prepended instead
|
||||
* of the receiver type, if the receiver type is not on the boot class path.
|
||||
* This is due to a temporary JVM limitation, in which MethodHandle
|
||||
* claims to be unable to access such classes. To work around this
|
||||
* bug, use {@code convertArguments} to normalize the type of the leading
|
||||
* argument to a type on the boot class path, such as {@code Object}.)
|
||||
* <p>
|
||||
* When called, the handle will treat the first argument as a receiver
|
||||
* and dispatch on the receiver's type to determine which method
|
||||
* implementation to enter.
|
||||
@ -923,18 +945,6 @@ public class MethodHandles {
|
||||
return invokers(type).exactInvoker();
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
|
||||
* Produce a method handle equivalent to an invokedynamic instruction
|
||||
* which has been linked to the given call site.
|
||||
* @return a method handle which always invokes the call site's target
|
||||
* @deprecated Use {@link CallSite#dynamicInvoker} instead.
|
||||
*/
|
||||
public static
|
||||
MethodHandle dynamicInvoker(CallSite site) throws NoAccessException {
|
||||
return site.dynamicInvoker();
|
||||
}
|
||||
|
||||
static Invokers invokers(MethodType type) {
|
||||
return MethodTypeImpl.invokers(IMPL_TOKEN, type);
|
||||
}
|
||||
@ -1071,7 +1081,6 @@ public class MethodHandles {
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a method handle which adapts the type of the
|
||||
* given method handle to a new type by pairwise argument conversion.
|
||||
* The original type and new type must have the same number of arguments.
|
||||
@ -1292,7 +1301,6 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a method handle of the requested return type which returns the given
|
||||
* constant value every time it is invoked.
|
||||
* <p>
|
||||
@ -1318,7 +1326,6 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a method handle of the requested type which returns the given
|
||||
* constant value every time it is invoked.
|
||||
* <p>
|
||||
@ -1343,7 +1350,6 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a method handle which returns its sole argument when invoked.
|
||||
* <p>The identity function for {@code void} takes no arguments and returns no values.
|
||||
* @param type the type of the sole parameter and return value of the desired method handle
|
||||
@ -1355,13 +1361,11 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a method handle of the requested type which returns its argument when invoked.
|
||||
* If the return type differs from the first argument type, the argument will be
|
||||
* converted as if by {@link #explicitCastArguments explicitCastArguments}.
|
||||
* All other arguments are discarded.
|
||||
* If there are additional arguments beyond the first, they are discarded.
|
||||
* <p>The identity function for {@code void} discards all its arguments.
|
||||
* <p>
|
||||
* @param type the type of the desired method handle
|
||||
* @return a method handle of the given type, which always returns its first argument
|
||||
* @throws WrongMethodTypeException if the first argument cannot be converted to the required return type
|
||||
@ -1446,20 +1450,20 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* <p><blockquote><pre>
|
||||
* import static java.dyn.MethodHandles.*;
|
||||
* import static java.dyn.MethodType.*;
|
||||
* ...
|
||||
* MethodHandle cat = lookup().findVirtual(String.class,
|
||||
* "concat", methodType(String.class, String.class));
|
||||
* System.out.println((String) cat.invokeExact("x", "y")); // xy
|
||||
* MethodHandle d0 = dropArguments(cat, 0, String.class);
|
||||
* System.out.println((String) d0.invokeExact("x", "y", "z")); // yz
|
||||
* MethodHandle d1 = dropArguments(cat, 1, String.class);
|
||||
* System.out.println((String) d1.invokeExact("x", "y", "z")); // xz
|
||||
* MethodHandle d2 = dropArguments(cat, 2, String.class);
|
||||
* System.out.println((String) d2.invokeExact("x", "y", "z")); // xy
|
||||
* MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
|
||||
* System.out.println((String) d12.invokeExact("x", 12, true, "z")); // xz
|
||||
import static java.dyn.MethodHandles.*;
|
||||
import static java.dyn.MethodType.*;
|
||||
...
|
||||
MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
assertEquals("xy", (String) cat.invokeExact("x", "y"));
|
||||
MethodHandle d0 = dropArguments(cat, 0, String.class);
|
||||
assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
|
||||
MethodHandle d1 = dropArguments(cat, 1, String.class);
|
||||
assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
|
||||
MethodHandle d2 = dropArguments(cat, 2, String.class);
|
||||
assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
|
||||
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
|
||||
assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
|
||||
* </pre></blockquote>
|
||||
* @param target the method handle to invoke after the arguments are dropped
|
||||
* @param valueTypes the type(s) of the argument(s) to drop
|
||||
@ -1532,13 +1536,13 @@ MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
MethodHandle upcase = lookup().findVirtual(String.class,
|
||||
"toUpperCase", methodType(String.class));
|
||||
System.out.println((String) cat.invokeExact("x", "y")); // xy
|
||||
assertEquals("xy", (String) cat.invokeExact("x", "y"));
|
||||
MethodHandle f0 = filterArguments(cat, 0, upcase);
|
||||
System.out.println((String) f0.invokeExact("x", "y")); // Xy
|
||||
assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
|
||||
MethodHandle f1 = filterArguments(cat, 1, upcase);
|
||||
System.out.println((String) f1.invokeExact("x", "y")); // xY
|
||||
assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
|
||||
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
|
||||
System.out.println((String) f2.invokeExact("x", "y")); // XY
|
||||
assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
||||
* </pre></blockquote>
|
||||
* @param target the method handle to invoke after arguments are filtered
|
||||
* @param pos the position of the first argument to filter
|
||||
@ -1571,7 +1575,7 @@ System.out.println((String) f2.invokeExact("x", "y")); // XY
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/** <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
/**
|
||||
* Adapt a target method handle {@code target} by post-processing
|
||||
* its return value with a unary filter function.
|
||||
* <p>
|
||||
@ -1693,6 +1697,9 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
* return fallback(a..., b...);
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* Note that the test arguments ({@code a...} in the pseudocode) cannot
|
||||
* be modified by execution of the test, and so are passed unchanged
|
||||
* from the caller to the target or fallback as appropriate.
|
||||
* @param test method handle used for test, must return boolean
|
||||
* @param target method handle to call if test passes
|
||||
* @param fallback method handle to call if test fails
|
||||
@ -1708,40 +1715,19 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
MethodType gtype = test.type();
|
||||
MethodType ttype = target.type();
|
||||
MethodType ftype = fallback.type();
|
||||
if (ttype != ftype)
|
||||
if (!ttype.equals(ftype))
|
||||
throw misMatchedTypes("target and fallback types", ttype, ftype);
|
||||
MethodType gtype2 = ttype.changeReturnType(boolean.class);
|
||||
if (gtype2 != gtype) {
|
||||
if (gtype.returnType() != boolean.class)
|
||||
throw newIllegalArgumentException("guard type is not a predicate "+gtype);
|
||||
int gpc = gtype.parameterCount(), tpc = ttype.parameterCount();
|
||||
if (gpc < tpc) {
|
||||
test = dropArguments(test, gpc, ttype.parameterList().subList(gpc, tpc));
|
||||
gtype = test.type();
|
||||
}
|
||||
if (gtype2 != gtype)
|
||||
if (gtype.returnType() != boolean.class)
|
||||
throw newIllegalArgumentException("guard type is not a predicate "+gtype);
|
||||
List<Class<?>> targs = ttype.parameterList();
|
||||
List<Class<?>> gargs = gtype.parameterList();
|
||||
if (!targs.equals(gargs)) {
|
||||
int gpc = gargs.size(), tpc = targs.size();
|
||||
if (gpc >= tpc || !targs.subList(0, gpc).equals(gargs))
|
||||
throw misMatchedTypes("target and test types", ttype, gtype);
|
||||
test = dropArguments(test, gpc, targs.subList(gpc, tpc));
|
||||
gtype = test.type();
|
||||
}
|
||||
/* {
|
||||
MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type());
|
||||
static MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) {
|
||||
return z ? t : f;
|
||||
}
|
||||
static MethodHandle compose(MethodHandle f, MethodHandle g) {
|
||||
Class<?> initargs = g.type().parameterArray();
|
||||
f = dropArguments(f, 1, initargs); // ignore 2nd copy of args
|
||||
return combineArguments(f, g);
|
||||
}
|
||||
// choose = \z.(z ? target : fallback)
|
||||
MethodHandle choose = findVirtual(MethodHandles.class, "choose",
|
||||
MethodType.methodType(boolean.class, MethodHandle.class, MethodHandle.class));
|
||||
choose = appendArgument(choose, target);
|
||||
choose = appendArgument(choose, fallback);
|
||||
MethodHandle dispatch = compose(choose, test);
|
||||
// dispatch = \(a...).(test(a...) ? target : fallback)
|
||||
return combineArguments(invoke, dispatch, 0);
|
||||
// return \(a...).((test(a...) ? target : fallback).invokeExact(a...))
|
||||
} */
|
||||
return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback);
|
||||
}
|
||||
|
||||
@ -1756,22 +1742,32 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
* If an exception matching the specified type is thrown, the fallback
|
||||
* handle is called instead on the exception, plus the original arguments.
|
||||
* <p>
|
||||
* The handler must have leading parameter of {@code exType} or a supertype,
|
||||
* followed by arguments which correspond <em>(how? TBD)</em> to
|
||||
* all the parameters of the target.
|
||||
* The target and handler must return the same type.
|
||||
* The target and handler must have the same corresponding
|
||||
* argument and return types, except that handler may omit trailing arguments
|
||||
* (similarly to the predicate in {@link #guardWithTest guardWithTest}).
|
||||
* Also, the handler must have an extra leading parameter of {@code exType} or a supertype.
|
||||
* <p> Here is pseudocode for the resulting adapter:
|
||||
* <blockquote><pre>
|
||||
* T target(A...);
|
||||
* T target(A..., B...);
|
||||
* T handler(ExType, A...);
|
||||
* T adapter(A... a) {
|
||||
* T adapter(A... a, B... b) {
|
||||
* try {
|
||||
* return target(a...);
|
||||
* return target(a..., b...);
|
||||
* } catch (ExType ex) {
|
||||
* return handler(ex, a...);
|
||||
* }
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* Note that the saved arguments ({@code a...} in the pseudocode) cannot
|
||||
* be modified by execution of the target, and so are passed unchanged
|
||||
* from the caller to the handler, if the handler is invoked.
|
||||
* <p>
|
||||
* The target and handler must return the same type, even if the handler
|
||||
* always throws. (This might happen, for instance, because the handler
|
||||
* is simulating a {@code finally} clause).
|
||||
* To create such a throwing handler, compose the handler creation logic
|
||||
* with {@link #throwException throwException},
|
||||
* in order to create a method handle of the correct return type.
|
||||
* @param target method handle to call
|
||||
* @param exType the type of exception which the handler will catch
|
||||
* @param handler method handle to call if a matching exception is thrown
|
||||
@ -1785,16 +1781,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
MethodHandle catchException(MethodHandle target,
|
||||
Class<? extends Throwable> exType,
|
||||
MethodHandle handler) {
|
||||
MethodType targetType = target.type();
|
||||
MethodType handlerType = handler.type();
|
||||
boolean ok = (targetType.parameterCount() ==
|
||||
handlerType.parameterCount() - 1);
|
||||
// for (int i = 0; ok && i < numExArgs; i++) {
|
||||
// if (targetType.parameterType(i) != handlerType.parameterType(1+i))
|
||||
// ok = false;
|
||||
// }
|
||||
if (!ok)
|
||||
throw newIllegalArgumentException("target and handler types do not match");
|
||||
MethodType ttype = target.type();
|
||||
MethodType htype = handler.type();
|
||||
if (htype.parameterCount() < 1 ||
|
||||
!htype.parameterType(0).isAssignableFrom(exType))
|
||||
throw newIllegalArgumentException("handler does not accept exception type "+exType);
|
||||
if (htype.returnType() != ttype.returnType())
|
||||
throw misMatchedTypes("target and handler return types", ttype, htype);
|
||||
List<Class<?>> targs = ttype.parameterList();
|
||||
List<Class<?>> hargs = htype.parameterList();
|
||||
hargs = hargs.subList(1, hargs.size()); // omit leading parameter from handler
|
||||
if (!targs.equals(hargs)) {
|
||||
int hpc = hargs.size(), tpc = targs.size();
|
||||
if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs))
|
||||
throw misMatchedTypes("target and handler types", ttype, htype);
|
||||
handler = dropArguments(handler, hpc, hargs.subList(hpc, tpc));
|
||||
htype = handler.type();
|
||||
}
|
||||
return MethodHandleImpl.makeGuardWithCatch(IMPL_TOKEN, target, exType, handler);
|
||||
}
|
||||
|
||||
@ -1813,10 +1816,10 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* Produce a wrapper instance of the given "SAM" type which redirects its calls to the given method handle.
|
||||
* A SAM type is a type which declares a single abstract method.
|
||||
* Additionally, it must have either no constructor (as an interface)
|
||||
* or have a public or protected constructor of zero arguments (as a class).
|
||||
* Produce a wrapper instance of the given "SAM" interface which redirects
|
||||
* its calls to the given method handle.
|
||||
* A SAM interface is an interface which declares a single abstract method.
|
||||
* The type must be public. (No additional access checks are performed.)
|
||||
* <p>
|
||||
* The resulting instance of the required SAM type will respond to
|
||||
* invocation of the SAM type's single abstract method by calling
|
||||
@ -1828,9 +1831,9 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
* The method handle may throw an <em>undeclared exception</em>,
|
||||
* which means any checked exception (or other checked throwable)
|
||||
* not declared by the SAM type's single abstract method.
|
||||
* If this happens, the throwable will be wrapped in an instance
|
||||
* of {@link UndeclaredThrowableException} and thrown in that
|
||||
* wrapped form.
|
||||
* If this happens, the throwable will be wrapped in an instance of
|
||||
* {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}
|
||||
* and thrown in that wrapped form.
|
||||
* <p>
|
||||
* The wrapper instance is guaranteed to be of a non-public
|
||||
* implementation class C in a package containing no classes
|
||||
@ -1841,18 +1844,36 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
* <li>the SAM type itself and any methods in the SAM type
|
||||
* <li>the supertypes of the SAM type (if any) and their methods
|
||||
* <li>{@link Object} and its methods
|
||||
* <li>{@link java.dyn.AsInstanceObject AsInstanceObject} and its methods</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* (Note: When determining the unique abstract method of a SAM interface,
|
||||
* the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode})
|
||||
* are disregarded. For example, {@link java.util.Comparator} is a SAM interface,
|
||||
* even though it re-declares the {@code Object.equals} method.)
|
||||
* <p>
|
||||
* No stable mapping is promised between the SAM type and
|
||||
* the implementation class C. Over time, several implementation
|
||||
* classes might be used for the same SAM type.
|
||||
* <p>
|
||||
* This method is not guaranteed to return a distinct
|
||||
* wrapper object for each separate call. If the JVM is able
|
||||
* to prove that a wrapper has already been created for a given
|
||||
* wrapper object for each separate call. If the implementation is able
|
||||
* to prove that a wrapper of the required SAM type
|
||||
* has already been created for a given
|
||||
* method handle, or for another method handle with the
|
||||
* same behavior, the JVM may return that wrapper in place of
|
||||
* same behavior, the implementation may return that wrapper in place of
|
||||
* a new wrapper.
|
||||
* <p>
|
||||
* This method is designed to apply to common use cases
|
||||
* where a single method handle must interoperate with
|
||||
* a type (class or interface) that implements a function-like
|
||||
* API. Additional variations, such as SAM classes with
|
||||
* private constructors, or interfaces with multiple but related
|
||||
* entry points, must be covered by hand-written or automatically
|
||||
* generated adapter classes. In those cases, consider implementing
|
||||
* {@link java.dyn.MethodHandles.AsInstanceObject AsInstanceObject}
|
||||
* in the adapters, so that generic code can extract the underlying
|
||||
* method handle without knowing where the SAM adapter came from.
|
||||
* @param target the method handle to invoke from the wrapper
|
||||
* @param samType the desired type of the wrapper, a SAM type
|
||||
* @return a correctly-typed wrapper for the given {@code target}
|
||||
@ -1870,7 +1891,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
throw new IllegalArgumentException("not a SAM type: "+samType.getName());
|
||||
MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes());
|
||||
if (!samMT.equals(target.type()))
|
||||
throw new IllegalArgumentException("wrong method type");
|
||||
throw new IllegalArgumentException("wrong method type: "+target+" should match "+sam);
|
||||
return samType.cast(Proxy.newProxyInstance(
|
||||
samType.getClassLoader(),
|
||||
new Class[]{ samType, AsInstanceObject.class },
|
||||
@ -1883,8 +1904,11 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (method.getDeclaringClass() == AsInstanceObject.class)
|
||||
return getArg(method.getName());
|
||||
assert method.equals(sam) : method;
|
||||
return target.invokeVarargs(args);
|
||||
if (method.equals(sam))
|
||||
return target.invokeVarargs(args);
|
||||
if (isObjectMethod(method))
|
||||
return callObjectMethod(this, method, args);
|
||||
throw new InternalError();
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -1907,13 +1931,44 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
public Class<?> getAsInstanceType();
|
||||
}
|
||||
|
||||
private static
|
||||
boolean isObjectMethod(Method m) {
|
||||
switch (m.getName()) {
|
||||
case "toString":
|
||||
return (m.getReturnType() == String.class
|
||||
&& m.getParameterTypes().length == 0);
|
||||
case "hashCode":
|
||||
return (m.getReturnType() == int.class
|
||||
&& m.getParameterTypes().length == 0);
|
||||
case "equals":
|
||||
return (m.getReturnType() == boolean.class
|
||||
&& m.getParameterTypes().length == 1
|
||||
&& m.getParameterTypes()[0] == Object.class);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static
|
||||
Object callObjectMethod(Object self, Method m, Object[] args) {
|
||||
assert(isObjectMethod(m)) : m;
|
||||
switch (m.getName()) {
|
||||
case "toString":
|
||||
return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode());
|
||||
case "hashCode":
|
||||
return System.identityHashCode(self);
|
||||
case "equals":
|
||||
return (self == args[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static
|
||||
Method getSamMethod(Class<?> samType) {
|
||||
Method sam = null;
|
||||
for (Method m : samType.getMethods()) {
|
||||
int mod = m.getModifiers();
|
||||
if (Modifier.isAbstract(mod)) {
|
||||
if (sam != null)
|
||||
if (sam != null && !isObjectMethod(sam))
|
||||
return null; // too many abstract methods
|
||||
sam = m;
|
||||
}
|
||||
|
||||
@ -56,21 +56,33 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
|
||||
* <p>
|
||||
* This type can be created only by factory methods.
|
||||
* All factory methods may cache values, though caching is not guaranteed.
|
||||
* Some factory methods are static, while others are virtual methods which
|
||||
* modify precursor method types, e.g., by changing a selected parameter.
|
||||
* <p>
|
||||
* Factory methods which operate on groups of parameter types
|
||||
* are systematically presented in two versions, so that both Java arrays and
|
||||
* Java lists can be used to work with groups of parameter types.
|
||||
* The query methods {@code parameterArray} and {@code parameterList}
|
||||
* also provide a choice between arrays and lists.
|
||||
* <p>
|
||||
* {@code MethodType} objects are sometimes derived from bytecode instructions
|
||||
* such as {@code invokedynamic}, specifically from the type descriptor strings associated
|
||||
* with the instructions in a class file's constant pool.
|
||||
* When this occurs, any classes named in the descriptor strings must be loaded.
|
||||
* (But they need not be initialized.)
|
||||
* This loading may occur at any time before the {@code MethodType} object is first derived.
|
||||
* <p>
|
||||
* Like classes and strings, method types 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 its component classes to be loaded as necessary.
|
||||
* 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.
|
||||
* 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>
|
||||
* When the JVM materializes a {@code MethodType} from a descriptor string,
|
||||
* all classes named in the descriptor must be accessible, and will be loaded.
|
||||
* (But the classes need not be initialized, as is the case with a {@code CONSTANT_Class}.)
|
||||
* This loading may occur at any time before the {@code MethodType} object is first derived.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public final
|
||||
class MethodType implements java.lang.reflect.Type {
|
||||
class MethodType {
|
||||
private final Class<?> rtype;
|
||||
private final Class<?>[] ptypes;
|
||||
private MethodTypeForm form; // erased form, plus cached data about primitives
|
||||
@ -300,6 +312,14 @@ class MethodType implements java.lang.reflect.Type {
|
||||
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[])}.
|
||||
* @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
|
||||
@ -508,7 +528,9 @@ class MethodType implements java.lang.reflect.Type {
|
||||
* parenthesis enclosed, comma separated list of type names,
|
||||
* followed immediately by the return type.
|
||||
* <p>
|
||||
* If a type name is array, it the base type followed
|
||||
* 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
|
||||
@ -517,35 +539,13 @@ class MethodType implements java.lang.reflect.Type {
|
||||
sb.append("(");
|
||||
for (int i = 0; i < ptypes.length; i++) {
|
||||
if (i > 0) sb.append(",");
|
||||
putName(sb, ptypes[i]);
|
||||
sb.append(ptypes[i].getSimpleName());
|
||||
}
|
||||
sb.append(")");
|
||||
putName(sb, rtype);
|
||||
sb.append(rtype.getSimpleName());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static void putName(StringBuilder sb, Class<?> cls) {
|
||||
int brackets = 0;
|
||||
while (cls.isArray()) {
|
||||
cls = cls.getComponentType();
|
||||
brackets++;
|
||||
}
|
||||
String n = cls.getName();
|
||||
/*
|
||||
if (n.startsWith("java.lang.")) {
|
||||
String nb = n.substring("java.lang.".length());
|
||||
if (nb.indexOf('.') < 0) n = nb;
|
||||
} else if (n.indexOf('.') < 0) {
|
||||
n = "."+n; // anonymous package
|
||||
}
|
||||
*/
|
||||
sb.append(n);
|
||||
while (brackets > 0) {
|
||||
sb.append("[]");
|
||||
brackets--;
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries which have to do with the bytecode architecture
|
||||
|
||||
/** The number of JVM stack slots required to invoke a method
|
||||
|
||||
206
jdk/src/share/classes/java/dyn/MutableCallSite.java
Normal file
206
jdk/src/share/classes/java/dyn/MutableCallSite.java
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 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;
|
||||
|
||||
import sun.dyn.*;
|
||||
import sun.dyn.empty.Empty;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A {@code MutableCallSite} is a {@link CallSite} whose target variable
|
||||
* behaves like an ordinary field.
|
||||
* An {@code invokedynamic} instruction linked to a {@code MutableCallSite} delegates
|
||||
* all calls to the site's current target.
|
||||
* The {@linkplain CallSite#dynamicInvoker dynamic invoker} of a mutable call site
|
||||
* also delegates each call to the site's current target.
|
||||
* <p>
|
||||
* Here is an example of a mutable call site which introduces a
|
||||
* state variable into a method handle chain.
|
||||
* <blockquote><pre>
|
||||
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
|
||||
MethodHandle MH_name = name.dynamicInvoker();
|
||||
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
|
||||
MethodHandle MH_upcase = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "toUpperCase", MT_str2);
|
||||
MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
|
||||
name.setTarget(MethodHandles.constant(String.class, "Rocky"));
|
||||
assertEquals("ROCKY", (String) worker1.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Fred"));
|
||||
assertEquals("FRED", (String) worker1.invokeExact());
|
||||
// (mutation can be continued indefinitely)
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* The same call site may be used in several places at once.
|
||||
* <blockquote><pre>
|
||||
MethodHandle MH_dear = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MT_str2).bindTo(", dear?");
|
||||
MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
|
||||
assertEquals("Fred, dear?", (String) worker2.invokeExact());
|
||||
name.setTarget(MethodHandles.constant(String.class, "Wilma"));
|
||||
assertEquals("WILMA", (String) worker1.invokeExact());
|
||||
assertEquals("Wilma, dear?", (String) worker2.invokeExact());
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* <em>Non-synchronization of target values:</em>
|
||||
* A write to a mutable call site's target does not force other threads
|
||||
* to become aware of the updated value. Threads which do not perform
|
||||
* suitable synchronization actions relative to the updated call site
|
||||
* may cache the old target value and delay their use of the new target
|
||||
* value indefinitely.
|
||||
* (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
|
||||
* to accept a new target value, even if there is no other synchronization.
|
||||
* <p>
|
||||
* For target values which will be frequently updated, consider using
|
||||
* a {@linkplain VolatileCallSite volatile call site} instead.
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Before this {@code CallSite} object is returned from a bootstrap method,
|
||||
* it is usually provided with a more useful target method,
|
||||
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public MutableCallSite(MethodType type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public MutableCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform 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>
|
||||
* This operation does not reverse any calls that have already started
|
||||
* on an old target value.
|
||||
* (Java supports {@linkplain java.lang.Object#wait() forward time travel} only.)
|
||||
* <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
|
||||
* (somehow) decached all previous versions of each call site's target.
|
||||
* <p>
|
||||
* To avoid race conditions, calls to {@code setTarget} and {@code sync}
|
||||
* 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).
|
||||
* On the other hand, reader threads may observe previous versions of
|
||||
* the target until the {@code sync} call returns
|
||||
* (and after the {@code setTarget} that attempts to convey the updated version).
|
||||
* <p>
|
||||
* 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
|
||||
* that may access one of the affected call sites.
|
||||
* <p>
|
||||
* The following effects are apparent, for each individual call site {@code S}:
|
||||
* <ul>
|
||||
* <li>A new volatile variable {@code V} is created, and written by the current thread.
|
||||
* As defined by the JMM, this write is a global synchronization event.
|
||||
* <li>As is normal with thread-local ordering of write events,
|
||||
* every action already performed by the current thread is
|
||||
* taken to happen before the volatile write to {@code V}.
|
||||
* (In some implementations, this means that the current thread
|
||||
* performs a global release operation.)
|
||||
* <li>Specifically, the write to the current target of {@code S} is
|
||||
* taken to happen before the volatile write to {@code V}.
|
||||
* <li>The volatile write to {@code V} is placed
|
||||
* (in an implementation specific manner)
|
||||
* in the global synchronization order.
|
||||
* <li>Consider an arbitrary thread {@code T} (other than the current thread).
|
||||
* If {@code T} executes a synchronization action {@code A}
|
||||
* after the volatile write to {@code V} (in the global synchronization order),
|
||||
* it is therefore required to see either the current target
|
||||
* of {@code S}, or a later write to that target,
|
||||
* if it executes a read on the target of {@code S}.
|
||||
* (This constraint is called "synchronization-order consistency".)
|
||||
* <li>The JMM specifically allows optimizing compilers to elide
|
||||
* reads or writes of variables that are known to be useless.
|
||||
* Such elided reads and writes have no effect on the happens-before
|
||||
* relation. Regardless of this fact, the volatile {@code V}
|
||||
* will not be elided, even though its written value is
|
||||
* indeterminate and its read value is not used.
|
||||
* </ul>
|
||||
* Because of the last point, the implementation behaves as if a
|
||||
* volatile read of {@code V} were performed by {@code T}
|
||||
* immediately after its action {@code A}. In the local ordering
|
||||
* of actions in {@code T}, this read happens before any future
|
||||
* read of the target of {@code S}. It is as if the
|
||||
* implementation arbitrarily picked a read of {@code S}'s target
|
||||
* by {@code T}, and forced a read of {@code V} to precede it,
|
||||
* 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}
|
||||
* 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.
|
||||
* 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.
|
||||
* 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 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>
|
||||
* 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,
|
||||
* and yet it will conform to the JMM behavior documented above.
|
||||
*/
|
||||
public static void sync(MutableCallSite[] sites) {
|
||||
STORE_BARRIER.lazySet(0);
|
||||
// FIXME: NYI
|
||||
}
|
||||
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
|
||||
}
|
||||
130
jdk/src/share/classes/java/dyn/Switcher.java
Normal file
130
jdk/src/share/classes/java/dyn/Switcher.java
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@ -28,63 +28,37 @@ package java.dyn;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable.
|
||||
* An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates
|
||||
* to its call site target immediately, even if the update occurs in another thread.
|
||||
* There may be a performance penalty for such tight coupling between threads.
|
||||
* <p>
|
||||
* Also, a volatile call site has the ability to be <em>invalidated</em>,
|
||||
* or reset to a well-defined fallback state.
|
||||
* Unlike {@code MutableCallSite}, there is no
|
||||
* {@linkplain MutableCallSite#sync sync operation} on volatile
|
||||
* call sites, since every write to a volatile variable is implicitly
|
||||
* synchronized with reader threads.
|
||||
* <p>
|
||||
* A volatile call site can be used as a switch to control the behavior
|
||||
* of another method handle. For example:
|
||||
* <blockquote><pre>
|
||||
MethodHandle strcat = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
|
||||
MethodHandle trueCon = MethodHandles.constant(boolean.class, true);
|
||||
MethodHandle falseCon = MethodHandles.constant(boolean.class, false);
|
||||
VolatileCallSite switcher = new VolatileCallSite(trueCon, falseCon);
|
||||
// following steps may be repeated to re-use the same switcher:
|
||||
MethodHandle worker1 = strcat;
|
||||
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, strcat.type(), 1, 0);
|
||||
MethodHandle worker = MethodHandles.guardWithTest(switcher.dynamicInvoker(), worker1, worker2);
|
||||
System.out.println((String) worker.invokeExact("met", "hod")); // method
|
||||
switcher.invalidate();
|
||||
System.out.println((String) worker.invokeExact("met", "hod")); // hodmet
|
||||
* </pre></blockquote>
|
||||
* In this case, the fallback path (worker2) does not cause a state change.
|
||||
* In a real application, the fallback path could cause call sites to relink
|
||||
* themselves in response to a global data structure change.
|
||||
* Thus, volatile call sites can be used to build dependency mechanisms.
|
||||
* In other respects, a {@code VolatileCallSite} is interchangeable
|
||||
* with {@code MutableCallSite}.
|
||||
* @see MutableCallSite
|
||||
* @author John Rose, JSR 292 EG
|
||||
*/
|
||||
public class VolatileCallSite extends CallSite {
|
||||
volatile MethodHandle fallback;
|
||||
|
||||
/** Create a call site with a volatile target.
|
||||
* The initial target and fallback are both set to a method handle
|
||||
* The initial target is set to a method handle
|
||||
* of the given type which will throw {@code IllegalStateException}.
|
||||
* @throws NullPointerException if the proposed type is null
|
||||
*/
|
||||
public VolatileCallSite(MethodType type) {
|
||||
super(type);
|
||||
fallback = target;
|
||||
}
|
||||
|
||||
/** Create a call site with a volatile target.
|
||||
* The fallback and target are both set to the same initial value.
|
||||
* The target is set to the given value.
|
||||
* @throws NullPointerException if the proposed target is null
|
||||
*/
|
||||
public VolatileCallSite(MethodHandle target) {
|
||||
super(target);
|
||||
fallback = target;
|
||||
}
|
||||
|
||||
/** Create a call site with a volatile target.
|
||||
* The fallback and target are set to the given initial values.
|
||||
*/
|
||||
public VolatileCallSite(MethodHandle target, MethodHandle fallback) {
|
||||
this(target);
|
||||
checkTargetChange(target, fallback); // make sure they have the same type
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
/** Internal override to nominally final getTarget. */
|
||||
@ -95,50 +69,11 @@ public class VolatileCallSite extends CallSite {
|
||||
|
||||
/**
|
||||
* Set the target method of this call site, as a volatile variable.
|
||||
* Has the same effect as {@link CallSite#setTarget}, with the additional
|
||||
* Has the same effect as {@link CallSite#setTarget CallSite.setTarget}, with the additional
|
||||
* effects associated with volatiles, in the Java Memory Model.
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(getTargetVolatile(), newTarget);
|
||||
setTargetVolatile(newTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fallback target for this call site.
|
||||
* It is initialized to the target the call site had when it was constructed,
|
||||
* but it may be changed by {@link setFallbackTarget}.
|
||||
* <p>
|
||||
* Like the regular target of a volatile call site,
|
||||
* the fallback target also has the behavior of a volatile variable.
|
||||
*/
|
||||
public MethodHandle getFallbackTarget() {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the fallback target for this call site.
|
||||
* @see #getFallbackTarget
|
||||
*/
|
||||
public void setFallbackTarget(MethodHandle newFallbackTarget) {
|
||||
checkTargetChange(fallback, newFallbackTarget);
|
||||
fallback = newFallbackTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this call site to a known state by changing the target to the fallback target value.
|
||||
* Equivalent to {@code setTarget(getFallbackTarget())}.
|
||||
*/
|
||||
public void invalidate() {
|
||||
setTargetVolatile(getFallbackTarget());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all call sites in a list by changing the target of each to its fallback value.
|
||||
*/
|
||||
public static void invalidateAll(List<VolatileCallSite> sites) {
|
||||
for (VolatileCallSite site : sites) {
|
||||
site.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
* changes being made to the Java Virtual Machine specification for JSR 292.
|
||||
* This information will be incorporated in a future version of the JVM specification.</em>
|
||||
*
|
||||
* <h3>{@code invokedynamic} instruction format</h3>
|
||||
* <h3><a name="indyinsn"></a>{@code invokedynamic} instruction format</h3>
|
||||
* In bytecode, an {@code invokedynamic} instruction is formatted as five bytes.
|
||||
* The first byte is the opcode 186 (hexadecimal {@code BA}).
|
||||
* The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions).
|
||||
@ -82,7 +82,7 @@
|
||||
* 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.)
|
||||
*
|
||||
* <h3>constant pool entries for {@code invokedynamic} instructions</h3>
|
||||
* <h3><a name="indycon"></a>constant pool entries for {@code invokedynamic} instructions</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18),
|
||||
* it must contain exactly four more bytes after the tag.
|
||||
* These bytes are interpreted as two 16-bit indexes, in the usual {@code u2} format.
|
||||
@ -109,7 +109,7 @@
|
||||
* <em>(Note: The Proposed Final Draft of this specification is likely to support
|
||||
* only the tag 18, not the tag 17.)</em>
|
||||
*
|
||||
* <h3>constant pool entries for {@linkplain java.dyn.MethodType method types}</h3>
|
||||
* <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.
|
||||
@ -121,13 +121,8 @@
|
||||
* but not initialized.
|
||||
* Access checking and error reporting is performed exactly as it is for
|
||||
* references by {@code ldc} instructions to {@code CONSTANT_Class} constants.
|
||||
* <p>
|
||||
* Every use of this constant pool entry must lead to the same outcome.
|
||||
* If the resolution of the names in the method type constant causes an exception to occur,
|
||||
* this exception must be recorded by the JVM, and re-thrown on every subsequent attempt
|
||||
* to use this particular constant.
|
||||
*
|
||||
* <h3>constant pool entries for {@linkplain java.dyn.MethodHandle method handles}</h3>
|
||||
* <h3><a name="mhcon"></a>constant pool entries for {@linkplain java.dyn.MethodHandle method handles}</h3>
|
||||
* If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15),
|
||||
* it must contain exactly three more bytes. The first byte after the tag is a subtag
|
||||
* value which must be in the range 1 through 9, and the last two must be an index to a
|
||||
@ -162,7 +157,8 @@
|
||||
* </table>
|
||||
* </code>
|
||||
* <p>
|
||||
* The special names {@code <init>} and {@code <clinit>} are not allowed except for subtag 8 as shown.
|
||||
* 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
|
||||
@ -183,10 +179,23 @@
|
||||
* Method handle constants for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic}
|
||||
* may force class initialization on their first invocation, just like the corresponding bytecodes.
|
||||
* <p>
|
||||
* Every use of this constant pool entry must lead to the same outcome.
|
||||
* If the resolution of the names in the method handle constant causes an exception to occur,
|
||||
* this exception must be recorded by the JVM, and re-thrown on every subsequent attempt
|
||||
* to use this particular constant.
|
||||
* The rules of section 5.4.3 of the
|
||||
* <a href="http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#73492">JVM Specification</a>
|
||||
* apply to the resolution of {@code CONSTANT_MethodType}, {@code CONSTANT_MethodHandle},
|
||||
* and {@code CONSTANT_InvokeDynamic} constants,
|
||||
* by the execution of {@code invokedynamic} and {@code ldc} instructions.
|
||||
* (Roughly speaking, this means that every use of a constant pool entry
|
||||
* must lead to the same outcome.
|
||||
* If the resoultion 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,
|
||||
* two distinct constant pool entries might not resolve to the same reference
|
||||
* even if they contain the same symbolic reference.
|
||||
*
|
||||
* <h2><a name="bsm"></a>Bootstrap Methods</h2>
|
||||
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
|
||||
@ -263,7 +272,7 @@
|
||||
* the expected {@code MethodType} </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>timing of linkage</h3>
|
||||
* <h3><a name="linktime"></a>timing of linkage</h3>
|
||||
* A dynamic call site is linked just before its first execution.
|
||||
* The bootstrap method call implementing the linkage occurs within
|
||||
* a thread that is attempting a first execution.
|
||||
@ -398,6 +407,7 @@
|
||||
* Such a practice is likely to produce large class files and constant pools.
|
||||
*
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
|
||||
* (Usage Note: There is no mechanism for specifying five or more positional arguments to the bootstrap method.
|
||||
* If there are two or more arguments, the Java code of the bootstrap method is required to extract them from
|
||||
* a varargs-style object array.
|
||||
@ -414,7 +424,7 @@
|
||||
* {@link java.dyn.MethodHandle#asSpreader asSpreader}
|
||||
* and {@link java.dyn.MethodHandle#asSpreader asCollector} methods.)
|
||||
*
|
||||
* <h2>Structure Summary</h2>
|
||||
* <h2><a name="structs"></a>Structure Summary</h2>
|
||||
* <blockquote><pre>// summary of constant and attribute structures
|
||||
struct CONSTANT_MethodHandle_info {
|
||||
u1 tag = 15;
|
||||
|
||||
@ -53,12 +53,13 @@ public class ClassValueTest {
|
||||
return "CV1:" + type.getName();
|
||||
}
|
||||
static int countForCV1;
|
||||
static final ClassValue<String> CV1 = new ClassValue<String>() {
|
||||
static final ClassValue<String> CV1 = new CV1();
|
||||
private static class CV1 extends ClassValue<String> {
|
||||
protected String computeValue(Class<?> type) {
|
||||
countForCV1++;
|
||||
return nameForCV1(type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static final Class[] CLASSES = {
|
||||
String.class,
|
||||
|
||||
@ -99,7 +99,7 @@ public class InvokeDynamicPrintArgs {
|
||||
private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
|
||||
// ignore caller and name, but match the type:
|
||||
Object bsmInfo = Arrays.asList(caller, name, type);
|
||||
return new CallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
|
||||
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
|
||||
}
|
||||
private static MethodType MT_bsm() {
|
||||
shouldNotCallThis();
|
||||
@ -117,7 +117,7 @@ public class InvokeDynamicPrintArgs {
|
||||
bsmInfo.addAll(Arrays.asList((Object[])arg));
|
||||
else
|
||||
bsmInfo.add(arg);
|
||||
return new CallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
|
||||
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
|
||||
}
|
||||
private static MethodType MT_bsm2() {
|
||||
shouldNotCallThis();
|
||||
|
||||
@ -107,16 +107,15 @@ assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy"));
|
||||
{} /// JAVADOC
|
||||
MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
cat = cat.asType(methodType(Object.class, String.class, String.class)); /*(String)*/
|
||||
assertEquals("xy", /*(String)*/ cat.invokeExact("x", "y"));
|
||||
assertEquals("xy", (String) cat.invokeExact("x", "y"));
|
||||
MethodHandle d0 = dropArguments(cat, 0, String.class);
|
||||
assertEquals("yz", /*(String)*/ d0.invokeExact("x", "y", "z"));
|
||||
assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
|
||||
MethodHandle d1 = dropArguments(cat, 1, String.class);
|
||||
assertEquals("xz", /*(String)*/ d1.invokeExact("x", "y", "z"));
|
||||
assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
|
||||
MethodHandle d2 = dropArguments(cat, 2, String.class);
|
||||
assertEquals("xy", /*(String)*/ d2.invokeExact("x", "y", "z"));
|
||||
assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
|
||||
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
|
||||
assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z"));
|
||||
assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
|
||||
}}
|
||||
}
|
||||
|
||||
@ -125,16 +124,15 @@ assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z"));
|
||||
{} /// JAVADOC
|
||||
MethodHandle cat = lookup().findVirtual(String.class,
|
||||
"concat", methodType(String.class, String.class));
|
||||
cat = cat.asType(methodType(Object.class, String.class, String.class)); /*(String)*/
|
||||
MethodHandle upcase = lookup().findVirtual(String.class,
|
||||
"toUpperCase", methodType(String.class));
|
||||
assertEquals("xy", /*(String)*/ cat.invokeExact("x", "y")); // xy
|
||||
assertEquals("xy", (String) cat.invokeExact("x", "y"));
|
||||
MethodHandle f0 = filterArguments(cat, 0, upcase);
|
||||
assertEquals("Xy", /*(String)*/ f0.invokeExact("x", "y")); // Xy
|
||||
assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
|
||||
MethodHandle f1 = filterArguments(cat, 1, upcase);
|
||||
assertEquals("xY", /*(String)*/ f1.invokeExact("x", "y")); // xY
|
||||
assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
|
||||
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
|
||||
assertEquals("XY", /*(String)*/ f2.invokeExact("x", "y")); // XY
|
||||
assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
||||
}}
|
||||
}
|
||||
|
||||
@ -144,24 +142,6 @@ assertEquals("XY", /*(String)*/ f2.invokeExact("x", "y")); // XY
|
||||
Assert.assertEquals(exp, act);
|
||||
}
|
||||
|
||||
@Test public void testVolatileCallSite() throws Throwable {
|
||||
{{
|
||||
{} /// JAVADOC
|
||||
MethodHandle strcat = MethodHandles.lookup()
|
||||
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
|
||||
MethodHandle trueCon = MethodHandles.constant(boolean.class, true);
|
||||
MethodHandle falseCon = MethodHandles.constant(boolean.class, false);
|
||||
VolatileCallSite switcher = new VolatileCallSite(trueCon, falseCon);
|
||||
// following steps may be repeated to re-use the same switcher:
|
||||
MethodHandle worker1 = strcat;
|
||||
MethodHandle worker2 = MethodHandles.permuteArguments(strcat, strcat.type(), 1, 0);
|
||||
MethodHandle worker = MethodHandles.guardWithTest(switcher.dynamicInvoker(), worker1, worker2);
|
||||
System.out.println((String) worker.invokeExact("met", "hod")); // method
|
||||
switcher.invalidate();
|
||||
System.out.println((String) worker.invokeExact("met", "hod")); // hodmet
|
||||
}}
|
||||
}
|
||||
|
||||
static MethodHandle asList;
|
||||
@Test public void testWithTypeHandler() throws Throwable {
|
||||
{{
|
||||
@ -182,9 +162,9 @@ MethodHandle collectingTypeHandler = lookup()
|
||||
methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
|
||||
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
|
||||
|
||||
System.out.println(makeAnyList.invokeGeneric());
|
||||
System.out.println(makeAnyList.invokeGeneric(1));
|
||||
System.out.println(makeAnyList.invokeGeneric("two", "too"));
|
||||
assertEquals("[]", makeAnyList.invokeGeneric().toString());
|
||||
assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
|
||||
assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@ -1688,8 +1688,8 @@ public class MethodHandlesTest {
|
||||
|
||||
// dynamic invoker
|
||||
countTest();
|
||||
CallSite site = new CallSite(type);
|
||||
inv = MethodHandles.dynamicInvoker(site);
|
||||
CallSite site = new MutableCallSite(type);
|
||||
inv = site.dynamicInvoker();
|
||||
|
||||
// see if we get the result of the original target:
|
||||
try {
|
||||
@ -1820,11 +1820,12 @@ public class MethodHandlesTest {
|
||||
MethodHandle throwOrReturn
|
||||
= PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn",
|
||||
MethodType.methodType(Object.class, Object.class, Throwable.class));
|
||||
MethodHandle thrower = throwOrReturn;
|
||||
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 target = MethodHandles.catchException(thrower,
|
||||
thrown.getClass(), ValueConversions.varargsList(1+nargs));
|
||||
thrown.getClass(), catcher);
|
||||
assertEquals(thrower.type(), target.type());
|
||||
//System.out.println("catching with "+target+" : "+throwOrReturn);
|
||||
Object[] args = randomArgs(nargs, Object.class);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user