提交 6850b5d0 编写于 作者: J jrose

7001424: implement JSR 292 EG adjustments, November 2010

Reviewed-by: twisti
上级 ad90e258
...@@ -35,14 +35,25 @@ import java.util.Collection; ...@@ -35,14 +35,25 @@ import java.util.Collection;
* which is called its {@code target}. * which is called its {@code target}.
* An {@code invokedynamic} instruction linked to a {@code CallSite} delegates * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
* all calls to the site's current target. * 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> * <p>
* If a mutable target is not required, the {@code invokedynamic} instruction * {@code CallSite} is an abstract class which does not allow
* should be linked to a {@linkplain ConstantCallSite constant call site}. * direct subclassing by users. It has three immediate,
* If a volatile target is required, because updates to the target must be * concrete subclasses that may be either instantiated or subclassed.
* reliably witnessed by other threads, the {@code invokedynamic} instruction * <ul>
* should be linked to a {@linkplain VolatileCallSite volatile call site}. * <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> * <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} * The new target must have the same {@linkplain MethodHandle#type() type}
* as the previous target. * as the previous target.
* Thus, though a call site can be relinked to a series of * 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 ...@@ -72,6 +83,7 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam
</pre></blockquote> </pre></blockquote>
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
abstract
public class CallSite { public class CallSite {
private static final Access IMPL_TOKEN = Access.getToken(); private static final Access IMPL_TOKEN = Access.getToken();
...@@ -87,7 +99,6 @@ public class CallSite { ...@@ -87,7 +99,6 @@ public class CallSite {
private MemberName calleeNameRemoveForPFD; private MemberName calleeNameRemoveForPFD;
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* Make a blank call site object with the given method type. * Make a blank call site object with the given method type.
* An initial target method is supplied which will throw * An initial target method is supplied which will throw
* an {@link IllegalStateException} if called. * an {@link IllegalStateException} if called.
...@@ -95,16 +106,20 @@ public class CallSite { ...@@ -95,16 +106,20 @@ public class CallSite {
* Before this {@code CallSite} object is returned from a bootstrap method, * Before this {@code CallSite} object is returned from a bootstrap method,
* it is usually provided with a more useful target method, * it is usually provided with a more useful target method,
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}. * 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(); target = MethodHandles.invokers(type).uninitializedCallSite();
} }
/** /**
* Make a blank call site object, possibly equipped with an initial target method handle. * 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 * @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 target.type(); // null check
this.target = target; this.target = target;
} }
...@@ -199,6 +214,8 @@ public class CallSite { ...@@ -199,6 +214,8 @@ public class CallSite {
* @throws NullPointerException if the proposed new target is null * @throws NullPointerException if the proposed new target is null
* @throws WrongMethodTypeException if the proposed new target * @throws WrongMethodTypeException if the proposed new target
* has a method type that differs from the previous 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) { public void setTarget(MethodHandle newTarget) {
checkTargetChange(this.target, newTarget); checkTargetChange(this.target, newTarget);
...@@ -216,14 +233,6 @@ public class CallSite { ...@@ -216,14 +233,6 @@ public class CallSite {
return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type); 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 * Produce a method handle equivalent to an invokedynamic instruction
* which has been linked to this call site. * which has been linked to this call site.
...@@ -240,7 +249,7 @@ public class CallSite { ...@@ -240,7 +249,7 @@ public class CallSite {
*/ */
public final MethodHandle dynamicInvoker() { public final MethodHandle dynamicInvoker() {
if (this instanceof ConstantCallSite) { 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 getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type()); MethodHandle invoker = MethodHandles.exactInvoker(this.type());
......
...@@ -28,44 +28,78 @@ package java.dyn; ...@@ -28,44 +28,78 @@ package java.dyn;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; 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 class.
* @author John Rose, JSR 292 EG * @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}. * Compute the given class's derived value for this {@code ClassValue}.
* <p> * <p>
* This method will be invoked within the first thread that accesses * 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> * <p>
* Normally, this method is invoked at most once per class, * Normally, this method is invoked at most once per class,
* but it may be invoked again in case of subsequent invocations * but it may be invoked again if there has been a call to
* of {@link #remove} followed by {@link #get}. * {@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. * 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() { 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. * Returns the value for the given class.
* If no value has yet been computed, it is obtained by * 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> * <p>
* The actual installation of the value on the class * The actual installation of the value on the class
* is performed while the class's synchronization lock * is performed atomically.
* is held. At that point, if racing threads have * At that point, if racing threads have
* computed values, one is chosen, and returned to * computed values, one is chosen, and returned to
* all the racing threads. * 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) { public T get(Class<?> type) {
ClassValueMap map = getMap(type); ClassValueMap map = getMap(type);
...@@ -81,9 +115,16 @@ public abstract class ClassValue<T> { ...@@ -81,9 +115,16 @@ public abstract class ClassValue<T> {
/** /**
* Removes the associated value for the given class. * Removes the associated value for the given class.
* If this value is subsequently {@linkplain #get read} for the same 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 * 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) { public void remove(Class<?> type) {
ClassValueMap map = getMap(type); ClassValueMap map = getMap(type);
...@@ -118,6 +159,7 @@ public abstract class ClassValue<T> { ...@@ -118,6 +159,7 @@ public abstract class ClassValue<T> {
// Warm up the table with a null entry. // Warm up the table with a null entry.
map.preInitializeEntry(this); map.preInitializeEntry(this);
} }
STORE_BARRIER.lazySet(0);
// All stores pending from table expansion are completed. // All stores pending from table expansion are completed.
synchronized (map) { synchronized (map) {
value = (T) map.initializeEntry(this, value); value = (T) map.initializeEntry(this, value);
......
...@@ -32,14 +32,16 @@ package java.dyn; ...@@ -32,14 +32,16 @@ package java.dyn;
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public class ConstantCallSite extends CallSite { 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) { public ConstantCallSite(MethodHandle target) {
super(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) { @Override public final void setTarget(MethodHandle ignore) {
throw new IllegalArgumentException("ConstantCallSite"); throw new UnsupportedOperationException("ConstantCallSite");
} }
} }
...@@ -67,4 +67,16 @@ public class InvokeDynamicBootstrapError extends LinkageError { ...@@ -67,4 +67,16 @@ public class InvokeDynamicBootstrapError extends LinkageError {
public InvokeDynamicBootstrapError(String s, Throwable cause) { public InvokeDynamicBootstrapError(String s, Throwable cause) {
super(s, 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 ...@@ -128,8 +128,11 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
* <p> * <p>
* Bytecode in the JVM can directly obtain a method handle * Bytecode in the JVM can directly obtain a method handle
* for any accessible method from a {@code ldc} instruction * for any accessible method from a {@code ldc} instruction
* which refers to a {@code CONSTANT_Methodref} or * which refers to a {@code CONSTANT_MethodHandle} constant pool entry.
* {@code CONSTANT_InterfaceMethodref} 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> * <p>
* Java code can also use a reflective API called * Java code can also use a reflective API called
* {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup} * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
...@@ -185,12 +188,20 @@ mh = lookup.findVirtual(java.util.List.class, "size", mt); ...@@ -185,12 +188,20 @@ mh = lookup.findVirtual(java.util.List.class, "size", mt);
// (Ljava/util/List;)I // (Ljava/util/List;)I
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
assert(i == 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> * </pre></blockquote>
* Each of the above calls generates a single invokevirtual instruction * Each of the above calls generates a single invokevirtual instruction
* with the name {@code invoke} and the type descriptors indicated in the comments. * with the name {@code invoke} and the type descriptors indicated in the comments.
* The argument types are taken directly from the actual arguments, * The argument types are taken directly from the actual arguments,
* while the return type is taken from the type parameter. * while the return type is taken from the cast immediately applied to the call.
* (This type parameter may be a primitive, and it defaults to {@code Object}.) * 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> * <p>
* <em>A note on generic typing:</em> Method handles do not represent * <em>A note on generic typing:</em> Method handles do not represent
* their function types in terms of Java parameterized (generic) types, * their function types in terms of Java parameterized (generic) types,
...@@ -216,6 +227,19 @@ assert(i == 3); ...@@ -216,6 +227,19 @@ assert(i == 3);
* fields, methods, and constructors can be represented directly * fields, methods, and constructors can be represented directly
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. * 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. * 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 MethodType
* @see MethodHandles * @see MethodHandles
...@@ -282,8 +306,20 @@ public abstract class MethodHandle ...@@ -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 @Override
public String toString() { public String toString() {
...@@ -512,7 +548,7 @@ public abstract class MethodHandle ...@@ -512,7 +548,7 @@ public abstract class MethodHandle
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length"); if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
int keepPosArgs = nargs - arrayLength; int keepPosArgs = nargs - arrayLength;
MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs); MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
newType = newType.insertParameterTypes(keepPosArgs, arrayElement); newType = newType.insertParameterTypes(keepPosArgs, arrayType);
return MethodHandles.spreadArguments(this, newType); return MethodHandles.spreadArguments(this, newType);
} }
...@@ -610,6 +646,14 @@ public abstract class MethodHandle ...@@ -610,6 +646,14 @@ public abstract class MethodHandle
* else if the type of the target does not exactly match * else if the type of the target does not exactly match
* the requested type, a {@link WrongMethodTypeException} is thrown. * the requested type, a {@link WrongMethodTypeException} is thrown.
* <p> * <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: * Therefore, the type handler is invoked as if by this code:
* <blockquote><pre> * <blockquote><pre>
* MethodHandle target = this; // original method handle * MethodHandle target = this; // original method handle
...@@ -637,9 +681,9 @@ MethodHandle collectingTypeHandler = lookup() ...@@ -637,9 +681,9 @@ MethodHandle collectingTypeHandler = lookup()
methodType(MethodHandle.class, MethodHandle.class, MethodType.class)); methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler); MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
System.out.println(makeAnyList.invokeGeneric()); // prints [] assertEquals("[]", makeAnyList.invokeGeneric().toString());
System.out.println(makeAnyList.invokeGeneric(1)); // prints [1] assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
System.out.println(makeAnyList.invokeGeneric("two", "too")); // prints [two, too] assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
* <pre><blockquote> * <pre><blockquote>
*/ */
public MethodHandle withTypeHandler(MethodHandle typeHandler) { public MethodHandle withTypeHandler(MethodHandle typeHandler) {
......
...@@ -56,21 +56,33 @@ import static sun.dyn.MemberName.newIllegalArgumentException; ...@@ -56,21 +56,33 @@ import static sun.dyn.MemberName.newIllegalArgumentException;
* <p> * <p>
* This type can be created only by factory methods. * This type can be created only by factory methods.
* All factory methods may cache values, though caching is not guaranteed. * 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> * <p>
* {@code MethodType} objects are sometimes derived from bytecode instructions * {@code MethodType} objects are sometimes derived from bytecode instructions
* such as {@code invokedynamic}, specifically from the type descriptor strings associated * such as {@code invokedynamic}, specifically from the type descriptor strings associated
* with the instructions in a class file's constant pool. * 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> * <p>
* Like classes and strings, method types can be represented directly * Like classes and strings, method types can also be represented directly
* in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. * in a class file's constant pool as constants. The may be loaded by an {@code ldc}
* Loading such a constant causes its component classes to be loaded as necessary. * 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 * @author John Rose, JSR 292 EG
*/ */
public final public final
class MethodType implements java.lang.reflect.Type { class MethodType {
private final Class<?> rtype; private final Class<?> rtype;
private final Class<?>[] ptypes; private final Class<?>[] ptypes;
private MethodTypeForm form; // erased form, plus cached data about primitives private MethodTypeForm form; // erased form, plus cached data about primitives
...@@ -300,6 +312,14 @@ class MethodType implements java.lang.reflect.Type { ...@@ -300,6 +312,14 @@ class MethodType implements java.lang.reflect.Type {
return insertParameterTypes(parameterCount(), 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[])}. /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
* @param num the position (zero-based) of the inserted parameter type(s) * @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 a new parameter types to insert into the parameter list
...@@ -508,7 +528,9 @@ class MethodType implements java.lang.reflect.Type { ...@@ -508,7 +528,9 @@ class MethodType implements java.lang.reflect.Type {
* parenthesis enclosed, comma separated list of type names, * parenthesis enclosed, comma separated list of type names,
* followed immediately by the return type. * followed immediately by the return type.
* <p> * <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. * by [], rather than the Class.getName of the array type.
*/ */
@Override @Override
...@@ -517,35 +539,13 @@ class MethodType implements java.lang.reflect.Type { ...@@ -517,35 +539,13 @@ class MethodType implements java.lang.reflect.Type {
sb.append("("); sb.append("(");
for (int i = 0; i < ptypes.length; i++) { for (int i = 0; i < ptypes.length; i++) {
if (i > 0) sb.append(","); if (i > 0) sb.append(",");
putName(sb, ptypes[i]); sb.append(ptypes[i].getSimpleName());
} }
sb.append(")"); sb.append(")");
putName(sb, rtype); sb.append(rtype.getSimpleName());
return sb.toString(); 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 /// Queries which have to do with the bytecode architecture
/** The number of JVM stack slots required to invoke a method /** The number of JVM stack slots required to invoke a method
......
/*
* 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();
}
/*
* 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; ...@@ -28,63 +28,37 @@ package java.dyn;
import java.util.List; 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. * 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 * 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. * 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> * <p>
* Also, a volatile call site has the ability to be <em>invalidated</em>, * Unlike {@code MutableCallSite}, there is no
* or reset to a well-defined fallback state. * {@linkplain MutableCallSite#sync sync operation} on volatile
* call sites, since every write to a volatile variable is implicitly
* synchronized with reader threads.
* <p> * <p>
* A volatile call site can be used as a switch to control the behavior * In other respects, a {@code VolatileCallSite} is interchangeable
* of another method handle. For example: * with {@code MutableCallSite}.
* <blockquote><pre> * @see MutableCallSite
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.
* @author John Rose, JSR 292 EG * @author John Rose, JSR 292 EG
*/ */
public class VolatileCallSite extends CallSite { public class VolatileCallSite extends CallSite {
volatile MethodHandle fallback;
/** Create a call site with a volatile target. /** 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}. * of the given type which will throw {@code IllegalStateException}.
* @throws NullPointerException if the proposed type is null
*/ */
public VolatileCallSite(MethodType type) { public VolatileCallSite(MethodType type) {
super(type); super(type);
fallback = target;
} }
/** Create a call site with a volatile 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) { public VolatileCallSite(MethodHandle target) {
super(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. */ /** Internal override to nominally final getTarget. */
...@@ -95,50 +69,11 @@ public class VolatileCallSite extends CallSite { ...@@ -95,50 +69,11 @@ public class VolatileCallSite extends CallSite {
/** /**
* Set the target method of this call site, as a volatile variable. * 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. * effects associated with volatiles, in the Java Memory Model.
*/ */
@Override public void setTarget(MethodHandle newTarget) { @Override public void setTarget(MethodHandle newTarget) {
checkTargetChange(getTargetVolatile(), newTarget); checkTargetChange(getTargetVolatile(), newTarget);
setTargetVolatile(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 @@ ...@@ -51,7 +51,7 @@
* changes being made to the Java Virtual Machine specification for JSR 292. * 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> * 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. * In bytecode, an {@code invokedynamic} instruction is formatted as five bytes.
* The first byte is the opcode 186 (hexadecimal {@code BA}). * The first byte is the opcode 186 (hexadecimal {@code BA}).
* The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions). * The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions).
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
* instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the * 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>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), * If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18),
* it must contain exactly four more bytes after the tag. * 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. * These bytes are interpreted as two 16-bit indexes, in the usual {@code u2} format.
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
* <em>(Note: The Proposed Final Draft of this specification is likely to support * <em>(Note: The Proposed Final Draft of this specification is likely to support
* only the tag 18, not the tag 17.)</em> * 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), * 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} * 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 signature.
...@@ -121,13 +121,8 @@ ...@@ -121,13 +121,8 @@
* but not initialized. * but not initialized.
* Access checking and error reporting is performed exactly as it is for * Access checking and error reporting is performed exactly as it is for
* references by {@code ldc} instructions to {@code CONSTANT_Class} constants. * 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), * 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 * 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 * value which must be in the range 1 through 9, and the last two must be an index to a
...@@ -162,7 +157,8 @@ ...@@ -162,7 +157,8 @@
* </table> * </table>
* </code> * </code>
* <p> * <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> * <p>
* The JVM verifier and linker apply the same access checks and restrictions for these references as for the hypothetical * 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. In particular, method handles to
...@@ -183,10 +179,23 @@ ...@@ -183,10 +179,23 @@
* Method handle constants for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic} * 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. * may force class initialization on their first invocation, just like the corresponding bytecodes.
* <p> * <p>
* Every use of this constant pool entry must lead to the same outcome. * The rules of section 5.4.3 of the
* If the resolution of the names in the method handle constant causes an exception to occur, * <a href="http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#73492">JVM Specification</a>
* this exception must be recorded by the JVM, and re-thrown on every subsequent attempt * apply to the resolution of {@code CONSTANT_MethodType}, {@code CONSTANT_MethodHandle},
* to use this particular constant. * 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> * <h2><a name="bsm"></a>Bootstrap Methods</h2>
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction), * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
...@@ -263,7 +272,7 @@ ...@@ -263,7 +272,7 @@
* the expected {@code MethodType} </li> * the expected {@code MethodType} </li>
* </ul> * </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. * A dynamic call site is linked just before its first execution.
* The bootstrap method call implementing the linkage occurs within * The bootstrap method call implementing the linkage occurs within
* a thread that is attempting a first execution. * a thread that is attempting a first execution.
...@@ -398,6 +407,7 @@ ...@@ -398,6 +407,7 @@
* Such a practice is likely to produce large class files and constant pools. * Such a practice is likely to produce large class files and constant pools.
* *
* <p style="font-size:smaller;"> * <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. * (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 * 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. * a varargs-style object array.
...@@ -414,7 +424,7 @@ ...@@ -414,7 +424,7 @@
* {@link java.dyn.MethodHandle#asSpreader asSpreader} * {@link java.dyn.MethodHandle#asSpreader asSpreader}
* and {@link java.dyn.MethodHandle#asSpreader asCollector} methods.) * 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 * <blockquote><pre>// summary of constant and attribute structures
struct CONSTANT_MethodHandle_info { struct CONSTANT_MethodHandle_info {
u1 tag = 15; u1 tag = 15;
......
...@@ -53,12 +53,13 @@ public class ClassValueTest { ...@@ -53,12 +53,13 @@ public class ClassValueTest {
return "CV1:" + type.getName(); return "CV1:" + type.getName();
} }
static int countForCV1; 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) { protected String computeValue(Class<?> type) {
countForCV1++; countForCV1++;
return nameForCV1(type); return nameForCV1(type);
} }
}; }
static final Class[] CLASSES = { static final Class[] CLASSES = {
String.class, String.class,
......
...@@ -99,7 +99,7 @@ public class InvokeDynamicPrintArgs { ...@@ -99,7 +99,7 @@ public class InvokeDynamicPrintArgs {
private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException { private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
// ignore caller and name, but match the type: // ignore caller and name, but match the type:
Object bsmInfo = Arrays.asList(caller, name, 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() { private static MethodType MT_bsm() {
shouldNotCallThis(); shouldNotCallThis();
...@@ -117,7 +117,7 @@ public class InvokeDynamicPrintArgs { ...@@ -117,7 +117,7 @@ public class InvokeDynamicPrintArgs {
bsmInfo.addAll(Arrays.asList((Object[])arg)); bsmInfo.addAll(Arrays.asList((Object[])arg));
else else
bsmInfo.add(arg); 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() { private static MethodType MT_bsm2() {
shouldNotCallThis(); shouldNotCallThis();
......
...@@ -107,16 +107,15 @@ assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy")); ...@@ -107,16 +107,15 @@ assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy"));
{} /// JAVADOC {} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class, MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, 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); 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); 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); 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); 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")); ...@@ -125,16 +124,15 @@ assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z"));
{} /// JAVADOC {} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class, MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, 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, MethodHandle upcase = lookup().findVirtual(String.class,
"toUpperCase", methodType(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); 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); 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); 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 ...@@ -144,24 +142,6 @@ assertEquals("XY", /*(String)*/ f2.invokeExact("x", "y")); // XY
Assert.assertEquals(exp, act); 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; static MethodHandle asList;
@Test public void testWithTypeHandler() throws Throwable { @Test public void testWithTypeHandler() throws Throwable {
{{ {{
...@@ -182,9 +162,9 @@ MethodHandle collectingTypeHandler = lookup() ...@@ -182,9 +162,9 @@ MethodHandle collectingTypeHandler = lookup()
methodType(MethodHandle.class, MethodHandle.class, MethodType.class)); methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler); MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
System.out.println(makeAnyList.invokeGeneric()); assertEquals("[]", makeAnyList.invokeGeneric().toString());
System.out.println(makeAnyList.invokeGeneric(1)); assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
System.out.println(makeAnyList.invokeGeneric("two", "too")); assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
}} }}
} }
......
...@@ -1688,8 +1688,8 @@ public class MethodHandlesTest { ...@@ -1688,8 +1688,8 @@ public class MethodHandlesTest {
// dynamic invoker // dynamic invoker
countTest(); countTest();
CallSite site = new CallSite(type); CallSite site = new MutableCallSite(type);
inv = MethodHandles.dynamicInvoker(site); inv = site.dynamicInvoker();
// see if we get the result of the original target: // see if we get the result of the original target:
try { try {
...@@ -1820,11 +1820,12 @@ public class MethodHandlesTest { ...@@ -1820,11 +1820,12 @@ public class MethodHandlesTest {
MethodHandle throwOrReturn MethodHandle throwOrReturn
= PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn", = PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn",
MethodType.methodType(Object.class, Object.class, Throwable.class)); MethodType.methodType(Object.class, Object.class, Throwable.class));
MethodHandle thrower = throwOrReturn; MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
while (thrower.type().parameterCount() < nargs) while (thrower.type().parameterCount() < nargs)
thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class); 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, MethodHandle target = MethodHandles.catchException(thrower,
thrown.getClass(), ValueConversions.varargsList(1+nargs)); thrown.getClass(), catcher);
assertEquals(thrower.type(), target.type()); assertEquals(thrower.type(), target.type());
//System.out.println("catching with "+target+" : "+throwOrReturn); //System.out.println("catching with "+target+" : "+throwOrReturn);
Object[] args = randomArgs(nargs, Object.class); Object[] args = randomArgs(nargs, Object.class);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册