--- a/jdk/src/share/classes/java/dyn/CallSite.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/CallSite.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -78,7 +78,7 @@
}
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
// ignore caller and name, but match the type:
- return new ConstantCallSite(MethodHandles.collectArguments(printArgs, type));
+ return new ConstantCallSite(printArgs.asType(type));
}
</pre></blockquote>
* @author John Rose, JSR 292 EG
@@ -86,6 +86,7 @@
abstract
public class CallSite {
private static final Access IMPL_TOKEN = Access.getToken();
+ static { MethodHandleImpl.initStatics(); }
// Fields used only by the JVM. Do not use or change.
private MemberName vmmethod; // supplied by the JVM (ref. to calling method)
@@ -125,8 +126,8 @@
}
/**
- * Report the type of this call site's target.
- * Although targets may change, the call site's type can never change.
+ * Returns the type of this call site's target.
+ * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
* The {@code setTarget} method enforces this invariant by refusing any new target that does
* not have the previous target's type.
* @return the type of the current target, which is also the type of any future target
@@ -154,73 +155,40 @@
}
/**
- * Report the current linkage state of the call site, a value which may change over time.
- * <p>
- * If a {@code CallSite} object is returned
- * from the bootstrap method of the {@code invokedynamic} instruction,
- * the {@code CallSite} is permanently bound to that instruction.
- * When the {@code invokedynamic} instruction is executed, the target method
- * of its associated call site object is invoked directly.
- * It is as if the instruction calls {@code getTarget} and then
- * calls {@link MethodHandle#invokeExact invokeExact} on the result.
- * <p>
- * Unless specified differently by a subclass,
- * the interactions of {@code getTarget} with memory are the same
- * as of a read from an ordinary variable, such as an array element or a
- * non-volatile, non-final field.
- * <p>
- * In particular, the current thread may choose to reuse the result
- * of a previous read of the target from memory, and may fail to see
- * a recent update to the target by another thread.
- * <p>
- * In a {@linkplain ConstantCallSite constant call site}, the {@code getTarget} method behaves
- * like a read from a {@code final} field of the {@code CallSite}.
- * <p>
- * In a {@linkplain VolatileCallSite volatile call site}, the {@code getTarget} method behaves
- * like a read from a {@code volatile} field of the {@code CallSite}.
- * <p>
- * This method may not be overridden by application code.
+ * Returns the target method of the call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
+ *
* @return the current linkage state of the call site, its target method handle
* @see ConstantCallSite
* @see VolatileCallSite
* @see #setTarget
+ * @see ConstantCallSite#getTarget
+ * @see MutableCallSite#getTarget
+ * @see VolatileCallSite#getTarget
*/
- public final MethodHandle getTarget() {
- return getTarget0();
- }
+ public abstract MethodHandle getTarget();
/**
- * Privileged implementations can override this to force final or volatile semantics on getTarget.
- */
- /*package-private*/
- MethodHandle getTarget0() {
- return target;
- }
-
- /**
- * Set the target method of this call site.
+ * Updates the target method of this call site, according to the
+ * behavior defined by this call site's specific class.
+ * The immediate subclasses of {@code CallSite} document the
+ * class-specific behaviors of this method.
* <p>
- * Unless a subclass of CallSite documents otherwise,
- * the interactions of {@code setTarget} with memory are the same
- * as of a write to an ordinary variable, such as an array element or a
- * non-volatile, non-final field.
- * <p>
- * In particular, unrelated threads may fail to see the updated target
- * until they perform a read from memory.
- * Stronger guarantees can be created by putting appropriate operations
- * into the bootstrap method and/or the target methods used
- * at any given call site.
+ * The type of the new target must be {@linkplain MethodType#equals equal to}
+ * the type of the old target.
+ *
* @param newTarget the new target
* @throws NullPointerException if the proposed new target is null
* @throws WrongMethodTypeException if the proposed new target
* has a method type that differs from the previous target
- * @throws UnsupportedOperationException if the call site is
- * in fact a {@link ConstantCallSite}
+ * @see CallSite#getTarget
+ * @see ConstantCallSite#setTarget
+ * @see MutableCallSite#setTarget
+ * @see VolatileCallSite#setTarget
*/
- public void setTarget(MethodHandle newTarget) {
- checkTargetChange(this.target, newTarget);
- setTargetNormal(newTarget);
- }
+ public abstract void setTarget(MethodHandle newTarget);
void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
MethodType oldType = oldTarget.type();
@@ -236,31 +204,31 @@
/**
* Produce a method handle equivalent to an invokedynamic instruction
* which has been linked to this call site.
- * <p>If this call site is a {@linkplain ConstantCallSite constant call site},
- * this method simply returns the call site's target, since that will never change.
- * <p>Otherwise, this method is equivalent to the following code:
- * <p><blockquote><pre>
+ * <p>
+ * This method is equivalent to the following code:
+ * <blockquote><pre>
* MethodHandle getTarget, invoker, result;
- * getTarget = MethodHandles.lookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
+ * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
* invoker = MethodHandles.exactInvoker(this.type());
* result = MethodHandles.foldArguments(invoker, getTarget)
* </pre></blockquote>
+ *
* @return a method handle which always invokes this call site's current target
*/
- public final MethodHandle dynamicInvoker() {
- if (this instanceof ConstantCallSite) {
- return getTarget0(); // will not change dynamically
- }
+ public abstract MethodHandle dynamicInvoker();
+
+ /*non-public*/ MethodHandle makeDynamicInvoker() {
MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget);
}
+
private static final MethodHandle GET_TARGET;
static {
try {
GET_TARGET = MethodHandles.Lookup.IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class));
- } catch (NoAccessException ignore) {
+ } catch (ReflectiveOperationException ignore) {
throw new InternalError();
}
}
--- a/jdk/src/share/classes/java/dyn/ClassValue.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/ClassValue.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,10 +31,14 @@
import java.lang.reflect.UndeclaredThrowableException;
/**
- * Lazily associate a computed value with (potentially) every class.
+ * Lazily associate a computed value with (potentially) every type.
+ * For example, if a dynamic language needs to construct a message dispatch
+ * table for each class encountered at a message send call site,
+ * it can use a {@code ClassValue} to cache information needed to
+ * perform the message send quickly, for each class encountered.
* @author John Rose, JSR 292 EG
*/
-public class ClassValue<T> {
+public abstract class ClassValue<T> {
/**
* Compute the given class's derived value for this {@code ClassValue}.
* <p>
@@ -45,61 +49,41 @@
* but it may be invoked again if there has been a call to
* {@link #remove remove}.
* <p>
- * If there is no override from a subclass, this method returns
- * the result of applying the {@code ClassValue}'s {@code computeValue}
- * method handle, which was supplied at construction time.
+ * If this method throws an exception, the corresponding call to {@code get}
+ * will terminate abnormally with that exception, and no class value will be recorded.
*
+ * @param type the type whose class value must be computed
* @return the newly computed value associated with this {@code ClassValue}, for the given class or interface
- * @throws UndeclaredThrowableException if the {@code computeValue} method handle invocation throws something other than a {@code RuntimeException} or {@code Error}
- * @throws UnsupportedOperationException if the {@code computeValue} method handle is null (subclasses must override)
+ * @see #get
+ * @see #remove
*/
- protected T computeValue(Class<?> type) {
- if (computeValue == null)
- return null;
- try {
- return (T) (Object) computeValue.invokeGeneric(type);
- } catch (Throwable ex) {
- if (ex instanceof Error) throw (Error) ex;
- if (ex instanceof RuntimeException) throw (RuntimeException) ex;
- throw new UndeclaredThrowableException(ex);
- }
- }
-
- private final MethodHandle computeValue;
-
- /**
- * Creates a new class value.
- * Subclasses which use this constructor must override
- * the {@link #computeValue computeValue} method,
- * since the default {@code computeValue} method requires a method handle,
- * which this constructor does not provide.
- */
- protected ClassValue() {
- this.computeValue = null;
- }
-
- /**
- * Creates a new class value, whose {@link #computeValue computeValue} method
- * will return the result of {@code computeValue.invokeGeneric(type)}.
- * @throws NullPointerException if the method handle parameter is null
- */
- public ClassValue(MethodHandle computeValue) {
- computeValue.getClass(); // trigger NPE if null
- this.computeValue = computeValue;
- }
+ protected abstract T computeValue(Class<?> type);
/**
* Returns the value for the given class.
* If no value has yet been computed, it is obtained by
- * by an invocation of the {@link #computeValue computeValue} method.
+ * an invocation of the {@link #computeValue computeValue} method.
* <p>
* The actual installation of the value on the class
* is performed atomically.
- * At that point, if racing threads have
+ * At that point, if several racing threads have
* computed values, one is chosen, and returned to
* all the racing threads.
+ * <p>
+ * The {@code type} parameter is typically a class, but it may be any type,
+ * such as an interface, a primitive type (like {@code int.class}), or {@code void.class}.
+ * <p>
+ * In the absence of {@code remove} calls, a class value has a simple
+ * state diagram: uninitialized and initialized.
+ * When {@code remove} calls are made,
+ * the rules for value observation are more complex.
+ * See the documentation for {@link #remove remove} for more information.
*
+ * @param type the type whose class value must be computed or retrieved
* @return the current value associated with this {@code ClassValue}, for the given class or interface
+ * @throws NullPointerException if the argument is null
+ * @see #remove
+ * @see #computeValue
*/
public T get(Class<?> type) {
ClassValueMap map = getMap(type);
@@ -119,12 +103,51 @@
* This may result in an additional invocation of the
* {@code computeValue computeValue} method for the given class.
* <p>
- * If racing threads perform a combination of {@code get} and {@code remove} calls,
- * the calls are serialized.
- * A value produced by a call to {@code computeValue} will be discarded, if
- * the corresponding {@code get} call was followed by a {@code remove} call
- * before the {@code computeValue} could complete.
- * In such a case, the {@code get} call will re-invoke {@code computeValue}.
+ * In order to explain the interaction between {@code get} and {@code remove} calls,
+ * we must model the state transitions of a class value to take into account
+ * the alternation between uninitialized and initialized states.
+ * To do this, number these states sequentially from zero, and note that
+ * uninitialized (or removed) states are numbered with even numbers,
+ * while initialized (or re-initialized) states have odd numbers.
+ * <p>
+ * When a thread {@code T} removes a class value in state {@code 2N},
+ * nothing happens, since the class value is already uninitialized.
+ * Otherwise, the state is advanced atomically to {@code 2N+1}.
+ * <p>
+ * When a thread {@code T} queries a class value in state {@code 2N},
+ * the thread first attempts to initialize the class value to state {@code 2N+1}
+ * by invoking {@code computeValue} and installing the resulting value.
+ * <p>
+ * When {@code T} attempts to install the newly computed value,
+ * if the state is still at {@code 2N}, the class value will be initialized
+ * with the computed value, advancing it to state {@code 2N+1}.
+ * <p>
+ * Otherwise, whether the new state is even or odd,
+ * {@code T} will discard the newly computed value
+ * and retry the {@code get} operation.
+ * <p>
+ * Discarding and retrying is an important proviso,
+ * since otherwise {@code T} could potentially install
+ * a disastrously stale value. For example:
+ * <ul>
+ * <li>{@code T} calls {@code CV.get(C)} and sees state {@code 2N}
+ * <li>{@code T} quickly computes a time-dependent value {@code V0} and gets ready to install it
+ * <li>{@code T} is hit by an unlucky paging or scheduling event, and goes to sleep for a long time
+ * <li>...meanwhile, {@code T2} also calls {@code CV.get(C)} and sees state {@code 2N}
+ * <li>{@code T2} quickly computes a similar time-dependent value {@code V1} and installs it on {@code CV.get(C)}
+ * <li>{@code T2} (or a third thread) then calls {@code CV.remove(C)}, undoing {@code T2}'s work
+ * <li> the previous actions of {@code T2} are repeated several times
+ * <li> also, the relevant computed values change over time: {@code V1}, {@code V2}, ...
+ * <li>...meanwhile, {@code T} wakes up and attempts to install {@code V0}; <em>this must fail</em>
+ * </ul>
+ * We can assume in the above scenario that {@code CV.computeValue} uses locks to properly
+ * observe the time-dependent states as it computes {@code V1}, etc.
+ * This does not remove the threat of a stale value, since there is a window of time
+ * between the return of {@code computeValue} in {@code T} and the installation
+ * of the the new value. No user synchronization is possible during this time.
+ *
+ * @param type the type whose class value must be removed
+ * @throws NullPointerException if the argument is null
*/
public void remove(Class<?> type) {
ClassValueMap map = getMap(type);
@@ -137,9 +160,9 @@
/// Implementation...
- /** The hash code for this type is based on the identity of the object,
- * and is well-dispersed for power-of-two tables.
- */
+ // The hash code for this type is based on the identity of the object,
+ // and is well-dispersed for power-of-two tables.
+ /** @deprecated This override, which is implementation-specific, will be removed for PFD. */
public final int hashCode() { return hashCode; }
private final int hashCode = HASH_CODES.getAndAdd(0x61c88647);
private static final AtomicInteger HASH_CODES = new AtomicInteger();
--- a/jdk/src/share/classes/java/dyn/ConstantCallSite.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/ConstantCallSite.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,16 +32,46 @@
* @author John Rose, JSR 292 EG
*/
public class ConstantCallSite extends CallSite {
- /** Create a call site with a permanent target.
+ /**
+ * Creates a call site with a permanent target.
+ * @param target the target to be permanently associated with this call site
* @throws NullPointerException if the proposed target is null
*/
public ConstantCallSite(MethodHandle target) {
super(target);
}
+
/**
- * Throw an {@link UnsupportedOperationException}, because this kind of call site cannot change its target.
+ * Returns the target method of the call site, which behaves
+ * like a {@code final} field of the {@code ConstantCallSite}.
+ * That is, the the target is always the original value passed
+ * to the constructor call which created this instance.
+ *
+ * @return the immutable linkage state of this call site, a constant method handle
+ * @throws UnsupportedOperationException because this kind of call site cannot change its target
+ */
+ @Override public final MethodHandle getTarget() {
+ return target;
+ }
+
+ /**
+ * Always throws an {@link UnsupportedOperationException}.
+ * This kind of call site cannot change its target.
+ * @param ignore a new target proposed for the call site, which is ignored
+ * @throws UnsupportedOperationException because this kind of call site cannot change its target
*/
@Override public final void setTarget(MethodHandle ignore) {
throw new UnsupportedOperationException("ConstantCallSite");
}
+
+ /**
+ * Returns this call site's permanent target.
+ * Since that target will never change, this is a correct implementation
+ * of {@link CallSite#dynamicInvoker CallSite.dynamicInvoker}.
+ * @return the immutable linkage state of this call site, a constant method handle
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return getTarget();
+ }
}
--- a/jdk/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Fri Feb 25 12:48:18 2011 -0800
@@ -31,8 +31,8 @@
* {@linkplain BootstrapMethod bootstrap method},
* or the bootstrap method has
* failed to provide a
- * {@linkplain CallSite} call site with a non-null {@linkplain MethodHandle target}
- * of the correct {@linkplain MethodType method type}.
+ * {@linkplain CallSite call site} with a {@linkplain CallSite#getTarget target}
+ * of the correct {@linkplain MethodHandle#type method type}.
*
* @author John Rose, JSR 292 EG
* @since 1.7
--- a/jdk/src/share/classes/java/dyn/Linkage.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/Linkage.java Fri Feb 25 12:48:18 2011 -0800
@@ -88,7 +88,7 @@
MethodHandle bootstrapMethod;
try {
bootstrapMethod = lookup.findStatic(runtime, name, BOOTSTRAP_METHOD_TYPE);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex);
}
MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod);
@@ -101,8 +101,9 @@
/**
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Invalidate all <code>invokedynamic</code> call sites everywhere.
- * @deprecated Use {@linkplain CallSite#setTarget call site target setting}
- * and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
+ * @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting},
+ * {@link MutableCallSite#syncAll call site update pushing},
+ * and {@link SwitchPoint#guardWithTest target switching} instead.
*/
public static
Object invalidateAll() {
@@ -113,8 +114,9 @@
* <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Invalidate all {@code invokedynamic} call sites in the bytecodes
* of any methods of the given class.
- * @deprecated Use {@linkplain CallSite#setTarget call site target setting}
- * and {@link VolatileCallSite#invalidateAll call site invalidation} instead.
+ * @deprecated Use {@linkplain MutableCallSite#setTarget call site target setting},
+ * {@link MutableCallSite#syncAll call site update pushing},
+ * and {@link SwitchPoint#guardWithTest target switching} instead.
*/
public static
Object invalidateCallerClass(Class<?> callerClass) {
--- a/jdk/src/share/classes/java/dyn/MethodHandle.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/MethodHandle.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,7 @@
import static sun.dyn.MemberName.newIllegalArgumentException; // utility
/**
- * A method handle is a typed, directly executable reference to a method,
+ * A method handle is a typed, directly executable reference to an underlying method,
* constructor, field, or similar low-level operation, with optional
* transformations of arguments or return values.
* These transformations are quite general, and include such patterns as
@@ -48,99 +48,182 @@
* will be removed before the Proposed Final Draft.
* Also, the final version will not include any public or
* protected constructors.</em>
- * <p>
- * Method handles are strongly typed according to signature.
- * They are not distinguished by method name or enclosing class.
- * A method handle must be invoked under a signature which matches
- * the method handle's own {@linkplain MethodType method type}.
+ *
+ * <h3>Method handle contents</h3>
+ * Method handles are dynamically and strongly typed according to type descriptor.
+ * They are not distinguished by the name or defining class of their underlying methods.
+ * A method handle must be invoked using type descriptor which matches
+ * the method handle's own {@linkplain #type method type}.
* <p>
* Every method handle reports its type via the {@link #type type} accessor.
- * The structure of this type is a series of classes, one of which is
+ * This type descriptor is a {@link java.dyn.MethodType MethodType} object,
+ * whose structure is a series of classes, one of which is
* the return type of the method (or {@code void.class} if none).
* <p>
- * Every method handle appears as an object containing a method named
- * {@link #invokeExact invokeExact}, whose signature exactly matches
- * the method handle's type.
- * A Java method call expression, which compiles to an
- * {@code invokevirtual} instruction,
- * can invoke this method from Java source code.
+ * A method handle's type controls the types of invocations it accepts,
+ * and the kinds of transformations that apply to it.
+ * <p>
+ * A method handle contains a pair of special invoker methods
+ * called {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}.
+ * Both invoker methods provide direct access to the method handle's
+ * underlying method, constructor, field, or other operation,
+ * as modified by transformations of arguments and return values.
+ * Both invokers accept calls which exactly match the method handle's own type.
+ * The {@code invokeGeneric} invoker also accepts a range of other call types.
+ * <p>
+ * Method handles are immutable and have no visible state.
+ * Of course, they can be bound to underlying methods or data which exhibit state.
+ * With respect to the Java Memory Model, any method handle will behave
+ * as if all of its (internal) fields are final variables. This means that any method
+ * handle made visible to the application will always be fully formed.
+ * This is true even if the method handle is published through a shared
+ * variable in a data race.
+ * <p>
+ * Method handles cannot be subclassed by the user.
+ * Implementations may (or may not) create internal subclasses of {@code MethodHandle}
+ * which may be visible via the {@link java.lang.Object#getClass Object.getClass}
+ * operation. The programmer should not draw conclusions about a method handle
+ * from its specific class, as the method handle class hierarchy (if any)
+ * may change from time to time or across implementations from different vendors.
+ *
+ * <h3>Method handle compilation</h3>
+ * A Java method call expression naming {@code invokeExact} or {@code invokeGeneric}
+ * can invoke a method handle from Java source code.
+ * From the viewpoint of source code, these methods can take any arguments
+ * and their result can be cast to any return type.
+ * Formally this is accomplished by giving the invoker methods
+ * {@code Object} return types and variable-arity {@code Object} arguments,
+ * but they have an additional quality called <em>signature polymorphism</em>
+ * which connects this freedom of invocation directly to the JVM execution stack.
* <p>
- * Every call to a method handle specifies an intended method type,
- * which must exactly match the type of the method handle.
- * (The type is specified in the {@code invokevirtual} instruction,
- * via a {@code CONSTANT_NameAndType} constant pool entry.)
- * The call looks within the receiver object for a method
- * named {@code invokeExact} of the intended method type.
- * The call fails with a {@link WrongMethodTypeException}
- * if the method does not exist, even if there is an {@code invokeExact}
- * method of a closely similar signature.
- * As with other kinds
- * of methods in the JVM, signature matching during method linkage
- * is exact, and does not allow for language-level implicit conversions
- * such as {@code String} to {@code Object} or {@code short} to {@code int}.
+ * As is usual with virtual methods, source-level calls to {@code invokeExact}
+ * and {@code invokeGeneric} compile to an {@code invokevirtual} instruction.
+ * More unusually, the compiler must record the actual argument types,
+ * and may not perform method invocation conversions on the arguments.
+ * Instead, it must push them on the stack according to their own unconverted types.
+ * The method handle object itself is pushed on the stack before the arguments.
+ * The compiler then calls the method handle with a type descriptor which
+ * describes the argument and return types.
+ * <p>
+ * To issue a complete type descriptor, the compiler must also determine
+ * the return type. This is based on a cast on the method invocation expression,
+ * if there is one, or else {@code Object} if the invocation is an expression
+ * or else {@code void} if the invocation is a statement.
+ * The cast may be to a primitive type (but not {@code void}).
* <p>
- * Each individual method handle also contains a method named
- * {@link #invokeGeneric invokeGeneric}, whose type is the same
- * as {@code invokeExact}, and is therefore also reported by
- * the {@link #type type} accessor.
+ * As a corner case, an uncasted {@code null} argument is given
+ * a type descriptor of {@code java.lang.Void}.
+ * The ambiguity with the type {@code Void} is harmless, since there are no references of type
+ * {@code Void} except the null reference.
+ *
+ * <h3>Method handle invocation</h3>
+ * The first time a {@code invokevirtual} instruction is executed
+ * it is linked, by symbolically resolving the names in the instruction
+ * and verifying that the method call is statically legal.
+ * This is true of calls to {@code invokeExact} and {@code invokeGeneric}.
+ * In this case, the type descriptor emitted by the compiler is checked for
+ * correct syntax and names it contains are resolved.
+ * Thus, an {@code invokevirtual} instruction which invokes
+ * a method handle will always link, as long
+ * as the type descriptor is syntactically well-formed
+ * and the types exist.
+ * <p>
+ * When the {@code invokevirtual} is executed after linking,
+ * the receiving method handle's type is first checked by the JVM
+ * to ensure that it matches the descriptor.
+ * If the type match fails, it means that the method which the
+ * caller is invoking is not present on the individual
+ * method handle being invoked.
+ * <p>
+ * In the case of {@code invokeExact}, the type descriptor of the invocation
+ * (after resolving symbolic type names) must exactly match the method type
+ * of the receiving method handle.
+ * In the case of {@code invokeGeneric}, the resolved type descriptor
+ * must be a valid argument to the receiver's {@link #asType asType} method.
+ * Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}.
+ * <p>
+ * After type matching, a call to {@code invokeExact} directly
+ * and immediately invoke the method handle's underlying method
+ * (or other behavior, as the case may be).
+ * <p>
* A call to {@code invokeGeneric} works the same as a call to
- * {@code invokeExact}, if the signature specified by the caller
+ * {@code invokeExact}, if the type descriptor specified by the caller
* exactly matches the method handle's own type.
* If there is a type mismatch, {@code invokeGeneric} attempts
- * to adjust the type of the target method handle
- * (as if by a call to {@link #asType asType})
- * to obtain an exactly invokable target.
+ * to adjust the type of the receiving method handle,
+ * as if by a call to {@link #asType asType},
+ * to obtain an exactly invokable method handle {@code M2}.
* This allows a more powerful negotiation of method type
* between caller and callee.
* <p>
- * A method handle is an unrestricted capability to call a method.
- * A method handle can be formed on a non-public method by a class
- * that has access to that method; the resulting handle can be used
- * in any place by any caller who receives a reference to it. Thus, access
- * checking is performed when the method handle is created, not
- * (as in reflection) every time it is called. Handles to non-public
- * methods, or in non-public classes, should generally be kept secret.
+ * (Note: The adjusted method handle {@code M2} is not directly observable,
+ * and implementations are therefore not required to materialize it.)
+ *
+ * <h3>Invocation checking</h3>
+ * In typical programs, method handle type matching will usually succeed.
+ * But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
+ * either directly (in the case of {@code invokeExact}) or indirectly as if
+ * by a failed call to {@code asType} (in the case of {@code invokeGeneric}).
+ * <p>
+ * Thus, a method type mismatch which might show up as a linkage error
+ * in a statically typed program can show up as
+ * a dynamic {@code WrongMethodTypeException}
+ * in a program which uses method handles.
+ * <p>
+ * Because method types contain "live" {@code Class} objects,
+ * method type matching takes into account both types names and class loaders.
+ * Thus, even if a method handle {@code M} is created in one
+ * class loader {@code L1} and used in another {@code L2},
+ * method handle calls are type-safe, because the caller's type
+ * descriptor, as resolved in {@code L2},
+ * is matched against the original callee method's type descriptor,
+ * as resolved in {@code L1}.
+ * The resolution in {@code L1} happens when {@code M} is created
+ * and its type is assigned, while the resolution in {@code L2} happens
+ * when the {@code invokevirtual} instruction is linked.
+ * <p>
+ * Apart from the checking of type descriptors,
+ * a method handle's capability to call its underlying method is unrestricted.
+ * If a method handle is formed on a non-public method by a class
+ * that has access to that method, the resulting handle can be used
+ * in any place by any caller who receives a reference to it.
+ * <p>
+ * Unlike with the Core Reflection API, where access is checked every time
+ * a reflective method is invoked,
+ * method handle access checking is performed
+ * <a href="MethodHandles.Lookup.html#access">when the method handle is created</a>.
+ * In the case of {@code ldc} (see below), access checking is performed as part of linking
+ * the constant pool entry underlying the constant method handle.
+ * <p>
+ * Thus, handles to non-public methods, or to methods in non-public classes,
+ * should generally be kept secret.
* They should not be passed to untrusted code unless their use from
* the untrusted code would be harmless.
- * <p>
- * Bytecode in the JVM can directly call a method handle's
- * {@code invokeExact} method from an {@code invokevirtual} instruction.
- * The receiver class type must be {@code MethodHandle} and the method name
- * must be {@code invokeExact}. The signature of the invocation
- * (after resolving symbolic type names) must exactly match the method type
- * of the target method.
- * Similarly, bytecode can directly call a method handle's {@code invokeGeneric}
- * method. The signature of the invocation (after resolving symbolic type names)
- * must either exactly match the method type or be a valid argument to
- * the target's {@link #asType asType} method.
+ *
+ * <h3>Method handle creation</h3>
+ * Java code can create a method handle that directly accesses
+ * any method, constructor, or field that is accessible to that code.
+ * This is done via a reflective, capability-based API called
+ * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
+ * For example, a static method handle can be obtained
+ * from {@link java.dyn.MethodHandles.Lookup#findStatic Lookup.findStatic}.
+ * There are also conversion methods from Core Reflection API objects,
+ * such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}.
* <p>
- * Every {@code invokeExact} and {@code invokeGeneric} method always
- * throws {@link java.lang.Throwable Throwable},
- * which is to say that there is no static restriction on what a method handle
- * can throw. Since the JVM does not distinguish between checked
- * and unchecked exceptions (other than by their class, of course),
- * there is no particular effect on bytecode shape from ascribing
- * checked exceptions to method handle invocations. But in Java source
- * code, methods which perform method handle calls must either explicitly
- * throw {@code java.lang.Throwable Throwable}, or else must catch all
- * throwables locally, rethrowing only those which are legal in the context,
- * and wrapping ones which are illegal.
- * <p>
- * Bytecode in the JVM can directly obtain a method handle
- * for any accessible method from a {@code ldc} instruction
- * which refers to a {@code CONSTANT_MethodHandle} constant pool entry.
- * (Each such entry refers directly to a {@code CONSTANT_Methodref},
+ * Like classes and strings, method handles that correspond to accessible
+ * fields, methods, and constructors can also be represented directly
+ * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
+ * A new type of constant pool entry, {@code CONSTANT_MethodHandle},
+ * refers directly to an associated {@code CONSTANT_Methodref},
* {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
* constant pool entry.
- * For more details, see the <a href="package-summary.html#mhcon">package summary</a>.)
+ * (For more details on method handle constants,
+ * see the <a href="package-summary.html#mhcon">package summary</a>.)
* <p>
- * Java code can also use a reflective API called
- * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
- * for creating and calling method handles.
- * For example, a static method handle can be obtained
- * from {@link java.dyn.MethodHandles.Lookup#findStatic Lookup.findStatic}.
- * There are also bridge methods from Core Reflection API objects,
- * such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.ureflect}.
+ * Method handles produced by lookups or constant loads from methods or
+ * constructors with the variable arity modifier bit ({@code 0x0080})
+ * have a corresponding variable arity, as if they were defined with
+ * the help of {@link #asVarargsCollector asVarargsCollector}.
* <p>
* A method reference may refer either to a static or non-static method.
* In the non-static case, the method handle type includes an explicit
@@ -153,61 +236,191 @@
* When a method handle to a virtual method is invoked, the method is
* always looked up in the receiver (that is, the first argument).
* <p>
- * A non-virtual method handles to a specific virtual method implementation
+ * A non-virtual method handle to a specific virtual method implementation
* can also be created. These do not perform virtual lookup based on
* receiver type. Such a method handle simulates the effect of
* an {@code invokespecial} instruction to the same method.
- * <p>
+ *
+ * <h3>Usage examples</h3>
* Here are some examples of usage:
* <p><blockquote><pre>
Object x, y; String s; int i;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
-// mt is {(char,char) => String}
+// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
-// (Ljava/lang/String;CC)Ljava/lang/String;
s = (String) mh.invokeExact("daddy",'d','n');
+// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assert(s.equals("nanny"));
// weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
assert(s.equals("savvy"));
-// mt is {Object[] => List}
+// mt is (Object[])List
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
-// mt is {(Object,Object,Object) => Object}
+assert(mh.isVarargsCollector());
+x = mh.invokeGeneric("one", "two");
+// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+assert(x.equals(java.util.Arrays.asList("one","two")));
+// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
-mh = MethodHandles.collectArguments(mh, mt);
-// mt is {(Object,Object,Object) => Object}
-// (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+mh = mh.asType(mt);
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
+// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList(1,2,3)));
// mt is { => int}
mt = MethodType.methodType(int.class);
mh = lookup.findVirtual(java.util.List.class, "size", mt);
-// (Ljava/util/List;)I
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
+// invokeExact(Ljava/util/List;)I
assert(i == 3);
mt = MethodType.methodType(void.class, String.class);
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
mh.invokeExact(System.out, "Hello, world.");
-// (Ljava/io/PrintStream;Ljava/lang/String;)V
+// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
* </pre></blockquote>
- * Each of the above calls generates a single invokevirtual instruction
- * with the name {@code invoke} and the type descriptors indicated in the comments.
- * The argument types are taken directly from the actual arguments,
- * while the return type is taken from the cast immediately applied to the call.
- * This cast may be to a primitive.
- * If it is missing, the type defaults to {@code Object} if the call
- * occurs in a context which uses the return value.
- * If the call occurs as a statement, a cast is impossible,
- * and there is no return type; the call is {@code void}.
+ * Each of the above calls to {@code invokeExact} or {@code invokeGeneric}
+ * generates a single invokevirtual instruction with
+ * the type descriptor indicated in the following comment.
+ *
+ * <h3>Exceptions</h3>
+ * The methods {@code invokeExact} and {@code invokeGeneric} are declared
+ * to throw {@link java.lang.Throwable Throwable},
+ * which is to say that there is no static restriction on what a method handle
+ * can throw. Since the JVM does not distinguish between checked
+ * and unchecked exceptions (other than by their class, of course),
+ * there is no particular effect on bytecode shape from ascribing
+ * checked exceptions to method handle invocations. But in Java source
+ * code, methods which perform method handle calls must either explicitly
+ * throw {@code java.lang.Throwable Throwable}, or else must catch all
+ * throwables locally, rethrowing only those which are legal in the context,
+ * and wrapping ones which are illegal.
+ *
+ * <h3><a name="sigpoly"></a>Signature polymorphism</h3>
+ * The unusual compilation and linkage behavior of
+ * {@code invokeExact} and {@code invokeGeneric}
+ * is referenced by the term <em>signature polymorphism</em>.
+ * A signature polymorphic method is one which can operate with
+ * any of a wide range of call signatures and return types.
+ * In order to make this work, both the Java compiler and the JVM must
+ * give special treatment to signature polymorphic methods.
+ * <p>
+ * In source code, a call to a signature polymorphic method will
+ * compile, regardless of the requested type descriptor.
+ * As usual, the Java compiler emits an {@code invokevirtual}
+ * instruction with the given type descriptor against the named method.
+ * The unusual part is that the type descriptor is derived from
+ * the actual argument and return types, not from the method declaration.
+ * <p>
+ * When the JVM processes bytecode containing signature polymorphic calls,
+ * it will successfully link any such call, regardless of its type descriptor.
+ * (In order to retain type safety, the JVM will guard such calls with suitable
+ * dynamic type checks, as described elsewhere.)
+ * <p>
+ * Bytecode generators, including the compiler back end, are required to emit
+ * untransformed type descriptors for these methods.
+ * Tools which determine symbolic linkage are required to accept such
+ * untransformed descriptors, without reporting linkage errors.
+ * <p>
+ * For the sake of tools (but not as a programming API), the signature polymorphic
+ * methods are marked with a private yet standard annotation,
+ * {@code @java.dyn.MethodHandle.PolymorphicSignature}.
+ * The annotation's retention is {@code RUNTIME}, so that all tools can see it.
+ *
+ * <h3>Formal rules for processing signature polymorphic methods</h3>
+ * <p>
+ * The following methods (and no others) are signature polymorphic:
+ * <ul>
+ * <li>{@link java.dyn.MethodHandle#invokeExact MethodHandle.invokeExact}
+ * <li>{@link java.dyn.MethodHandle#invokeGeneric MethodHandle.invokeGeneric}
+ * </ul>
+ * <p>
+ * A signature polymorphic method will be declared with the following properties:
+ * <ul>
+ * <li>It must be native.
+ * <li>It must take a single varargs parameter of the form {@code Object...}.
+ * <li>It must produce a return value of type {@code Object}.
+ * <li>It must be contained within the {@code java.dyn} package.
+ * </ul>
+ * Because of these requirements, a signature polymorphic method is able to accept
+ * any number and type of actual arguments, and can, with a cast, produce a value of any type.
+ * However, the JVM will treat these declaration features as a documentation convention,
+ * rather than a description of the actual structure of the methods as executed.
* <p>
- * <em>A note on generic typing:</em> Method handles do not represent
- * their function types in terms of Java parameterized (generic) types,
- * because there are three mismatches between function types and parameterized
+ * When a call to a signature polymorphic method is compiled, the associated linkage information for
+ * its arguments is not array of {@code Object} (as for other similar varargs methods)
+ * but rather the erasure of the static types of all the arguments.
+ * <p>
+ * In an argument position of a method invocation on a signature polymorphic method,
+ * a null literal has type {@code java.lang.Void}, unless cast to a reference type.
+ * (Note: This typing rule allows the null type to have its own encoding in linkage information
+ * distinct from other types.
+ * <p>
+ * The linkage information for the return type is derived from a context-dependent target typing convention.
+ * The return type for a signature polymorphic method invocation is determined as follows:
+ * <ul>
+ * <li>If the method invocation expression is an expression statement, the method is {@code void}.
+ * <li>Otherwise, if the method invocation expression is the immediate operand of a cast,
+ * the return type is the erasure of the cast type.
+ * <li>Otherwise, the return type is the method's nominal return type, {@code Object}.
+ * </ul>
+ * (Programmers are encouraged to use explicit casts unless it is clear that a signature polymorphic
+ * call will be used as a plain {@code Object} expression.)
+ * <p>
+ * The linkage information for argument and return types is stored in the descriptor for the
+ * compiled (bytecode) call site. As for any invocation instruction, the arguments and return value
+ * will be passed directly on the JVM stack, in accordance with the descriptor,
+ * and without implicit boxing or unboxing.
+ *
+ * <h3>Interoperation between method handles and the Core Reflection API</h3>
+ * Using factory methods in the {@link java.dyn.MethodHandles.Lookup Lookup} API,
+ * any class member represented by a Core Reflection API object
+ * can be converted to a behaviorally equivalent method handle.
+ * For example, a reflective {@link java.lang.reflect.Method Method} can
+ * be converted to a method handle using
+ * {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect}.
+ * The resulting method handles generally provide more direct and efficient
+ * access to the underlying class members.
+ * <p>
+ * As a special case,
+ * when the Core Reflection API is used to view the signature polymorphic
+ * methods {@code invokeExact} or {@code invokeGeneric} in this class,
+ * they appear as single, non-polymorphic native methods.
+ * Calls to these native methods do not result in method handle invocations.
+ * Since {@code invokevirtual} instructions can natively
+ * invoke method handles under any type descriptor, this reflective view conflicts
+ * with the normal presentation via bytecodes.
+ * Thus, these two native methods, as viewed by
+ * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
+ * are placeholders only.
+ * If invoked via {@link java.lang.reflect.Method#invoke Method.invoke},
+ * they will throw {@code UnsupportedOperationException}.
+ * <p>
+ * In order to obtain an invoker method for a particular type descriptor,
+ * use {@link java.dyn.MethodHandles#exactInvoker MethodHandles.exactInvoker},
+ * or {@link java.dyn.MethodHandles#genericInvoker MethodHandles.genericInvoker}.
+ * The {@link java.dyn.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
+ * API is also able to return a method handle
+ * to call {@code invokeExact} or {@code invokeGeneric},
+ * for any specified type descriptor .
+ *
+ * <h3>Interoperation between method handles and Java generics</h3>
+ * A method handle can be obtained on a method, constructor, or field
+ * which is declared with Java generic types.
+ * As with the Core Reflection API, the type of the method handle
+ * will constructed from the erasure of the source-level type.
+ * When a method handle is invoked, the types of its arguments
+ * or the return value cast type may be generic types or type instances.
+ * If this occurs, the compiler will replace those
+ * types by their erasures when when it constructs the type descriptor
+ * for the {@code invokevirtual} instruction.
+ * <p>
+ * Method handles do not represent
+ * their function-like types in terms of Java parameterized (generic) types,
+ * because there are three mismatches between function-like types and parameterized
* Java types.
- * <ol>
+ * <ul>
* <li>Method types range over all possible arities,
* from no arguments to up to 255 of arguments (a limit imposed by the JVM).
* Generics are not variadic, and so cannot represent this.</li>
@@ -217,29 +430,7 @@
* often generic across a wide range of function types, including
* those of multiple arities. It is impossible to represent such
* genericity with a Java type parameter.</li>
- * </ol>
- * Signature polymorphic methods in this class appear to be documented
- * as having type parameters for return types and a parameter, but that is
- * merely a documentation convention. These type parameters do
- * not play a role in type-checking method handle invocations.
- * <p>
- * Like classes and strings, method handles that correspond to accessible
- * fields, methods, and constructors can be represented directly
- * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
- * Loading such a constant causes the component classes of its type to be loaded as necessary.
- * <p>
- * Method handles cannot be subclassed by the user.
- * Implementations may (or may not) create internal subclasses of {@code MethodHandle}
- * which may be visible via the {@code java.lang.Object#getClass Object.getClass}
- * operation. The programmer should not draw conclusions about a method handle
- * from its specific class, as the method handle class hierarchy (if any)
- * may change from time to time or across implementations from different vendors.
- * <p>
- * With respect to the Java Memory Model, any method handle will behave
- * as if all of its fields are final variables. This means that any method
- * handle made visible to the application will always be fully formed.
- * This is true even if the method handle is published through a shared
- * variables in a data race.
+ * </ul>
*
* @see MethodType
* @see MethodHandles
@@ -251,15 +442,16 @@
extends MethodHandleImpl
{
private static Access IMPL_TOKEN = Access.getToken();
+ static { MethodHandleImpl.initStatics(); }
// interface MethodHandle<R throws X extends Exception,A...>
// { MethodType<R throws X,A...> type(); public R invokeExact(A...) throws X; }
/**
* Internal marker interface which distinguishes (to the Java compiler)
- * those methods which are signature polymorphic.
+ * those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
*/
- @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.TYPE})
+ @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@interface PolymorphicSignature { }
@@ -270,7 +462,7 @@
* Every invocation of this method handle via {@code invokeExact} must exactly match this type.
* @return the method handle type
*/
- public final MethodType type() {
+ public MethodType type() {
return type;
}
@@ -307,40 +499,27 @@
}
/**
- * 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() {
- return MethodHandleImpl.getNameString(IMPL_TOKEN, this);
- }
-
- /**
- * Invoke the method handle, allowing any caller signature, but requiring an exact signature match.
- * The signature at the call site of {@code invokeExact} must
+ * Invoke the method handle, allowing any caller type descriptor, but requiring an exact type match.
+ * The type descriptor at the call site of {@code invokeExact} must
* exactly match this method handle's {@link #type type}.
* No conversions are allowed on arguments or return values.
- * @throws WrongMethodTypeException if the target's type is not identical with the caller's type signature
+ * <p>
+ * When this method is observed via the Core Reflection API,
+ * it will appear as a single native method, taking an object array and returning an object.
+ * If this native method is invoked directly via
+ * {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI,
+ * or indirectly via {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect},
+ * it will throw an {@code UnsupportedOperationException}.
+ * @throws WrongMethodTypeException if the target's type is not identical with the caller's type descriptor
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
*/
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
/**
- * Invoke the method handle, allowing any caller signature,
- * and optionally performing conversions for arguments and return types.
+ * Invoke the method handle, allowing any caller type descriptor,
+ * and optionally performing conversions on arguments and return values.
* <p>
- * If the call site signature exactly matches this method handle's {@link #type type},
+ * If the call site type descriptor exactly matches this method handle's {@link #type type},
* the call proceeds as if by {@link #invokeExact invokeExact}.
* <p>
* Otherwise, the call proceeds as if this method handle were first
@@ -353,14 +532,20 @@
* adaptations directly on the caller's arguments,
* and call the target method handle according to its own exact type.
* <p>
- * If the method handle is equipped with a
- * {@linkplain #withTypeHandler type handler}, the handler must produce
- * an entry point of the call site's exact type.
- * Otherwise, the signature at the call site of {@code invokeGeneric} must
- * be a valid argument to the standard {@code asType} method.
+ * The type descriptor at the call site of {@code invokeGeneric} must
+ * be a valid argument to the receivers {@code asType} method.
* In particular, the caller must specify the same argument arity
- * as the callee's type.
- * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type signature
+ * as the callee's type,
+ * if the callee is not a {@linkplain #asVarargsCollector variable arity collector}.
+ * <p>
+ * When this method is observed via the Core Reflection API,
+ * it will appear as a single native method, taking an object array and returning an object.
+ * If this native method is invoked directly via
+ * {@link java.lang.reflect.Method#invoke Method.invoke}, via JNI,
+ * or indirectly via {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.unreflect},
+ * it will throw an {@code UnsupportedOperationException}.
+ * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type descriptor
+ * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
*/
public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable;
@@ -400,16 +585,22 @@
* <p>
* This call is equivalent to the following code:
* <p><blockquote><pre>
- * MethodHandle invoker = MethodHandles.varargsInvoker(this.type(), 0);
+ * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
* Object result = invoker.invokeExact(this, arguments);
* </pre></blockquote>
+ * <p>
+ * Unlike the signature polymorphic methods {@code invokeExact} and {@code invokeGeneric},
+ * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
+ * It can therefore be used as a bridge between native or reflective code and method handles.
+ *
* @param arguments the arguments to pass to the target
* @return the result returned by the target
- * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments
+ * @throws ClassCastException if an argument cannot be converted by reference casting
+ * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments
* @throws Throwable anything thrown by the target method invocation
- * @see MethodHandles#varargsInvoker
+ * @see MethodHandles#spreadInvoker
*/
- public final Object invokeWithArguments(Object... arguments) throws Throwable {
+ public Object invokeWithArguments(Object... arguments) throws Throwable {
int argc = arguments == null ? 0 : arguments.length;
MethodType type = type();
if (type.parameterCount() != argc) {
@@ -417,7 +608,7 @@
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
}
if (argc <= 10) {
- MethodHandle invoker = MethodHandles.invokers(type).genericInvoker();
+ MethodHandle invoker = invokers(type).genericInvoker();
switch (argc) {
case 0: return invoker.invokeExact(this);
case 1: return invoker.invokeExact(this,
@@ -456,19 +647,11 @@
}
// more than ten arguments get boxed in a varargs list:
- MethodHandle invoker = MethodHandles.invokers(type).varargsInvoker(0);
+ MethodHandle invoker = invokers(type).spreadInvoker(0);
return invoker.invokeExact(this, arguments);
}
/** Equivalent to {@code invokeWithArguments(arguments.toArray())}. */
- public final Object invokeWithArguments(java.util.List<?> arguments) throws Throwable {
- return invokeWithArguments(arguments.toArray());
- }
- @Deprecated
- public final Object invokeVarargs(Object... arguments) throws Throwable {
- return invokeWithArguments(arguments);
- }
- @Deprecated
- public final Object invokeVarargs(java.util.List<?> arguments) throws Throwable {
+ public Object invokeWithArguments(java.util.List<?> arguments) throws Throwable {
return invokeWithArguments(arguments.toArray());
}
@@ -488,13 +671,7 @@
* to match up the caller's and callee's types.
* <p>
* This method is equivalent to {@link MethodHandles#convertArguments convertArguments},
- * except for method handles produced by {@link #withTypeHandler withTypeHandler},
- * in which case the specified type handler is used for calls to {@code asType}.
- * <p>
- * Note that the default behavior of {@code asType} only performs
- * pairwise argument conversion and return value conversion.
- * Because of this, unless the method handle has a type handler,
- * the original type and new type must have the same number of arguments.
+ * except for variable arity method handles produced by {@link #asVarargsCollector asVarargsCollector}.
*
* @param newType the expected type of the new method handle
* @return a method handle which delegates to {@code this} after performing
@@ -508,14 +685,16 @@
}
/**
- * Produce a method handle which adapts, as its <i>target</i>,
+ * Make an adapter which accepts a trailing array argument
+ * and spreads its elements as positional arguments.
+ * The new method handle adapts, as its <i>target</i>,
* the current method handle. The type of the adapter will be
* the same as the type of the target, except that the final
* {@code arrayLength} parameters of the target's type are replaced
* by a single array parameter of type {@code arrayType}.
* <p>
* If the array element type differs from any of the corresponding
- * argument types on original target,
+ * argument types on the original target,
* the original target is adapted to take the array elements directly,
* as if by a call to {@link #asType asType}.
* <p>
@@ -539,8 +718,9 @@
* @throws IllegalArgumentException if target does not have at least
* {@code arrayLength} parameter types
* @throws WrongMethodTypeException if the implied {@code asType} call fails
+ * @see #asCollector
*/
- public final MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
+ public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
MethodType oldType = type();
@@ -553,13 +733,15 @@
}
/**
- * Produce a method handle which adapts, as its <i>target</i>,
+ * Make an adapter which accepts a given number of trailing
+ * positional arguments and collects them into an array argument.
+ * The new method handle adapts, as its <i>target</i>,
* the current method handle. The type of the adapter will be
* the same as the type of the target, except that a single trailing
* parameter (usually of type {@code arrayType}) is replaced by
* {@code arrayLength} parameters whose type is element type of {@code arrayType}.
* <p>
- * If the array type differs from the final argument type on original target,
+ * If the array type differs from the final argument type on the original target,
* the original target is adapted to take the array type directly,
* as if by a call to {@link #asType asType}.
* <p>
@@ -570,21 +752,31 @@
* What the target eventually returns is returned unchanged by the adapter.
* <p>
* (The array may also be a shared constant when {@code arrayLength} is zero.)
- * @param arrayType usually {@code Object[]}, the type of the array argument which will collect the arguments
+ * <p>
+ * (<em>Note:</em> The {@code arrayType} is often identical to the last
+ * parameter type of the original target.
+ * It is an explicit argument for symmetry with {@code asSpreader}, and also
+ * to allow the target to use a simple {@code Object} as its last parameter type.)
+ * <p>
+ * In order to create a collecting adapter which is not restricted to a particular
+ * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead.
+ * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
* @param arrayLength the number of arguments to collect into a new array argument
* @return a new method handle which collects some trailing argument
* into an array, before calling the original method handle
* @throws IllegalArgumentException if {@code arrayType} is not an array type
- or {@code arrayType} is not assignable to this method handle's trailing parameter type
- * @throws IllegalArgumentException if {@code arrayLength} is not
- * a legal array size
+ * or {@code arrayType} is not assignable to this method handle's trailing parameter type,
+ * or {@code arrayLength} is not a legal array size
* @throws WrongMethodTypeException if the implied {@code asType} call fails
+ * @see #asSpreader
+ * @see #asVarargsCollector
*/
- public final MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+ public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
MethodType oldType = type();
int nargs = oldType.parameterCount();
+ if (nargs == 0) throw newIllegalArgumentException("no trailing argument");
MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
newType = newType.insertParameterTypes(nargs-1,
java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
@@ -592,8 +784,185 @@
}
/**
- * Produce a method handle which binds the given argument
- * to the current method handle as <i>target</i>.
+ * Make a <em>variable arity</em> adapter which is able to accept
+ * any number of trailing positional arguments and collect them
+ * into an array argument.
+ * <p>
+ * The type and behavior of the adapter will be the same as
+ * the type and behavior of the target, except that certain
+ * {@code invokeGeneric} and {@code asType} requests can lead to
+ * trailing positional arguments being collected into target's
+ * trailing parameter.
+ * Also, the last parameter type of the adapter will be
+ * {@code arrayType}, even if the target has a different
+ * last parameter type.
+ * <p>
+ * When called with {@link #invokeExact invokeExact}, the adapter invokes
+ * the target with no argument changes.
+ * (<em>Note:</em> This behavior is different from a
+ * {@linkplain #asCollector fixed arity collector},
+ * since it accepts a whole array of indeterminate length,
+ * rather than a fixed number of arguments.)
+ * <p>
+ * When called with {@link #invokeGeneric invokeGeneric}, if the caller
+ * type is the same as the adapter, the adapter invokes the target as with
+ * {@code invokeExact}.
+ * (This is the normal behavior for {@code invokeGeneric} when types match.)
+ * <p>
+ * Otherwise, if the caller and adapter arity are the same, and the
+ * trailing parameter type of the caller is a reference type identical to
+ * or assignable to the trailing parameter type of the adapter,
+ * the arguments and return values are converted pairwise,
+ * as if by {@link MethodHandles#convertArguments convertArguments}.
+ * (This is also normal behavior for {@code invokeGeneric} in such a case.)
+ * <p>
+ * Otherwise, the arities differ, or the adapter's trailing parameter
+ * type is not assignable from the corresponding caller type.
+ * In this case, the adapter replaces all trailing arguments from
+ * the original trailing argument position onward, by
+ * a new array of type {@code arrayType}, whose elements
+ * comprise (in order) the replaced arguments.
+ * <p>
+ * The caller type must provides as least enough arguments,
+ * and of the correct type, to satisfy the target's requirement for
+ * positional arguments before the trailing array argument.
+ * Thus, the caller must supply, at a minimum, {@code N-1} arguments,
+ * where {@code N} is the arity of the target.
+ * Also, there must exist conversions from the incoming arguments
+ * to the target's arguments.
+ * As with other uses of {@code invokeGeneric}, if these basic
+ * requirements are not fulfilled, a {@code WrongMethodTypeException}
+ * may be thrown.
+ * <p>
+ * In all cases, what the target eventually returns is returned unchanged by the adapter.
+ * <p>
+ * In the final case, it is exactly as if the target method handle were
+ * temporarily adapted with a {@linkplain #asCollector fixed arity collector}
+ * to the arity required by the caller type.
+ * (As with {@code asCollector}, if the array length is zero,
+ * a shared constant may be used instead of a new array.
+ * If the implied call to {@code asCollector} would throw
+ * an {@code IllegalArgumentException} or {@code WrongMethodTypeException},
+ * the call to the variable arity adapter must throw
+ * {@code WrongMethodTypeException}.)
+ * <p>
+ * The behavior of {@link #asType asType} is also specialized for
+ * variable arity adapters, to maintain the invariant that
+ * {@code invokeGeneric} is always equivalent to an {@code asType}
+ * call to adjust the target type, followed by {@code invokeExact}.
+ * Therefore, a variable arity adapter responds
+ * to an {@code asType} request by building a fixed arity collector,
+ * if and only if the adapter and requested type differ either
+ * in arity or trailing argument type.
+ * The resulting fixed arity collector has its type further adjusted
+ * (if necessary) to the requested type by pairwise conversion,
+ * as if by another application of {@code asType}.
+ * <p>
+ * When a method handle is obtained by executing an {@code ldc} instruction
+ * of a {@code CONSTANT_MethodHandle} constant, and the target method is marked
+ * as a variable arity method (with the modifier bit {@code 0x0080}),
+ * the method handle will accept multiple arities, as if the method handle
+ * constant were created by means of a call to {@code asVarargsCollector}.
+ * <p>
+ * In order to create a collecting adapter which collects a predetermined
+ * number of arguments, and whose type reflects this predetermined number,
+ * use {@link #asCollector asCollector} instead.
+ * <p>
+ * No method handle transformations produce new method handles with
+ * variable arity, unless they are documented as doing so.
+ * Therefore, besides {@code asVarargsCollector},
+ * all methods in {@code MethodHandle} and {@code MethodHandles}
+ * will return a method handle with fixed arity,
+ * except in the cases where they are specified to return their original
+ * operand (e.g., {@code asType} of the method handle's own type).
+ * <p>
+ * Calling {@code asVarargsCollector} on a method handle which is already
+ * of variable arity will produce a method handle with the same type and behavior.
+ * It may (or may not) return the original variable arity method handle.
+ * <p>
+ * Here is an example, of a list-making variable arity method handle:
+ * <blockquote><pre>
+MethodHandle asList = publicLookup()
+ .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+ .asVarargsCollector(Object[].class);
+assertEquals("[]", asList.invokeGeneric().toString());
+assertEquals("[1]", asList.invokeGeneric(1).toString());
+assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
+List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals(1, ls.size());
+assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
+ * </pre></blockquote>
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * These rules are designed as a dynamically-typed variation
+ * of the Java rules for variable arity methods.
+ * In both cases, callers to a variable arity method or method handle
+ * can either pass zero or more positional arguments, or else pass
+ * pre-collected arrays of any length. Users should be aware of the
+ * special role of the final argument, and of the effect of a
+ * type match on that final argument, which determines whether
+ * or not a single trailing argument is interpreted as a whole
+ * array or a single element of an array to be collected.
+ * Note that the dynamic type of the trailing argument has no
+ * effect on this decision, only a comparison between the static
+ * type descriptor of the call site and the type of the method handle.)
+ * <p style="font-size:smaller;">
+ * As a result of the previously stated rules, the variable arity behavior
+ * of a method handle may be suppressed, by binding it to the exact invoker
+ * of its own type, as follows:
+ * <blockquote><pre>
+MethodHandle vamh = publicLookup()
+ .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+ .asVarargsCollector(Object[].class);
+MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
+assert(vamh.type().equals(mh.type()));
+assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+boolean failed = false;
+try { mh.invokeGeneric(1,2,3); }
+catch (WrongMethodTypeException ex) { failed = true; }
+assert(failed);
+ * </pre></blockquote>
+ * This transformation has no behavioral effect if the method handle is
+ * not of variable arity.
+ *
+ * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
+ * @return a new method handle which can collect any number of trailing arguments
+ * into an array, before calling the original method handle
+ * @throws IllegalArgumentException if {@code arrayType} is not an array type
+ * or {@code arrayType} is not assignable to this method handle's trailing parameter type
+ * @see #asCollector
+ * @see #isVarargsCollector
+ */
+ public MethodHandle asVarargsCollector(Class<?> arrayType) {
+ Class<?> arrayElement = arrayType.getComponentType();
+ if (arrayElement == null) throw newIllegalArgumentException("not an array type");
+ return MethodHandles.asVarargsCollector(this, arrayType);
+ }
+
+ /**
+ * Determine if this method handle
+ * supports {@linkplain #asVarargsCollector variable arity} calls.
+ * Such method handles arise from the following sources:
+ * <ul>
+ * <li>a call to {@linkplain #asVarargsCollector asVarargsCollector}
+ * <li>a call to a {@linkplain java.dyn.MethodHandles.Lookup lookup method}
+ * which resolves to a variable arity Java method or constructor
+ * <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
+ * which resolves to a variable arity Java method or constructor
+ * </ul>
+ * @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls
+ * @see #asVarargsCollector
+ */
+ public boolean isVarargsCollector() {
+ return false;
+ }
+
+ /**
+ * Bind a value {@code x} to the first argument of a method handle, without invoking it.
+ * The new method handle adapts, as its <i>target</i>,
+ * to the current method handle.
* The type of the bound handle will be
* the same as the type of the target, except that a single leading
* reference parameter will be omitted.
@@ -614,79 +983,27 @@
* to the leading parameter type of the target
* @see MethodHandles#insertArguments
*/
- public final MethodHandle bindTo(Object x) {
+ public MethodHandle bindTo(Object x) {
return MethodHandles.insertArguments(this, 0, x);
}
/**
- * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
- * Create a new method handle with the same type as this one,
- * but whose {@code asType} method invokes the given
- * {@code typeHandler} on this method handle,
- * instead of the standard {@code MethodHandles.convertArguments}.
- * <p>
- * The new method handle will have the same behavior as the
- * old one when invoked by {@code invokeExact}.
- * For {@code invokeGeneric} calls which exactly match
- * the method type, the two method handles will also
- * have the same behavior.
- * For other {@code invokeGeneric} calls, the {@code typeHandler}
- * will control the behavior of the new method handle.
- * <p>
- * Thus, a method handle with an {@code asType} handler can
- * be configured to accept more than one arity of {@code invokeGeneric}
- * call, and potentially every possible arity.
- * It can also be configured to supply default values for
- * optional arguments, when the caller does not specify them.
- * <p>
- * The given method handle must take two arguments and return
- * one result. The result it returns must be a method handle
- * of exactly the requested type. If the result returned by
- * the target is null, a {@link NullPointerException} is thrown,
- * 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:
+ * 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 target = this; // original method handle
- * MethodHandle adapter = ...; // adapted method handle
- * MethodType requestedType = ...; // argument to asType()
- * if (type().equals(requestedType))
- * return adapter;
- * MethodHandle result = (MethodHandle)
- * typeHandler.invokeGeneric(target, requestedType);
- * if (!result.type().equals(requestedType))
- * throw new WrongMethodTypeException();
- * return result;
+ * "MethodHandle" + type().toString()
* </pre></blockquote>
* <p>
- * For example, here is a list-making variable-arity method handle:
- * <blockquote><pre>
-MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
-MethodHandle asList = lookup()
- .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
- return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
-}
-MethodHandle collectingTypeHandler = lookup()
- .findStatic(lookup().lookupClass(), "collectingTypeHandler",
- methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
-MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
-
-assertEquals("[]", makeAnyList.invokeGeneric().toString());
-assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
-assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
- * <pre><blockquote>
+ * 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
*/
- public MethodHandle withTypeHandler(MethodHandle typeHandler) {
- return MethodHandles.withTypeHandler(this, typeHandler);
+ @Override
+ public String toString() {
+ return MethodHandleImpl.getNameString(IMPL_TOKEN, this);
}
}
--- a/jdk/src/share/classes/java/dyn/MethodHandles.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/MethodHandles.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import sun.dyn.Access;
import sun.dyn.MemberName;
import sun.dyn.MethodHandleImpl;
+import sun.dyn.WrapperInstance;
import sun.dyn.util.ValueConversions;
import sun.dyn.util.VerifyAccess;
import sun.dyn.util.Wrapper;
@@ -45,10 +46,10 @@
* This class consists exclusively of static methods that operate on or return
* method handles. They fall into several categories:
* <ul>
- * <li>Factory methods which create method handles for methods and fields.
- * <li>Invoker methods which can invoke method handles on dynamically typed arguments and/or varargs arrays.
- * <li>Combinator methods, which combine or transforming pre-existing method handles into new ones.
- * <li>Factory methods which create method handles that emulate other common JVM operations or control flow patterns.
+ * <li>Lookup methods which help create method handles for methods and fields.
+ * <li>Combinator methods, which combine or transform pre-existing method handles into new ones.
+ * <li>Other factory methods to create method handles that emulate other common JVM operations or control flow patterns.
+ * <li>Wrapper methods which can convert between method handles and other function-like "SAM types".
* </ul>
* <p>
* @author John Rose, JSR 292 EG
@@ -97,47 +98,155 @@
* when the creation requires access checking.
* Method handles do not perform
* access checks when they are called, but rather when they are created.
- * (This is a major difference
- * from reflective {@link Method}, which performs access checking
- * against every caller, on every call.)
* Therefore, method handle access
* restrictions must be enforced when a method handle is created.
* The caller class against which those restrictions are enforced
* is known as the {@linkplain #lookupClass lookup class}.
- * A lookup object embodies an
- * authenticated lookup class, and can be used to create any number
+ * <p>
+ * A lookup class which needs to create method handles will call
+ * {@link MethodHandles#lookup MethodHandles.lookup} to create a factory for itself.
+ * When the {@code Lookup} factory object is created, the identity of the lookup class is
+ * determined, and securely stored in the {@code Lookup} object.
+ * The lookup class (or its delegates) may then use factory methods
+ * on the {@code Lookup} object to create method handles for access-checked members.
+ * This includes all methods, constructors, and fields which are allowed to the lookup class,
+ * even private ones.
+ * <p>
+ * The factory methods on a {@code Lookup} object correspond to all major
+ * use cases for methods, constructors, and fields.
+ * Here is a summary of the correspondence between these factory methods and
+ * the behavior the resulting method handles:
+ * <code>
+ * <table border=1 cellpadding=5 summary="lookup method behaviors">
+ * <tr><th>lookup expression</th><th>member</th><th>behavior</th></tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)}</td>
+ * <td>FT f;</td><td>(T) this.f;</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findStaticGetter lookup.findStaticGetter(C.class,"f",FT.class)}</td>
+ * <td>static<br>FT f;</td><td>(T) C.f;</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findSetter lookup.findSetter(C.class,"f",FT.class)}</td>
+ * <td>FT f;</td><td>this.f = x;</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findStaticSetter lookup.findStaticSetter(C.class,"f",FT.class)}</td>
+ * <td>static<br>FT f;</td><td>C.f = arg;</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findVirtual lookup.findVirtual(C.class,"m",MT)}</td>
+ * <td>T m(A*);</td><td>(T) this.m(arg*);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findStatic lookup.findStatic(C.class,"m",MT)}</td>
+ * <td>static<br>T m(A*);</td><td>(T) C.m(arg*);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findSpecial lookup.findSpecial(C.class,"m",MT,this.class)}</td>
+ * <td>T m(A*);</td><td>(T) super.m(arg*);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findConstructor lookup.findConstructor(C.class,MT)}</td>
+ * <td>C(A*);</td><td>(T) new C(arg*);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#unreflectGetter lookup.unreflectGetter(aField)}</td>
+ * <td>(static)?<br>FT f;</td><td>(FT) aField.get(thisOrNull);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#unreflectSetter lookup.unreflectSetter(aField)}</td>
+ * <td>(static)?<br>FT f;</td><td>aField.set(thisOrNull, arg);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
+ * <td>(static)?<br>T m(A*);</td><td>(T) aMethod.invoke(thisOrNull, arg*);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#unreflectConstructor lookup.unreflectConstructor(aConstructor)}</td>
+ * <td>C(A*);</td><td>(C) aConstructor.newInstance(arg*);</td>
+ * </tr>
+ * <tr>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
+ * <td>(static)?<br>T m(A*);</td><td>(T) aMethod.invoke(thisOrNull, arg*);</td>
+ * </tr>
+ * </table>
+ * </code>
+ * Here, the type {@code C} is the class or interface being searched for a member,
+ * documented as a parameter named {@code refc} in the lookup methods.
+ * The method or constructor type {@code MT} is composed from the return type {@code T}
+ * and the sequence of argument types {@code A*}.
+ * Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}.
+ * The formal parameter {@code this} stands for the self-reference of type {@code C};
+ * if it is present, it is always the leading argument to the method handle invocation.
+ * The name {@code arg} stands for all the other method handle arguments.
+ * In the code examples for the Core Reflection API, the name {@code thisOrNull}
+ * stands for a null reference if the accessed method or field is static,
+ * and {@code this} otherwise.
+ * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
+ * for reflective objects corresponding to the given members.
+ * <p>
+ * The equivalence between looked-up method handles and underlying
+ * class members can break down in a few ways:
+ * <ul>
+ * <li>If {@code C} is not symbolically accessible from the lookup class's loader,
+ * the lookup can still succeed, even when there is no equivalent
+ * Java expression or bytecoded constant.
+ * <li>Likewise, if {@code T} or {@code MT}
+ * is not symbolically accessible from the lookup class's loader,
+ * the lookup can still succeed.
+ * For example, lookups for {@code MethodHandle.invokeExact} and
+ * {@code MethodHandle.invokeGeneric} will always succeed, regardless of requested type.
+ * <li>If there is a security manager installed, it can forbid the lookup
+ * on various grounds (<a href="#secmgr">see below</a>).
+ * By contrast, the {@code ldc} instruction is not subject to
+ * security manager checks.
+ * </ul>
+ *
+ * <h3><a name="access"></a>Access checking</h3>
+ * Access checks are applied in the factory methods of {@code Lookup},
+ * when a method handle is created.
+ * This is a key difference from the Core Reflection API, since
+ * {@link java.lang.reflect.Method#invoke Method.invoke}
+ * performs access checking against every caller, on every call.
+ * <p>
+ * All access checks start from a {@code Lookup} object, which
+ * compares its recorded lookup class against all requests to
+ * create method handles.
+ * A single {@code Lookup} object can be used to create any number
* of access-checked method handles, all checked against a single
* lookup class.
* <p>
- * A class which needs to create method handles will call
- * {@link MethodHandles#lookup MethodHandles.lookup} to create a factory for itself.
- * It may then use this factory to create method handles on
- * all of its methods, including private ones.
- * It may also delegate the lookup (e.g., to a metaobject protocol)
- * by passing the lookup object to other code.
- * If this other code creates method handles, they will be access
- * checked against the original lookup class, and not with any higher
- * privileges.
+ * A {@code Lookup} object can be shared with other trusted code,
+ * such as a metaobject protocol.
+ * A shared {@code Lookup} object delegates the capability
+ * to create method handles on private members of the lookup class.
+ * Even if privileged code uses the {@code Lookup} object,
+ * the access checking is confined to the privileges of the
+ * original lookup class.
* <p>
- * Access checks only apply to named and reflected methods.
- * Other method handle creation methods, such as
- * {@link #convertArguments MethodHandles.convertArguments},
- * do not require any access checks, and can be done independently
- * of any lookup class.
- * <h3>How access errors are handled</h3>
* A lookup can fail, because
* the containing class is not accessible to the lookup class, or
* because the desired class member is missing, or because the
* desired class member is not accessible to the lookup class.
- * It can also fail if a security manager is installed and refuses
- * access. In any of these cases, an exception will be
- * thrown from the attempted lookup.
+ * In any of these cases, a {@code ReflectiveOperationException} will be
+ * thrown from the attempted lookup. The exact class will be one of
+ * the following:
+ * <ul>
+ * <li>NoSuchMethodException — if a method is requested but does not exist
+ * <li>NoSuchFieldException — if a field is requested but does not exist
+ * <li>IllegalAccessException — if the member exists but an access check fails
+ * </ul>
* <p>
* In general, the conditions under which a method handle may be
- * created for a method {@code M} are exactly as restrictive as the conditions
- * under which the lookup class could have compiled a call to {@code M}.
+ * looked up for a method {@code M} are exactly equivalent to the conditions
+ * under which the lookup class could have compiled and resolved a call to {@code M}.
+ * And the effect of invoking the method handle resulting from the lookup
+ * is exactly equivalent to executing the compiled and resolved call to {@code M}.
+ * The same point is true of fields and constructors.
* <p>
- * In some cases, this access is obtained by the Java compiler by creating
+ * In some cases, access between nested classes is obtained by the Java compiler by creating
* an wrapper method to access a private method of another class
* in the same top-level declaration.
* For example, a nested class {@code C.D}
@@ -149,6 +258,61 @@
* A workaround for this limitation is the {@link Lookup#in Lookup.in} method,
* which can transform a lookup on {@code C.E} into one on any of those other
* classes, without special elevation of privilege.
+ * <p>
+ * Although bytecode instructions can only refer to classes in
+ * a related class loader, this API can search for methods in any
+ * class, as long as a reference to its {@code Class} object is
+ * available. Such cross-loader references are also possible with the
+ * Core Reflection API, and are impossible to bytecode instructions
+ * such as {@code invokestatic} or {@code getfield}.
+ * There is a {@linkplain java.lang.SecurityManager security manager API}
+ * to allow applications to check such cross-loader references.
+ * These checks apply to both the {@code MethodHandles.Lookup} API
+ * and the Core Reflection API
+ * (as found on {@link java.lang.Class Class}).
+ * <p>
+ * Access checks only apply to named and reflected methods,
+ * constructors, and fields.
+ * Other method handle creation methods, such as
+ * {@link #convertArguments MethodHandles.convertArguments},
+ * do not require any access checks, and are done
+ * with static methods of {@link MethodHandles},
+ * independently of any {@code Lookup} object.
+ *
+ * <h3>Security manager interactions</h3>
+ * <a name="secmgr"></a>
+ * If a security manager is present, member lookups are subject to
+ * additional checks.
+ * From one to four calls are made to the security manager.
+ * Any of these calls can refuse access by throwing a
+ * {@link java.lang.SecurityException SecurityException}.
+ * Define {@code smgr} as the security manager,
+ * {@code refc} as the containing class in which the member
+ * is being sought, and {@code defc} as the class in which the
+ * member is actually defined.
+ * The calls are made according to the following rules:
+ * <ul>
+ * <li>In all cases, {@link SecurityManager#checkMemberAccess
+ * smgr.checkMemberAccess(refc, Member.PUBLIC)} is called.
+ * <li>If the class loader of the lookup class is not
+ * the same as or an ancestor of the class loader of {@code refc},
+ * then {@link SecurityManager#checkPackageAccess
+ * smgr.checkPackageAccess(refcPkg)} is called,
+ * where {@code refcPkg} is the package of {@code refc}.
+ * <li>If the retrieved member is not public,
+ * {@link SecurityManager#checkMemberAccess
+ * smgr.checkMemberAccess(defc, Member.DECLARED)} is called.
+ * (Note that {@code defc} might be the same as {@code refc}.)
+ * <li>If the retrieved member is not public,
+ * and if {@code defc} and {@code refc} are in different class loaders,
+ * and if the class loader of the lookup class is not
+ * the same as or an ancestor of the class loader of {@code defc},
+ * then {@link SecurityManager#checkPackageAccess
+ * smgr.checkPackageAccess(defcPkg)} is called,
+ * where {@code defcPkg} is the package of {@code defc}.
+ * </ul>
+ * In all cases, the requesting class presented to the security
+ * manager will be the lookup class from the current {@code Lookup} object.
*/
public static final
class Lookup {
@@ -194,12 +358,12 @@
return (mods != 0) ? mods : PACKAGE;
}
- /** Which class is performing the lookup? It is this class against
+ /** Tells which class is performing the lookup. It is this class against
* which checks are performed for visibility and access permissions.
* <p>
* The class implies a maximum level of access permission,
* but the permissions may be additionally limited by the bitmask
- * {@link #lookupModes}, which controls whether non-public members
+ * {@link #lookupModes lookupModes}, which controls whether non-public members
* can be accessed.
*/
public Class<?> lookupClass() {
@@ -211,7 +375,7 @@
return (allowedModes == TRUSTED) ? null : lookupClass;
}
- /** Which types of members can this lookup object produce?
+ /** Tells which access-protection classes of members this lookup object can produce.
* The result is a bit-mask of the bits
* {@linkplain #PUBLIC PUBLIC (0x01)},
* {@linkplain #PRIVATE PRIVATE (0x02)},
@@ -257,7 +421,7 @@
}
/**
- * Create a lookup on the specified new lookup class.
+ * Creates a lookup on the specified new lookup class.
* The resulting object will report the specified
* class as its own {@link #lookupClass lookupClass}.
* <p>
@@ -275,6 +439,10 @@
* then no members, not even public members, will be accessible.
* (In all other cases, public members will continue to be accessible.)
* </ul>
+ *
+ * @param requestedLookupClass the desired lookup class for the new lookup object
+ * @return a lookup object which reports the desired lookup class
+ * @throws NullPointerException if the argument is null
*/
public Lookup in(Class<?> requestedLookupClass) {
requestedLookupClass.getClass(); // null check
@@ -322,11 +490,12 @@
}
/**
- * Display the name of the class from which lookups are to be made.
+ * Displays 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:
+ * of a slash and a keyword. The keyword represents the strongest
+ * allowed access, and 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".
@@ -337,26 +506,37 @@
* 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}
+ * {@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.
+ * <p>
+ * (It may seem strange that protected access should be
+ * stronger than private access. Viewed independently from
+ * package access, protected access is the first to be lost,
+ * because it requires a direct subclass relationship between
+ * caller and callee.)
+ * @see #in
*/
@Override
public String toString() {
String cname = lookupClass.getName();
switch (allowedModes) {
- case TRUSTED:
- return "/trusted"; // internal only
+ case 0: // no privileges
+ return cname + "/noaccess";
case PUBLIC:
return cname + "/public";
case PUBLIC|PACKAGE:
return cname + "/package";
- case 0: // no privileges
- return cname + "/noaccess";
+ case ALL_MODES & ~PROTECTED:
+ return cname + "/private";
case ALL_MODES:
return cname;
- default:
- return cname + "/private";
+ case TRUSTED:
+ return "/trusted"; // internal only; not exported
+ default: // Should not happen, but it's a bitfield...
+ cname = cname + "/" + Integer.toHexString(allowedModes);
+ assert(false) : cname;
+ return cname;
}
}
@@ -371,29 +551,37 @@
}
/**
- * Produce a method handle for a static method.
+ * Produces a method handle for a static method.
* The type of the method handle will be that of the method.
* (Since static methods do not take receivers, there is no
* additional receiver argument inserted into the method handle type,
- * as there would be with {@link #findVirtual} or {@link #findSpecial}.)
+ * as there would be with {@link #findVirtual findVirtual} or {@link #findSpecial findSpecial}.)
* The method and all its argument types must be accessible to the lookup class.
* If the method's class has not yet been initialized, that is done
* immediately, before the method handle is returned.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
* @param refc the class from which the method is accessed
* @param name the name of the method
* @param type the type of the method
* @return the desired method handle
- * @exception NoAccessException if the method does not exist or access checking fails
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails, or if the method is not {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
public
- MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoAccessException {
+ MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(refc, name, type, true);
checkMethod(refc, method, true);
return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, lookupClassOrNull());
}
/**
- * Produce a method handle for a virtual method.
+ * Produces a method handle for a virtual method.
* The type of the method handle will be that of the method,
* with the receiver type (usually {@code refc}) prepended.
* The method and all its argument types must be accessible to the lookup class.
@@ -403,13 +591,31 @@
* implementation to enter.
* (The dispatching action is identical with that performed by an
* {@code invokevirtual} or {@code invokeinterface} instruction.)
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * Because of the general equivalence between {@code invokevirtual}
+ * instructions and method handles produced by {@code findVirtual},
+ * if the class is {@code MethodHandle} and the name string is
+ * {@code invokeExact} or {@code invokeGeneric}, the resulting
+ * method handle is equivalent to one produced by
+ * {@link java.dyn.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
+ * {@link java.dyn.MethodHandles#genericInvoker MethodHandles.genericInvoker}
+ * with the same {@code type} argument.
+ *
* @param refc the class or interface from which the method is accessed
* @param name the name of the method
* @param type the type of the method, with the receiver argument omitted
* @return the desired method handle
- * @exception NoAccessException if the method does not exist or access checking fails
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails, or if the method is {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoAccessException {
+ public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(refc, name, type, false);
checkMethod(refc, method, false);
MethodHandle mh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClassOrNull());
@@ -417,7 +623,7 @@
}
/**
- * Produce a method handle which creates an object and initializes it, using
+ * Produces a method handle which creates an object and initializes it, using
* the constructor of the specified type.
* The parameter types of the method handle will be those of the constructor,
* while the return type will be a reference to the constructor's class.
@@ -426,23 +632,47 @@
* immediately, before the method handle is returned.
* <p>
* Note: The requested type must have a return type of {@code void}.
- * This is consistent with the JVM's treatment of constructor signatures.
+ * This is consistent with the JVM's treatment of constructor type descriptors.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
* @param refc the class or interface from which the method is accessed
* @param type the type of the method, with the receiver argument omitted, and a void return type
* @return the desired method handle
- * @exception NoAccessException if the method does not exist or access checking fails
+ * @throws NoSuchMethodException if the constructor does not exist
+ * @throws IllegalAccessException if access checking fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoAccessException {
+ public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
String name = "<init>";
MemberName ctor = resolveOrFail(refc, name, type, false, false, lookupClassOrNull());
assert(ctor.isConstructor());
checkAccess(refc, ctor);
MethodHandle rawMH = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull());
- return MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawMH);
+ MethodHandle allocMH = MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawMH);
+ return fixVarargs(allocMH, rawMH);
+ }
+
+ /** Return a version of MH which matches matchMH w.r.t. isVarargsCollector. */
+ private static MethodHandle fixVarargs(MethodHandle mh, MethodHandle matchMH) {
+ boolean va1 = mh.isVarargsCollector();
+ boolean va2 = matchMH.isVarargsCollector();
+ if (va1 == va2) {
+ return mh;
+ } else if (va2) {
+ MethodType type = mh.type();
+ int arity = type.parameterCount();
+ return mh.asVarargsCollector(type.parameterType(arity-1));
+ } else {
+ throw new InternalError("already varargs, but template is not: "+mh);
+ }
}
/**
- * Produce an early-bound method handle for a virtual method,
+ * Produces an early-bound method handle for a virtual method,
* as if called from an {@code invokespecial}
* instruction from {@code caller}.
* The type of the method handle will be that of the method,
@@ -458,15 +688,23 @@
* If the explicitly specified caller class is not identical with the
* lookup class, or if this lookup object does not have private access
* privileges, the access fails.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
* @param refc the class or interface from which the method is accessed
* @param name the name of the method (which must not be "<init>")
* @param type the type of the method, with the receiver argument omitted
* @param specialCaller the proposed calling class to perform the {@code invokespecial}
* @return the desired method handle
- * @exception NoAccessException if the method does not exist or access checking fails
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
- Class<?> specialCaller) throws NoAccessException {
+ Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
checkSpecialCaller(specialCaller);
MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
checkMethod(refc, method, false);
@@ -475,69 +713,89 @@
}
/**
- * Produce a method handle giving read access to a non-static field.
+ * Produces a method handle giving read access to a non-static field.
* The type of the method handle will have a return type of the field's
* value type.
* The method handle's single argument will be the instance containing
* the field.
* Access checking is performed immediately on behalf of the lookup class.
+ * @param refc the class or interface from which the method is accessed
* @param name the field's name
* @param type the field's type
* @return a method handle which can load values from the field
- * @exception NoAccessException if access checking fails
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+ public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
return makeAccessor(refc, name, type, false, false);
}
/**
- * Produce a method handle giving write access to a non-static field.
+ * Produces a method handle giving write access to a non-static field.
* The type of the method handle will have a void return type.
* The method handle will take two arguments, the instance containing
* the field, and the value to be stored.
* The second argument will be of the field's value type.
* Access checking is performed immediately on behalf of the lookup class.
+ * @param refc the class or interface from which the method is accessed
* @param name the field's name
* @param type the field's type
* @return a method handle which can store values into the field
- * @exception NoAccessException if access checking fails
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+ public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
return makeAccessor(refc, name, type, false, true);
}
/**
- * Produce a method handle giving read access to a static field.
+ * Produces a method handle giving read access to a static field.
* The type of the method handle will have a return type of the field's
* value type.
* The method handle will take no arguments.
* Access checking is performed immediately on behalf of the lookup class.
+ * @param refc the class or interface from which the method is accessed
* @param name the field's name
* @param type the field's type
* @return a method handle which can load values from the field
- * @exception NoAccessException if access checking fails
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+ public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
return makeAccessor(refc, name, type, true, false);
}
/**
- * Produce a method handle giving write access to a static field.
+ * Produces a method handle giving write access to a static field.
* The type of the method handle will have a void return type.
* The method handle will take a single
* argument, of the field's value type, the value to be stored.
* Access checking is performed immediately on behalf of the lookup class.
+ * @param refc the class or interface from which the method is accessed
* @param name the field's name
* @param type the field's type
* @return a method handle which can store values into the field
- * @exception NoAccessException if access checking fails
+ * @throws NoSuchFieldException if the field does not exist
+ * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoAccessException {
+ public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
return makeAccessor(refc, name, type, true, true);
}
/**
- * Produce an early-bound method handle for a non-static method.
+ * Produces an early-bound method handle for a non-static method.
* The receiver must have a supertype {@code defc} in which a method
* of the given name and type is accessible to the lookup class.
* The method and all its argument types must be accessible to the lookup class.
@@ -547,28 +805,47 @@
* so that every call to the method handle will invoke the
* requested method on the given receiver.
* <p>
- * This is equivalent to the following expression:
- * <code>
- * {@link #insertArguments insertArguments}({@link #findVirtual findVirtual}(defc, name, type), receiver)
- * </code>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set
+ * <em>and</em> the trailing array argument is not the only argument.
+ * (If the trailing array argument is the only argument,
+ * the given receiver value will be bound to it.)
+ * <p>
+ * This is equivalent to the following code:
+ * <blockquote><pre>
+MethodHandle mh0 = {@link #findVirtual findVirtual}(defc, name, type);
+MethodHandle mh1 = mh0.{@link MethodHandle#bindTo bindTo}(receiver);
+MethodType mt1 = mh1.type();
+if (mh0.isVarargsCollector() && mt1.parameterCount() > 0) {
+ mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
+return mh1;
+ * </pre></blockquote>
* where {@code defc} is either {@code receiver.getClass()} or a super
* type of that class, in which the requested method is accessible
* to the lookup class.
+ * (Note that {@code bindTo} does not preserve variable arity.)
* @param receiver the object from which the method is accessed
* @param name the name of the method
* @param type the type of the method, with the receiver argument omitted
* @return the desired method handle
- * @exception NoAccessException if the method does not exist or access checking fails
+ * @throws NoSuchMethodException if the method does not exist
+ * @throws IllegalAccessException if access checking fails
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException {
+ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
Class<? extends Object> refc = receiver.getClass(); // may get NPE
MemberName method = resolveOrFail(refc, name, type, false);
checkMethod(refc, method, false);
MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, lookupClassOrNull());
MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
if (bmh == null)
- throw newNoAccessException(method, lookupClass());
- return bmh;
+ throw newNoAccessException(method, this);
+ if (dmh.type().parameterCount() == 0)
+ return dmh; // bound the trailing parameter; no varargs possible
+ return fixVarargs(bmh, dmh);
}
/**
@@ -581,11 +858,16 @@
* If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class.
* If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
* @param m the reflected method
* @return a method handle which can invoke the reflected method
- * @exception NoAccessException if access checking fails
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
*/
- public MethodHandle unreflect(Method m) throws NoAccessException {
+ public MethodHandle unreflect(Method m) throws IllegalAccessException {
MemberName method = new MemberName(m);
assert(method.isMethod());
if (!m.isAccessible()) checkMethod(method.getDeclaringClass(), method, method.isStatic());
@@ -595,7 +877,7 @@
}
/**
- * Produce a method handle for a reflected method.
+ * Produces a method handle for a reflected method.
* It will bypass checks for overriding methods on the receiver,
* as if by a {@code invokespecial} instruction from within the {@code specialCaller}.
* The type of the method handle will be that of the method,
@@ -603,12 +885,17 @@
* If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class,
* as if {@code invokespecial} instruction were being linked.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the method's variable arity modifier bit ({@code 0x0080}) is set.
* @param m the reflected method
* @param specialCaller the class nominally calling the method
* @return a method handle which can invoke the reflected method
- * @exception NoAccessException if access checking fails
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if any argument is null
*/
- public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws NoAccessException {
+ public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
checkSpecialCaller(specialCaller);
MemberName method = new MemberName(m);
assert(method.isMethod());
@@ -619,7 +906,7 @@
}
/**
- * Produce a method handle for a reflected constructor.
+ * Produces a method handle for a reflected constructor.
* The type of the method handle will be that of the constructor,
* with the return type changed to the declaring class.
* The method handle will perform a {@code newInstance} operation,
@@ -628,20 +915,26 @@
* <p>
* If the constructor's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class.
+ * <p>
+ * The returned method handle will have
+ * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
+ * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
* @param c the reflected constructor
* @return a method handle which can invoke the reflected constructor
- * @exception NoAccessException if access checking fails
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
*/
- public MethodHandle unreflectConstructor(Constructor c) throws NoAccessException {
+ public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException {
MemberName ctor = new MemberName(c);
assert(ctor.isConstructor());
if (!c.isAccessible()) checkAccess(c.getDeclaringClass(), ctor);
MethodHandle rawCtor = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull());
- return MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawCtor);
+ MethodHandle allocator = MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawCtor);
+ return fixVarargs(allocator, rawCtor);
}
/**
- * Produce a method handle giving read access to a reflected field.
+ * Produces a method handle giving read access to a reflected field.
* The type of the method handle will have a return type of the field's
* value type.
* If the field is static, the method handle will take no arguments.
@@ -651,14 +944,15 @@
* access checking is performed immediately on behalf of the lookup class.
* @param f the reflected field
* @return a method handle which can load values from the reflected field
- * @exception NoAccessException if access checking fails
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
*/
- public MethodHandle unreflectGetter(Field f) throws NoAccessException {
+ public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), false);
}
/**
- * Produce a method handle giving write access to a reflected field.
+ * Produces a method handle giving write access to a reflected field.
* The type of the method handle will have a void return type.
* If the field is static, the method handle will take a single
* argument, of the field's value type, the value to be stored.
@@ -668,40 +962,47 @@
* access checking is performed immediately on behalf of the lookup class.
* @param f the reflected field
* @return a method handle which can store values into the reflected field
- * @exception NoAccessException if access checking fails
+ * @throws IllegalAccessException if access checking fails
+ * @throws NullPointerException if the argument is null
*/
- public MethodHandle unreflectSetter(Field f) throws NoAccessException {
+ public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
return makeAccessor(f.getDeclaringClass(), new MemberName(f), f.isAccessible(), true);
}
/// Helper methods, all package-private.
- MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoAccessException {
+ MemberName resolveOrFail(Class<?> refc, String name, Class<?> type, boolean isStatic) throws NoSuchFieldException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
+ name.getClass(); type.getClass(); // NPE
int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
+ return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
+ NoSuchFieldException.class);
}
- MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoAccessException {
+ MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic) throws NoSuchMethodException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
+ name.getClass(); type.getClass(); // NPE
int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull());
+ return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), true, lookupClassOrNull(),
+ NoSuchMethodException.class);
}
MemberName resolveOrFail(Class<?> refc, String name, MethodType type, boolean isStatic,
- boolean searchSupers, Class<?> specialCaller) throws NoAccessException {
+ boolean searchSupers, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
checkSymbolicClass(refc); // do this before attempting to resolve
+ name.getClass(); type.getClass(); // NPE
int mods = (isStatic ? Modifier.STATIC : 0);
- return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller);
+ return IMPL_NAMES.resolveOrFail(new MemberName(refc, name, type, mods), searchSupers, specialCaller,
+ NoSuchMethodException.class);
}
- void checkSymbolicClass(Class<?> refc) throws NoAccessException {
+ void checkSymbolicClass(Class<?> refc) throws IllegalAccessException {
Class<?> caller = lookupClassOrNull();
if (caller != null && !VerifyAccess.isClassAccessible(refc, caller))
- throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), caller);
+ throw newNoAccessException("symbolic reference class is not public", new MemberName(refc), this);
}
- void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws NoAccessException {
+ void checkMethod(Class<?> refc, MemberName m, boolean wantStatic) throws IllegalAccessException {
String message;
if (m.isConstructor())
message = "expected a method, not a constructor";
@@ -711,10 +1012,10 @@
message = wantStatic ? "expected a static method" : "expected a non-static method";
else
{ checkAccess(refc, m); return; }
- throw newNoAccessException(message, m, lookupClass());
+ throw newNoAccessException(message, m, this);
}
- void checkAccess(Class<?> refc, MemberName m) throws NoAccessException {
+ void checkAccess(Class<?> refc, MemberName m) throws IllegalAccessException {
int allowedModes = this.allowedModes;
if (allowedModes == TRUSTED) return;
int mods = m.getModifiers();
@@ -729,22 +1030,25 @@
&& VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
// Protected members can also be checked as if they were package-private.
return;
- throw newNoAccessException(accessFailedMessage(refc, m), m, lookupClass());
+ throw newNoAccessException(accessFailedMessage(refc, m), m, this);
}
String accessFailedMessage(Class<?> refc, MemberName m) {
Class<?> defc = m.getDeclaringClass();
int mods = m.getModifiers();
- if (!VerifyAccess.isClassAccessible(defc, lookupClass()))
+ // check the class first:
+ boolean classOK = (Modifier.isPublic(defc.getModifiers()) &&
+ (defc == refc ||
+ Modifier.isPublic(refc.getModifiers())));
+ if (!classOK && (allowedModes & PACKAGE) != 0) {
+ classOK = (VerifyAccess.isClassAccessible(defc, lookupClass()) &&
+ (defc == refc ||
+ VerifyAccess.isClassAccessible(refc, lookupClass())));
+ }
+ if (!classOK)
return "class is not public";
- if (refc != defc && !VerifyAccess.isClassAccessible(refc, lookupClass()))
- return "symbolic reference "+refc.getName()+" is not public";
if (Modifier.isPublic(mods))
return "access to public member failed"; // (how?)
- else if (allowedModes == PUBLIC)
- return "member is not public";
- else if (allowedModes == 0)
- return "attempted member access through a non-public class";
if (Modifier.isPrivate(mods))
return "member is private";
if (Modifier.isProtected(mods))
@@ -754,17 +1058,17 @@
private static final boolean ALLOW_NESTMATE_ACCESS = false;
- void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException {
+ void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
if (allowedModes == TRUSTED) return;
if ((allowedModes & PRIVATE) == 0
|| (specialCaller != lookupClass()
&& !(ALLOW_NESTMATE_ACCESS &&
VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
throw newNoAccessException("no private access for invokespecial",
- new MemberName(specialCaller), lookupClass());
+ new MemberName(specialCaller), this);
}
- MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws NoAccessException {
+ MethodHandle restrictProtectedReceiver(MemberName method, MethodHandle mh) throws IllegalAccessException {
// The accessing class only has the right to use a protected member
// on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc.
if (!method.isProtected() || method.isStatic()
@@ -776,7 +1080,7 @@
else
return restrictReceiver(method, mh, lookupClass());
}
- MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws NoAccessException {
+ MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
assert(!method.isStatic());
Class<?> defc = method.getDeclaringClass(); // receiver type of mh is too wide
if (defc.isInterface() || !defc.isAssignableFrom(caller)) {
@@ -785,22 +1089,23 @@
MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh;
MethodType narrowType = rawType.changeParameterType(0, caller);
- return MethodHandleImpl.convertArguments(IMPL_TOKEN, mh, narrowType, rawType, null);
+ MethodHandle narrowMH = MethodHandleImpl.convertArguments(IMPL_TOKEN, mh, narrowType, rawType, null);
+ return fixVarargs(narrowMH, mh);
}
MethodHandle makeAccessor(Class<?> refc, String name, Class<?> type,
- boolean isStatic, boolean isSetter) throws NoAccessException {
+ boolean isStatic, boolean isSetter) throws NoSuchFieldException, IllegalAccessException {
MemberName field = resolveOrFail(refc, name, type, isStatic);
if (isStatic != field.isStatic())
throw newNoAccessException(isStatic
? "expected a static field"
: "expected a non-static field",
- field, lookupClass());
+ field, this);
return makeAccessor(refc, field, false, isSetter);
}
MethodHandle makeAccessor(Class<?> refc, MemberName field,
- boolean trusted, boolean isSetter) throws NoAccessException {
+ boolean trusted, boolean isSetter) throws IllegalAccessException {
assert(field.isField());
if (trusted)
return MethodHandleImpl.accessField(IMPL_TOKEN, field, isSetter, lookupClassOrNull());
@@ -811,12 +1116,13 @@
}
/**
- * Produce a method handle giving read access to elements of an array.
+ * Produces a method handle giving read access to elements of an array.
* The type of the method handle will have a return type of the array's
* element type. Its first argument will be the array type,
* and the second will be {@code int}.
* @param arrayClass an array type
* @return a method handle which can load values from the given array type
+ * @throws NullPointerException if the argument is null
* @throws IllegalArgumentException if arrayClass is not an array type
*/
public static
@@ -825,11 +1131,12 @@
}
/**
- * Produce a method handle giving write access to elements of an array.
+ * Produces a method handle giving write access to elements of an array.
* The type of the method handle will have a void return type.
* Its last argument will be the array's element type.
* The first and second arguments will be the array type and int.
* @return a method handle which can store values into the array type
+ * @throws NullPointerException if the argument is null
* @throws IllegalArgumentException if arrayClass is not an array type
*/
public static
@@ -840,47 +1147,7 @@
/// method handle invocation (reflective style)
/**
- * Produce a method handle which will invoke any method handle of the
- * given type on a standard set of {@code Object} type arguments.
- * The resulting invoker will be a method handle with the following
- * arguments:
- * <ul>
- * <li>a single {@code MethodHandle} target
- * <li>zero or more {@code Object} values (one for each argument in {@code type})
- * </ul>
- * <p>
- * The invoker will behave like a call to {@link MethodHandle.invokeGeneric} with
- * the indicated {@code type}.
- * That is, if the target is exactly of the given {@code type}, it will behave
- * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle.asType}
- * is used to convert the target to the required {@code type}.
- * <p>
- * The type of the returned invoker will not be the given {@code type}, but rather
- * will have all parameter and return types replaced by {@code Object}.
- * <p>
- * Before invoking its target, the invoker will apply reference casts as
- * necessary and unbox and widen primitive arguments, as if by {@link #convertArguments}.
- * The return value of the invoker will be an {@code Object} reference,
- * boxing a primitive value if the original type returns a primitive,
- * and always null if the original type returns void.
- * <p>
- * This method is equivalent to the following code (though it may be more efficient):
- * <p><blockquote><pre>
- * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type);
- * MethodType genericType = type.generic();
- * genericType = genericType.insertParameterType(0, MethodHandle.class);
- * return invoker.asType(genericType);
- * </pre></blockquote>
- * @param type the type of target methods which the invoker will apply to
- * @return a method handle suitable for invoking any method handle of the given type
- */
- static public
- MethodHandle genericInvoker(MethodType type) {
- return invokers(type).genericInvoker();
- }
-
- /**
- * Produce a method handle which will invoke any method handle of the
+ * Produces a method handle which will invoke any method handle of the
* given {@code type} on a standard set of {@code Object} type arguments
* and a single trailing {@code Object[]} array.
* The resulting invoker will be a method handle with the following
@@ -891,10 +1158,10 @@
* <li>an {@code Object[]} array containing more arguments
* </ul>
* <p>
- * The invoker will behave like a call to {@link MethodHandle.invokeGeneric} with
+ * The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} with
* the indicated {@code type}.
* That is, if the target is exactly of the given {@code type}, it will behave
- * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle.asType}
+ * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
* is used to convert the target to the required {@code type}.
* <p>
* The type of the returned invoker will not be the given {@code type}, but rather
@@ -909,34 +1176,56 @@
* <p>
* This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre>
- * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type);
- * MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
- * vaType = vaType.insertParameterType(0, MethodHandle.class);
- * int spreadArgCount = type.parameterCount - objectArgCount;
- * invoker = invoker.asSpreader(Object.class, spreadArgCount);
- * return invoker.asType(vaType);
+MethodHandle invoker = MethodHandles.genericInvoker(type);
+int spreadArgCount = type.parameterCount - objectArgCount;
+invoker = invoker.asSpreader(Object[].class, spreadArgCount);
+return invoker;
* </pre></blockquote>
+ * <p>
+ * This method throws no reflective or security exceptions.
* @param type the desired target type
* @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
* @return a method handle suitable for invoking any method handle of the given type
*/
static public
- MethodHandle varargsInvoker(MethodType type, int objectArgCount) {
+ MethodHandle spreadInvoker(MethodType type, int objectArgCount) {
if (objectArgCount < 0 || objectArgCount > type.parameterCount())
throw new IllegalArgumentException("bad argument count "+objectArgCount);
- return invokers(type).varargsInvoker(objectArgCount);
+ return invokers(type).spreadInvoker(objectArgCount);
}
/**
- * Produce a method handle which will take a invoke any method handle of the
- * given type. The resulting invoker will have a type which is
+ * Produces a special <em>invoker method handle</em> which can be used to
+ * invoke any method handle of the given type, as if by {@code invokeExact}.
+ * The resulting invoker will have a type which is
* exactly equal to the desired type, except that it will accept
* an additional leading argument of type {@code MethodHandle}.
* <p>
* This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre>
- * lookup().findVirtual(MethodHandle.class, "invokeExact", type);
+publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
* </pre></blockquote>
+ *
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * Invoker method handles can be useful when working with variable method handles
+ * of unknown types.
+ * For example, to emulate an {@code invokeExact} call to a variable method
+ * handle {@code M}, extract its type {@code T},
+ * look up the invoker method {@code X} for {@code T},
+ * and call the invoker method, as {@code X.invokeGeneric(T, A...)}.
+ * (It would not work to call {@code X.invokeExact}, since the type {@code T}
+ * is unknown.)
+ * If spreading, collecting, or other argument transformations are required,
+ * they can be applied once to the invoker {@code X} and reused on many {@code M}
+ * method handle values, as long as they are compatible with the type of {@code X}.
+ * <p>
+ * <em>(Note: The invoker method is not available via the Core Reflection API.
+ * An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke}
+ * on the declared {@code invokeExact} or {@code invokeGeneric} method will raise an
+ * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
+ * <p>
+ * This method throws no reflective or security exceptions.
* @param type the desired target type
* @return a method handle suitable for invoking any method handle of the given type
*/
@@ -945,12 +1234,38 @@
return invokers(type).exactInvoker();
}
+ /**
+ * Produces a special <em>invoker method handle</em> which can be used to
+ * invoke any method handle of the given type, as if by {@code invokeGeneric}.
+ * The resulting invoker will have a type which is
+ * exactly equal to the desired type, except that it will accept
+ * an additional leading argument of type {@code MethodHandle}.
+ * <p>
+ * Before invoking its target, the invoker will apply reference casts as
+ * necessary and unbox and widen primitive arguments, as if by {@link #convertArguments convertArguments}.
+ * The return value of the invoker will be an {@code Object} reference,
+ * boxing a primitive value if the original type returns a primitive,
+ * and always null if the original type returns void.
+ * <p>
+ * This method is equivalent to the following code (though it may be more efficient):
+ * <p><blockquote><pre>
+publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
+ * </pre></blockquote>
+ * <p>
+ * This method throws no reflective or security exceptions.
+ * @param type the desired target type
+ * @return a method handle suitable for invoking any method handle convertible to the given type
+ */
+ static public
+ MethodHandle genericInvoker(MethodType type) {
+ return invokers(type).genericInvoker();
+ }
+
static Invokers invokers(MethodType type) {
return MethodTypeImpl.invokers(IMPL_TOKEN, type);
}
/**
- * <em>WORK IN PROGRESS:</em>
* Perform value checking, exactly as if for an adapted method handle.
* It is assumed that the given value is either null, of type T0,
* or (if T0 is primitive) of the wrapper type corresponding to T0.
@@ -1021,7 +1336,7 @@
/// method handle modification (creation from other method handles)
/**
- * Produce a method handle which adapts the type of the
+ * Produces 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.
* The resulting method handle is guaranteed to report a type
@@ -1060,6 +1375,7 @@
* @return a method handle which delegates to {@code target} after performing
* any necessary argument conversions, and arranges for any
* necessary return value conversions
+ * @throws NullPointerException if either argument is null
* @throws WrongMethodTypeException if the conversion cannot be made
* @see MethodHandle#asType
* @see MethodHandles#explicitCastArguments
@@ -1081,7 +1397,7 @@
}
/**
- * Produce a method handle which adapts the type of the
+ * Produces 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.
* The resulting method handle is guaranteed to report a type
@@ -1113,6 +1429,7 @@
* @return a method handle which delegates to {@code target} after performing
* any necessary argument conversions, and arranges for any
* necessary return value conversions
+ * @throws NullPointerException if either argument is null
* @throws WrongMethodTypeException if the conversion cannot be made
* @see MethodHandle#asType
* @see MethodHandles#convertArguments
@@ -1160,7 +1477,7 @@
*/
/**
- * Produce a method handle which adapts the calling sequence of the
+ * Produces a method handle which adapts the calling sequence of the
* given method handle to a new type, by reordering the arguments.
* The resulting method handle is guaranteed to report a type
* which is equal to the desired new type.
@@ -1208,6 +1525,7 @@
* @param reorder a string which controls the reordering
* @return a method handle which delegates to {@code target} after it
* drops unused arguments and moves and/or duplicates the other arguments
+ * @throws NullPointerException if any argument is null
*/
public static
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
@@ -1233,11 +1551,10 @@
}
/**
- * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Equivalent to the following code:
* <p><blockquote><pre>
* int spreadPos = newType.parameterCount() - 1;
- * Class<?> spreadType = newType.parameterType(spreadPos);
+ * Class<?> spreadType = newType.parameterType(spreadPos);
* int spreadCount = target.type().parameterCount() - spreadPos;
* MethodHandle adapter = target.asSpreader(spreadType, spreadCount);
* adapter = adapter.asType(newType);
@@ -1247,9 +1564,8 @@
* @param newType the expected type of the new method handle
* @return a method handle which spreads its final argument,
* before calling the original method handle
- * @deprecated Use {@link MethodHandle#asSpreader}
*/
- public static
+ /*non-public*/ static
MethodHandle spreadArguments(MethodHandle target, MethodType newType) {
MethodType oldType = target.type();
int inargs = newType.parameterCount();
@@ -1267,11 +1583,10 @@
}
/**
- * <em>METHOD WILL BE REMOVED FOR PFD:</em>
* Equivalent to the following code:
* <p><blockquote><pre>
* int collectPos = target.type().parameterCount() - 1;
- * Class<?> collectType = target.type().parameterType(collectPos);
+ * Class<?> collectType = target.type().parameterType(collectPos);
* if (!collectType.isArray()) collectType = Object[].class;
* int collectCount = newType.parameterCount() - collectPos;
* MethodHandle adapter = target.asCollector(collectType, collectCount);
@@ -1282,9 +1597,8 @@
* @param newType the expected type of the new method handle
* @return a method handle which collects some trailing argument
* into an array, before calling the original method handle
- * @deprecated Use {@link MethodHandle#asCollector} instead.
*/
- public static
+ /*non-public*/ static
MethodHandle collectArguments(MethodHandle target, MethodType newType) {
MethodType oldType = target.type();
int inargs = newType.parameterCount();
@@ -1301,7 +1615,7 @@
}
/**
- * Produce a method handle of the requested return type which returns the given
+ * Produces a method handle of the requested return type which returns the given
* constant value every time it is invoked.
* <p>
* Before the method handle is returned, the passed-in value is converted to the requested type.
@@ -1312,12 +1626,15 @@
* @param type the return type of the desired method handle
* @param value the value to return
* @return a method handle of the given return type and no arguments, which always returns the given value
- * @throws WrongMethodTypeException if the value cannot be converted to the required return type
+ * @throws NullPointerException if the {@code type} argument is null
+ * @throws ClassCastException if the value cannot be converted to the required return type
+ * @throws IllegalArgumentException if the given type is {@code void.class}
*/
public static
MethodHandle constant(Class<?> type, Object value) {
if (type.isPrimitive()) {
- if (type == void.class) return identity(type);
+ if (type == void.class)
+ throw newIllegalArgumentException("void type");
Wrapper w = Wrapper.forPrimitiveType(type);
return identity(type).bindTo(w.convert(value, type));
} else {
@@ -1326,64 +1643,22 @@
}
/**
- * Produce a method handle of the requested type which returns the given
- * constant value every time it is invoked.
- * <p>
- * Before the method handle is returned, the passed-in value is converted to the requested return type,
- * as if by {@link #explicitCastArguments #explicitCastArguments}.
- * That is, if the return type is primitive, the value is unboxed,
- * and the primitive value is widened and/or narrowed.
- * Otherwise, reference conversions are attempted.
- * @param type the type of the desired method handle
- * @param value the value to return
- * @return a method handle of the given return type and no arguments, which always returns the given value
- * @throws WrongMethodTypeException if the value cannot be converted to the required return type
+ * Produces 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
+ * @return a unary method handle which accepts and returns the given type
+ * @throws NullPointerException if the argument is null
+ * @throws IllegalArgumentException if the given type is {@code void.class}
*/
public static
- MethodHandle constant(MethodType type, Object value) {
- MethodHandle target = constant(type.returnType(), value);
- int len = type.parameterCount();
- if (len == 0)
- return target.asType(type);
- target = target.asType(type.dropParameterTypes(0, len));
- return dropArguments(target, 0, type.parameterList().subList(0, len));
- }
-
- /**
- * 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
- * @return a unary method handle which accepts and returns the given type
- */
- public static
MethodHandle identity(Class<?> type) {
+ if (type == void.class)
+ throw newIllegalArgumentException("void type");
return ValueConversions.identity(type);
}
- /**
- * 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}.
- * If there are additional arguments beyond the first, they are discarded.
- * <p>The identity function for {@code void} discards all its arguments.
- * @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
- */
- public static
- MethodHandle identity(MethodType type) {
- MethodHandle target = identity(type.returnType());
- int len = type.parameterCount();
- if (len == 1)
- return explicitCastArguments(target, type);
- if (len == 0)
- throw new IllegalArgumentException("not enough arguments");
- target = explicitCastArguments(target, type.dropParameterTypes(1, len));
- return dropArguments(target, 1, type.parameterList().subList(1, len));
- }
-
/**
- * Produce a method handle which calls the original method handle {@code target},
+ * Produces a method handle which calls the original method handle {@code target},
* after inserting the given argument(s) at the given position.
* The formal parameters to {@code target} which will be supplied by those
* arguments are called <em>bound parameters</em>, because the new method
@@ -1404,6 +1679,7 @@
* @param values the series of arguments to insert
* @return a method handle which inserts an additional argument,
* before calling the original method handle
+ * @throws NullPointerException if the {@code target} argument or the {@code values} array is null
* @see MethodHandle#bindTo
*/
public static
@@ -1438,7 +1714,7 @@
}
/**
- * Produce a method handle which calls the original method handle,
+ * Produces a method handle which calls the original method handle,
* after dropping the given argument(s) at the given position.
* The type of the new method handle will insert the given argument
* type(s), at that position, into the original handle's type.
@@ -1470,6 +1746,9 @@
* @param pos position of first argument to drop (zero for the leftmost)
* @return a method handle which drops arguments of the given types,
* before calling the original method handle
+ * @throws NullPointerException if the {@code target} argument is null,
+ * or if the {@code valueTypes} list or any of its elements is null
+ * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class}
*/
public static
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
@@ -1487,7 +1766,7 @@
}
/**
- * Produce a method handle which calls the original method handle,
+ * Produces a method handle which calls the original method handle,
* after dropping the given argument(s) at the given position.
* The type of the new method handle will insert the given argument
* type(s), at that position, into the original handle's type.
@@ -1500,6 +1779,9 @@
* @param pos position of first argument to drop (zero for the leftmost)
* @return a method handle which drops arguments of the given types,
* before calling the original method handle
+ * @throws NullPointerException if the {@code target} argument is null,
+ * or if the {@code valueTypes} array or any of its elements is null
+ * @throws IllegalArgumentException if any of the {@code valueTypes} is {@code void.class}
*/
public static
MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
@@ -1514,7 +1796,8 @@
* <p>
* The pre-processing is performed by one or more method handles,
* specified in the elements of the {@code filters} array.
- * (If there are no elements in the array, the original target is returned.)
+ * Null arguments in the array are ignored, and the corresponding arguments left unchanged.
+ * (If there are no non-null elements in the array, the original target is returned.)
* Each filter is applied to the corresponding argument of the adapter.
* <p>
* If a filter {@code F} applies to the {@code N}th argument of
@@ -1544,12 +1827,16 @@
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
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
* @param filters method handles to call initially on filtered arguments
* @return method handle which incorporates the specified argument filtering logic
- * @throws IllegalArgumentException if an element of {@code filters} is null or
- * does not match a corresponding argument type of {@code target} as described above
+ * @throws NullPointerException if the {@code target} argument is null
+ * or if the {@code filters} array is null
+ * @throws IllegalArgumentException if a non-null element of {@code filters}
+ * does not match a corresponding argument type of {@code target} as described above,
+ * or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()}
*/
public static
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
@@ -1557,17 +1844,18 @@
MethodHandle adapter = target;
MethodType adapterType = targetType;
int maxPos = targetType.parameterCount();
- int curPos = pos;
+ if (pos + filters.length > maxPos)
+ throw newIllegalArgumentException("too many filters");
+ int curPos = pos-1; // pre-incremented
for (MethodHandle filter : filters) {
- if (curPos >= maxPos)
- throw newIllegalArgumentException("too many filters");
+ curPos += 1;
+ if (filter == null) continue; // ignore null elements of filters
MethodType filterType = filter.type();
if (filterType.parameterCount() != 1
|| filterType.returnType() != targetType.parameterType(curPos))
throw newIllegalArgumentException("target and filter types do not match");
adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0));
adapter = MethodHandleImpl.filterArgument(IMPL_TOKEN, adapter, curPos, filter);
- curPos += 1;
}
MethodType midType = adapter.type();
if (midType != adapterType)
@@ -1602,7 +1890,8 @@
* @param target the method handle to invoke before filtering the return value
* @param filter method handle to call on the return value
* @return method handle which incorporates the specified return value filtering logic
- * @throws IllegalArgumentException if {@code filter} is null or
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if {@code filter}
* does not match the return type of {@code target} as described above
*/
public static
@@ -1612,9 +1901,11 @@
if (filterType.parameterCount() != 1
|| filterType.parameterType(0) != targetType.returnType())
throw newIllegalArgumentException("target and filter types do not match");
+ // result = fold( lambda(retval, arg...) { filter(retval) },
+ // lambda( arg...) { target(arg...) } )
// FIXME: Too many nodes here.
- MethodHandle returner = dropArguments(filter, 0, targetType.parameterList());
- return foldArguments(returner, exactInvoker(target.type()).bindTo(target));
+ MethodHandle returner = dropArguments(filter, 1, targetType.parameterList());
+ return foldArguments(returner, target);
}
/**
@@ -1637,7 +1928,7 @@
* (Note that {@link #dropArguments(MethodHandle,int,List) dropArguments} can be used to remove any arguments
* that either the {@code combiner} or {@code target} does not wish to receive.
* If some of the incoming arguments are destined only for the combiner,
- * consider using {@link MethodHandle#asCollector} instead, since those
+ * consider using {@link MethodHandle#asCollector asCollector} instead, since those
* arguments will not need to be live on the stack on entry to the
* target.)
* <p>
@@ -1656,6 +1947,7 @@
* @param target the method handle to invoke after arguments are combined
* @param combiner method handle to call initially on the incoming arguments
* @return method handle which incorporates the specified argument folding logic
+ * @throws NullPointerException if either argument is null
* @throws IllegalArgumentException if the first argument type of
* {@code target} is not the same as {@code combiner}'s return type,
* or if the following argument types of {@code target}
@@ -1704,6 +1996,7 @@
* @param target method handle to call if test passes
* @param fallback method handle to call if test fails
* @return method handle which incorporates the specified if/then/else logic
+ * @throws NullPointerException if any argument is null
* @throws IllegalArgumentException if {@code test} does not return boolean,
* or if all three method types do not match (with the return
* type of {@code test} changed to match that of {@code target}).
@@ -1772,6 +2065,7 @@
* @param exType the type of exception which the handler will catch
* @param handler method handle to call if a matching exception is thrown
* @return method handle which incorporates the specified try/catch logic
+ * @throws NullPointerException if any argument is null
* @throws IllegalArgumentException if {@code handler} does not accept
* the given exception type, or if the method handle types do
* not match in their return types and their
@@ -1802,12 +2096,14 @@
}
/**
- * Produce a method handle which will throw exceptions of the given {@code exType}.
+ * Produces a method handle which will throw exceptions of the given {@code exType}.
* The method handle will accept a single argument of {@code exType},
* and immediately throw it as an exception.
* The method type will nominally specify a return of {@code returnType}.
* The return type may be anything convenient: It doesn't matter to the
* method handle's behavior, since it will never return normally.
+ * @return method handle which can throw the given exceptions
+ * @throws NullPointerException if either argument is null
*/
public static
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
@@ -1815,18 +2111,43 @@
}
/**
- * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
- * Produce a wrapper instance of the given "SAM" interface which redirects
+ * Produces an instance of the given "SAM" interface which redirects
* its calls to the given method handle.
+ * <p>
* A SAM interface is an interface which declares a single abstract method.
- * The type must be public. (No additional access checks are performed.)
+ * 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.
+ * Also, if the SAM interface has a supertype,
+ * the SAM interface may override an inherited method.
+ * Any such overrides are respected, and the method handle will be accessible
+ * by either the inherited method or the SAM method.
+ * In particular, a {@linkplain java.lang.reflect.Method#isBridge bridge method}
+ * may be created if the methods have different return types.
+ * <p>
+ * 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
* the given {@code target} on the incoming arguments,
* and returning or throwing whatever the {@code target}
* returns or throws. The invocation will be as if by
- * {@code target.invokeExact}.
+ * {@code target.invokeGeneric}.
+ * The target's type will be checked before the SAM
+ * instance is created, as if by a call to {@code asType},
+ * which may result in a {@code WrongMethodTypeException}.
+ * <p>
+ * The wrapper instance will implement the requested SAM interface
+ * and its super-types, but no other SAM types.
+ * This means that the SAM instance will not unexpectedly
+ * pass an {@code instanceof} test for any unrequested type.
+ * <p style="font-size:smaller;">
+ * <em>Implementation Note:</em>
+ * Therefore, each SAM instance must implement a unique SAM type.
+ * Implementations may not bundle together
+ * multiple SAM types onto single implementation classes
+ * in the style of {@link java.awt.AWTEventMulticaster}.
* <p>
* The method handle may throw an <em>undeclared exception</em>,
* which means any checked exception (or other checked throwable)
@@ -1835,54 +2156,46 @@
* {@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
- * or methods except system-defined classes and methods.
- * The implementation class C will have no public supertypes
- * or public methods beyond the following:
- * <ul>
- * <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.)
+ * Like {@link java.lang.Integer#valueOf Integer.valueOf},
+ * {@code asInstance} is a factory method whose results are defined
+ * by their behavior.
+ * It is not guaranteed to return a new instance for every call.
* <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 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 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.
+ * Future versions of this API may accept additional types,
+ * such as abstract classes with single abstract methods.
+ * Future versions of this API may also equip wrapper instances
+ * with one or more additional public "marker" interfaces.
+ *
* @param target the method handle to invoke from the wrapper
* @param samType the desired type of the wrapper, a SAM type
* @return a correctly-typed wrapper for the given {@code target}
- * @throws IllegalArgumentException if the {@code target} throws
- * an undeclared exception
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if the {@code samType} is not a
+ * valid argument to this method
+ * @throws WrongMethodTypeException if the {@code target} cannot
+ * be converted to the type required by the SAM type
*/
- // ISSUE: Should we delegate equals/hashCode to the targets?
- // Not useful unless there is a stable equals/hashCode behavior
- // for MethodHandle, but there isn't.
+ // Other notes to implementors:
+ // <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>
+ // 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 implementation may return that wrapper in place of
+ // a new wrapper.
+ // <p>
+ // This method is designed to apply to common use cases
+ // where a single method handle must interoperate with
+ // an interface that implements a function-like
+ // API. Additional variations, such as SAM classes with
+ // private constructors, or interfaces with multiple but related
+ // entry points, must be covered by hand-written or automatically
+ // generated adapter classes.
+ //
public static
<T> T asInstance(final MethodHandle target, final Class<T> samType) {
// POC implementation only; violates the above contract several ways
@@ -1890,22 +2203,23 @@
if (sam == null)
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: "+target+" should match "+sam);
+ MethodHandle checkTarget = target.asType(samMT); // make throw WMT
+ checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
+ final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, samMT.parameterCount());
return samType.cast(Proxy.newProxyInstance(
samType.getClassLoader(),
- new Class[]{ samType, AsInstanceObject.class },
+ new Class[]{ samType, WrapperInstance.class },
new InvocationHandler() {
private Object getArg(String name) {
- if ((Object)name == "getAsInstanceTarget") return target;
- if ((Object)name == "getAsInstanceType") return samType;
+ if ((Object)name == "getWrapperInstanceTarget") return target;
+ if ((Object)name == "getWrapperInstanceType") return samType;
throw new AssertionError();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if (method.getDeclaringClass() == AsInstanceObject.class)
+ if (method.getDeclaringClass() == WrapperInstance.class)
return getArg(method.getName());
if (method.equals(sam))
- return target.invokeVarargs(args);
+ return vaTarget.invokeExact(args);
if (isObjectMethod(method))
return callObjectMethod(this, method, args);
throw new InternalError();
@@ -1914,21 +2228,49 @@
}
/**
- * <em>PROVISIONAL API, WORK IN PROGRESS:</em>
- * Interface implemented by every object which is produced by {@link #asInstance asInstance}.
- * The methods of this interface allow a caller to recover the parameters
- * to {@code asInstance}.
- * This allows applications to repeatedly convert between method handles
- * and SAM objects, without the risk of creating unbounded delegation chains.
+ * Determine if the given object was produced by a call to {@link #asInstance asInstance}.
+ * @param x any reference
+ * @return true if the reference is not null and points to an object produced by {@code asInstance}
*/
- public interface AsInstanceObject {
- /** Produce or recover a target method handle which is behaviorally
- * equivalent to the SAM method of this object.
- */
- public MethodHandle getAsInstanceTarget();
- /** Recover the SAM type for which this object was created.
- */
- public Class<?> getAsInstanceType();
+ public static
+ boolean isWrapperInstance(Object x) {
+ return x instanceof WrapperInstance;
+ }
+
+ private static WrapperInstance asWrapperInstance(Object x) {
+ try {
+ if (x != null)
+ return (WrapperInstance) x;
+ } catch (ClassCastException ex) {
+ }
+ throw new IllegalArgumentException("not a wrapper instance");
+ }
+
+ /**
+ * Produces or recovers a target method handle which is behaviorally
+ * equivalent to the SAM method of this wrapper instance.
+ * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}.
+ * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
+ * @param x any reference
+ * @return a method handle implementing the SAM method
+ * @throws IllegalArgumentException if the reference x is not to a wrapper instance
+ */
+ public static
+ MethodHandle wrapperInstanceTarget(Object x) {
+ return asWrapperInstance(x).getWrapperInstanceTarget();
+ }
+
+ /**
+ * Recover the SAM type for which this wrapper instance was created.
+ * The object {@code x} must have been produced by a call to {@link #asInstance asInstance}.
+ * This requirement may be tested via {@link #isWrapperInstance isWrapperInstance}.
+ * @param x any reference
+ * @return the SAM type for which the wrapper was created
+ * @throws IllegalArgumentException if the reference x is not to a wrapper instance
+ */
+ public static
+ Class<?> wrapperInstanceType(Object x) {
+ return asWrapperInstance(x).getWrapperInstanceType();
}
private static
@@ -1991,7 +2333,7 @@
}
/*non-public*/
- static MethodHandle withTypeHandler(MethodHandle target, MethodHandle typeHandler) {
- return MethodHandleImpl.withTypeHandler(IMPL_TOKEN, target, typeHandler);
+ static MethodHandle asVarargsCollector(MethodHandle target, Class<?> arrayType) {
+ return MethodHandleImpl.asVarargsCollector(IMPL_TOKEN, target, arrayType);
}
}
--- a/jdk/src/share/classes/java/dyn/MethodType.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/MethodType.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
import java.util.List;
import sun.dyn.Access;
import sun.dyn.Invokers;
+import sun.dyn.MethodHandleImpl;
import sun.dyn.MethodTypeImpl;
import sun.dyn.util.BytecodeDescriptor;
import static sun.dyn.MemberName.newIllegalArgumentException;
@@ -41,8 +42,8 @@
* and expected by a method handle caller. Method types must be properly
* matched between a method handle and all its callers,
* and the JVM's operations enforce this matching at, specifically
- * during calls to {@link MethodHandle#invokeExact}
- * and {@link MethodHandle#invokeGeneric}, and during execution
+ * during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
+ * and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution
* of {@code invokedynamic} instructions.
* <p>
* The structure is a return type accompanied by any number of parameter types.
@@ -70,8 +71,9 @@
* with the instructions in a class file's constant pool.
* <p>
* Like classes and strings, method types can also be represented directly
- * in a class file's constant pool as constants. The may be loaded by an {@code ldc}
- * instruction which refers to a suitable {@code CONSTANT_MethodType} constant pool entry.
+ * in a class file's constant pool as constants.
+ * A method type may be loaded by an {@code ldc} instruction which refers
+ * to a suitable {@code CONSTANT_MethodType} constant pool entry.
* The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string.
* For more details, see the <a href="package-summary.html#mtcon">package summary</a>.
* <p>
@@ -82,9 +84,14 @@
* @author John Rose, JSR 292 EG
*/
public final
-class MethodType {
+class MethodType implements java.io.Serializable {
+ private static final long serialVersionUID = 292L; // {rtype, {ptype...}}
+
+ // The rtype and ptypes fields define the structural identity of the method type:
private final Class<?> rtype;
private final Class<?>[] ptypes;
+
+ // The remaining fields are caches of various sorts:
private MethodTypeForm form; // erased form, plus cached data about primitives
private MethodType wrapAlt; // alternative wrapped/unwrapped version
private Invokers invokers; // cache of handy higher-order adapters
@@ -117,6 +124,9 @@
});
}
+ /**
+ * Check the given parameters for validity and store them into the final fields.
+ */
private MethodType(Class<?> rtype, Class<?>[] ptypes) {
checkRtype(rtype);
checkPtypes(ptypes);
@@ -124,15 +134,32 @@
this.ptypes = ptypes;
}
- private void checkRtype(Class<?> rtype) {
+ private static void checkRtype(Class<?> rtype) {
rtype.equals(rtype); // null check
}
- private void checkPtypes(Class<?>[] ptypes) {
+ private static int checkPtype(Class<?> ptype) {
+ ptype.getClass(); //NPE
+ if (ptype == void.class)
+ throw newIllegalArgumentException("parameter type cannot be void");
+ if (ptype == double.class || ptype == long.class) return 1;
+ return 0;
+ }
+ /** Return number of extra slots (count of long/double args). */
+ private static int checkPtypes(Class<?>[] ptypes) {
+ int slots = 0;
for (Class<?> ptype : ptypes) {
- ptype.equals(ptype); // null check
- if (ptype == void.class)
- throw newIllegalArgumentException("parameter type cannot be void");
+ slots += checkPtype(ptype);
}
+ checkSlotCount(ptypes.length + slots);
+ return slots;
+ }
+ private static void checkSlotCount(int count) {
+ if ((count & 0xFF) != count)
+ throw newIllegalArgumentException("bad parameter count "+count);
+ }
+ private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
+ if (num instanceof Integer) num = "bad index: "+num;
+ return new IndexOutOfBoundsException(num.toString());
}
static final HashMap<MethodType,MethodType> internTable
@@ -140,27 +167,39 @@
static final Class<?>[] NO_PTYPES = {};
- /** Find or create an instance of the given method type.
+ /**
+ * Find or create an instance of the given method type.
* @param rtype the return type
* @param ptypes the parameter types
- * @return a method type with the given parts
- * @throws NullPointerException if rtype or any ptype is null
- * @throws IllegalArgumentException if any of the ptypes is void
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
+ * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
return makeImpl(rtype, ptypes, false);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
+ * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
+ */
public static
- MethodType methodType(Class<?> rtype, List<? extends Class<?>> ptypes) {
+ MethodType methodType(Class<?> rtype, List<Class<?>> ptypes) {
boolean notrust = false; // random List impl. could return evil ptypes array
return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * The leading parameter type is prepended to the remaining array.
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The leading parameter type is prepended to the remaining array.
+ * @return a method type with the given components
+ * @throws NullPointerException if {@code rtype} or {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is null
+ * @throws IllegalArgumentException if {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
@@ -170,25 +209,37 @@
return makeImpl(rtype, ptypes1, true);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * The resulting method has no parameter types.
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The resulting method has no parameter types.
+ * @return a method type with the given return value
+ * @throws NullPointerException if {@code rtype} is null
*/
public static
MethodType methodType(Class<?> rtype) {
return makeImpl(rtype, NO_PTYPES, true);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * The resulting method has the single given parameter type.
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The resulting method has the single given parameter type.
+ * @return a method type with the given return value and parameter type
+ * @throws NullPointerException if {@code rtype} or {@code ptype0} is null
+ * @throws IllegalArgumentException if {@code ptype0} is {@code void.class}
*/
public static
MethodType methodType(Class<?> rtype, Class<?> ptype0) {
return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * The resulting method has the same parameter types as {@code ptypes},
- * and the specified return type.
+ /**
+ * Finds or creates a method type with the given components.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * The resulting method has the same parameter types as {@code ptypes},
+ * and the specified return type.
+ * @throws NullPointerException if {@code rtype} or {@code ptypes} is null
*/
public static
MethodType methodType(Class<?> rtype, MethodType ptypes) {
@@ -237,17 +288,20 @@
private static final MethodType[] objectOnlyTypes = new MethodType[20];
/**
- * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
+ * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be {@code Object},
* except the final varargs parameter if any, which will be {@code Object[]}.
* @param objectArgCount number of parameters (excluding the varargs parameter if any)
* @param varargs whether there will be a varargs parameter, of type {@code Object[]}
* @return a totally generic method type, given only its count of parameters and varargs
+ * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* @see #genericMethodType(int)
*/
public static
MethodType genericMethodType(int objectArgCount, boolean varargs) {
MethodType mt;
+ checkSlotCount(objectArgCount);
int ivarargs = (!varargs ? 0 : 1);
int ootIndex = objectArgCount*2 + ivarargs;
if (ootIndex < objectOnlyTypes.length) {
@@ -265,9 +319,12 @@
}
/**
+ * Finds or creates a method type whose components are all {@code Object}.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be Object.
* @param objectArgCount number of parameters
* @return a totally generic method type, given only its count of parameters
+ * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* @see #genericMethodType(int, boolean)
*/
public static
@@ -275,27 +332,41 @@
return genericMethodType(objectArgCount, false);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
+ /**
+ * Finds or creates a method type with a single different parameter type.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param num the index (zero-based) of the parameter type to change
* @param nptype a new parameter type to replace the old one with
* @return the same type, except with the selected parameter changed
+ * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
+ * @throws IllegalArgumentException if {@code nptype} is {@code void.class}
+ * @throws NullPointerException if {@code nptype} is null
*/
public MethodType changeParameterType(int num, Class<?> nptype) {
if (parameterType(num) == nptype) return this;
+ checkPtype(nptype);
Class<?>[] nptypes = ptypes.clone();
nptypes[num] = nptype;
return makeImpl(rtype, nptypes, true);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param num the position (zero-based) of the inserted parameter type(s)
- * @param ptypesToInsert zero or more a new parameter types to insert into the parameter list
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
* @return the same type, except with the selected parameter(s) inserted
+ * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) {
int len = ptypes.length;
if (num < 0 || num > len)
- throw newIllegalArgumentException("num="+num); //SPECME
+ throw newIndexOutOfBoundsException(num);
+ int ins = checkPtypes(ptypesToInsert);
+ checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
int ilen = ptypesToInsert.length;
if (ilen == 0) return this;
Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
@@ -304,40 +375,61 @@
return makeImpl(rtype, nptypes, true);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
* @return the same type, except with the selected parameter(s) appended
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType appendParameterTypes(Class<?>... ptypesToInsert) {
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param num the position (zero-based) of the inserted parameter type(s)
+ * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
+ * @return the same type, except with the selected parameter(s) inserted
+ * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
+ */
+ public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
+ return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES));
+ }
+
+ /**
+ * Finds or creates a method type with additional parameter types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
+ * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
* @return the same type, except with the selected parameter(s) appended
+ * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
+ * or if the resulting method type would have more than 255 parameter slots
+ * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
*/
public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
return insertParameterTypes(parameterCount(), ptypesToInsert);
}
- /** 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
- * @return the same type, except with the selected parameter(s) inserted
- */
- public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
- return insertParameterTypes(num, ptypesToInsert.toArray(NO_PTYPES));
- }
-
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
+ /**
+ * Finds or creates a method type with some parameter types omitted.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param start the index (zero-based) of the first parameter type to remove
* @param end the index (greater than {@code start}) of the first parameter type after not to remove
* @return the same type, except with the selected parameter(s) removed
+ * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
+ * or if {@code end} is negative or greater than {@code parameterCount()}
+ * or if {@code start} is greater than {@code end}
*/
public MethodType dropParameterTypes(int start, int end) {
int len = ptypes.length;
if (!(0 <= start && start <= end && end <= len))
- throw newIllegalArgumentException("start="+start+" end="+end); //SPECME
+ throw newIndexOutOfBoundsException("start="+start+" end="+end);
if (start == end) return this;
Class<?>[] nptypes;
if (start == 0) {
@@ -361,17 +453,20 @@
return makeImpl(rtype, nptypes, true);
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
+ /**
+ * Finds or creates a method type with a different return type.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* @param nrtype a return parameter type to replace the old one with
* @return the same type, except with the return type change
+ * @throws NullPointerException if {@code nrtype} is null
*/
public MethodType changeReturnType(Class<?> nrtype) {
if (returnType() == nrtype) return this;
return makeImpl(nrtype, ptypes, true);
}
- /** Convenience method.
- * Report if this type contains a primitive argument or return value.
+ /**
+ * Reports if this type contains a primitive argument or return value.
* The return type {@code void} counts as a primitive.
* @return true if any of the types are primitives
*/
@@ -379,8 +474,8 @@
return form.hasPrimitives();
}
- /** Convenience method.
- * Report if this type contains a wrapper argument or return value.
+ /**
+ * Reports if this type contains a wrapper argument or return value.
* Wrappers are types which box primitive values, such as {@link Integer}.
* The reference type {@code java.lang.Void} counts as a wrapper.
* @return true if any of the types are wrappers
@@ -389,8 +484,9 @@
return unwrap() != this;
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * Erase all reference types to {@code Object}.
+ /**
+ * Erases all reference types to {@code Object}.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All primitive types (including {@code void}) will remain unchanged.
* @return a version of the original type with all reference types replaced
*/
@@ -398,8 +494,9 @@
return form.erasedType();
}
- /** Convenience method for {@link #genericMethodType(int)}.
- * Convert all types, both reference and primitive, to {@code Object}.
+ /**
+ * Converts all types, both reference and primitive, to {@code Object}.
+ * Convenience method for {@link #genericMethodType(int) genericMethodType}.
* The expression {@code type.wrap().erase()} produces the same value
* as {@code type.generic()}.
* @return a version of the original type with all types replaced
@@ -408,8 +505,9 @@
return genericMethodType(parameterCount());
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * Convert all primitive types to their corresponding wrapper types.
+ /**
+ * Converts all primitive types to their corresponding wrapper types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All reference types (including wrapper types) will remain unchanged.
* A {@code void} return type is changed to the type {@code java.lang.Void}.
* The expression {@code type.wrap().erase()} produces the same value
@@ -420,8 +518,9 @@
return hasPrimitives() ? wrapWithPrims(this) : this;
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
+ /**
* Convert all wrapper types to their corresponding primitive types.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All primitive types (including {@code void}) will remain unchanged.
* A return type of {@code java.lang.Void} is changed to {@code void}.
* @return a version of the original type with all wrapper types replaced
@@ -456,23 +555,33 @@
return uwt;
}
- /** @param num the index (zero-based) of the desired parameter type
- * @return the selected parameter type
+ /**
+ * Returns the parameter type at the specified index, within this method type.
+ * @param num the index (zero-based) of the desired parameter type
+ * @return the selected parameter type
+ * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
*/
public Class<?> parameterType(int num) {
return ptypes[num];
}
- /** @return the number of parameter types */
+ /**
+ * Returns the number of parameter types in this method type.
+ * @return the number of parameter types
+ */
public int parameterCount() {
return ptypes.length;
}
- /** @return the return type */
+ /**
+ * Returns the return type of this method type.
+ * @return the return type
+ */
public Class<?> returnType() {
return rtype;
}
/**
- * Convenience method to present the arguments as a list.
+ * Presents the parameter types as a list (a convenience method).
+ * The list will be immutable.
* @return the parameter types (as an immutable list)
*/
public List<Class<?>> parameterList() {
@@ -480,7 +589,7 @@
}
/**
- * Convenience method to present the arguments as an array.
+ * Presents the parameter types as an array (a convenience method).
* Changes to the array will not result in changes to the type.
* @return the parameter types (as a fresh copy if necessary)
*/
@@ -524,14 +633,14 @@
}
/**
+ * Returns a string representation of the method type,
+ * of the form {@code "(PT0,PT1...)RT"}.
* The string representation of a method type is a
* parenthesis enclosed, comma separated list of type names,
* followed immediately by the return type.
* <p>
* Each type is represented by its
* {@link java.lang.Class#getSimpleName simple name}.
- * If a type name name is array, it the base type followed
- * by [], rather than the Class.getName of the array type.
*/
@Override
public String toString() {
@@ -548,21 +657,22 @@
/// Queries which have to do with the bytecode architecture
- /** The number of JVM stack slots required to invoke a method
+ /** Reports the number of JVM stack slots required to invoke a method
* of this type. Note that (for historic reasons) the JVM requires
* a second stack slot to pass long and double arguments.
- * So this method returns {@link #parameterCount()} plus the
+ * So this method returns {@link #parameterCount() parameterCount} plus the
* number of long and double parameters (if any).
* <p>
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* @return the number of JVM stack slots for this type's parameters
+ * @deprecated Will be removed for PFD.
*/
public int parameterSlotCount() {
return form.parameterSlotCount();
}
- /** Number of JVM stack slots which carry all parameters including and after
+ /** Reports the number of JVM stack slots which carry all parameters including and after
* the given position, which must be in the range of 0 to
* {@code parameterCount} inclusive. Successive parameters are
* more shallowly stacked, and parameters are indexed in the bytecodes
@@ -583,6 +693,8 @@
* @param num an index (zero-based, inclusive) within the parameter types
* @return the index of the (shallowest) JVM stack slot transmitting the
* given parameter
+ * @throws IllegalArgumentException if {@code num} is negative or greater than {@code parameterCount()}
+ * @deprecated Will be removed for PFD.
*/
public int parameterSlotDepth(int num) {
if (num < 0 || num > ptypes.length)
@@ -590,7 +702,7 @@
return form.parameterToArgSlot(num-1);
}
- /** The number of JVM stack slots required to receive a return value
+ /** Reports the number of JVM stack slots required to receive a return value
* from a method of this type.
* If the {@link #returnType() return type} is void, it will be zero,
* else if the return type is long or double, it will be two, else one.
@@ -598,13 +710,15 @@
* This method is included for the benfit of applications that must
* generate bytecodes that process method handles and invokedynamic.
* @return the number of JVM stack slots (0, 1, or 2) for this type's return value
+ * @deprecated Will be removed for PFD.
*/
public int returnSlotCount() {
return form.returnSlotCount();
}
- /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}.
- * Find or create an instance of the given method type.
+ /**
+ * Find or create an instance of a method type, given the spelling of its bytecode descriptor.
+ * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* Any class or interface name embedded in the descriptor string
* will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
* on the given loader (or if it is null, on the system class loader).
@@ -614,10 +728,10 @@
* not all reachable from a common class loader.
* <p>
* This method is included for the benfit of applications that must
- * generate bytecodes that process method handles and invokedynamic.
- * @param descriptor a bytecode-level signature string "(T...)T"
+ * generate bytecodes that process method handles and {@code invokedynamic}.
+ * @param descriptor a bytecode-level type descriptor string "(T...)T"
* @param loader the class loader in which to look up the types
- * @return a method type matching the bytecode-level signature
+ * @return a method type matching the bytecode-level type descriptor
* @throws IllegalArgumentException if the string is not well-formed
* @throws TypeNotPresentException if a named type cannot be found
*/
@@ -631,19 +745,121 @@
}
/**
- * Create a bytecode descriptor representation of the method type.
+ * Produces a bytecode descriptor representation of the method type.
* <p>
- * Note that this is not a strict inverse of {@link #fromMethodDescriptorString}.
+ * Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
* Two distinct classes which share a common name but have different class loaders
* will appear identical when viewed within descriptor strings.
* <p>
* This method is included for the benfit of applications that must
- * generate bytecodes that process method handles and invokedynamic.
- * {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader)},
+ * generate bytecodes that process method handles and {@code invokedynamic}.
+ * {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
* because the latter requires a suitable class loader argument.
- * @return the bytecode signature representation
+ * @return the bytecode type descriptor representation
*/
public String toMethodDescriptorString() {
return BytecodeDescriptor.unparse(this);
}
+
+ /// Serialization.
+
+ /**
+ * There are no serializable fields for {@code MethodType}.
+ */
+ private static final java.io.ObjectStreamField[] serialPersistentFields = { };
+
+ /**
+ * Save the {@code MethodType} instance to a stream.
+ *
+ * @serialData
+ * For portability, the serialized format does not refer to named fields.
+ * Instead, the return type and parameter type arrays are written directly
+ * from the {@code writeObject} method, using two calls to {@code s.writeObject}
+ * as follows:
+ * <blockquote><pre>
+s.writeObject(this.returnType());
+s.writeObject(this.parameterArray());
+ * </pre></blockquote>
+ * <p>
+ * The deserialized field values are checked as if they were
+ * provided to the factory method {@link #methodType(Class,Class[]) methodType}.
+ * For example, null values, or {@code void} parameter types,
+ * will lead to exceptions during deserialization.
+ * @param the stream to write the object to
+ */
+ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
+ s.defaultWriteObject(); // requires serialPersistentFields to be an empty array
+ s.writeObject(returnType());
+ s.writeObject(parameterArray());
+ }
+
+ /**
+ * Reconstitute the {@code MethodType} instance from a stream (that is,
+ * deserialize it).
+ * This instance is a scratch object with bogus final fields.
+ * It provides the parameters to the factory method called by
+ * {@link #readResolve readResolve}.
+ * After that call it is discarded.
+ * @param the stream to read the object from
+ * @see #MethodType()
+ * @see #readResolve
+ * @see #writeObject
+ */
+ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject(); // requires serialPersistentFields to be an empty array
+
+ Class<?> returnType = (Class<?>) s.readObject();
+ Class<?>[] parameterArray = (Class<?>[]) s.readObject();
+
+ // Probably this object will never escape, but let's check
+ // the field values now, just to be sure.
+ checkRtype(returnType);
+ checkPtypes(parameterArray);
+
+ parameterArray = parameterArray.clone(); // make sure it is unshared
+ MethodType_init(returnType, parameterArray);
+ }
+
+ /**
+ * For serialization only.
+ * Sets the final fields to null, pending {@code Unsafe.putObject}.
+ */
+ private MethodType() {
+ this.rtype = null;
+ this.ptypes = null;
+ }
+ private void MethodType_init(Class<?> rtype, Class<?>[] ptypes) {
+ // In order to communicate these values to readResolve, we must
+ // store them into the implementation-specific final fields.
+ checkRtype(rtype);
+ checkPtypes(ptypes);
+ unsafe.putObject(this, rtypeOffset, rtype);
+ unsafe.putObject(this, ptypesOffset, ptypes);
+ }
+
+ // Support for resetting final fields while deserializing
+ private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+ private static final long rtypeOffset, ptypesOffset;
+ static {
+ try {
+ rtypeOffset = unsafe.objectFieldOffset
+ (MethodType.class.getDeclaredField("rtype"));
+ ptypesOffset = unsafe.objectFieldOffset
+ (MethodType.class.getDeclaredField("ptypes"));
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+ /**
+ * Resolves and initializes a {@code MethodType} object
+ * after serialization.
+ * @return the fully initialized {@code MethodType} object
+ */
+ private Object readResolve() {
+ // Do not use a trusted path for deserialization:
+ //return makeImpl(rtype, ptypes, true);
+ // Verify all operands, and make sure ptypes is unshared:
+ return methodType(rtype, ptypes);
+ }
}
--- a/jdk/src/share/classes/java/dyn/MutableCallSite.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/MutableCallSite.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -73,7 +73,7 @@
* (This is a normal consequence of the Java Memory Model as applied
* to object fields.)
* <p>
- * The {@link #sync sync} operation provides a way to force threads
+ * The {@link #syncAll syncAll} operation provides a way to force threads
* to accept a new target value, even if there is no other synchronization.
* <p>
* For target values which will be frequently updated, consider using
@@ -82,13 +82,17 @@
*/
public class MutableCallSite extends CallSite {
/**
- * Make a blank call site object with the given method type.
- * An initial target method is supplied which will throw
- * an {@link IllegalStateException} if called.
+ * Creates a blank call site object with the given method type.
+ * The initial target is set to a method handle of the given type
+ * which will throw an {@link IllegalStateException} if called.
+ * <p>
+ * The type of the call site is permanently set to the given type.
* <p>
* Before this {@code CallSite} object is returned from a bootstrap method,
+ * or invoked in some other manner,
* it is usually provided with a more useful target method,
* via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
+ * @param type the method type that this call site will have
* @throws NullPointerException if the proposed type is null
*/
public MutableCallSite(MethodType type) {
@@ -96,8 +100,9 @@
}
/**
- * Make a blank call site object, possibly equipped with an initial target method handle.
- * @param target the method handle which will be the initial target of the call site
+ * Creates a call site object with an initial target method handle.
+ * The type of the call site is permanently set to the initial target's type.
+ * @param target the method handle that will be the initial target of the call site
* @throws NullPointerException if the proposed target is null
*/
public MutableCallSite(MethodHandle target) {
@@ -105,7 +110,59 @@
}
/**
- * Perform a synchronization operation on each call site in the given array,
+ * Returns the target method of the call site, which behaves
+ * like a normal field of the {@code MutableCallSite}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, the current thread may choose to reuse the result
+ * of a previous read of the target from memory, and may fail to see
+ * a recent update to the target by another thread.
+ *
+ * @return the linkage state of this call site, a method handle which can change over time
+ * @see #setTarget
+ */
+ @Override public final MethodHandle getTarget() {
+ return target;
+ }
+
+ /**
+ * Updates the target method of this call site, as a normal variable.
+ * The type of the new target must agree with the type of the old target.
+ * <p>
+ * The interactions with memory are the same
+ * as of a write to an ordinary variable, such as an array element or a
+ * non-volatile, non-final field.
+ * <p>
+ * In particular, unrelated threads may fail to see the updated target
+ * until they perform a read from memory.
+ * Stronger guarantees can be created by putting appropriate operations
+ * into the bootstrap method and/or the target methods used
+ * at any given call site.
+ *
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see #getTarget
+ */
+ @Override public void setTarget(MethodHandle newTarget) {
+ checkTargetChange(this.target, newTarget);
+ setTargetNormal(newTarget);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return makeDynamicInvoker();
+ }
+
+ /**
+ * Performs a synchronization operation on each call site in the given array,
* forcing all other threads to throw away any cached values previously
* loaded from the target of any of the call sites.
* <p>
@@ -115,19 +172,29 @@
* <p>
* The overall effect is to force all future readers of each call site's target
* to accept the most recently stored value.
- * ("Most recently" is reckoned relative to the {@code sync} itself.)
- * Conversely, the {@code sync} call may block until all readers have
+ * ("Most recently" is reckoned relative to the {@code syncAll} itself.)
+ * Conversely, the {@code syncAll} call may block until all readers have
* (somehow) decached all previous versions of each call site's target.
* <p>
- * To avoid race conditions, calls to {@code setTarget} and {@code sync}
+ * To avoid race conditions, calls to {@code setTarget} and {@code syncAll}
* should generally be performed under some sort of mutual exclusion.
* Note that reader threads may observe an updated target as early
* as the {@code setTarget} call that install the value
- * (and before the {@code sync} that confirms the value).
+ * (and before the {@code syncAll} that confirms the value).
* On the other hand, reader threads may observe previous versions of
- * the target until the {@code sync} call returns
+ * the target until the {@code syncAll} call returns
* (and after the {@code setTarget} that attempts to convey the updated version).
* <p>
+ * This operation is likely to be expensive and should be used sparingly.
+ * If possible, it should be buffered for batch processing on sets of call sites.
+ * <p>
+ * If {@code sites} contains a null element,
+ * a {@code NullPointerException} will be raised.
+ * In this case, some non-null elements in the array may be
+ * processed before the method returns abnormally.
+ * Which elements these are (if any) is implementation-dependent.
+ *
+ * <h3>Java Memory Model details</h3>
* In terms of the Java Memory Model, this operation performs a synchronization
* action which is comparable in effect to the writing of a volatile variable
* by the current thread, and an eventual volatile read by every other thread
@@ -171,18 +238,17 @@
* thereby ensuring communication of the new target value.
* <p>
* As long as the constraints of the Java Memory Model are obeyed,
- * implementations may delay the completion of a {@code sync}
+ * implementations may delay the completion of a {@code syncAll}
* operation while other threads ({@code T} above) continue to
* use previous values of {@code S}'s target.
* However, implementations are (as always) encouraged to avoid
* livelock, and to eventually require all threads to take account
* of the updated target.
- * <p>
- * This operation is likely to be expensive and should be used sparingly.
- * If possible, it should be buffered for batch processing on sets of call sites.
+ *
* <p style="font-size:smaller;">
- * (This is a static method on a set of call sites, not a
- * virtual method on a single call site, for performance reasons.
+ * <em>Discussion:</em>
+ * For performance reasons, {@code syncAll} is not a virtual method
+ * on a single call site, but rather applies to a set of call sites.
* Some implementations may incur a large fixed overhead cost
* for processing one or more synchronization operations,
* but a small incremental cost for each additional call site.
@@ -191,15 +257,25 @@
* in order to make them notice the updated target value.
* However, it may be observed that a single call to synchronize
* several sites has the same formal effect as many calls,
- * each on just one of the sites.)
- * <p>
+ * each on just one of the sites.
+ *
+ * <p style="font-size:smaller;">
+ * <em>Implementation Note:</em>
* Simple implementations of {@code MutableCallSite} may use
* a volatile variable for the target of a mutable call site.
- * In such an implementation, the {@code sync} method can be a no-op,
+ * In such an implementation, the {@code syncAll} method can be a no-op,
* and yet it will conform to the JMM behavior documented above.
+ *
+ * @param sites an array of call sites to be synchronized
+ * @throws NullPointerException if the {@code sites} array reference is null
+ * or the array contains a null
*/
- public static void sync(MutableCallSite[] sites) {
+ public static void syncAll(MutableCallSite[] sites) {
+ if (sites.length == 0) return;
STORE_BARRIER.lazySet(0);
+ for (int i = 0; i < sites.length; i++) {
+ sites[i].getClass(); // trigger NPE on first null
+ }
// FIXME: NYI
}
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
--- a/jdk/src/share/classes/java/dyn/NoAccessException.java Thu Feb 24 15:16:02 2011 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * 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;
-
-/**
- * Thrown to indicate that a caller has attempted to create a method handle
- * which accesses a field, method, or class to which the caller does not have access.
- * This unchecked exception is analogous to {@link IllegalAccessException},
- * which is a checked exception thrown when reflective invocation fails
- * because of an access check. With method handles, this same access
- * checking is performed by the {@link MethodHandles.Lookup lookup object}
- * on behalf of the method handle creator,
- * at the time of creation.
- * @author John Rose, JSR 292 EG
- * @since 1.7
- */
-public class NoAccessException extends ReflectiveOperationException {
- private static final long serialVersionUID = 292L;
-
- /**
- * Constructs a {@code NoAccessException} with no detail message.
- */
- public NoAccessException() {
- super();
- }
-
- /**
- * Constructs a {@code NoAccessException} with the specified
- * detail message.
- *
- * @param s the detail message
- */
- public NoAccessException(String s) {
- super(s);
- }
-
- /**
- * Constructs a {@code NoAccessException} with the specified cause.
- *
- * @param cause the underlying cause of the exception
- */
- public NoAccessException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Constructs a {@code NoAccessException} with the specified
- * detail message and cause.
- *
- * @param s the detail message
- * @param cause the underlying cause of the exception
- */
- public NoAccessException(String s, Throwable cause) {
- super(s, cause);
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/dyn/SwitchPoint.java Fri Feb 25 12:48:18 2011 -0800
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+/**
+ * <p>
+ * A {@code SwitchPoint} is an object which can publish state transitions to other threads.
+ * A switch point is initially in the <em>valid</em> state, but may at any time be
+ * changed to the <em>invalid</em> state. Invalidation cannot be reversed.
+ * A switch point can combine a <em>guarded pair</em> of method handles into a
+ * <em>guarded delegator</em>.
+ * The guarded delegator is a method handle which delegates to one of the old method handles.
+ * The state of the switch point determines which of the two gets the delegation.
+ * <p>
+ * A single switch point may be used to control any number of method handles.
+ * (Indirectly, therefore, it can control any number of call sites.)
+ * This is done by using the single switch point as a factory for combining
+ * any number of guarded method handle pairs into guarded delegators.
+ * <p>
+ * When a guarded delegator is created from a guarded pair, the pair
+ * is wrapped in a new method handle {@code M},
+ * which is permanently associated with the switch point that created it.
+ * Each pair consists of a target {@code T} and a fallback {@code F}.
+ * While the switch point is valid, invocations to {@code M} are delegated to {@code T}.
+ * After it is invalidated, invocations are delegated to {@code F}.
+ * <p>
+ * Invalidation is global and immediate, as if the switch point contained a
+ * volatile boolean variable consulted on every call to {@code M}.
+ * The invalidation is also permanent, which means the switch point
+ * can change state only once.
+ * The switch point will always delegate to {@code F} after being invalidated.
+ * At that point {@code guardWithTest} may ignore {@code T} and return {@code F}.
+ * <p>
+ * Here is an example of a switch point in action:
+ * <blockquote><pre>
+MethodType MT_str2 = MethodType.methodType(String.class, String.class);
+MethodHandle MH_strcat = MethodHandles.lookup()
+ .findVirtual(String.class, "concat", MT_str2);
+SwitchPoint spt = new SwitchPoint();
+// the following steps may be repeated to re-use the same switch point:
+MethodHandle worker1 = strcat;
+MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
+MethodHandle worker = spt.guardWithTest(worker1, worker2);
+assertEquals("method", (String) worker.invokeExact("met", "hod"));
+SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
+assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
+ * </pre></blockquote>
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * Switch points are useful without subclassing. They may also be subclassed.
+ * This may be useful in order to associate application-specific invalidation logic
+ * with the switch point.
+ * <p style="font-size:smaller;">
+ * <em>Implementation Note:</em>
+ * A switch point behaves as if implemented on top of {@link MutableCallSite},
+ * approximately as follows:
+ * <blockquote><pre>
+public class SwitchPoint {
+ private static final MethodHandle
+ K_true = MethodHandles.constant(boolean.class, true),
+ K_false = MethodHandles.constant(boolean.class, false);
+ private final MutableCallSite mcs;
+ private final MethodHandle mcsInvoker;
+ public SwitchPoint() {
+ this.mcs = new MutableCallSite(K_true);
+ this.mcsInvoker = mcs.dynamicInvoker();
+ }
+ public MethodHandle guardWithTest(
+ MethodHandle target, MethodHandle fallback) {
+ // Note: mcsInvoker is of type ()boolean.
+ // Target and fallback may take any arguments, but must have the same type.
+ return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
+ }
+ public static void invalidateAll(SwitchPoint[] spts) {
+ List<MutableCallSite> mcss = new ArrayList<>();
+ for (SwitchPoint spt : spts) mcss.add(spt.mcs);
+ for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
+ MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
+ }
+}
+ * </pre></blockquote>
+ * @author Remi Forax, JSR 292 EG
+ */
+public class SwitchPoint {
+ private static final MethodHandle
+ K_true = MethodHandles.constant(boolean.class, true),
+ K_false = MethodHandles.constant(boolean.class, false);
+
+ private final MutableCallSite mcs;
+ private final MethodHandle mcsInvoker;
+
+ /**
+ * Creates a new switch point.
+ */
+ public SwitchPoint() {
+ this.mcs = new MutableCallSite(K_true);
+ this.mcsInvoker = mcs.dynamicInvoker();
+ }
+
+ /**
+ * Returns a method handle which always delegates either to the target or the fallback.
+ * The method handle will delegate to the target exactly as long as the switch point is valid.
+ * After that, it will permanently delegate to the fallback.
+ * <p>
+ * The target and fallback must be of exactly the same method type,
+ * and the resulting combined method handle will also be of this type.
+ *
+ * @param target the method handle selected by the switch point as long as it is valid
+ * @param fallback the method handle selected by the switch point after it is invalidated
+ * @return a combined method handle which always calls either the target or fallback
+ * @throws NullPointerException if either argument is null
+ * @see MethodHandles#guardWithTest
+ */
+ public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
+ if (mcs.getTarget() == K_false)
+ return fallback; // already invalid
+ return MethodHandles.guardWithTest(mcsInvoker, target, fallback);
+ }
+
+ /**
+ * Sets all of the given switch points into the invalid state.
+ * After this call executes, no thread will observe any of the
+ * switch points to be in a valid state.
+ * <p>
+ * This operation is likely to be expensive and should be used sparingly.
+ * If possible, it should be buffered for batch processing on sets of switch points.
+ * <p>
+ * If {@code switchPoints} contains a null element,
+ * a {@code NullPointerException} will be raised.
+ * In this case, some non-null elements in the array may be
+ * processed before the method returns abnormally.
+ * Which elements these are (if any) is implementation-dependent.
+ *
+ * <p style="font-size:smaller;">
+ * <em>Discussion:</em>
+ * For performance reasons, {@code invalidateAll} is not a virtual method
+ * on a single switch point, but rather applies to a set of switch points.
+ * Some implementations may incur a large fixed overhead cost
+ * for processing one or more invalidation operations,
+ * but a small incremental cost for each additional invalidation.
+ * In any case, this operation is likely to be costly, since
+ * other threads may have to be somehow interrupted
+ * in order to make them notice the updated switch point state.
+ * However, it may be observed that a single call to invalidate
+ * several switch points has the same formal effect as many calls,
+ * each on just one of the switch points.
+ *
+ * <p style="font-size:smaller;">
+ * <em>Implementation Note:</em>
+ * Simple implementations of {@code SwitchPoint} may use
+ * a private {@link MutableCallSite} to publish the state of a switch point.
+ * In such an implementation, the {@code invalidateAll} method can
+ * simply change the call site's target, and issue one call to
+ * {@linkplain MutableCallSite#syncAll synchronize} all the
+ * private call sites.
+ *
+ * @param switchPoints an array of call sites to be synchronized
+ * @throws NullPointerException if the {@code switchPoints} array reference is null
+ * or the array contains a null
+ */
+ public static void invalidateAll(SwitchPoint[] switchPoints) {
+ if (switchPoints.length == 0) return;
+ MutableCallSite[] sites = new MutableCallSite[switchPoints.length];
+ for (int i = 0; i < switchPoints.length; i++) {
+ SwitchPoint spt = switchPoints[i];
+ if (spt == null) break; // MSC.syncAll will trigger a NPE
+ sites[i] = spt.mcs;
+ spt.mcs.setTarget(K_false);
+ }
+ MutableCallSite.syncAll(sites);
+ }
+}
--- a/jdk/src/share/classes/java/dyn/Switcher.java Thu Feb 24 15:16:02 2011 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.dyn;
-
-/**
- * <p>
- * A {@code Switcher} is an object which can publish state transitions to other threads.
- * A switcher is initially in the <em>valid</em> state, but may at any time be
- * changed to the <em>invalid</em> state. Invalidation cannot be reversed.
- * <p>
- * A single switcher may be used to create any number of guarded method handle pairs.
- * Each guarded pair is wrapped in a new method handle {@code M},
- * which is permanently associated with the switcher that created it.
- * Each pair consists of a target {@code T} and a fallback {@code F}.
- * While the switcher is valid, invocations to {@code M} are delegated to {@code T}.
- * After it is invalidated, invocations are delegated to {@code F}.
- * <p>
- * Invalidation is global and immediate, as if the switcher contained a
- * volatile boolean variable consulted on every call to {@code M}.
- * The invalidation is also permanent, which means the switcher
- * can change state only once.
- * <p>
- * Here is an example of a switcher in action:
- * <blockquote><pre>
-MethodType MT_str2 = MethodType.methodType(String.class, String.class);
-MethodHandle MH_strcat = MethodHandles.lookup()
- .findVirtual(String.class, "concat", MT_str2);
-Switcher switcher = new Switcher();
-// the following steps may be repeated to re-use the same switcher:
-MethodHandle worker1 = strcat;
-MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0);
-MethodHandle worker = switcher.guardWithTest(worker1, worker2);
-assertEquals("method", (String) worker.invokeExact("met", "hod"));
-switcher.invalidate();
-assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
- * </pre></blockquote>
- * <p>
- * <em>Implementation Note:</em>
- * A switcher behaves as if implemented on top of {@link MutableCallSite},
- * approximately as follows:
- * <blockquote><pre>
-public class Switcher {
- private static final MethodHandle
- K_true = MethodHandles.constant(boolean.class, true),
- K_false = MethodHandles.constant(boolean.class, false);
- private final MutableCallSite mcs;
- private final MethodHandle mcsInvoker;
- public Switcher() {
- this.mcs = new MutableCallSite(K_true);
- this.mcsInvoker = mcs.dynamicInvoker();
- }
- public MethodHandle guardWithTest(
- MethodHandle target, MethodHandle fallback) {
- // Note: mcsInvoker is of type boolean().
- // Target and fallback may take any arguments, but must have the same type.
- return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
- }
- public static void invalidateAll(Switcher[] switchers) {
- List<MutableCallSite> mcss = new ArrayList<>();
- for (Switcher s : switchers) mcss.add(s.mcs);
- for (MutableCallSite mcs : mcss) mcs.setTarget(K_false);
- MutableCallSite.sync(mcss.toArray(new MutableCallSite[0]));
- }
-}
- * </pre></blockquote>
- * @author Remi Forax, JSR 292 EG
- */
-public class Switcher {
- private static final MethodHandle
- K_true = MethodHandles.constant(boolean.class, true),
- K_false = MethodHandles.constant(boolean.class, false);
-
- private final MutableCallSite mcs;
- private final MethodHandle mcsInvoker;
-
- /** Create a switcher. */
- public Switcher() {
- this.mcs = new MutableCallSite(K_true);
- this.mcsInvoker = mcs.dynamicInvoker();
- }
-
- /**
- * Return a method handle which always delegates either to the target or the fallback.
- * The method handle will delegate to the target exactly as long as the switcher is valid.
- * After that, it will permanently delegate to the fallback.
- * <p>
- * The target and fallback must be of exactly the same method type,
- * and the resulting combined method handle will also be of this type.
- * @see MethodHandles#guardWithTest
- */
- public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
- if (mcs.getTarget() == K_false)
- return fallback; // already invalid
- return MethodHandles.guardWithTest(mcsInvoker, target, fallback);
- }
-
- /** Set all of the given switchers into the invalid state. */
- public static void invalidateAll(Switcher[] switchers) {
- MutableCallSite[] sites = new MutableCallSite[switchers.length];
- int fillp = 0;
- for (Switcher switcher : switchers) {
- sites[fillp++] = switcher.mcs;
- switcher.mcs.setTarget(K_false);
- }
- MutableCallSite.sync(sites);
- }
-}
--- a/jdk/src/share/classes/java/dyn/VolatileCallSite.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/VolatileCallSite.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,7 @@
* There may be a performance penalty for such tight coupling between threads.
* <p>
* Unlike {@code MutableCallSite}, there is no
- * {@linkplain MutableCallSite#sync sync operation} on volatile
+ * {@linkplain MutableCallSite#syncAll syncAll operation} on volatile
* call sites, since every write to a volatile variable is implicitly
* synchronized with reader threads.
* <p>
@@ -44,36 +44,68 @@
* @author John Rose, JSR 292 EG
*/
public class VolatileCallSite extends CallSite {
- /** Create a call site with a volatile target.
- * The initial target is set to a method handle
- * of the given type which will throw {@code IllegalStateException}.
+ /**
+ * Creates a call site with a volatile binding to its target.
+ * The initial target is set to a method handle
+ * of the given type which will throw an {@code IllegalStateException} if called.
+ * @param type the method type that this call site will have
* @throws NullPointerException if the proposed type is null
*/
public VolatileCallSite(MethodType type) {
super(type);
}
- /** Create a call site with a volatile target.
- * The target is set to the given value.
+ /**
+ * Creates a call site with a volatile binding to its target.
+ * The target is set to the given value.
+ * @param target the method handle that will be the initial target of the call site
* @throws NullPointerException if the proposed target is null
*/
public VolatileCallSite(MethodHandle target) {
super(target);
}
- /** Internal override to nominally final getTarget. */
- @Override
- MethodHandle getTarget0() {
+ /**
+ * Returns the target method of the call site, which behaves
+ * like a {@code volatile} field of the {@code VolatileCallSite}.
+ * <p>
+ * The interactions of {@code getTarget} with memory are the same
+ * as of a read from a {@code volatile} field.
+ * <p>
+ * In particular, the current thread is required to issue a fresh
+ * read of the target from memory, and must not fail to see
+ * a recent update to the target by another thread.
+ *
+ * @return the linkage state of this call site, a method handle which can change over time
+ * @see #setTarget
+ */
+ @Override public final MethodHandle getTarget() {
return getTargetVolatile();
}
/**
- * Set the target method of this call site, as a volatile variable.
- * Has the same effect as {@link CallSite#setTarget CallSite.setTarget}, with the additional
- * effects associated with volatiles, in the Java Memory Model.
+ * Updates the target method of this call site, as a volatile variable.
+ * The type of the new target must agree with the type of the old target.
+ * <p>
+ * The interactions with memory are the same as of a write to a volatile field.
+ * In particular, any threads is guaranteed to see the updated target
+ * the next time it calls {@code getTarget}.
+ * @param newTarget the new target
+ * @throws NullPointerException if the proposed new target is null
+ * @throws WrongMethodTypeException if the proposed new target
+ * has a method type that differs from the previous target
+ * @see #getTarget
*/
@Override public void setTarget(MethodHandle newTarget) {
checkTargetChange(getTargetVolatile(), newTarget);
setTargetVolatile(newTarget);
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final MethodHandle dynamicInvoker() {
+ return makeDynamicInvoker();
+ }
}
--- a/jdk/src/share/classes/java/dyn/WrongMethodTypeException.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/WrongMethodTypeException.java Fri Feb 25 12:48:18 2011 -0800
@@ -29,7 +29,7 @@
* Thrown to indicate that code has attempted to call a method handle
* via the wrong method type. As with the bytecode representation of
* normal Java method calls, method handle calls are strongly typed
- * to a specific signature associated with a call site.
+ * to a specific type descriptor associated with a call site.
* <p>
* This exception may also be thrown when two method handles are
* composed, and the system detects that their types cannot be
--- a/jdk/src/share/classes/java/dyn/package-info.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/java/dyn/package-info.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,21 +24,20 @@
*/
/**
- * This package contains dynamic language support provided directly by
+ * The {@code java.lang.invoke} package contains dynamic language support provided directly by
* the Java core class libraries and virtual machine.
+ *
+ * <p style="font-size:smaller;">
+ * <em>Historic Note:</em> In some early versions of Java SE 7,
+ * the name of this package is {@code java.dyn}.
* <p>
* Certain types in this package have special relations to dynamic
* language support in the virtual machine:
* <ul>
- * <li>In source code, a call to
- * {@link java.dyn.MethodHandle#invokeExact MethodHandle.invokeExact} or
- * {@link java.dyn.MethodHandle#invokeGeneric MethodHandle.invokeGeneric}
- * will compile and link, regardless of the requested type signature.
- * As usual, the Java compiler emits an {@code invokevirtual}
- * instruction with the given signature against the named method.
- * The JVM links any such call (regardless of signature) to a dynamically
- * typed method handle invocation. In the case of {@code invokeGeneric},
- * argument and return value conversions are applied.
+ * <li>The class {@link java.dyn.MethodHandle MethodHandle} contains
+ * <a href="MethodHandle.html#sigpoly">signature polymorphic methods</a>
+ * which can be linked regardless of their type descriptor.
+ * Normally, method linkage requires exact matching of type descriptors.
* </li>
*
* <li>The JVM bytecode format supports immediate constants of
@@ -58,12 +57,11 @@
* The final two bytes are reserved for future use and required to be zero.
* The constant pool reference of an {@code invokedynamic} instruction is to a entry
* with tag {@code CONSTANT_InvokeDynamic} (decimal 18). See below for its format.
- * (The tag value 17 is also temporarily allowed. See below.)
* The entry specifies the following information:
* <ul>
* <li>a bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant)</li>
* <li>the dynamic invocation name (a UTF8 string)</li>
- * <li>the argument and return types of the call (encoded as a signature in a UTF8 string)</li>
+ * <li>the argument and return types of the call (encoded as a type descriptor in a UTF8 string)</li>
* <li>optionally, a sequence of additional <em>static arguments</em> to the bootstrap method ({@code ldc}-type constants)</li>
* </ul>
* <p>
@@ -78,9 +76,9 @@
* as <a href="#bsm">described below</a>.
*
* <p style="font-size:smaller;">
- * (Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
+ * <em>Historic Note:</em> Some older JVMs may allow the index of a {@code CONSTANT_NameAndType}
* instead of a {@code CONSTANT_InvokeDynamic}. In earlier, obsolete versions of this API, the
- * bootstrap method was specified dynamically, in a per-class basis, during class initialization.)
+ * bootstrap method was specified dynamically, in a per-class basis, during class initialization.
*
* <h3><a name="indycon"></a>constant pool entries for {@code invokedynamic} instructions</h3>
* If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18),
@@ -90,33 +88,21 @@
* <em>bootstrap method table</em>, which is stored in the {@code BootstrapMethods}
* attribute as <a href="#bsmattr">described below</a>.
* The second pair of bytes must be an index to a {@code CONSTANT_NameAndType}.
- * This table is not part of the constant pool. Instead, it is stored
- * in a class attribute named {@code BootstrapMethods}, described below.
* <p>
* The first index specifies a bootstrap method used by the associated dynamic call sites.
* The second index specifies the method name, argument types, and return type of the dynamic call site.
* The structure of such an entry is therefore analogous to a {@code CONSTANT_Methodref},
* except that the bootstrap method specifier reference replaces
* the {@code CONSTANT_Class} reference of a {@code CONSTANT_Methodref} entry.
- * <p>
- * Some older JVMs may allow an older constant pool entry tag of decimal 17.
- * The format and behavior of a constant pool entry with this tag is identical to
- * an entry with a tag of decimal 18, except that the first index refers directly
- * to a {@code CONSTANT_MethodHandle} to use as the bootstrap method.
- * This format does not require the bootstrap method table.
- *
- * <p style="font-size:smaller;">
- * <em>(Note: The Proposed Final Draft of this specification is likely to support
- * only the tag 18, not the tag 17.)</em>
*
* <h3><a name="mtcon"></a>constant pool entries for {@linkplain java.dyn.MethodType method types}</h3>
* If a constant pool entry has the tag {@code CONSTANT_MethodType} (decimal 16),
* it must contain exactly two more bytes, which must be an index to a {@code CONSTANT_Utf8}
- * entry which represents a method type signature.
+ * entry which represents a method type descriptor.
* <p>
* The JVM will ensure that on first
* execution of an {@code ldc} instruction for this entry, a {@link java.dyn.MethodType MethodType}
- * will be created which represents the signature.
+ * will be created which represents the type descriptor.
* Any classes mentioned in the {@code MethodType} will be loaded if necessary,
* but not initialized.
* Access checking and error reporting is performed exactly as it is for
@@ -141,38 +127,75 @@
* type is created. Any classes mentioned in this reification will be loaded if necessary,
* but not initialized, and access checking and error reporting performed as usual.
* <p>
+ * Unlike the reflective {@code Lookup} API, there are no security manager calls made
+ * when these constants are resolved.
+ * <p>
* The method handle itself will have a type and behavior determined by the subtag as follows:
* <code>
* <table border=1 cellpadding=5 summary="CONSTANT_MethodHandle subtypes">
- * <tr><th>N</th><th>subtag name</th><th>member</th><th>MH type</th><th>MH behavior</th></tr>
- * <tr><td>1</td><td>REF_getField</td><td>C.f:T</td><td>(C)T</td><td>getfield C.f:T</td></tr>
- * <tr><td>2</td><td>REF_getStatic</td><td>C.f:T</td><td>( )T</td><td>getstatic C.f:T</td></tr>
- * <tr><td>3</td><td>REF_putField</td><td>C.f:T</td><td>(C,T)void</td><td>putfield C.f:T</td></tr>
- * <tr><td>4</td><td>REF_putStatic</td><td>C.f:T</td><td>(T)void</td><td>putstatic C.f:T</td></tr>
- * <tr><td>5</td><td>REF_invokeVirtual</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokevirtual C.m(A*)T</td></tr>
- * <tr><td>6</td><td>REF_invokeStatic</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokestatic C.m(A*)T</td></tr>
- * <tr><td>7</td><td>REF_invokeSpecial</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokespecial C.m(A*)T</td></tr>
- * <tr><td>8</td><td>REF_newInvokeSpecial</td><td>C.<init>(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.<init>(A*)void</td></tr>
- * <tr><td>9</td><td>REF_invokeInterface</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokeinterface C.m(A*)T</td></tr>
+ * <tr><th>N</th><th>subtag name</th><th>member</th><th>MH type</th><th>bytecode behavior</th><th>lookup expression</th></tr>
+ * <tr><td>1</td><td>REF_getField</td><td>C.f:T</td><td>(C)T</td><td>getfield C.f:T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findGetter findGetter(C.class,"f",T.class)}</td></tr>
+ * <tr><td>2</td><td>REF_getStatic</td><td>C.f:T</td><td>( )T</td><td>getstatic C.f:T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findStaticGetter findStaticGetter(C.class,"f",T.class)}</td></tr>
+ * <tr><td>3</td><td>REF_putField</td><td>C.f:T</td><td>(C,T)void</td><td>putfield C.f:T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findSetter findSetter(C.class,"f",T.class)}</td></tr>
+ * <tr><td>4</td><td>REF_putStatic</td><td>C.f:T</td><td>(T)void</td><td>putstatic C.f:T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findStaticSetter findStaticSetter(C.class,"f",T.class)}</td></tr>
+ * <tr><td>5</td><td>REF_invokeVirtual</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokevirtual C.m(A*)T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}</td></tr>
+ * <tr><td>6</td><td>REF_invokeStatic</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokestatic C.m(A*)T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findStatic findStatic(C.class,"m",MT)}</td></tr>
+ * <tr><td>7</td><td>REF_invokeSpecial</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokespecial C.m(A*)T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findSpecial findSpecial(C.class,"m",MT,this.class)}</td></tr>
+ * <tr><td>8</td><td>REF_newInvokeSpecial</td><td>C.<init>(A*)void</td><td>(A*)C</td><td>new C; dup; invokespecial C.<init>(A*)void</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findConstructor findConstructor(C.class,MT)}</td></tr>
+ * <tr><td>9</td><td>REF_invokeInterface</td><td>C.m(A*)T</td><td>(C,A*)T</td><td>invokeinterface C.m(A*)T</td>
+ * <td>{@linkplain java.dyn.MethodHandles.Lookup#findVirtual findVirtual(C.class,"m",MT)}</td></tr>
* </table>
* </code>
+ * Here, the type {@code C} is taken from the {@code CONSTANT_Class} reference associated
+ * with the {@code CONSTANT_NameAndType} descriptor.
+ * The field name {@code f} or method name {@code m} is taken from the {@code CONSTANT_NameAndType}
+ * as is the result type {@code T} and (in the case of a method or constructor) the argument type sequence
+ * {@code A*}.
+ * <p>
+ * Each method handle constant has an equivalent instruction sequence called its <em>bytecode behavior</em>.
+ * In general, creating a method handle constant can be done in exactly the same circumstances that
+ * the JVM would successfully resolve the symbolic references in the bytecode behavior.
+ * Also, the type of a method handle constant is such that a valid {@code invokeExact} call
+ * on the method handle has exactly the same JVM stack effects as the <em>bytecode behavior</em>.
+ * Finally, calling a method handle constant on a valid set of arguments has exactly the same effect
+ * and returns the same result (if any) as the corresponding <em>bytecode behavior</em>.
+ * <p>
+ * Each method handle constant also has an equivalent reflective <em>lookup expression</em>,
+ * which is a query to a method in {@link java.dyn.MethodHandles.Lookup}.
+ * In the example lookup method expression given in the table above, the name {@code MT}
+ * stands for a {@code MethodType} built from {@code T} and the sequence of argument types {@code A*}.
+ * (Note that the type {@code C} is not prepended to the query type {@code MT} even if the member is non-static.)
+ * In the case of {@code findSpecial}, the name {@code this.class} refers to the class containing
+ * the bytecodes.
* <p>
* The special name {@code <clinit>} is not allowed.
* The special name {@code <init>} is not allowed except for subtag 8 as shown.
* <p>
* The JVM verifier and linker apply the same access checks and restrictions for these references as for the hypothetical
- * bytecode instructions specified in the last column of the table. In particular, method handles to
+ * bytecode instructions specified in the last column of the table.
+ * A method handle constant will successfully resolve to a method handle if the symbolic references
+ * of the corresponding bytecode instruction(s) would also resolve successfully.
+ * Otherwise, an attempt to resolve the constant will throw equivalent linkage errors.
+ * In particular, method handles to
* private and protected members can be created in exactly those classes for which the corresponding
* normal accesses are legal.
* <p>
* A constant may refer to a method or constructor with the {@code varargs}
- * bit (hexadecimal {@code 80}) set in its modifier bitmask.
- * The method handle constant produced for such a method behaves the same
+ * bit (hexadecimal {@code 0x0080}) set in its modifier bitmask.
+ * The method handle constant produced for such a method behaves as if
+ * it were created by {@link java.dyn.MethodHandle#asVarargsCollector asVarargsCollector}.
+ * In other words, the constant method handle will exhibit variable arity,
+ * when invoked via {@code invokeGeneric}.
+ * On the other hand, its behavior with respect to {@code invokeExact} will be the same
* as if the {@code varargs} bit were not set.
- * The argument-collecting behavior of {@code varargs} can be emulated by
- * adapting the method handle constant with
- * {@link java.dyn.MethodHandle#asCollector asCollector}.
- * There is no provision for doing this automatically.
* <p>
* Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types
* resolve class names, they do not force class initialization.
@@ -186,14 +209,14 @@
* by the execution of {@code invokedynamic} and {@code ldc} instructions.
* (Roughly speaking, this means that every use of a constant pool entry
* must lead to the same outcome.
- * If the resoultion succeeds, the same object reference is produced
+ * If the resolution succeeds, the same object reference is produced
* by every subsequent execution of the same instruction.
* If the resolution of the constant causes an error to occur,
* the same error will be re-thrown on every subsequent attempt
* to use this particular constant.)
* <p>
* Constants created by the resolution of these constant pool types are not necessarily
- * interned. Except for {@link CONSTANT_Class} and {@link CONSTANT_String} entries,
+ * interned. Except for {@code CONSTANT_Class} and {@code CONSTANT_String} entries,
* two distinct constant pool entries might not resolve to the same reference
* even if they contain the same symbolic reference.
*
@@ -207,31 +230,31 @@
* <p>
* Each {@code invokedynamic} instruction statically specifies its own
* bootstrap method as a constant pool reference.
- * The constant pool reference also specifies the call site's name and type signature,
+ * The constant pool reference also specifies the call site's name and type descriptor,
* just like {@code invokevirtual} and the other invoke instructions.
* <p>
* Linking starts with resolving the constant pool entry for the
* bootstrap method, and resolving a {@link java.dyn.MethodType MethodType} object for
- * the type signature of the dynamic call site.
+ * the type descriptor of the dynamic call site.
* This resolution process may trigger class loading.
* It may therefore throw an error if a class fails to load.
* This error becomes the abnormal termination of the dynamic
* call site execution.
* Linkage does not trigger class initialization.
* <p>
- * Next, the bootstrap method call is started, with four or five values being stacked:
+ * Next, the bootstrap method call is started, with at least four values being stacked:
* <ul>
* <li>a {@code MethodHandle}, the resolved bootstrap method itself </li>
* <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em> in which dynamic call site occurs </li>
* <li>a {@code String}, the method name mentioned in the call site </li>
- * <li>a {@code MethodType}, the resolved type signature of the call </li>
- * <li>optionally, a single object representing one or more <a href="#args">additional static arguments</a> </li>
+ * <li>a {@code MethodType}, the resolved type descriptor of the call </li>
+ * <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
* </ul>
* The method handle is then applied to the other values as if by
* {@link java.dyn.MethodHandle#invokeGeneric invokeGeneric}.
* The returned result must be a {@link java.dyn.CallSite CallSite} (or a subclass).
* The type of the call site's target must be exactly equal to the type
- * derived from the dynamic call site signature and passed to
+ * derived from the dynamic call site's type descriptor and passed to
* the bootstrap method.
* The call site then becomes permanently linked to the dynamic call site.
* <p>
@@ -299,11 +322,11 @@
* chosen target object.
*
* <p style="font-size:smaller;">
- * (Historic Note: Unlike some previous versions of this specification,
+ * <em>Historic Note:</em> Unlike some previous versions of this specification,
* these rules do not enable the JVM to duplicate dynamic call sites,
* or to issue “causeless” bootstrap method calls.
* Every dynamic call site transitions at most once from unlinked to linked,
- * just before its first invocation.)
+ * just before its first invocation.
*
* <h3><a name="bsmattr">the {@code BootstrapMethods} attribute </h3>
* Each {@code CONSTANT_InvokeDynamic} entry contains an index which references
@@ -349,7 +372,6 @@
* Static arguments are specified constant pool indexes stored in the {@code BootstrapMethods} attribute.
* Before the bootstrap method is invoked, each index is used to compute an {@code Object}
* reference to the indexed value in the constant pool.
- * If the value is a primitive type, it is converted to a reference by boxing conversion.
* The valid constant pool entries are listed in this table:
* <code>
* <table border=1 cellpadding=5 summary="Static argument types">
@@ -369,21 +391,43 @@
* the instruction's bootstrap method will be invoked on three arguments,
* conveying the instruction's caller class, name, and method type.
* If the {@code invokedynamic} instruction specifies one or more static arguments,
- * a fourth argument will be passed to the bootstrap argument,
- * either an {@code Object} reference to the sole extra argument (if there is one)
- * or an {@code Object} array of references to all the arguments (if there are two or more),
- * as if the bootstrap method is a variable-arity method.
+ * those values will be passed as additional arguments to the method handle.
+ * (Note that because there is a limit of 255 arguments to any method,
+ * at most 252 extra arguments can be supplied.)
+ * The bootstrap method will be invoked as if by either {@code invokeGeneric}
+ * or {@code invokeWithArguments}. (There is no way to tell the difference.)
+ * <p>
+ * The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments.
+ * For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
+ * If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
+ * then some or all of the arguments specified here may be collected into a trailing array parameter.
+ * (This is not a special rule, but rather a useful consequence of the interaction
+ * between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
+ * and the {@code java.dyn.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
+ * <p>
+ * Given these rules, here are examples of legal bootstrap method declarations,
+ * given various numbers {@code N} of extra arguments.
+ * The first rows (marked {@code *}) will work for any number of extra arguments.
* <code>
* <table border=1 cellpadding=5 summary="Static argument types">
* <tr><th>N</th><th>sample bootstrap method</th></tr>
+ * <tr><td>*</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
+ * <tr><td>*</td><td><code>CallSite bootstrap(Object... args)</code></td></tr>
+ * <tr><td>*</td><td><code>CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)</code></td></tr>
* <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type)</code></td></tr>
+ * <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, Object... nameAndType)</code></td></tr>
* <tr><td>1</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)</code></td></tr>
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
+ * <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)</code></td></tr>
+ * <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)</code></td></tr>
* </table>
* </code>
+ * The last example assumes that the extra arguments are of type
+ * {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively.
+ * The second-to-last example assumes that all extra arguments are of type
+ * {@code CONSTANT_String}.
+ * The other examples work with all types of extra arguments.
* <p>
- * The argument and return types listed here are used by the {@code invokeGeneric}
- * call to the bootstrap method.
* As noted above, the actual method type of the bootstrap method can vary.
* For example, the fourth argument could be {@code MethodHandle},
* if that is the type of the corresponding constant in
@@ -391,14 +435,8 @@
* In that case, the {@code invokeGeneric} call will pass the extra method handle
* constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric}
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
- * (If a string constant were passed instead, by badly generated code, that cast would then fail.)
- * <p>
- * If the fourth argument is an array, the array element type must be {@code Object},
- * since object arrays (as produced by the JVM at this point) cannot be converted
- * to other array types.
- * <p>
- * If an array is provided, it will appear to be freshly allocated.
- * That is, the same array will not appear to two bootstrap method calls.
+ * (If a string constant were passed instead, by badly generated code, that cast would then fail,
+ * resulting in an {@code InvokeDynamicBootstrapError}.)
* <p>
* Extra bootstrap method arguments are intended to allow language implementors
* to safely and compactly encode metadata.
@@ -406,24 +444,6 @@
* since each call site could be given its own unique bootstrap method.
* Such a practice is likely to produce large class files and constant pools.
*
- * <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.
- * This design uses varargs because it anticipates some use cases where bootstrap arguments
- * contribute components of variable-length structures, such as virtual function tables
- * or interpreter token streams.
- * Such parameters would be awkward or impossible to manage if represented
- * as normal positional method arguments,
- * since there would need to be one Java method per length.
- * On balance, leaving out the varargs feature would cause more trouble to users than keeping it.
- * Also, this design allows bootstrap methods to be called in a limited JVM stack depth.
- * At both the user and JVM level, the difference between varargs and non-varargs
- * calling sequences can easily be bridged via the
- * {@link java.dyn.MethodHandle#asSpreader asSpreader}
- * and {@link java.dyn.MethodHandle#asSpreader asCollector} methods.)
- *
* <h2><a name="structs"></a>Structure Summary</h2>
* <blockquote><pre>// summary of constant and attribute structures
struct CONSTANT_MethodHandle_info {
@@ -435,11 +455,6 @@
u1 tag = 16;
u2 descriptor_index; // index to CONSTANT_Utf8, as in NameAndType
}
-struct CONSTANT_InvokeDynamic_17_info {
- u1 tag = 17;
- u2 bootstrap_method_index; // index to CONSTANT_MethodHandle
- u2 name_and_type_index; // same as for CONSTANT_Methodref, etc.
-}
struct CONSTANT_InvokeDynamic_info {
u1 tag = 18;
u2 bootstrap_method_attr_index; // index into BootstrapMethods_attr
@@ -456,9 +471,6 @@
} bootstrap_methods[bootstrap_method_count];
}
* </pre></blockquote>
- * <p>
- * <em>Note: The Proposed Final Draft of JSR 292 may remove the constant tag 17,
- * for the sake of simplicity.</em>
*
* @author John Rose, JSR 292 EG
*/
--- a/jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -478,37 +478,60 @@
return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
}
- static MethodHandle makeTypeHandler(Access token,
- MethodHandle target, MethodHandle typeHandler) {
+ static MethodHandle makeVarargsCollector(Access token,
+ MethodHandle target, Class<?> arrayType) {
Access.check(token);
- return new WithTypeHandler(target, typeHandler);
+ return new AsVarargsCollector(target, arrayType);
}
- static class WithTypeHandler extends AdapterMethodHandle {
- final MethodHandle target, typeHandler;
- WithTypeHandler(MethodHandle target, MethodHandle typeHandler) {
+ static class AsVarargsCollector extends AdapterMethodHandle {
+ final MethodHandle target;
+ final Class<?> arrayType;
+ MethodHandle cache;
+
+ AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
super(target, target.type(), makeConv(OP_RETYPE_ONLY));
this.target = target;
- this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE);
+ this.arrayType = arrayType;
+ this.cache = target.asCollector(arrayType, 0);
+ }
+
+ @Override
+ public boolean isVarargsCollector() {
+ return true;
}
+ @Override
public MethodHandle asType(MethodType newType) {
- if (this.type() == newType)
- return this;
+ MethodType type = this.type();
+ int collectArg = type.parameterCount() - 1;
+ int newArity = newType.parameterCount();
+ if (newArity == collectArg+1 &&
+ type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
+ // if arity and trailing parameter are compatible, do normal thing
+ return super.asType(newType);
+ }
+ // check cache
+ if (cache.type().parameterCount() == newArity)
+ return cache.asType(newType);
+ // build and cache a collector
+ int arrayLength = newArity - collectArg;
+ MethodHandle collector;
try {
- MethodHandle retyped = (MethodHandle) typeHandler.invokeExact(target, newType);
- // Contract: Must return the desired type, or throw WMT
- if (retyped.type() != newType)
- throw new WrongMethodTypeException(retyped.toString());
- return retyped;
- } catch (Throwable ex) {
- if (ex instanceof Error) throw (Error)ex;
- if (ex instanceof RuntimeException) throw (RuntimeException)ex;
- throw new RuntimeException(ex);
+ collector = target.asCollector(arrayType, arrayLength);
+ } catch (IllegalArgumentException ex) {
+ throw new WrongMethodTypeException("cannot build collector");
}
+ cache = collector;
+ return collector.asType(newType);
}
- private static final MethodType TYPE_HANDLER_TYPE
- = MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class);
+
+ public MethodHandle asVarargsCollector(Class<?> arrayType) {
+ MethodType type = this.type();
+ if (type.parameterType(type.parameterCount()-1) == arrayType)
+ return this;
+ return super.asVarargsCollector(arrayType);
+ }
}
/** Can a checkcast adapter validly convert the target to newType?
@@ -939,7 +962,7 @@
@Override
public String toString() {
- return nonAdapter((MethodHandle)vmtarget).toString();
+ return MethodHandleImpl.getNameString(IMPL_TOKEN, nonAdapter((MethodHandle)vmtarget), this);
}
private static MethodHandle nonAdapter(MethodHandle mh) {
--- a/jdk/src/share/classes/sun/dyn/CallSiteImpl.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/CallSiteImpl.java Fri Feb 25 12:48:18 2011 -0800
@@ -37,17 +37,17 @@
static CallSite makeSite(MethodHandle bootstrapMethod,
// Callee information:
String name, MethodType type,
- // Call-site attributes, if any:
+ // Extra arguments for BSM, if any:
Object info,
// Caller information:
MemberName callerMethod, int callerBCI) {
Class<?> callerClass = callerMethod.getDeclaringClass();
Object caller;
- if (bootstrapMethod.type().parameterType(0) == Class.class)
+ if (bootstrapMethod.type().parameterType(0) == Class.class && TRANSITIONAL_BEFORE_PFD)
caller = callerClass; // remove for PFD
else
caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass);
- if (bootstrapMethod == null) {
+ if (bootstrapMethod == null && TRANSITIONAL_BEFORE_PFD) {
// If there is no bootstrap method, throw IncompatibleClassChangeError.
// This is a valid generic error type for resolution (JLS 12.3.3).
throw new IncompatibleClassChangeError
@@ -56,30 +56,35 @@
CallSite site;
try {
Object binding;
+ info = maybeReBox(info);
if (info == null) {
- if (false) // switch when invokeGeneric works
- binding = bootstrapMethod.invokeGeneric(caller, name, type);
- else
- binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
+ binding = bootstrapMethod.invokeGeneric(caller, name, type);
+ } else if (!info.getClass().isArray()) {
+ binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
} else {
- info = maybeReBox(info);
- if (false) // switch when invokeGeneric works
- binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
+ Object[] argv = (Object[]) info;
+ if (3 + argv.length > 255)
+ new InvokeDynamicBootstrapError("too many bootstrap method arguments");
+ MethodType bsmType = bootstrapMethod.type();
+ if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
+ binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
else
- binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type, info });
+ binding = MethodHandles.spreadInvoker(bsmType, 3)
+ .invokeGeneric(bootstrapMethod, caller, name, type, argv);
}
//System.out.println("BSM for "+name+type+" => "+binding);
if (binding instanceof CallSite) {
site = (CallSite) binding;
- } else if (binding instanceof MethodHandle) {
+ } else if (binding instanceof MethodHandle && TRANSITIONAL_BEFORE_PFD) {
// Transitional!
MethodHandle target = (MethodHandle) binding;
site = new ConstantCallSite(target);
} else {
- throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite");
+ throw new ClassCastException("bootstrap method failed to produce a CallSite");
}
- PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
- callerMethod, callerBCI);
+ if (TRANSITIONAL_BEFORE_PFD)
+ PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
+ callerMethod, callerBCI);
assert(site.getTarget() != null);
assert(site.getTarget().type().equals(type));
} catch (Throwable ex) {
@@ -93,6 +98,8 @@
return site;
}
+ private static boolean TRANSITIONAL_BEFORE_PFD = true; // FIXME: remove for PFD
+
private static Object maybeReBox(Object x) {
if (x instanceof Integer) {
int xi = (int) x;
@@ -117,11 +124,12 @@
static {
try {
PRIVATE_INITIALIZE_CALL_SITE =
+ !TRANSITIONAL_BEFORE_PFD ? null :
MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
MethodType.methodType(void.class,
String.class, MethodType.class,
MemberName.class, int.class));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
--- a/jdk/src/share/classes/sun/dyn/FilterGeneric.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/FilterGeneric.java Fri Feb 25 12:48:18 2011 -0800
@@ -187,7 +187,7 @@
MethodHandle entryPoint = null;
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/FilterOneArgument.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/FilterOneArgument.java Fri Feb 25 12:48:18 2011 -0800
@@ -56,7 +56,7 @@
INVOKE =
MethodHandleImpl.IMPL_LOOKUP.findVirtual(FilterOneArgument.class, "invoke",
MethodType.genericMethodType(1));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
--- a/jdk/src/share/classes/sun/dyn/FromGeneric.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/FromGeneric.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
import java.dyn.*;
import java.lang.reflect.*;
import sun.dyn.util.*;
+import static sun.dyn.MethodTypeImpl.invokers;
/**
* Adapters which mediate between incoming calls which are generic
@@ -128,7 +129,7 @@
MethodType targetType, MethodType internalType) {
// All the adapters we have here have reference-untyped internal calls.
assert(internalType == internalType.erase());
- MethodHandle invoker = MethodHandles.exactInvoker(targetType);
+ MethodHandle invoker = invokers(targetType).exactInvoker();
// cast all narrow reference types, unbox all primitive arguments:
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
MethodHandle fixArgs = AdapterMethodHandle.convertArguments(Access.TOKEN,
@@ -203,7 +204,7 @@
MethodHandle entryPoint = null;
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/InvokeGeneric.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/InvokeGeneric.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
import java.dyn.*;
import java.lang.reflect.*;
import sun.dyn.util.*;
+import static sun.dyn.MethodTypeImpl.invokers;
/**
* Adapters which manage MethodHanndle.invokeGeneric calls.
@@ -43,7 +44,7 @@
/** Compute and cache information for this adapter, so that it can
* call out to targets of the erasure-family of the given erased type.
*/
- private InvokeGeneric(MethodType erasedCallerType) throws NoAccessException {
+ private InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException {
this.erasedCallerType = erasedCallerType;
this.initialInvoker = makeInitialInvoker();
assert initialInvoker.type().equals(erasedCallerType
@@ -63,14 +64,14 @@
try {
InvokeGeneric gen = new InvokeGeneric(form.erasedType());
form.genericInvoker = genericInvoker = gen.initialInvoker;
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
return genericInvoker;
}
- private MethodHandle makeInitialInvoker() throws NoAccessException {
+ private MethodHandle makeInitialInvoker() throws ReflectiveOperationException {
// postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)}
MethodHandle postDispatch = makePostDispatchInvoker();
MethodHandle invoker;
@@ -87,14 +88,14 @@
private MethodHandle makePostDispatchInvoker() {
// Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...).
MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS);
- return MethodHandles.exactInvoker(invokerType);
+ return invokers(invokerType).exactInvoker();
}
private MethodHandle dropDispatchArguments(MethodHandle targetInvoker) {
assert(targetInvoker.type().parameterType(0) == MethodHandle.class);
return MethodHandles.dropArguments(targetInvoker, 1, EXTRA_ARGS);
}
- private MethodHandle dispatcher(String dispatchName) throws NoAccessException {
+ private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException {
return lookup().bind(this, dispatchName,
MethodType.methodType(MethodHandle.class,
MethodType.class, MethodHandle.class));
@@ -108,7 +109,7 @@
*/
private MethodHandle dispatch(MethodType callerType, MethodHandle target) {
MethodType targetType = target.type();
- if (USE_AS_TYPE_PATH || target instanceof AdapterMethodHandle.WithTypeHandler) {
+ if (USE_AS_TYPE_PATH || target.isVarargsCollector()) {
MethodHandle newTarget = target.asType(callerType);
targetType = callerType;
Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType);
--- a/jdk/src/share/classes/sun/dyn/Invokers.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/Invokers.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,7 +47,7 @@
private /*lazy*/ MethodHandle genericInvoker;
// generic (untyped) invoker for the outgoing call; accepts a single Object[]
- private final /*lazy*/ MethodHandle[] varargsInvokers;
+ private final /*lazy*/ MethodHandle[] spreadInvokers;
// invoker for an unbound callsite
private /*lazy*/ MethodHandle uninitializedCallSite;
@@ -55,10 +55,9 @@
/** Compute and cache information common to all collecting adapters
* that implement members of the erasure-family of the given erased type.
*/
- public Invokers(Access token, MethodType targetType) {
- Access.check(token);
+ /*non-public*/ Invokers(MethodType targetType) {
this.targetType = targetType;
- this.varargsInvokers = new MethodHandle[targetType.parameterCount()+1];
+ this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
}
public static MethodType invokerType(MethodType targetType) {
@@ -69,8 +68,8 @@
MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker;
try {
- invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
- } catch (NoAccessException ex) {
+ invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType);
+ } catch (ReflectiveOperationException ex) {
throw new InternalError("JVM cannot find invoker for "+targetType);
}
assert(invokerType(targetType) == invoker.type());
@@ -101,13 +100,12 @@
return invoker;
}
- public MethodHandle varargsInvoker(int objectArgCount) {
- MethodHandle vaInvoker = varargsInvokers[objectArgCount];
+ public MethodHandle spreadInvoker(int objectArgCount) {
+ MethodHandle vaInvoker = spreadInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker;
MethodHandle gInvoker = genericInvoker();
- MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
- vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType));
- varargsInvokers[objectArgCount] = vaInvoker;
+ vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
+ spreadInvokers[objectArgCount] = vaInvoker;
return vaInvoker;
}
@@ -118,7 +116,7 @@
if (invoker != null) return invoker;
if (targetType.parameterCount() > 0) {
MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
- Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0);
+ Invokers invokers0 = MethodTypeImpl.invokers(type0);
invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
0, targetType.parameterList());
assert(invoker.type().equals(targetType));
@@ -130,7 +128,7 @@
THROW_UCS = MethodHandleImpl.IMPL_LOOKUP
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
--- a/jdk/src/share/classes/sun/dyn/MemberName.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MemberName.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -451,8 +451,6 @@
return type.toString(); // class java.lang.String
// else it is a field, method, or constructor
StringBuilder buf = new StringBuilder();
- if (!isResolved())
- buf.append("*.");
if (getDeclaringClass() != null) {
buf.append(getName(clazz));
buf.append('.');
@@ -512,14 +510,24 @@
public static RuntimeException newIllegalArgumentException(String message) {
return new IllegalArgumentException(message);
}
- public static NoAccessException newNoAccessException(MemberName name, Class<?> lookupClass) {
- return newNoAccessException("cannot access", name, lookupClass);
+ public static IllegalAccessException newNoAccessException(MemberName name, Object from) {
+ return newNoAccessException("cannot access", name, from);
+ }
+ public static IllegalAccessException newNoAccessException(String message,
+ MemberName name, Object from) {
+ message += ": " + name;
+ if (from != null) message += ", from " + from;
+ return new IllegalAccessException(message);
}
- public static NoAccessException newNoAccessException(String message,
- MemberName name, Class<?> lookupClass) {
- message += ": " + name;
- if (lookupClass != null) message += ", from " + lookupClass.getName();
- return new NoAccessException(message);
+ public static ReflectiveOperationException newNoAccessException(MemberName name) {
+ if (name.isResolved())
+ return new IllegalAccessException(name.toString());
+ else if (name.isConstructor())
+ return new NoSuchMethodException(name.toString());
+ else if (name.isMethod())
+ return new NoSuchMethodException(name.toString());
+ else
+ return new NoSuchFieldException(name.toString());
}
public static Error uncaughtException(Exception ex) {
Error err = new InternalError("uncaught exception");
@@ -643,14 +651,20 @@
/** Produce a resolved version of the given member.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
* Access checking is performed on behalf of the given {@code lookupClass}.
- * If lookup fails or access is not permitted, a {@linkplain NoAccessException} is thrown.
+ * If lookup fails or access is not permitted, a {@linkplain ReflectiveOperationException} is thrown.
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
*/
- public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass) throws NoAccessException {
+ public
+ <NoSuchMemberException extends ReflectiveOperationException>
+ MemberName resolveOrFail(MemberName m, boolean searchSupers, Class<?> lookupClass,
+ Class<NoSuchMemberException> nsmClass)
+ throws IllegalAccessException, NoSuchMemberException {
MemberName result = resolveOrNull(m, searchSupers, lookupClass);
if (result != null)
return result;
- throw newNoAccessException(m, lookupClass);
+ ReflectiveOperationException ex = newNoAccessException(m);
+ if (ex instanceof IllegalAccessException) throw (IllegalAccessException) ex;
+ throw nsmClass.cast(ex);
}
/** Return a list of all methods defined by the given class.
* Super types are searched (for inherited members) if {@code searchSupers} is true.
--- a/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import sun.dyn.util.VerifyType;
-import java.dyn.NoAccessException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -136,6 +135,8 @@
}
static {
+ if (!MethodHandleNatives.JVM_SUPPORT) // force init of native API
+ throw new InternalError("No JVM support for JSR 292");
// Force initialization of Lookup, so it calls us back as initLookup:
MethodHandles.publicLookup();
if (IMPL_LOOKUP_INIT == null)
@@ -167,11 +168,11 @@
* @param doDispatch whether the method handle will test the receiver type
* @param lookupClass access-check relative to this class
* @return a direct handle to the matching method
- * @throws NoAccessException if the given method cannot be accessed by the lookup class
+ * @throws IllegalAccessException if the given method cannot be accessed by the lookup class
*/
public static
MethodHandle findMethod(Access token, MemberName method,
- boolean doDispatch, Class<?> lookupClass) throws NoAccessException {
+ boolean doDispatch, Class<?> lookupClass) throws IllegalAccessException {
Access.check(token); // only trusted calls
MethodType mtype = method.getMethodType();
if (!method.isStatic()) {
@@ -184,7 +185,10 @@
if (!mh.isValid())
throw newNoAccessException(method, lookupClass);
assert(mh.type() == mtype);
- return mh;
+ if (!method.isVarargs())
+ return mh;
+ else
+ return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1));
}
public static
@@ -302,7 +306,7 @@
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(AllocateObject.class, name, MethodType.genericMethodType(nargs));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
@@ -317,7 +321,7 @@
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(AllocateObject.class, "invoke_V", MethodType.genericMethodType(0, true));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
@@ -469,7 +473,7 @@
MethodHandle mh;
try {
mh = IMPL_LOOKUP.findVirtual(FieldAccessor.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
@@ -537,7 +541,7 @@
MethodHandle mh;
try {
mh = IMPL_LOOKUP.findStatic(FieldAccessor.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
if (caclass != null) {
@@ -1009,7 +1013,7 @@
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(GuardWithTest.class, name, MethodType.genericMethodType(nargs));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
@@ -1024,7 +1028,7 @@
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithTest.class, "invoke_V", MethodType.genericMethodType(0, true));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
@@ -1145,7 +1149,7 @@
MethodHandle invoke = null;
try {
invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (invoke == null) break;
invokes.add(invoke);
@@ -1160,7 +1164,7 @@
static {
try {
VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw uncaughtException(ex);
}
}
@@ -1207,20 +1211,30 @@
THROW_EXCEPTION
= IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException",
MethodType.methodType(Empty.class, Throwable.class));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
- public static String getNameString(Access token, MethodHandle target) {
+ public static String getNameString(Access token, MethodHandle target, Object type) {
Access.check(token);
+ if (!(type instanceof MethodType)) {
+ if (type == null)
+ type = target.type();
+ else if (type instanceof MethodHandle)
+ type = ((MethodHandle)type).type();
+ }
MemberName name = null;
if (target != null)
name = MethodHandleNatives.getMethodName(target);
if (name == null)
- return "invoke" + target.type();
- return name.getName() + target.type();
+ return "invoke" + type;
+ return name.getName() + type;
+ }
+
+ public static String getNameString(Access token, MethodHandle target) {
+ return getNameString(token, target, null);
}
static String addTypeString(Object obj, MethodHandle target) {
@@ -1263,8 +1277,8 @@
return MethodHandleNatives.getBootstrap(callerClass);
}
- public static MethodHandle withTypeHandler(Access token, MethodHandle target, MethodHandle typeHandler) {
+ public static MethodHandle asVarargsCollector(Access token, MethodHandle target, Class<?> arrayType) {
Access.check(token);
- return AdapterMethodHandle.makeTypeHandler(token, target, typeHandler);
+ return AdapterMethodHandle.makeVarargsCollector(token, target, arrayType);
}
}
--- a/jdk/src/share/classes/sun/dyn/MethodHandleNatives.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleNatives.java Fri Feb 25 12:48:18 2011 -0800
@@ -350,7 +350,7 @@
case REF_invokeInterface: return lookup.findVirtual( defc, name, (MethodType) type );
}
throw new IllegalArgumentException("bad MethodHandle constant "+name+" : "+type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
Error err = new IncompatibleClassChangeError();
err.initCause(ex);
throw err;
--- a/jdk/src/share/classes/sun/dyn/MethodTypeImpl.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodTypeImpl.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -498,9 +498,12 @@
public static Invokers invokers(Access token, MethodType type) {
Access.check(token);
+ return invokers(type);
+ }
+ /*non-public*/ static Invokers invokers(MethodType type) {
Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
if (inv != null) return inv;
- inv = new Invokers(token, type);
+ inv = new Invokers(type);
METHOD_TYPE_FRIEND.setInvokers(type, inv);
return inv;
}
--- a/jdk/src/share/classes/sun/dyn/SpreadGeneric.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/SpreadGeneric.java Fri Feb 25 12:48:18 2011 -0800
@@ -167,7 +167,7 @@
MethodHandle entryPoint = null;
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.findSpecial(acls, iname, entryType, acls);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;
--- a/jdk/src/share/classes/sun/dyn/ToGeneric.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/ToGeneric.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
import sun.dyn.util.ValueConversions;
import sun.dyn.util.Wrapper;
import static sun.dyn.MemberName.newIllegalArgumentException;
+import static sun.dyn.MethodTypeImpl.invokers;
/**
* Adapters which mediate between incoming calls which are not generic
@@ -72,7 +73,7 @@
assert(entryType.erase() == entryType); // for now
// incoming call will first "forget" all reference types except Object
this.entryType = entryType;
- MethodHandle invoker0 = MethodHandles.exactInvoker(entryType.generic());
+ MethodHandle invoker0 = invokers(entryType.generic()).exactInvoker();
MethodType rawEntryTypeInit;
Adapter ad = findAdapter(rawEntryTypeInit = entryType);
if (ad != null) {
@@ -284,7 +285,7 @@
try {
entryPoint = MethodHandleImpl.IMPL_LOOKUP.
findSpecial(acls, iname, entryPointType, acls);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (entryPoint == null) continue;
Constructor<? extends Adapter> ctor = null;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/dyn/WrapperInstance.java Fri Feb 25 12:48:18 2011 -0800
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.dyn;
+
+import java.dyn.MethodHandle;
+
+/**
+ * Private API used inside of java.dyn.MethodHandles.
+ * Interface implemented by every object which is produced by
+ * {@link java.dyn.MethodHandles#asInstance MethodHandles.asInstance}.
+ * The methods of this interface allow a caller to recover the parameters
+ * to {@code asInstance}.
+ * This allows applications to repeatedly convert between method handles
+ * and SAM objects, without the risk of creating unbounded delegation chains.
+ */
+public interface WrapperInstance {
+ /** Produce or recover a target method handle which is behaviorally
+ * equivalent to the SAM method of this object.
+ */
+ public MethodHandle getWrapperInstanceTarget();
+ /** Recover the SAM type for which this object was created.
+ */
+ public Class<?> getWrapperInstanceType();
+}
+
--- a/jdk/src/share/classes/sun/dyn/util/ValueConversions.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/util/ValueConversions.java Fri Feb 25 12:48:18 2011 -0800
@@ -153,7 +153,7 @@
try {
// actually, type is wrong; the Java method takes Object
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
mh = null;
}
} else {
@@ -289,7 +289,7 @@
if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
mh = null;
}
} else {
@@ -408,7 +408,7 @@
if (exact) {
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
mh = null;
}
} else {
@@ -492,7 +492,7 @@
case INT: case LONG: case FLOAT: case DOUBLE:
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
mh = null;
}
break;
@@ -654,7 +654,7 @@
type = type.appendParameterTypes(wrap.primitiveType());
try {
mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
mh = null;
}
if (mh == null && wrap == Wrapper.VOID) {
@@ -723,7 +723,7 @@
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (array == null) break;
arrays.add(array);
@@ -784,7 +784,7 @@
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
}
if (array == null) break;
arrays.add(array);
--- a/jdk/src/share/classes/sun/dyn/util/VerifyAccess.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/util/VerifyAccess.java Fri Feb 25 12:48:18 2011 -0800
@@ -25,7 +25,6 @@
package sun.dyn.util;
-import java.dyn.NoAccessException;
import java.lang.reflect.Modifier;
import sun.dyn.MemberName;
import sun.dyn.MethodHandleImpl;
@@ -139,6 +138,8 @@
* <li>C is public.
* <li>C and D are members of the same runtime package.
* </ul>
+ * @param refc the symbolic reference class to which access is being checked (C)
+ * @param lookupClass the class performing the lookup (D)
*/
public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass) {
int mods = refc.getModifiers();
--- a/jdk/test/java/dyn/InvokeDynamicPrintArgs.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/test/java/dyn/InvokeDynamicPrintArgs.java Fri Feb 25 12:48:18 2011 -0800
@@ -23,15 +23,19 @@
/* @test
* @summary smoke test for invokedynamic instructions
- * @library indify
+ * @build indify.Indify
* @compile InvokeDynamicPrintArgs.java
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic
* indify.Indify
* --verify-specifier-count=3 --transitionalJSR292=false
* --expand-properties --classpath ${test.classes}
- * --java InvokeDynamicPrintArgs --check-output
+ * --java test.java.dyn.InvokeDynamicPrintArgs --check-output
*/
+package test.java.dyn;
+
+import org.junit.Test;
+
import java.util.*;
import java.io.*;
@@ -53,6 +57,20 @@
closeBuf();
}
+ @Test
+ public void testInvokeDynamicPrintArgs() throws IOException {
+ System.err.println(System.getProperties());
+ String testClassPath = System.getProperty("build.test.classes.dir");
+ if (testClassPath == null) throw new RuntimeException();
+ String[] args = new String[]{
+ "--verify-specifier-count=3", "--transitionalJSR292=false",
+ "--expand-properties", "--classpath", testClassPath,
+ "--java", "test.java.dyn.InvokeDynamicPrintArgs", "--check-output"
+ };
+ System.err.println("Indify: "+Arrays.toString(args));
+ indify.Indify.main(args);
+ }
+
private static PrintStream oldOut;
private static ByteArrayOutputStream buf;
private static void openBuf() {
@@ -79,11 +97,11 @@
}
private static final String[] EXPECT_OUTPUT = {
"Printing some argument lists, starting with a empty one:",
- "[InvokeDynamicPrintArgs, nothing, ()void][]",
- "[InvokeDynamicPrintArgs, bar, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
- "[InvokeDynamicPrintArgs, bar2, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
- "[InvokeDynamicPrintArgs, baz, (java.lang.String,int,double)void, 1234.5][baz arg, 2, 3.14]",
- "[InvokeDynamicPrintArgs, foo, (java.lang.String)void][foo arg]",
+ "[test.java.dyn.InvokeDynamicPrintArgs, nothing, ()void][]",
+ "[test.java.dyn.InvokeDynamicPrintArgs, bar, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
+ "[test.java.dyn.InvokeDynamicPrintArgs, bar2, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
+ "[test.java.dyn.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]",
+ "[test.java.dyn.InvokeDynamicPrintArgs, foo, (String)void][foo arg]",
"Done printing argument lists."
};
@@ -110,18 +128,15 @@
return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
}
- private static CallSite bsm2(Lookup caller, String name, MethodType type, Object arg) throws ReflectiveOperationException {
+ private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException {
// ignore caller and name, but match the type:
List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type));
- if (arg instanceof Object[])
- bsmInfo.addAll(Arrays.asList((Object[])arg));
- else
- bsmInfo.add(arg);
+ bsmInfo.addAll(Arrays.asList((Object[])arg));
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
}
private static MethodType MT_bsm2() {
shouldNotCallThis();
- return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object.class);
+ return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class);
}
private static MethodHandle MH_bsm2() throws ReflectiveOperationException {
shouldNotCallThis();
--- a/jdk/test/java/dyn/InvokeGenericTest.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/test/java/dyn/InvokeGenericTest.java Fri Feb 25 12:48:18 2011 -0800
@@ -320,7 +320,7 @@
MethodHandle callable(List<Class<?>> params) {
MethodHandle mh = CALLABLES.get(params);
if (mh == null) {
- mh = collectArguments(collector_MH, methodType(Object.class, params));
+ mh = collector_MH.asType(methodType(Object.class, params));
CALLABLES.put(params, mh);
}
return mh;
@@ -338,7 +338,7 @@
= LOOKUP.findStatic(LOOKUP.lookupClass(),
"collector",
methodType(Object.class, Object[].class));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
--- a/jdk/test/java/dyn/JavaDocExamplesTest.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/test/java/dyn/JavaDocExamplesTest.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -74,7 +74,7 @@
// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class,
// "hashCode", methodType(int.class));
-// form required if NoAccessException is intercepted:
+// form required if ReflectiveOperationException is intercepted:
static final private MethodHandle CONCAT_2, HASHCODE_2;
static {
try {
@@ -82,7 +82,7 @@
"concat", methodType(String.class, String.class));
HASHCODE_2 = LOOKUP.findVirtual(Object.class,
"hashCode", methodType(int.class));
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
@@ -142,37 +142,79 @@
Assert.assertEquals(exp, act);
}
-static MethodHandle asList;
- @Test public void testWithTypeHandler() throws Throwable {
+ @Test public void testMethodHandlesSummary() throws Throwable {
{{
{} /// JAVADOC
-MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
-MethodHandle asList = lookup()
- .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
-
-JavaDocExamplesTest.asList = asList;
-/*
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
- return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
-}
-*/
-
-MethodHandle collectingTypeHandler = lookup()
- .findStatic(lookup().lookupClass(), "collectingTypeHandler",
- methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
-MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
-
-assertEquals("[]", makeAnyList.invokeGeneric().toString());
-assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
-assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
+Object x, y; String s; int i;
+MethodType mt; MethodHandle mh;
+MethodHandles.Lookup lookup = MethodHandles.lookup();
+// mt is (char,char)String
+mt = MethodType.methodType(String.class, char.class, char.class);
+mh = lookup.findVirtual(String.class, "replace", mt);
+s = (String) mh.invokeExact("daddy",'d','n');
+// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
+assert(s.equals("nanny"));
+// weakly typed invocation (using MHs.invoke)
+s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
+assert(s.equals("savvy"));
+// mt is (Object[])List
+mt = MethodType.methodType(java.util.List.class, Object[].class);
+mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
+assert(mh.isVarargsCollector());
+x = mh.invokeGeneric("one", "two");
+// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+assert(x.equals(java.util.Arrays.asList("one","two")));
+// mt is (Object,Object,Object)Object
+mt = MethodType.genericMethodType(3);
+mh = mh.asType(mt);
+x = mh.invokeExact((Object)1, (Object)2, (Object)3);
+// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+assert(x.equals(java.util.Arrays.asList(1,2,3)));
+// mt is { => int}
+mt = MethodType.methodType(int.class);
+mh = lookup.findVirtual(java.util.List.class, "size", mt);
+i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
+// invokeExact(Ljava/util/List;)I
+assert(i == 3);
+mt = MethodType.methodType(void.class, String.class);
+mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
+mh.invokeExact(System.out, "Hello, world.");
+// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
+{}
}}
}
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
- //System.out.println("Converting "+asList+" to "+newType);
- MethodHandle conv = asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
- //System.out.println(" =>"+conv);
- return conv;
+ @Test public void testAsVarargsCollector() throws Throwable {
+ {{
+{} /// JAVADOC
+MethodHandle asList = publicLookup()
+ .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+ .asVarargsCollector(Object[].class);
+assertEquals("[]", asList.invokeGeneric().toString());
+assertEquals("[1]", asList.invokeGeneric(1).toString());
+assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
+List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals(1, ls.size());
+assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
+ }}
+ }
+
+ @Test public void testVarargsCollectorSuppression() throws Throwable {
+ {{
+{} /// JAVADOC
+MethodHandle vamh = publicLookup()
+ .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+ .asVarargsCollector(Object[].class);
+MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
+assert(vamh.type().equals(mh.type()));
+assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+boolean failed = false;
+try { mh.invokeGeneric(1,2,3); }
+catch (WrongMethodTypeException ex) { failed = true; }
+assert(failed);
+{}
+ }}
+ }
}
-
-}
--- a/jdk/test/java/dyn/MethodHandlesTest.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/test/java/dyn/MethodHandlesTest.java Fri Feb 25 12:48:18 2011 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -323,6 +323,44 @@
return x.getClass().getSimpleName();
}
+ /** Return lambda(arg...[arity]) { new Object[]{ arg... } } */
+ static MethodHandle varargsList(int arity) {
+ return ValueConversions.varargsList(arity);
+ }
+ /** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */
+ static MethodHandle varargsArray(int arity) {
+ return ValueConversions.varargsArray(arity);
+ }
+ /** Variation of varargsList, but with the given rtype. */
+ static MethodHandle varargsList(int arity, Class<?> rtype) {
+ MethodHandle list = varargsList(arity);
+ MethodType listType = list.type().changeReturnType(rtype);
+ if (List.class.isAssignableFrom(rtype) || rtype == void.class || rtype == Object.class) {
+ // OK
+ } else if (rtype.isAssignableFrom(String.class)) {
+ if (LIST_TO_STRING == null)
+ try {
+ LIST_TO_STRING = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToString",
+ MethodType.methodType(String.class, List.class));
+ } catch (Exception ex) { throw new RuntimeException(ex); }
+ list = MethodHandles.filterReturnValue(list, LIST_TO_STRING);
+ } else if (rtype.isPrimitive()) {
+ if (LIST_TO_INT == null)
+ try {
+ LIST_TO_INT = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToInt",
+ MethodType.methodType(int.class, List.class));
+ } catch (Exception ex) { throw new RuntimeException(ex); }
+ list = MethodHandles.filterReturnValue(list, LIST_TO_INT);
+ list = MethodHandles.explicitCastArguments(list, listType);
+ } else {
+ throw new RuntimeException("varargsList: "+rtype);
+ }
+ return list.asType(listType);
+ }
+ private static MethodHandle LIST_TO_STRING, LIST_TO_INT;
+ private static String listToString(List x) { return x.toString(); }
+ private static int listToInt(List x) { return x.toString().hashCode(); }
+
static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
return changeArgTypes(target, 0, 999, argType);
}
@@ -458,8 +496,12 @@
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.in(defc).findStatic(defc, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (name.contains("bogus"))
+ assertTrue(noAccess instanceof NoSuchMethodException);
+ else
+ assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -528,8 +570,12 @@
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.in(defc).findVirtual(defc, methodName, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (name.contains("bogus"))
+ assertTrue(noAccess instanceof NoSuchMethodException);
+ else
+ assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -558,11 +604,12 @@
testFindSpecial(SubExample.class, Example.class, void.class, "v0");
testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
// Do some negative testing:
+ testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus");
+ testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus");
for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
- testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
}
}
@@ -583,8 +630,12 @@
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller));
target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (name.contains("bogus"))
+ assertTrue(noAccess instanceof NoSuchMethodException);
+ else
+ assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -639,8 +690,12 @@
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
target = lookup.in(defc).bind(receiver, methodName, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (name.contains("bogus"))
+ assertTrue(noAccess instanceof NoSuchMethodException);
+ else
+ assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
@@ -698,14 +753,9 @@
Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable {
countTest(positive);
MethodType type = MethodType.methodType(ret, params);
- Method rmethod = null;
+ Method rmethod = defc.getDeclaredMethod(name, params);
MethodHandle target = null;
Exception noAccess = null;
- try {
- rmethod = defc.getDeclaredMethod(name, params);
- } catch (NoSuchMethodException ex) {
- throw new NoAccessException(ex);
- }
boolean isStatic = (rcvc == null);
boolean isSpecial = (specialCaller != null);
try {
@@ -714,8 +764,12 @@
target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller);
else
target = lookup.in(defc).unreflect(rmethod);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
noAccess = ex;
+ if (name.contains("bogus"))
+ assertTrue(noAccess instanceof NoSuchMethodException);
+ else
+ assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
@@ -824,25 +878,28 @@
if (type == float.class) {
float v = 'F';
if (isStatic) v++;
- assert(value.equals(v));
+ assertTrue(value.equals(v));
}
- assert(name.equals(field.getName()));
- assert(type.equals(field.getType()));
- assert(isStatic == (Modifier.isStatic(field.getModifiers())));
+ assertTrue(name.equals(field.getName()));
+ assertTrue(type.equals(field.getType()));
+ assertTrue(isStatic == (Modifier.isStatic(field.getModifiers())));
cases.add(new Object[]{ field, value });
}
}
+ cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class });
+ cases.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class });
CASES = cases.toArray(new Object[0][]);
}
}
- static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC_FIELD = 3;
+ static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10;
static boolean testModeMatches(int testMode, boolean isStatic) {
switch (testMode) {
- case TEST_FIND_STATIC_FIELD: return isStatic;
+ case TEST_FIND_STATIC: return isStatic;
case TEST_FIND_FIELD: return !isStatic;
- default: return true; // unreflect matches both
+ case TEST_UNREFLECT: return true; // unreflect matches both
}
+ throw new InternalError("testMode="+testMode);
}
@Test
@@ -858,54 +915,161 @@
@Test
public void testFindStaticGetter() throws Throwable {
startTest("findStaticGetter");
- testGetter(TEST_FIND_STATIC_FIELD);
+ testGetter(TEST_FIND_STATIC);
}
public void testGetter(int testMode) throws Throwable {
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
for (Object[] c : HasFields.CASES) {
- Field f = (Field)c[0];
- Object value = c[1];
- Class<?> type = f.getType();
- testGetter(lookup, f, type, value, testMode);
+ boolean positive = (c[1] != Error.class);
+ testGetter(positive, lookup, c[0], c[1], testMode);
+ }
+ testGetter(true, lookup,
+ new Object[]{ true, System.class, "out", java.io.PrintStream.class },
+ System.out, testMode);
+ for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
+ testGetter(false, lookup,
+ new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
+ null, testMode);
}
}
- public void testGetter(MethodHandles.Lookup lookup,
- Field f, Class<?> type, Object value, int testMode) throws Throwable {
- boolean isStatic = Modifier.isStatic(f.getModifiers());
- Class<?> fclass = f.getDeclaringClass();
- String fname = f.getName();
- Class<?> ftype = f.getType();
+ public void testGetter(boolean positive, MethodHandles.Lookup lookup,
+ Object fieldRef, Object value, int testMode) throws Throwable {
+ testAccessor(positive, lookup, fieldRef, value, testMode);
+ }
+
+ public void testAccessor(boolean positive, MethodHandles.Lookup lookup,
+ Object fieldRef, Object value, int testMode0) throws Throwable {
+ boolean isGetter = ((testMode0 & TEST_SETTER) == 0);
+ int testMode = testMode0 & ~TEST_SETTER;
+ boolean isStatic;
+ Class<?> fclass;
+ String fname;
+ Class<?> ftype;
+ Field f = (fieldRef instanceof Field ? (Field)fieldRef : null);
+ if (f != null) {
+ isStatic = Modifier.isStatic(f.getModifiers());
+ fclass = f.getDeclaringClass();
+ fname = f.getName();
+ ftype = f.getType();
+ } else {
+ Object[] scnt = (Object[]) fieldRef;
+ isStatic = (Boolean) scnt[0];
+ fclass = (Class<?>) scnt[1];
+ fname = (String) scnt[2];
+ ftype = (Class<?>) scnt[3];
+ try {
+ f = fclass.getDeclaredField(fname);
+ } catch (ReflectiveOperationException ex) {
+ f = null;
+ }
+ }
if (!testModeMatches(testMode, isStatic)) return;
- countTest(true);
- MethodType expType = MethodType.methodType(type, HasFields.class);
+ if (f == null && testMode == TEST_UNREFLECT) return;
+ countTest(positive);
+ MethodType expType;
+ if (isGetter)
+ expType = MethodType.methodType(ftype, HasFields.class);
+ else
+ expType = MethodType.methodType(void.class, HasFields.class, ftype);
if (isStatic) expType = expType.dropParameterTypes(0, 1);
- MethodHandle mh = lookup.unreflectGetter(f);
+ Exception noAccess = null;
+ MethodHandle mh;
+ try {
+ switch (testMode0) {
+ case TEST_UNREFLECT: mh = lookup.unreflectGetter(f); break;
+ case TEST_FIND_FIELD: mh = lookup.findGetter(fclass, fname, ftype); break;
+ case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype); break;
+ case TEST_SETTER|
+ TEST_UNREFLECT: mh = lookup.unreflectSetter(f); break;
+ case TEST_SETTER|
+ TEST_FIND_FIELD: mh = lookup.findSetter(fclass, fname, ftype); break;
+ case TEST_SETTER|
+ TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype); break;
+ default:
+ throw new InternalError("testMode="+testMode);
+ }
+ } catch (ReflectiveOperationException ex) {
+ mh = null;
+ noAccess = ex;
+ if (fname.contains("bogus"))
+ assertTrue(noAccess instanceof NoSuchFieldException);
+ else
+ assertTrue(noAccess instanceof IllegalAccessException);
+ }
+ if (verbosity >= 3)
+ System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype
+ +" => "+mh
+ +(noAccess == null ? "" : " !! "+noAccess));
+ if (positive && noAccess != null) throw new RuntimeException(noAccess);
+ assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null);
+ if (!positive) return; // negative test failed as expected
+ assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount());
+
+
assertSame(mh.type(), expType);
assertNameStringContains(mh, fname);
HasFields fields = new HasFields();
Object sawValue;
- Class<?> rtype = type;
- if (type != int.class) rtype = Object.class;
- mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(rtype));
- Object expValue = value;
- for (int i = 0; i <= 1; i++) {
- if (isStatic) {
- if (type == int.class)
- sawValue = (int) mh.invokeExact(); // do these exactly
- else
- sawValue = mh.invokeExact();
- } else {
- if (type == int.class)
- sawValue = (int) mh.invokeExact((Object) fields);
- else
- sawValue = mh.invokeExact((Object) fields);
+ Class<?> vtype = ftype;
+ if (ftype != int.class) vtype = Object.class;
+ if (isGetter) {
+ mh = MethodHandles.convertArguments(mh, mh.type().generic()
+ .changeReturnType(vtype));
+ } else {
+ int last = mh.type().parameterCount() - 1;
+ mh = MethodHandles.convertArguments(mh, mh.type().generic()
+ .changeReturnType(void.class)
+ .changeParameterType(last, vtype));
+ }
+ if (f != null && f.getDeclaringClass() == HasFields.class) {
+ assertEquals(f.get(fields), value); // clean to start with
+ }
+ if (isGetter) {
+ Object expValue = value;
+ for (int i = 0; i <= 1; i++) {
+ if (isStatic) {
+ if (ftype == int.class)
+ sawValue = (int) mh.invokeExact(); // do these exactly
+ else
+ sawValue = mh.invokeExact();
+ } else {
+ if (ftype == int.class)
+ sawValue = (int) mh.invokeExact((Object) fields);
+ else
+ sawValue = mh.invokeExact((Object) fields);
+ }
+ assertEquals(sawValue, expValue);
+ if (f != null && f.getDeclaringClass() == HasFields.class
+ && !Modifier.isFinal(f.getModifiers())) {
+ Object random = randomArg(ftype);
+ f.set(fields, random);
+ expValue = random;
+ } else {
+ break;
+ }
}
- assertEquals(sawValue, expValue);
- Object random = randomArg(type);
- f.set(fields, random);
- expValue = random;
+ } else {
+ for (int i = 0; i <= 1; i++) {
+ Object putValue = randomArg(ftype);
+ if (isStatic) {
+ if (ftype == int.class)
+ mh.invokeExact((int)putValue); // do these exactly
+ else
+ mh.invokeExact(putValue);
+ } else {
+ if (ftype == int.class)
+ mh.invokeExact((Object) fields, (int)putValue);
+ else
+ mh.invokeExact((Object) fields, putValue);
+ }
+ if (f != null && f.getDeclaringClass() == HasFields.class) {
+ assertEquals(f.get(fields), putValue);
+ }
+ }
}
- f.set(fields, value); // put it back
+ if (f != null && f.getDeclaringClass() == HasFields.class) {
+ f.set(fields, value); // put it back
+ }
}
@@ -922,61 +1086,24 @@
@Test
public void testFindStaticSetter() throws Throwable {
startTest("findStaticSetter");
- testSetter(TEST_FIND_STATIC_FIELD);
+ testSetter(TEST_FIND_STATIC);
}
public void testSetter(int testMode) throws Throwable {
Lookup lookup = PRIVATE; // FIXME: test more lookups than this one
startTest("unreflectSetter");
for (Object[] c : HasFields.CASES) {
- Field f = (Field)c[0];
- Object value = c[1];
- Class<?> type = f.getType();
- testSetter(lookup, f, type, value, testMode);
+ boolean positive = (c[1] != Error.class);
+ testSetter(positive, lookup, c[0], c[1], testMode);
+ }
+ for (int isStaticN = 0; isStaticN <= 1; isStaticN++) {
+ testSetter(false, lookup,
+ new Object[]{ (isStaticN != 0), System.class, "bogus", char.class },
+ null, testMode);
}
}
- public void testSetter(MethodHandles.Lookup lookup,
- Field f, Class<?> type, Object value, int testMode) throws Throwable {
- boolean isStatic = Modifier.isStatic(f.getModifiers());
- Class<?> fclass = f.getDeclaringClass();
- String fname = f.getName();
- Class<?> ftype = f.getType();
- if (!testModeMatches(testMode, isStatic)) return;
- countTest(true);
- MethodType expType = MethodType.methodType(void.class, HasFields.class, type);
- if (isStatic) expType = expType.dropParameterTypes(0, 1);
- MethodHandle mh;
- if (testMode == TEST_UNREFLECT)
- mh = lookup.unreflectSetter(f);
- else if (testMode == TEST_FIND_FIELD)
- mh = lookup.findSetter(fclass, fname, ftype);
- else if (testMode == TEST_FIND_STATIC_FIELD)
- mh = lookup.findStaticSetter(fclass, fname, ftype);
- else throw new InternalError();
- assertSame(mh.type(), expType);
- assertNameStringContains(mh, fname);
- HasFields fields = new HasFields();
- Object sawValue;
- Class<?> vtype = type;
- if (type != int.class) vtype = Object.class;
- int last = mh.type().parameterCount() - 1;
- mh = MethodHandles.convertArguments(mh, mh.type().generic().changeReturnType(void.class).changeParameterType(last, vtype));
- assertEquals(f.get(fields), value); // clean to start with
- for (int i = 0; i <= 1; i++) {
- Object putValue = randomArg(type);
- if (isStatic) {
- if (type == int.class)
- mh.invokeExact((int)putValue); // do these exactly
- else
- mh.invokeExact(putValue);
- } else {
- if (type == int.class)
- mh.invokeExact((Object) fields, (int)putValue);
- else
- mh.invokeExact((Object) fields, putValue);
- }
- assertEquals(f.get(fields), putValue);
- }
- f.set(fields, value); // put it back
+ public void testSetter(boolean positive, MethodHandles.Lookup lookup,
+ Object fieldRef, Object value, int testMode) throws Throwable {
+ testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER);
}
@Test
@@ -1108,18 +1235,6 @@
}
}
- static MethodHandle typeHandler2(MethodHandle target, MethodType newType) {
- MethodType oldType = target.type();
- int oldArity = oldType.parameterCount();
- int newArity = newType.parameterCount();
- if (newArity < oldArity)
- return MethodHandles.insertArguments(target, oldArity, "OPTIONAL");
- else if (newArity > oldArity)
- return MethodHandles.dropArguments(target, oldArity-1, newType.parameterType(oldArity-1));
- else
- return target; // attempt no further conversions
- }
-
@Test
public void testConvertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
@@ -1137,23 +1252,6 @@
testConvert(true, true, id, rtype, name, params);
}
- @Test
- public void testTypeHandler() throws Throwable {
- MethodHandle id = Callee.ofType(1);
- MethodHandle th2 = PRIVATE.findStatic(MethodHandlesTest.class, "typeHandler2",
- MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
- MethodHandle id2 = id.withTypeHandler(th2);
- testConvert(true, false, id2, null, "id", Object.class);
- testConvert(true, true, id2, null, "id", Object.class);
- if (true) return; //FIXME
- testConvert(true, false, id2, null, "id", String.class); // FIXME: throws WMT
- testConvert(false, true, id2, null, "id", String.class); // FIXME: should not succeed
- testConvert(false, false, id2, null, "id", Object.class, String.class); //FIXME: array[1] line 1164
- testConvert(true, true, id2, null, "id", Object.class, String.class);
- testConvert(false, false, id2, null, "id");
- testConvert(true, true, id2, null, "id");
- }
-
void testConvert(boolean positive, boolean useAsType,
MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
countTest(positive);
@@ -1183,9 +1281,9 @@
RuntimeException error = null;
try {
if (useAsType)
- target = MethodHandles.convertArguments(id, newType);
+ target = id.asType(newType);
else
- target = id.asType(newType);
+ target = MethodHandles.convertArguments(id, newType);
} catch (RuntimeException ex) {
error = ex;
}
@@ -1205,6 +1303,20 @@
}
@Test
+ public void testVarargsCollector() throws Throwable {
+ MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
+ MethodType.methodType(Object.class, String.class, Object[].class));
+ vac0 = vac0.bindTo("vac");
+ MethodHandle vac = vac0.asVarargsCollector(Object[].class);
+ testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
+ testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac");
+ for (Class<?> at : new Class[] { Object.class, String.class, Integer.class }) {
+ testConvert(true, true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
+ testConvert(true, true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
+ }
+ }
+
+ @Test
public void testPermuteArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("permuteArguments");
@@ -1300,7 +1412,7 @@
types[i] = args[i].getClass();
}
int inargs = args.length, outargs = reorder.length;
- assert(inargs == types.length);
+ assertTrue(inargs == types.length);
if (verbosity >= 3)
System.out.println("permuteArguments "+Arrays.toString(reorder));
Object[] permArgs = new Object[outargs];
@@ -1317,7 +1429,7 @@
}
MethodType inType = MethodType.methodType(Object.class, types);
MethodType outType = MethodType.methodType(Object.class, permTypes);
- MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType);
+ MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType);
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
Object result = newTarget.invokeWithArguments(args);
Object expected = Arrays.asList(permArgs);
@@ -1344,7 +1456,7 @@
}
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest();
- MethodHandle target = ValueConversions.varargsArray(nargs);
+ MethodHandle target = varargsArray(nargs);
MethodHandle target2 = changeArgTypes(target, argType);
if (verbosity >= 3)
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
@@ -1374,7 +1486,7 @@
spreadParams.clear(); spreadParams.add(Object[].class);
}
MethodType newType = MethodType.methodType(Object.class, newParams);
- MethodHandle result = MethodHandles.spreadArguments(target2, newType);
+ MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
Object[] returnValue;
if (pos == 0) {
// In the following line, the first cast implies
@@ -1408,7 +1520,7 @@
public void testCollectArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest();
// fake up a MH with the same type as the desired adapter:
- MethodHandle fake = ValueConversions.varargsArray(nargs);
+ MethodHandle fake = varargsArray(nargs);
fake = changeArgTypes(fake, argType);
MethodType newType = fake.type();
Object[] args = randomArgs(newType.parameterArray());
@@ -1416,12 +1528,12 @@
Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1);
collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length);
// here is the MH which will witness the collected argument tail:
- MethodHandle target = ValueConversions.varargsArray(pos+1);
+ MethodHandle target = varargsArray(pos+1);
target = changeArgTypes(target, 0, pos, argType);
target = changeArgTypes(target, pos, pos+1, Object[].class);
if (verbosity >= 3)
System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]");
- MethodHandle result = MethodHandles.collectArguments(target, newType);
+ MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType);
Object[] returnValue = (Object[]) result.invokeWithArguments(args);
// assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]);
// returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]);
@@ -1445,7 +1557,7 @@
void testInsertArguments(int nargs, int pos, int ins) throws Throwable {
countTest();
- MethodHandle target = ValueConversions.varargsArray(nargs + ins);
+ MethodHandle target = varargsArray(nargs + ins);
Object[] args = randomArgs(target.type().parameterArray());
List<Object> resList = Arrays.asList(args);
List<Object> argsToPass = new ArrayList<Object>(resList);
@@ -1465,6 +1577,55 @@
}
@Test
+ public void testFilterReturnValue() throws Throwable {
+ if (CAN_SKIP_WORKING) return;
+ startTest("filterReturnValue");
+ Class<?> classOfVCList = varargsList(1).invokeWithArguments(0).getClass();
+ assertTrue(List.class.isAssignableFrom(classOfVCList));
+ for (int nargs = 0; nargs <= 3; nargs++) {
+ for (Class<?> rtype : new Class[] { Object.class,
+ List.class,
+ int.class,
+ //byte.class, //FIXME: add this
+ //long.class, //FIXME: add this
+ CharSequence.class,
+ String.class }) {
+ testFilterReturnValue(nargs, rtype);
+ }
+ }
+ }
+
+ void testFilterReturnValue(int nargs, Class<?> rtype) throws Throwable {
+ countTest();
+ MethodHandle target = varargsList(nargs, rtype);
+ MethodHandle filter;
+ if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class))
+ filter = varargsList(1); // add another layer of list-ness
+ else
+ filter = MethodHandles.identity(rtype);
+ filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype));
+ Object[] argsToPass = randomArgs(nargs, Object.class);
+ if (verbosity >= 3)
+ System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter);
+ MethodHandle target2 = MethodHandles.filterReturnValue(target, filter);
+ if (verbosity >= 4)
+ System.out.println("filtered target: "+target2);
+ // Simulate expected effect of filter on return value:
+ Object unfiltered = target.invokeWithArguments(argsToPass);
+ Object expected = filter.invokeWithArguments(unfiltered);
+ if (verbosity >= 4)
+ System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName());
+ if (verbosity >= 4)
+ System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName());
+ Object result = target2.invokeWithArguments(argsToPass);
+ if (verbosity >= 3)
+ System.out.println("result: "+result+" : "+result.getClass().getSimpleName());
+ if (!expected.equals(result))
+ System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
+ assertEquals(expected, result);
+ }
+
+ @Test
public void testFilterArguments() throws Throwable {
if (CAN_SKIP_WORKING) return;
startTest("filterArguments");
@@ -1477,8 +1638,8 @@
void testFilterArguments(int nargs, int pos) throws Throwable {
countTest();
- MethodHandle target = ValueConversions.varargsList(nargs);
- MethodHandle filter = ValueConversions.varargsList(1);
+ MethodHandle target = varargsList(nargs);
+ MethodHandle filter = varargsList(1);
filter = MethodHandles.convertArguments(filter, filter.type().generic());
Object[] argsToPass = randomArgs(nargs, Object.class);
if (verbosity >= 3)
@@ -1492,7 +1653,7 @@
if (verbosity >= 3)
System.out.println("result: "+result);
if (!expected.equals(result))
- System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+argsToPass+" => "+result);
+ System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected);
assertEquals(expected, result);
}
@@ -1512,8 +1673,8 @@
void testFoldArguments(int nargs, int pos, int fold) throws Throwable {
if (pos != 0) return; // can fold only at pos=0 for now
countTest();
- MethodHandle target = ValueConversions.varargsList(1 + nargs);
- MethodHandle combine = ValueConversions.varargsList(fold).asType(MethodType.genericMethodType(fold));
+ MethodHandle target = varargsList(1 + nargs);
+ MethodHandle combine = varargsList(fold).asType(MethodType.genericMethodType(fold));
List<Object> argsToPass = Arrays.asList(randomArgs(nargs, Object.class));
if (verbosity >= 3)
System.out.println("fold "+target+" with "+combine);
@@ -1529,7 +1690,7 @@
if (verbosity >= 3)
System.out.println("result: "+result);
if (!expected.equals(result))
- System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result);
+ System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected);
assertEquals(expected, result);
}
@@ -1548,7 +1709,7 @@
void testDropArguments(int nargs, int pos, int drop) throws Throwable {
countTest();
- MethodHandle target = ValueConversions.varargsArray(nargs);
+ MethodHandle target = varargsArray(nargs);
Object[] args = randomArgs(target.type().parameterArray());
MethodHandle target2 = MethodHandles.dropArguments(target, pos,
Collections.nCopies(drop, Object.class).toArray(new Class[0]));
@@ -1599,7 +1760,8 @@
boolean testRetCode = type.returnType() != void.class;
MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee",
MethodType.genericMethodType(0, true));
- target = MethodHandles.collectArguments(target, type);
+ assertTrue(target.isVarargsCollector());
+ target = target.asType(type);
Object[] args = randomArgs(type.parameterArray());
List<Object> targetPlusArgs = new ArrayList<Object>(Arrays.asList(args));
targetPlusArgs.add(0, target);
@@ -1644,14 +1806,14 @@
assertCalled("invokee", args);
// varargs invoker #0
calledLog.clear();
- inv = MethodHandles.varargsInvoker(type, 0);
+ inv = MethodHandles.spreadInvoker(type, 0);
result = inv.invokeExact(target, args);
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
if (nargs >= 1) {
// varargs invoker #1
calledLog.clear();
- inv = MethodHandles.varargsInvoker(type, 1);
+ inv = MethodHandles.spreadInvoker(type, 1);
result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
@@ -1659,7 +1821,7 @@
if (nargs >= 2) {
// varargs invoker #2
calledLog.clear();
- inv = MethodHandles.varargsInvoker(type, 2);
+ inv = MethodHandles.spreadInvoker(type, 2);
result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
@@ -1667,7 +1829,7 @@
if (nargs >= 3) {
// varargs invoker #3
calledLog.clear();
- inv = MethodHandles.varargsInvoker(type, 3);
+ inv = MethodHandles.spreadInvoker(type, 3);
result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args);
@@ -1676,7 +1838,7 @@
// varargs invoker #0..N
countTest();
calledLog.clear();
- inv = MethodHandles.varargsInvoker(type, k);
+ inv = MethodHandles.spreadInvoker(type, k);
List<Object> targetPlusVarArgs = new ArrayList<Object>(targetPlusArgs);
List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
Object[] tail = tailList.toArray();
@@ -1823,7 +1985,7 @@
MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
while (thrower.type().parameterCount() < nargs)
thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class);
- MethodHandle catcher = ValueConversions.varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs));
+ MethodHandle catcher = varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs));
MethodHandle target = MethodHandles.catchException(thrower,
thrown.getClass(), catcher);
assertEquals(thrower.type(), target.type());
@@ -2089,25 +2251,12 @@
}
}
// Test error checking:
- MethodHandle genericMH = ValueConversions.varargsArray(0);
- genericMH = MethodHandles.convertArguments(genericMH, genericMH.type().generic());
- for (Class<?> sam : new Class[] { Runnable.class,
- Fooable.class,
- Iterable.class }) {
- try {
- // Must throw, because none of these guys has generic type.
- MethodHandles.asInstance(genericMH, sam);
- System.out.println("Failed to throw");
- assertTrue(false);
- } catch (IllegalArgumentException ex) {
- }
- }
for (Class<?> nonSAM : new Class[] { Object.class,
String.class,
CharSequence.class,
Example.class }) {
try {
- MethodHandles.asInstance(ValueConversions.varargsArray(0), nonSAM);
+ MethodHandles.asInstance(varargsArray(0), nonSAM);
System.out.println("Failed to throw");
assertTrue(false);
} catch (IllegalArgumentException ex) {
@@ -2159,12 +2308,13 @@
MethodHandle array = null;
try {
array = lookup.findStatic(ValueConversions.class, name, type);
- } catch (NoAccessException ex) {
+ } catch (ReflectiveOperationException ex) {
+ // break from loop!
}
if (array == null) break;
arrays.add(array);
}
- assert(arrays.size() == 11); // current number of methods
+ assertTrue(arrays.size() == 11); // current number of methods
return arrays.toArray(new MethodHandle[0]);
}
static final MethodHandle[] ARRAYS = makeArrays();
@@ -2211,22 +2361,23 @@
Object a8, Object a9)
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
static MethodHandle[] makeLists() {
- ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
+ ArrayList<MethodHandle> lists = new ArrayList<MethodHandle>();
MethodHandles.Lookup lookup = IMPL_LOOKUP;
for (;;) {
- int nargs = arrays.size();
+ int nargs = lists.size();
MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
String name = "list";
- MethodHandle array = null;
+ MethodHandle list = null;
try {
- array = lookup.findStatic(ValueConversions.class, name, type);
- } catch (NoAccessException ex) {
+ list = lookup.findStatic(ValueConversions.class, name, type);
+ } catch (ReflectiveOperationException ex) {
+ // break from loop!
}
- if (array == null) break;
- arrays.add(array);
+ if (list == null) break;
+ lists.add(list);
}
- assert(arrays.size() == 11); // current number of methods
- return arrays.toArray(new MethodHandle[0]);
+ assertTrue(lists.size() == 11); // current number of methods
+ return lists.toArray(new MethodHandle[0]);
}
static final MethodHandle[] LISTS = makeLists();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/dyn/MethodTypeTest.java Fri Feb 25 12:48:18 2011 -0800
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2008, 2011 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @summary unit tests for java.dyn.MethodType
+ * @compile MethodTypeTest.java
+ * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.MethodTypeTest
+ */
+
+package test.java.dyn;
+
+import sun.dyn.MemberName;
+import java.dyn.MethodType;
+import java.lang.reflect.Method;
+
+import java.util.*;
+import org.junit.*;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jrose
+ */
+public class MethodTypeTest {
+
+ private Class<?> rtype;
+ private Class<?>[] ptypes;
+ private MethodType mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov;
+ private MethodType mt_iSI, mt_ISi, mt_ISI, mt_iSi;
+ private MethodType mt_viO, mt_iO2, mt_OOi, mt_iOi;
+ private MethodType mt_VIO, mt_IO2, mt_OOI, mt_IOI, mt_VIS;
+ private MethodType mt_vOiSzA, mt_OO99;
+ private MethodType[] GALLERY;
+ private Method compareTo;
+
+ @Before
+ public void setUp() throws Exception {
+ rtype = void.class;
+ ptypes = new Class<?>[] { int.class, String.class };
+
+ mt_viS = MethodType.methodType(void.class, int.class, String.class);
+ mt_OO = MethodType.methodType(Object.class, Object.class);
+ mt_OO2 = MethodType.methodType(Object.class, Object.class, Object.class);
+ mt_vv = MethodType.methodType(void.class);
+ mt_Vv = MethodType.methodType(Void.class);
+ mt_Ov = MethodType.methodType(Object.class);
+ mt_iSI = MethodType.methodType(int.class, String.class, Integer.class);
+ mt_ISi = MethodType.methodType(Integer.class, String.class, int.class);
+ mt_ISI = MethodType.methodType(Integer.class, String.class, Integer.class);
+ mt_iSi = MethodType.methodType(int.class, String.class, int.class);
+
+ compareTo = String.class.getDeclaredMethod("compareTo", String.class);
+
+ mt_viO = MethodType.methodType(void.class, int.class, Object.class);
+ mt_iO2 = MethodType.methodType(int.class, Object.class, Object.class);
+ mt_OOi = MethodType.methodType(Object.class, Object.class, int.class);
+ mt_iOi = MethodType.methodType(int.class, Object.class, int.class);
+
+ mt_VIO = MethodType.methodType(Void.class, Integer.class, Object.class);
+ mt_IO2 = MethodType.methodType(Integer.class, Object.class, Object.class);
+ mt_OOI = MethodType.methodType(Object.class, Object.class, Integer.class);
+ mt_IOI = MethodType.methodType(Integer.class, Object.class, Integer.class);
+ mt_VIS = MethodType.methodType(Void.class, Integer.class, String.class);
+
+ mt_vOiSzA = MethodType.methodType(void.class, Object.class, int.class, String.class, boolean.class, Object[].class);
+ mt_OO99 = MethodType.genericMethodType(99);
+
+ GALLERY = new MethodType[] {
+ mt_viS, mt_OO, mt_OO2, mt_vv, mt_Vv, mt_Ov,
+ mt_iSI, mt_ISi, mt_ISI, mt_iSi,
+ mt_viO, mt_iO2, mt_OOi, mt_iOi,
+ mt_VIO, mt_IO2, mt_OOI, mt_IOI,
+ mt_VIS, mt_vOiSzA, mt_OO99
+ };
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ /** Make sure the method types are all distinct. */
+ @Test
+ public void testDistinct() {
+ List<MethodType> gallery2 = new ArrayList<>();
+ for (MethodType mt : GALLERY) {
+ assertFalse(mt.toString(), gallery2.contains(mt));
+ gallery2.add(mt);
+ }
+ // check self-equality also:
+ assertEquals(Arrays.asList(GALLERY), gallery2);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Class_ClassArr() {
+ System.out.println("make (from type array)");
+ MethodType result = MethodType.methodType(rtype, ptypes);
+ assertSame(mt_viS, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Class_List() {
+ System.out.println("make (from type list)");
+ MethodType result = MethodType.methodType(rtype, Arrays.asList(ptypes));
+ assertSame(mt_viS, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_3args() {
+ System.out.println("make (from type with varargs)");
+ MethodType result = MethodType.methodType(rtype, ptypes[0], ptypes[1]);
+ assertSame(mt_viS, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Class() {
+ System.out.println("make (from single type)");
+ Class<?> rt = Integer.class;
+ MethodType expResult = MethodType.methodType(rt, new Class<?>[0]);
+ MethodType result = MethodType.methodType(rt);
+ assertSame(expResult, result);
+ }
+
+ @Test
+ public void testMakeGeneric() {
+ System.out.println("makeGeneric");
+ int objectArgCount = 2;
+ MethodType expResult = mt_OO2;
+ MethodType result = MethodType.genericMethodType(objectArgCount);
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_Method() {
+ System.out.println("make (via MemberName.getMethodType)");
+ MethodType expResult = MethodType.methodType(int.class, String.class);
+ MemberName name = new MemberName(compareTo);
+ MethodType result = name.getMethodType();
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_MethodType() {
+ System.out.println("make (from rtype, MethodType)");
+ MethodType expResult = mt_iO2;
+ MethodType result = MethodType.methodType(int.class, mt_IO2);
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of make method, of class MethodType.
+ */
+ @Test
+ public void testMake_String_ClassLoader() {
+ System.out.println("make (from bytecode signature)");
+ ClassLoader loader = null;
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ String obj = "Ljava/lang/Object;";
+ assertEquals(obj, concat(Object.class));
+ String[] expResults = {
+ "(ILjava/lang/String;)V",
+ concat("(", obj, 2, ")", Object.class),
+ "()V", "()"+obj,
+ concat("(", String.class, Integer.class, ")I"),
+ concat("(", String.class, "I)", Integer.class),
+ concat("(", String.class, Integer.class, ")", Integer.class),
+ concat("(", String.class, "I)I")
+ };
+ for (int i = 0; i < instances.length; i++) {
+ MethodType instance = instances[i];
+ String result = instance.toMethodDescriptorString();
+ assertEquals("#"+i, expResults[i], result);
+ MethodType parsed = MethodType.fromMethodDescriptorString(result, loader);
+ assertSame("--#"+i, instance, parsed);
+ }
+ }
+ private static String concat(Object... parts) {
+ StringBuilder sb = new StringBuilder();
+ Object prevPart = "";
+ for (Object part : parts) {
+ if (part instanceof Class) {
+ part = "L"+((Class)part).getName()+";";
+ }
+ if (part instanceof Integer) {
+ for (int n = (Integer) part; n > 1; n--)
+ sb.append(prevPart);
+ part = "";
+ }
+ sb.append(part);
+ prevPart = part;
+ }
+ return sb.toString().replace('.', '/');
+ }
+
+ @Test
+ public void testHasPrimitives() {
+ System.out.println("hasPrimitives");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ boolean[] expResults = {true, false, true, false, true, true, false, true};
+ for (int i = 0; i < instances.length; i++) {
+ boolean result = instances[i].hasPrimitives();
+ assertEquals("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testHasWrappers() {
+ System.out.println("hasWrappers");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ boolean[] expResults = {false, false, false, false, true, true, true, false};
+ for (int i = 0; i < instances.length; i++) {
+ System.out.println(" hasWrappers "+instances[i]);
+ boolean result = instances[i].hasWrappers();
+ assertEquals("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testErase() {
+ System.out.println("erase");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_viO, mt_OO2, mt_vv, mt_Ov, mt_iO2, mt_OOi, mt_OO2, mt_iOi};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].erase();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testGeneric() {
+ System.out.println("generic");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_OO2, mt_OO2, mt_Ov, mt_Ov, mt_OO2, mt_OO2, mt_OO2, mt_OO2};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].generic();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testWrap() {
+ System.out.println("wrap");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_VIS, mt_OO2, mt_Vv, mt_Ov, mt_ISI, mt_ISI, mt_ISI, mt_ISI};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].wrap();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ @Test
+ public void testUnwrap() {
+ System.out.println("unwrap");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ MethodType[] expResults = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSi, mt_iSi, mt_iSi, mt_iSi};
+ for (int i = 0; i < instances.length; i++) {
+ MethodType result = instances[i].unwrap();
+ assertSame("#"+i, expResults[i], result);
+ }
+ }
+
+ /**
+ * Test of parameterType method, of class MethodType.
+ */
+ @Test
+ public void testParameterType() {
+ System.out.println("parameterType");
+ for (int num = 0; num < ptypes.length; num++) {
+ MethodType instance = mt_viS;
+ Class<?> expResult = ptypes[num];
+ Class<?> result = instance.parameterType(num);
+ assertSame(expResult, result);
+ }
+ }
+
+ /**
+ * Test of parameterCount method, of class MethodType.
+ */
+ @Test
+ public void testParameterCount() {
+ System.out.println("parameterCount");
+ MethodType instance = mt_viS;
+ int expResult = 2;
+ int result = instance.parameterCount();
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of returnType method, of class MethodType.
+ */
+ @Test
+ public void testReturnType() {
+ System.out.println("returnType");
+ MethodType instance = mt_viS;
+ Class<?> expResult = void.class;
+ Class<?> result = instance.returnType();
+ assertSame(expResult, result);
+ }
+
+ /**
+ * Test of parameterList method, of class MethodType.
+ */
+ @Test
+ public void testParameterList() {
+ System.out.println("parameterList");
+ MethodType instance = mt_viS;
+ List<Class<?>> expResult = Arrays.asList(ptypes);
+ List<Class<?>> result = instance.parameterList();
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of parameterArray method, of class MethodType.
+ */
+ @Test
+ public void testParameterArray() {
+ System.out.println("parameterArray");
+ MethodType instance = mt_viS;
+ Class<?>[] expResult = ptypes;
+ Class<?>[] result = instance.parameterArray();
+ assertEquals(Arrays.asList(expResult), Arrays.asList(result));
+ }
+
+ /**
+ * Test of equals method, of class MethodType.
+ */
+ @Test
+ public void testEquals_Object() {
+ System.out.println("equals");
+ Object x = null;
+ MethodType instance = mt_viS;
+ boolean expResult = false;
+ boolean result = instance.equals(x);
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of equals method, of class MethodType.
+ */
+ @Test
+ public void testEquals_MethodType() {
+ System.out.println("equals");
+ MethodType that = mt_viS;
+ MethodType instance = mt_viS;
+ boolean expResult = true;
+ boolean result = instance.equals(that);
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of hashCode method, of class MethodType.
+ */
+ @Test
+ public void testHashCode() {
+ System.out.println("hashCode");
+ MethodType instance = mt_viS;
+ ArrayList<Class<?>> types = new ArrayList<Class<?>>();
+ types.add(instance.returnType());
+ types.addAll(instance.parameterList());
+ int expResult = types.hashCode();
+ int result = instance.hashCode();
+ assertEquals(expResult, result);
+ }
+
+ /**
+ * Test of toString method, of class MethodType.
+ */
+ @Test
+ public void testToString() {
+ System.out.println("toString");
+ MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi};
+ //String expResult = "void[int, class java.lang.String]";
+ String[] expResults = {
+ "(int,String)void",
+ "(Object,Object)Object",
+ "()void",
+ "()Object",
+ "(String,Integer)int",
+ "(String,int)Integer",
+ "(String,Integer)Integer",
+ "(String,int)int"
+ };
+ for (int i = 0; i < instances.length; i++) {
+ MethodType instance = instances[i];
+ String result = instance.toString();
+ System.out.println("#"+i+":"+result);
+ assertEquals("#"+i, expResults[i], result);
+ }
+ }
+
+ private static byte[] writeSerial(Object x) throws java.io.IOException {
+ try (java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(bout)
+ ) {
+ out.writeObject(x);
+ out.flush();
+ return bout.toByteArray();
+ }
+ }
+ private static Object readSerial(byte[] wire) throws java.io.IOException, ClassNotFoundException {
+ try (java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(wire);
+ java.io.ObjectInputStream in = new java.io.ObjectInputStream(bin)) {
+ return in.readObject();
+ }
+ }
+ private static void testSerializedEquality(Object x) throws java.io.IOException, ClassNotFoundException {
+ if (x instanceof Object[])
+ x = Arrays.asList((Object[]) x); // has proper equals method
+ byte[] wire = writeSerial(x);
+ Object y = readSerial(wire);
+ assertEquals(x, y);
+ }
+
+ /** Test (de-)serialization. */
+ @Test
+ public void testSerialization() throws Throwable {
+ System.out.println("serialization");
+ for (MethodType mt : GALLERY) {
+ testSerializedEquality(mt);
+ }
+ testSerializedEquality(GALLERY);
+
+ // Make a list of mixed objects:
+ List<Object> stuff = new ArrayList<>();
+ Collections.addAll(stuff, GALLERY); // copy #1
+ Object[] triples = Arrays.copyOfRange(GALLERY, 0, GALLERY.length/2);
+ Collections.addAll(stuff, triples); // copy #3 (partial)
+ for (MethodType mt : GALLERY) {
+ Collections.addAll(stuff, mt.parameterArray());
+ }
+ Collections.shuffle(stuff, new Random(292));
+ Collections.addAll(stuff, GALLERY); // copy #2
+ testSerializedEquality(stuff);
+ }
+
+ /** Test serialization formats. */
+ @Test
+ public void testPortableSerialFormat() throws Throwable {
+ System.out.println("portable serial format");
+ Object[][] cases = {
+ { mt_vv, new byte[] { // ()void
+ (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x64, (byte)0x79, (byte)0x6e,
+ (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
+ (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x6f, (byte)0x69, (byte)0x64,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00,
+ (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e,
+ (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61,
+ (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab, (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb,
+ (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
+ } },
+ { mt_OO, new byte[] { // (Object)Object
+ (byte)0xac, (byte)0xed, (byte)0x00, (byte)0x05, (byte)0x73, (byte)0x72, (byte)0x00, (byte)0x13,
+ (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x64, (byte)0x79, (byte)0x6e,
+ (byte)0x2e, (byte)0x4d, (byte)0x65, (byte)0x74, (byte)0x68, (byte)0x6f, (byte)0x64, (byte)0x54,
+ (byte)0x79, (byte)0x70, (byte)0x65, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x01, (byte)0x24, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70,
+ (byte)0x76, (byte)0x72, (byte)0x00, (byte)0x10, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61,
+ (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2e, (byte)0x4f, (byte)0x62,
+ (byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x78,
+ (byte)0x70, (byte)0x75, (byte)0x72, (byte)0x00, (byte)0x12, (byte)0x5b, (byte)0x4c, (byte)0x6a,
+ (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2e, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67,
+ (byte)0x2e, (byte)0x43, (byte)0x6c, (byte)0x61, (byte)0x73, (byte)0x73, (byte)0x3b, (byte)0xab,
+ (byte)0x16, (byte)0xd7, (byte)0xae, (byte)0xcb, (byte)0xcd, (byte)0x5a, (byte)0x99, (byte)0x02,
+ (byte)0x00, (byte)0x00, (byte)0x78, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
+ (byte)0x71, (byte)0x00, (byte)0x7e, (byte)0x00, (byte)0x03, (byte)0x78,
+ } },
+ };
+ boolean generateData = false;
+ //generateData = true;
+ for (Object[] c : cases) {
+ MethodType mt = (MethodType) c[0];
+ System.out.println("deserialize "+mt);
+ byte[] wire = (byte[]) c[1];
+ if (generateData) {
+ wire = writeSerial(mt);
+ final String INDENT = " ";
+ System.out.print("{ // "+mt);
+ for (int i = 0; i < wire.length; i++) {
+ if (i % 8 == 0) { System.out.println(); System.out.print(INDENT+" "); }
+ String hex = Integer.toHexString(wire[i] & 0xFF);
+ if (hex.length() == 1) hex = "0"+hex;
+ System.out.print(" (byte)0x"+hex+",");
+ }
+ System.out.println();
+ System.out.println(INDENT+"}");
+ System.out.flush();
+ }
+ Object decode;
+ try {
+ decode = readSerial(wire);
+ } catch (Exception ex) {
+ decode = ex; // oops!
+ }
+ assertEquals(mt, decode);
+ }
+ }
+}
--- a/jdk/test/java/dyn/indify/Indify.java Thu Feb 24 15:16:02 2011 -0800
+++ b/jdk/test/java/dyn/indify/Indify.java Fri Feb 25 12:48:18 2011 -0800
@@ -98,8 +98,9 @@
(same output as above)
* </pre></blockquote>
* <p>
- * Until the format of {@code CONSTANT_InvokeDynamic} entries is finalized,
- * the {@code --transitionalJSR292} switch is recommended (and turned on by default).
+ * Before OpenJDK build b123, the format of {@code CONSTANT_InvokeDynamic} is in transition,
+ * and the switch {@code --transitionalJSR292=yes} is recommended.
+ * It is turned <em>off</em> by default, but users of earlier builds may need to turn it on.
* <p>
* A version of this transformation built on top of <a href="http://asm.ow2.org/">http://asm.ow2.org/</a> would be welcome.
* @author John Rose
@@ -116,7 +117,7 @@
public boolean overwrite = false;
public boolean quiet = false;
public boolean verbose = false;
- public boolean transitionalJSR292 = true; // default to false later
+ public boolean transitionalJSR292 = false; // final version is distributed
public boolean all = false;
public int verifySpecifierCount = -1;
@@ -158,6 +159,7 @@
av = avl.toArray(new String[0]);
Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader());
java.lang.reflect.Method main = mainClass.getMethod("main", String[].class);
+ try { main.setAccessible(true); } catch (SecurityException ex) { }
main.invoke(null, (Object) av);
}
@@ -223,8 +225,8 @@
private boolean booleanOption(String s) {
if (s == null) return true;
switch (s) {
- case "true": case "yes": case "1": return true;
- case "false": case "no": case "0": return false;
+ case "true": case "yes": case "on": case "1": return true;
+ case "false": case "no": case "off": case "0": return false;
}
throw new IllegalArgumentException("unrecognized boolean flag="+s);
}
@@ -284,7 +286,7 @@
}
File classPathFile(File pathDir, String className) {
- String qualname = className+".class";
+ String qualname = className.replace('.','/')+".class";
qualname = qualname.replace('/', File.separatorChar);
return new File(pathDir, qualname);
}
@@ -339,6 +341,7 @@
private File findClassInPath(String name) {
for (String s : classpath) {
File f = classPathFile(new File(s), name);
+ //System.out.println("Checking for "+f);
if (f.exists() && f.canRead()) {
return f;
}
@@ -353,11 +356,11 @@
}
}
private Class<?> transformAndLoadClass(File f) throws ClassNotFoundException, IOException {
- if (verbose) System.out.println("Loading class from "+f);
+ if (verbose) System.err.println("Loading class from "+f);
ClassFile cf = new ClassFile(f);
Logic logic = new Logic(cf);
boolean changed = logic.transform();
- if (verbose && !changed) System.out.println("(no change)");
+ if (verbose && !changed) System.err.println("(no change)");
logic.reportPatternMethods(!verbose, keepgoing);
byte[] bytes = cf.toByteArray();
return defineClass(null, bytes, 0, bytes.length);
@@ -525,6 +528,7 @@
throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
}
}
+ if (!quiet) System.err.flush();
}
// mark constant pool entries according to participation in patterns
@@ -696,6 +700,18 @@
args.clear();
break;
+ case opc_new:
+ {
+ String type = pool.getString(CONSTANT_Class, (short)i.u2At(1));
+ //System.out.println("new "+type);
+ switch (type) {
+ case "java/lang/StringBuilder":
+ jvm.push("StringBuilder");
+ continue decode; // go to next instruction
+ }
+ break decode; // bail out
+ }
+
case opc_getstatic:
{
// int.class compiles to getstatic Integer.TYPE
@@ -732,8 +748,9 @@
case opc_invokestatic:
case opc_invokevirtual:
+ case opc_invokespecial:
{
- boolean hasRecv = (bc == opc_invokevirtual);
+ boolean hasRecv = (bc != opc_invokestatic);
int methi = i.u2At(1);
char mark = poolMarks[methi];
Short[] ref = pool.getMemberRef((short)methi);
@@ -770,6 +787,7 @@
if (mark == 'T' || mark == 'H' || mark == 'I') {
ownMethod = findMember(cf.methods, ref[1], ref[2]);
}
+ //if (intrinsic != null) System.out.println("intrinsic = "+intrinsic);
switch (intrinsic == null ? "" : intrinsic) {
case "fromMethodDescriptorString":
con = makeMethodTypeCon(args.get(0));
@@ -860,6 +878,15 @@
}
}
break decode;
+ case "StringBuilder.append":
+ // allow calls like ("value = "+x)
+ removeEmptyJVMSlots(args);
+ args.subList(1, args.size()).clear();
+ continue;
+ case "StringBuilder.toString":
+ args.clear();
+ args.add(intrinsic);
+ continue;
}
if (!hasRecv && ownMethod != null && patternMark != 0) {
con = constants.get(ownMethod);
@@ -1506,6 +1533,7 @@
out.write(bytes);
} else {
trueSize = flatten(out);
+ //if (!(item instanceof Code)) System.err.println("wrote complex attr name="+(int)(char)name+" size="+trueSize+" data="+Arrays.toString(flatten()));
}
if (trueSize != size && size >= 0)
System.err.println("warning: attribute size changed "+size+" to "+trueSize);
@@ -1525,7 +1553,7 @@
}
public List<Attr> attrs() { return null; } // Code overrides this
public byte[] flatten() {
- ByteArrayOutputStream buf = new ByteArrayOutputStream(size);
+ ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size));
flatten(buf);
return buf.toByteArray();
}
@@ -1642,6 +1670,7 @@
opc_invokestatic = 184,
opc_invokeinterface = 185,
opc_invokedynamic = 186,
+ opc_new = 187,
opc_anewarray = 189,
opc_checkcast = 192,
opc_ifnull = 198,