Mercurial > hg > icedtea9-forest > jdk
changeset 3404:869190935eed jdk7-b123
Merge
author | igor |
---|---|
date | Tue, 21 Dec 2010 18:45:45 -0800 |
parents | 36898b974d28 (current diff) 2dbd18b83bad (diff) |
children | 83480217896c 403a88eeac15 |
files | src/share/classes/java/dyn/BootstrapMethod.java src/share/classes/java/dyn/LinkagePermission.java src/share/classes/java/dyn/MethodHandleProvider.java src/share/classes/sun/dyn/JavaMethodHandle.java test/java/dyn/JavaDocExamples.java |
diffstat | 41 files changed, 5514 insertions(+), 2012 deletions(-) [+] |
line wrap: on
line diff
--- a/make/java/dyn/Makefile Tue Dec 21 15:27:55 2010 -0800 +++ b/make/java/dyn/Makefile Tue Dec 21 18:45:45 2010 -0800 @@ -36,9 +36,7 @@ LANGUAGE_VERSION = -source 7 CLASS_VERSION = -target 7 -# Actually, it will be less disruptive to compile with the same -# -target option as the rest of the system, and just turn on -# the specific compiler option we need here: -OTHER_JAVACFLAGS = -XDinvokedynamic +# Tell the compiler not to accept transitional forms. +OTHER_JAVACFLAGS = -XDallowTransitionalJSR292=no include $(BUILDDIR)/common/Classes.gmk
--- a/src/share/classes/java/dyn/BootstrapMethod.java Tue Dec 21 15:27:55 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +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; - -import java.lang.annotation.*; - -/** - * Annotation on InvokeDynamic method calls which requests the JVM to use a specific - * <a href="package-summary.html#bsm">bootstrap method</a> - * to link the call. This annotation is not retained as such in the class file, - * but is transformed into a constant-pool entry for the invokedynamic instruction which - * specifies the desired bootstrap method. - * <p> - * If only the <code>value</code> is given, it must name a subclass of {@link CallSite} - * with a constructor which accepts a class, string, and method type. - * If the <code>value</code> and <code>name</code> are both given, there must be - * a static method in the given class of the given name which accepts a class, string, - * and method type, and returns a reference coercible to {@link CallSite}. - * <p> - * This annotation can be placed either on the return type of a single {@link InvokeDynamic} - * call (see examples) or else it can be placed on an enclosing class or method, where it - * determines a default bootstrap method for any {@link InvokeDynamic} calls which are not - * specifically annotated with a bootstrap method. - * Every {@link InvokeDynamic} call must be given a bootstrap method. - * <p> - * Examples: -<blockquote><pre> -@BootstrapMethod(value=MyLanguageRuntime.class, name="bootstrapDynamic") -String x = (String) InvokeDynamic.greet(); -//BSM => MyLanguageRuntime.bootstrapDynamic(Here.class, "greet", methodType(String.class)) -@BootstrapMethod(MyCallSite.class) -void example() throws Throwable { - InvokeDynamic.greet(); - //BSM => new MyCallSite(Here.class, "greet", methodType(void.class)) -} -</pre></blockquote> - * <p> - */ -@Target({ElementType.TYPE_USE, - // For defaulting every indy site within a class or method; cf. @SuppressWarnings: - ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR - }) -@Retention(RetentionPolicy.SOURCE) -public @interface BootstrapMethod { - /** The class containing the bootstrap method. */ - Class<?> value(); - - /** The name of the bootstrap method. - * If this is the empty string, an instance of the bootstrap class is created, - * and a constructor is invoked. - * Otherwise, there must be a static method of the required name. - */ - String name() default ""; // empty string denotes a constructor with 'new' - - /** The argument types of the bootstrap method, as passed out by the JVM. - * There is usually no reason to override the default. - */ - Class<?>[] arguments() default {Class.class, String.class, MethodType.class}; -}
--- a/src/share/classes/java/dyn/CallSite.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/CallSite.java Tue Dec 21 18:45:45 2010 -0800 @@ -26,40 +26,45 @@ package java.dyn; import sun.dyn.*; +import sun.dyn.empty.Empty; +import sun.misc.Unsafe; import java.util.Collection; /** * A {@code CallSite} is a holder for a variable {@link MethodHandle}, * which is called its {@code target}. - * Every call to a {@code CallSite} is delegated to the site's current target. + * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates + * all calls to the site's current target. + * A {@code CallSite} may be associated with several {@code invokedynamic} + * instructions, or it may be "free floating", associated with none. + * In any case, it may be invoked through an associated method handle + * called its {@linkplain #dynamicInvoker dynamic invoker}. * <p> - * A call site is initially created in an <em>unlinked</em> state, - * which is distinguished by a null target variable. - * Before the call site may be invoked (and before certain other - * operations are attempted), the call site must be linked to - * a non-null target. + * {@code CallSite} is an abstract class which does not allow + * direct subclassing by users. It has three immediate, + * concrete subclasses that may be either instantiated or subclassed. + * <ul> + * <li>If a mutable target is not required, an {@code invokedynamic} instruction + * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}. + * <li>If a mutable target is required which has volatile variable semantics, + * because updates to the target must be immediately and reliably witnessed by other threads, + * a {@linkplain VolatileCallSite volatile call site} may be used. + * <li>Otherwise, if a mutable target is required, + * a {@linkplain MutableCallSite mutable call site} may be used. + * </ul> * <p> - * A call site may be <em>relinked</em> by changing its target. - * The new target must be non-null and must have the same - * {@linkplain MethodHandle#type() type} + * A non-constant call site may be <em>relinked</em> by changing its target. + * The new target must have the same {@linkplain MethodHandle#type() type} * as the previous target. * Thus, though a call site can be relinked to a series of * successive targets, it cannot change its type. * <p> - * Linkage happens once in the lifetime of any given {@code CallSite} object. - * Because of call site invalidation, this linkage can be repeated for - * a single {@code invokedynamic} instruction, with multiple {@code CallSite} objects. - * When a {@code CallSite} is unlinked from an {@code invokedynamic} instruction, - * the instruction is reset so that it is no longer associated with - * the {@code CallSite} object, but the {@code CallSite} does not change - * state. - * <p> * Here is a sample use of call sites and bootstrap methods which links every * dynamic call site to print its arguments: <blockquote><pre><!-- see indy-demo/src/PrintArgsDemo.java --> -@BootstrapMethod(value=PrintArgsDemo.class, name="bootstrapDynamic") static void test() throws Throwable { - InvokeDynamic.baz("baz arg", 2, 3.14); + // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION + InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14); } private static void printArgs(Object... args) { System.out.println(java.util.Arrays.deepToString(args)); @@ -71,16 +76,15 @@ printArgs = lookup.findStatic(thisClass, "printArgs", MethodType.methodType(void.class, Object[].class)); } -private static CallSite bootstrapDynamic(Class caller, String name, MethodType type) { +private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) { // ignore caller and name, but match the type: - return new CallSite(MethodHandles.collectArguments(printArgs, type)); + return new ConstantCallSite(MethodHandles.collectArguments(printArgs, type)); } </pre></blockquote> * @author John Rose, JSR 292 EG */ -public class CallSite - implements MethodHandleProvider -{ +abstract +public class CallSite { private static final Access IMPL_TOKEN = Access.getToken(); // Fields used only by the JVM. Do not use or change. @@ -88,61 +92,47 @@ private int vmindex; // supplied by the JVM (BCI within calling method) // The actual payload of this call site: - private MethodHandle target; + /*package-private*/ + MethodHandle target; // Remove this field for PFD and delete deprecated methods: private MemberName calleeNameRemoveForPFD; /** - * Make a blank call site object. - * Before it is returned from a bootstrap method, this {@code CallSite} object - * must be provided with - * a target method via a call to {@link CallSite#setTarget(MethodHandle) setTarget}, - * or by a subclass override of {@link CallSite#initialTarget(Class,String,MethodType) initialTarget}. + * Make a blank call site object with the given method type. + * An initial target method is supplied which will throw + * an {@link IllegalStateException} if called. + * <p> + * Before this {@code CallSite} object is returned from a bootstrap method, + * it is usually provided with a more useful target method, + * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}. + * @throws NullPointerException if the proposed type is null */ - public CallSite() { + /*package-private*/ + CallSite(MethodType type) { + target = MethodHandles.invokers(type).uninitializedCallSite(); } /** * Make a blank call site object, possibly equipped with an initial target method handle. - * The initial target reference may be null, in which case the {@code CallSite} object - * must be provided with a target method via a call to {@link CallSite#setTarget}, - * or by a subclass override of {@link CallSite#initialTarget}. - * @param target the method handle which will be the initial target of the call site, or null if there is none yet + * @param target the method handle which will be the initial target of the call site + * @throws NullPointerException if the proposed target is null */ - public CallSite(MethodHandle target) { + /*package-private*/ + CallSite(MethodHandle target) { + target.type(); // null check this.target = target; } - /** @deprecated transitional form defined in EDR but removed in PFD */ - public CallSite(Class<?> caller, String name, MethodType type) { - this.calleeNameRemoveForPFD = new MemberName(caller, name, type); - } - /** @deprecated transitional form defined in EDR but removed in PFD */ - public Class<?> callerClass() { - MemberName callee = this.calleeNameRemoveForPFD; - return callee == null ? null : callee.getDeclaringClass(); - } - /** @deprecated transitional form defined in EDR but removed in PFD */ - public String name() { - MemberName callee = this.calleeNameRemoveForPFD; - return callee == null ? null : callee.getName(); - } - /** @deprecated transitional form defined in EDR but removed in PFD */ + /** + * Report the type of this call site's target. + * Although targets may change, the call site's type can never change. + * 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 + */ public MethodType type() { - MemberName callee = this.calleeNameRemoveForPFD; - return callee == null ? (target == null ? null : target.type()) : callee.getMethodType(); - } - /** @deprecated transitional form defined in EDR but removed in PFD */ - protected MethodHandle initialTarget() { - return initialTarget(callerClass(), name(), type()); - } - - /** Report if the JVM has linked this {@code CallSite} object to a dynamic call site instruction. - * Once it is linked, it is never unlinked. - */ - private boolean isLinked() { - return vmmethod != null; + return target.type(); } /** Called from JVM (or low-level Java code) after the BSM returns the newly created CallSite. @@ -152,68 +142,66 @@ MethodType type, MemberName callerMethod, int callerBCI) { - if (this.isLinked()) { + if (this.vmmethod != null) { + // FIXME throw new InvokeDynamicBootstrapError("call site has already been linked to an invokedynamic instruction"); } - MethodHandle target = this.target; - if (target == null) { - this.target = target = this.initialTarget(callerMethod.getDeclaringClass(), name, type); - } - if (!target.type().equals(type)) { + if (!this.type().equals(type)) { throw wrongTargetType(target, type); } this.vmindex = callerBCI; this.vmmethod = callerMethod; - assert(this.isLinked()); } /** - * Just after a call site is created by a bootstrap method handle, - * if the target has not been initialized by the factory method itself, - * the method {@code initialTarget} is called to produce an initial - * non-null target. (Live call sites must never have null targets.) - * <p> - * The arguments are the same as those passed to the bootstrap method. - * Thus, a bootstrap method is free to ignore the arguments and simply - * create a "blank" {@code CallSite} object of an appropriate subclass. + * Report the current linkage state of the call site, a value which may change over time. * <p> - * If the bootstrap method itself does not initialize the call site, - * this method must be overridden, because it just raises an - * {@code InvokeDynamicBootstrapError}, which in turn causes the - * linkage of the {@code invokedynamic} instruction to terminate - * abnormally. - * @deprecated transitional form defined in EDR but removed in PFD - */ - protected MethodHandle initialTarget(Class<?> callerClass, String name, MethodType type) { - throw new InvokeDynamicBootstrapError("target must be initialized before call site is linked: "+name+type); - } - - /** - * Report the current linkage state of the call site. (This is mutable.) - * The value may not be null after the {@code CallSite} object is returned - * from the bootstrap method of the {@code invokedynamic} instruction. - * When an {@code invokedynamic} instruction is executed, the target method - * of its associated {@code call site} object is invoked directly, - * as if via {@link MethodHandle}{@code .invoke}. + * 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> - * The interactions of {@code getTarget} with memory are the same + * 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. - * @return the current linkage state of the call site + * <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. + * @return the current linkage state of the call site, its target method handle + * @see ConstantCallSite + * @see VolatileCallSite * @see #setTarget */ - public MethodHandle getTarget() { + public final MethodHandle getTarget() { + return getTarget0(); + } + + /** + * 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. * <p> - * The interactions of {@code setTarget} with memory are the same + * 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> @@ -224,43 +212,32 @@ * at any given call site. * @param newTarget the new target * @throws NullPointerException if the proposed new target is null - * @throws WrongMethodTypeException if the call site is linked and the proposed new target + * @throws WrongMethodTypeException if the proposed new target * has a method type that differs from the previous target + * @throws UnsupportedOperationException if the call site is + * in fact a {@link ConstantCallSite} */ public void setTarget(MethodHandle newTarget) { + checkTargetChange(this.target, newTarget); + setTargetNormal(newTarget); + } + + void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) { + MethodType oldType = oldTarget.type(); MethodType newType = newTarget.type(); // null check! - MethodHandle oldTarget = this.target; - if (oldTarget == null) { - // CallSite is not yet linked. - assert(!isLinked()); - this.target = newTarget; // might be null! - return; - } - MethodType oldType = oldTarget.type(); - if (!newTarget.type().equals(oldType)) + if (!newType.equals(oldType)) throw wrongTargetType(newTarget, oldType); - if (oldTarget != newTarget) - CallSiteImpl.setCallSiteTarget(IMPL_TOKEN, this, newTarget); } private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) { - return new WrongMethodTypeException(String.valueOf(target)+target.type()+" should be of type "+type); - } - - /** Produce a printed representation that displays information about this call site - * that may be useful to the human reader. - */ - @Override - public String toString() { - return "CallSite"+(target == null ? "" : target.type()); + return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type); } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce a method handle equivalent to an invokedynamic instruction * which has been linked to this call site. - * <p>If this call site is a {@link ConstantCallSite}, this method - * simply returns the call site's target, since that will not change. + * <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> * MethodHandle getTarget, invoker, result; @@ -271,8 +248,9 @@ * @return a method handle which always invokes this call site's current target */ public final MethodHandle dynamicInvoker() { - if (this instanceof ConstantCallSite) - return getTarget(); // will not change dynamically + if (this instanceof ConstantCallSite) { + return getTarget0(); // will not change dynamically + } MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, GET_TARGET, this); MethodHandle invoker = MethodHandles.exactInvoker(this.type()); return MethodHandles.foldArguments(invoker, getTarget); @@ -287,9 +265,34 @@ } } - /** Implementation of {@link MethodHandleProvider} which returns {@code this.dynamicInvoker()}. */ - public final MethodHandle asMethodHandle() { return dynamicInvoker(); } + /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */ + /*package-private*/ + static Empty uninitializedCallSite() { + throw new IllegalStateException("uninitialized call site"); + } + + // unsafe stuff: + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long TARGET_OFFSET; + + static { + try { + TARGET_OFFSET = unsafe.objectFieldOffset(CallSite.class.getDeclaredField("target")); + } catch (Exception ex) { throw new Error(ex); } + } - /** Implementation of {@link MethodHandleProvider}, which returns {@code this.dynamicInvoker().asType(type)}. */ - public final MethodHandle asMethodHandle(MethodType type) { return dynamicInvoker().asType(type); } + /*package-private*/ + void setTargetNormal(MethodHandle newTarget) { + target = newTarget; + //CallSiteImpl.setCallSiteTarget(IMPL_TOKEN, this, newTarget); + } + /*package-private*/ + MethodHandle getTargetVolatile() { + return (MethodHandle) unsafe.getObjectVolatile(this, TARGET_OFFSET); + } + /*package-private*/ + void setTargetVolatile(MethodHandle newTarget) { + unsafe.putObjectVolatile(this, TARGET_OFFSET, newTarget); + //CallSiteImpl.setCallSiteTarget(IMPL_TOKEN, this, newTarget); + } }
--- a/src/share/classes/java/dyn/ClassValue.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/ClassValue.java Tue Dec 21 18:45:45 2010 -0800 @@ -28,44 +28,78 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.lang.reflect.UndeclaredThrowableException; /** * Lazily associate a computed value with (potentially) every class. * @author John Rose, JSR 292 EG */ -public abstract class ClassValue<T> { +public class ClassValue<T> { /** * Compute the given class's derived value for this {@code ClassValue}. * <p> * This method will be invoked within the first thread that accesses - * the value with the {@link #get}. + * the value with the {@link #get get} method. * <p> * Normally, this method is invoked at most once per class, - * but it may be invoked again in case of subsequent invocations - * of {@link #remove} followed by {@link #get}. + * but it may be invoked again if there has been a call to + * {@link #remove remove}. + * <p> + * If there is no override from a subclass, this method returns + * the result of applying the {@code ClassValue}'s {@code computeValue} + * method handle, which was supplied at construction time. * - * @return the computed value for this thread-local + * @return the newly computed value associated with this {@code ClassValue}, for the given class or interface + * @throws UndeclaredThrowableException if the {@code computeValue} method handle invocation throws something other than a {@code RuntimeException} or {@code Error} + * @throws UnsupportedOperationException if the {@code computeValue} method handle is null (subclasses must override) */ - protected abstract T computeValue(Class<?> type); + protected T computeValue(Class<?> type) { + if (computeValue == null) + return null; + try { + return (T) (Object) computeValue.invokeGeneric(type); + } catch (Throwable ex) { + if (ex instanceof Error) throw (Error) ex; + if (ex instanceof RuntimeException) throw (RuntimeException) ex; + throw new UndeclaredThrowableException(ex); + } + } + + private final MethodHandle computeValue; /** * Creates a new class value. + * Subclasses which use this constructor must override + * the {@link #computeValue computeValue} method, + * since the default {@code computeValue} method requires a method handle, + * which this constructor does not provide. */ protected ClassValue() { + this.computeValue = null; + } + + /** + * Creates a new class value, whose {@link #computeValue computeValue} method + * will return the result of {@code computeValue.invokeGeneric(type)}. + * @throws NullPointerException if the method handle parameter is null + */ + public ClassValue(MethodHandle computeValue) { + computeValue.getClass(); // trigger NPE if null + this.computeValue = computeValue; } /** * Returns the value for the given class. * If no value has yet been computed, it is obtained by - * by an invocation of the {@link #computeValue} method. + * by an invocation of the {@link #computeValue computeValue} method. * <p> * The actual installation of the value on the class - * is performed while the class's synchronization lock - * is held. At that point, if racing threads have + * is performed atomically. + * At that point, if racing threads have * computed values, one is chosen, and returned to * all the racing threads. * - * @return the current thread's value of this thread-local + * @return the current value associated with this {@code ClassValue}, for the given class or interface */ public T get(Class<?> type) { ClassValueMap map = getMap(type); @@ -81,9 +115,16 @@ /** * Removes the associated value for the given class. * If this value is subsequently {@linkplain #get read} for the same class, - * its value will be reinitialized by invoking its {@link #computeValue} method. + * its value will be reinitialized by invoking its {@link #computeValue computeValue} method. * This may result in an additional invocation of the - * {@code computeValue} method for the given class. + * {@code computeValue computeValue} method for the given class. + * <p> + * If racing threads perform a combination of {@code get} and {@code remove} calls, + * the calls are serialized. + * A value produced by a call to {@code computeValue} will be discarded, if + * the corresponding {@code get} call was followed by a {@code remove} call + * before the {@code computeValue} could complete. + * In such a case, the {@code get} call will re-invoke {@code computeValue}. */ public void remove(Class<?> type) { ClassValueMap map = getMap(type); @@ -118,6 +159,7 @@ // Warm up the table with a null entry. map.preInitializeEntry(this); } + STORE_BARRIER.lazySet(0); // All stores pending from table expansion are completed. synchronized (map) { value = (T) map.initializeEntry(this, value);
--- a/src/share/classes/java/dyn/ConstantCallSite.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/ConstantCallSite.java Tue Dec 21 18:45:45 2010 -0800 @@ -27,17 +27,21 @@ /** * A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed. - * The only way to relink an {@code invokedynamic} instruction bound to a {@code ConstantCallSite} is - * to invalidate the instruction as a whole. + * An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently + * bound to the call site's target. * @author John Rose, JSR 292 EG */ public class ConstantCallSite extends CallSite { - /** Create a call site with a permanent target. */ + /** Create a call site with a permanent target. + * @throws NullPointerException if the proposed target is null + */ public ConstantCallSite(MethodHandle target) { super(target); } - /** Throw an {@link IllegalArgumentException}, because this kind of call site cannot change its target. */ + /** + * Throw an {@link UnsupportedOperationException}, because this kind of call site cannot change its target. + */ @Override public final void setTarget(MethodHandle ignore) { - throw new IllegalArgumentException("ConstantCallSite"); + throw new UnsupportedOperationException("ConstantCallSite"); } }
--- a/src/share/classes/java/dyn/InvokeDynamic.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/InvokeDynamic.java Tue Dec 21 18:45:45 2010 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -26,55 +26,8 @@ package java.dyn; /** - * {@code InvokeDynamic} is a class with neither methods nor instances, - * which serves only as a syntactic marker in Java source code for - * an {@code invokedynamic} instruction. - * (See <a href="package-summary.html#jvm_mods">the package information</a> for specifics on this instruction.) - * <p> - * The {@code invokedynamic} instruction is incomplete without a target method. - * The target method is a property of the reified {@linkplain CallSite call site object} - * which is linked to each active {@code invokedynamic} instruction. - * The call site object is initially produced by a - * {@linkplain BootstrapMethod bootstrap method} - * associated with the class whose bytecodes include the dynamic call site. - * <p> - * The type {@code InvokeDynamic} has no particular meaning as a - * class or interface supertype, or an object type; it can never be instantiated. - * Logically, it denotes a source of all dynamically typed methods. - * It may be viewed as a pure syntactic marker of static calls. - * It may be imported for ease of use. - * <p> - * Here are some examples: -<blockquote><pre><!-- see indy-demo/src/JavaDocExamples.java --> -@BootstrapMethod(value=Here.class, name="bootstrapDynamic") -static void example() throws Throwable { - Object x; String s; int i; - x = InvokeDynamic.greet("world"); // greet(Ljava/lang/String;)Ljava/lang/Object; - s = (String) InvokeDynamic.hail(x); // hail(Ljava/lang/Object;)Ljava/lang/String; - InvokeDynamic.cogito(); // cogito()V - i = (int) InvokeDynamic.#"op:+"(2, 3); // "op:+"(II)I + * This is a place-holder class. Some HotSpot implementations need to see it. + */ +final class InvokeDynamic { + private InvokeDynamic() { throw new InternalError(); } // do not instantiate } -static MethodHandle bootstrapDynamic(Class caller, String name, MethodType type) { ... } -</pre></blockquote> - * Each of the above calls generates a single invokedynamic instruction - * with the name-and-type descriptors indicated in the comments. - * <p> - * The argument types are taken directly from the actual arguments, - * while the return type corresponds to the target of the assignment. - * (Currently, the return type must be given as a false type parameter. - * This type parameter is an irregular use of the generic type syntax, - * and is likely to change in favor of a convention based on target typing.) - * <p> - * The final example uses a special syntax for uttering non-Java names. - * Any name legal to the JVM may be given between the double quotes. - * <p> - * None of these calls is complete without a bootstrap method, - * which must be declared for the enclosing class or method. - * @author John Rose, JSR 292 EG - */ -@MethodHandle.PolymorphicSignature -public final class InvokeDynamic { - private InvokeDynamic() { throw new InternalError(); } // do not instantiate - - // no statically defined static methods -}
--- a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java Tue Dec 21 18:45:45 2010 -0800 @@ -67,4 +67,16 @@ public InvokeDynamicBootstrapError(String s, Throwable cause) { super(s, cause); } + + /** + * Constructs a {@code InvokeDynamicBootstrapError} with the specified + * cause. + * + * @param cause the cause, may be {@code null}. + */ + public InvokeDynamicBootstrapError(Throwable cause) { + // cf. Throwable(Throwable cause) constructor. + super(cause == null ? null : cause.toString()); + initCause(cause); + } }
--- a/src/share/classes/java/dyn/Linkage.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/Linkage.java Tue Dec 21 18:45:45 2010 -0800 @@ -29,15 +29,16 @@ import java.util.WeakHashMap; import sun.dyn.Access; import sun.dyn.MethodHandleImpl; +import sun.dyn.util.VerifyAccess; import sun.reflect.Reflection; -import static sun.dyn.util.VerifyAccess.checkBootstrapPrivilege; import static sun.dyn.MemberName.newIllegalArgumentException; /** - * This class consists exclusively of static methods that control - * the linkage of {@code invokedynamic} instructions, and specifically - * their reification as {@link CallSite} objects. + * <em>CLASS WILL BE REMOVED FOR PFD:</em> + * Static routines for controlling invokedynamic behavior. + * Replaced by non-static APIs. * @author John Rose, JSR 292 EG + * @deprecated This class will be removed in the Public Final Draft. */ public class Linkage { private static final Access IMPL_TOKEN = Access.getToken(); @@ -45,68 +46,24 @@ private Linkage() {} // do not instantiate /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * <em>METHOD WILL BE REMOVED FOR PFD:</em> * Register a <em>bootstrap method</em> to use when linking dynamic call sites within * a given caller class. - * <p> - * A bootstrap method must be a method handle with a return type of {@link CallSite} - * and the following arguments: - * <ul> - * <li>the class containing the {@code invokedynamic} instruction, for which the bootstrap method was registered - * <li>the name of the method being invoked (a {@link String}) - * <li>the type of the method being invoked (a {@link MethodType}) - * </ul> - * The bootstrap method acts as a factory method which accepts the given arguments - * and returns a {@code CallSite} object (possibly of a subclass of {@code CallSite}). - * <p> - * The registration must take place exactly once, either before the class has begun - * being initialized, or from within the class's static initializer. - * Registration will fail with an exception if any of the following conditions hold: - * <ul> - * <li>The immediate caller of this method is in a different package than the given caller class, - * and there is a security manager, and its {@code checkPermission} call throws - * when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass). - * <li>The given caller class already has a bootstrap method registered. - * <li>The given caller class is already fully initialized. - * <li>The given caller class is in the process of initialization, in another thread. - * </ul> - * Because of these rules, a class may install its own bootstrap method in - * a static initializer. - * @param callerClass a class that may have {@code invokedynamic} sites - * @param bootstrapMethod the method to use to bootstrap all such sites - * @exception IllegalArgumentException if the class argument is null or - * a primitive class, or if the bootstrap method is the wrong type - * @exception IllegalStateException if the class already has a bootstrap - * method, or if the its static initializer has already run - * or is already running in another thread - * @exception SecurityException if there is a security manager installed, - * and a {@link LinkagePermission} check fails for "registerBootstrapMethod" - * @deprecated Use @{@link BootstrapMethod} annotations instead + * @deprecated Use @{@link BootstrapMethod} annotations instead. */ public static void registerBootstrapMethod(Class callerClass, MethodHandle bootstrapMethod) { Class callc = Reflection.getCallerClass(2); - checkBootstrapPrivilege(callc, callerClass, "registerBootstrapMethod"); - checkBSM(bootstrapMethod); + if (callc != null && !VerifyAccess.isSamePackage(callerClass, callc)) + throw new IllegalArgumentException("cannot set bootstrap method on "+callerClass); MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod); } - static private void checkBSM(MethodHandle mh) { - if (mh == null) throw newIllegalArgumentException("null bootstrap method"); - if (mh.type() == BOOTSTRAP_METHOD_TYPE) return; - throw new WrongMethodTypeException(mh.toString()); - } - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * <em>METHOD WILL BE REMOVED FOR PFD:</em> * Simplified version of {@code registerBootstrapMethod} for self-registration, * to be called from a static initializer. - * Finds a static method of the required type in the - * given runtime class, and installs it on the caller class. - * @throws NoSuchMethodException if there is no such method - * @throws IllegalStateException if the caller class's static initializer - * has already run, or is already running in another thread - * @deprecated Use @{@link BootstrapMethod} annotations instead + * @deprecated Use @{@link BootstrapMethod} annotations instead. */ public static void registerBootstrapMethod(Class<?> runtime, String name) { @@ -115,15 +72,9 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * <em>METHOD WILL BE REMOVED FOR PFD:</em> * Simplified version of {@code registerBootstrapMethod} for self-registration, - * to be called from a static initializer. - * Finds a static method of the required type in the - * caller class itself, and installs it on the caller class. - * @throws IllegalArgumentException if there is no such method - * @throws IllegalStateException if the caller class's static initializer - * has already run, or is already running in another thread - * @deprecated Use @{@link BootstrapMethod} annotations instead + * @deprecated Use @{@link BootstrapMethod} annotations instead. */ public static void registerBootstrapMethod(String name) { @@ -140,82 +91,33 @@ } catch (NoAccessException ex) { throw new IllegalArgumentException("no such bootstrap method in "+runtime+": "+name, ex); } - checkBSM(bootstrapMethod); MethodHandleImpl.registerBootstrap(IMPL_TOKEN, callerClass, bootstrapMethod); } - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * Report the bootstrap method registered for a given caller class. - * Returns null if the class has never yet registered a bootstrap method. - * Only callers privileged to set the bootstrap method may inquire - * about it, because a bootstrap method is potentially a back-door entry - * point into its class. - * @exception IllegalArgumentException if the argument is null or - * a primitive class - * @exception SecurityException if there is a security manager installed, - * and the immediate caller of this method is not in the same - * package as the caller class - * and a {@link LinkagePermission} check fails for "getBootstrapMethod" - * @deprecated - */ - public static - MethodHandle getBootstrapMethod(Class callerClass) { - Class callc = Reflection.getCallerClass(2); - checkBootstrapPrivilege(callc, callerClass, "getBootstrapMethod"); - return MethodHandleImpl.getBootstrap(IMPL_TOKEN, callerClass); - } - - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * The type of any bootstrap method is a three-argument method - * {@code (Class, String, MethodType)} returning a {@code CallSite}. - */ - public static final MethodType BOOTSTRAP_METHOD_TYPE + private static final MethodType BOOTSTRAP_METHOD_TYPE = MethodType.methodType(CallSite.class, Class.class, String.class, MethodType.class); /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * <em>METHOD WILL BE REMOVED FOR PFD:</em> * Invalidate all <code>invokedynamic</code> call sites everywhere. - * <p> - * When this method returns, every <code>invokedynamic</code> instruction - * will invoke its bootstrap method on next call. - * <p> - * It is unspecified whether call sites already known to the Java - * code will continue to be associated with <code>invokedynamic</code> - * instructions. If any call site is still so associated, its - * {@link CallSite#getTarget()} method is guaranteed to return null - * the invalidation operation completes. - * <p> - * Invalidation operations are likely to be slow. Use them sparingly. + * @deprecated Use {@linkplain CallSite#setTarget call site target setting} + * and {@link VolatileCallSite#invalidateAll call site invalidation} instead. */ public static Object invalidateAll() { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPermission(new LinkagePermission("invalidateAll")); - } - throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException(); } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * <em>METHOD WILL BE REMOVED FOR PFD:</em> * Invalidate all {@code invokedynamic} call sites in the bytecodes * of any methods of the given class. - * <p> - * When this method returns, every matching <code>invokedynamic</code> - * instruction will invoke its bootstrap method on next call. - * <p> - * For additional semantics of call site invalidation, - * see {@link #invalidateAll()}. + * @deprecated Use {@linkplain CallSite#setTarget call site target setting} + * and {@link VolatileCallSite#invalidateAll call site invalidation} instead. */ public static Object invalidateCallerClass(Class<?> callerClass) { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPermission(new LinkagePermission("invalidateAll", callerClass)); - } - throw new UnsupportedOperationException("NYI"); + throw new UnsupportedOperationException(); } }
--- a/src/share/classes/java/dyn/LinkagePermission.java Tue Dec 21 15:27:55 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +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; - -import java.security.*; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.StringTokenizer; - -/** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * This class is for managing runtime permission checking for - * operations performed by methods in the {@link Linkage} class. - * Like a {@link RuntimePermission}, on which it is modeled, - * a {@code LinkagePermission} contains a target name but - * no actions list; you either have the named permission - * or you don't. - * <p> - * The following table lists all the possible {@code LinkagePermission} target names, - * and for each provides a description of what the permission allows - * and a discussion of the risks of granting code the permission. - * <p> - * - * <table border=1 cellpadding=5 summary="permission target name, - * what the target allows,and associated risks"> - * <tr> - * <th>Permission Target Name</th> - * <th>What the Permission Allows</th> - * <th>Risks of Allowing this Permission</th> - * </tr> - * - * <tr> - * <td>invalidateAll</td> - * <td>Force the relinking of invokedynamic call sites everywhere.</td> - * <td>This could allow an attacker to slow down the system, - * or perhaps expose timing bugs in a dynamic language implementations, - * by forcing redundant relinking operations.</td> - * </tr> - * - * - * <tr> - * <td>invalidateCallerClass.{class name}</td> - * <td>Force the relinking of invokedynamic call sites in the given class.</td> - * <td>See {@code invalidateAll}.</td> - * </tr> - * </table> - * <p>ISSUE: Is this still needed? - * - * @see java.lang.RuntimePermission - * @see java.lang.SecurityManager - * - * @author John Rose, JSR 292 EG - */ - -public final class LinkagePermission extends BasicPermission { - private static final long serialVersionUID = 292L; - - /** - * Create a new LinkagePermission with the given name. - * The name is the symbolic name of the LinkagePermission, such as - * "invalidateCallerClass.*", etc. An asterisk - * may appear at the end of the name, following a ".", or by itself, to - * signify a wildcard match. - * - * @param name the name of the LinkagePermission - */ - public LinkagePermission(String name) { - super(name); - } - - /** - * Create a new LinkagePermission with the given name on the given class. - * Equivalent to {@code LinkagePermission(name+"."+clazz.getName())}. - * - * @param name the name of the LinkagePermission - * @param clazz the class affected by the permission - */ - public LinkagePermission(String name, Class<?> clazz) { - super(name + "." + clazz.getName()); - } -}
--- a/src/share/classes/java/dyn/MethodHandle.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/MethodHandle.java Tue Dec 21 18:45:45 2010 -0800 @@ -37,20 +37,29 @@ * A method handle is a typed, directly executable reference to a method, * constructor, field, or similar low-level operation, with optional * transformations of arguments or return values. - * (These transformations include conversion, insertion, deletion, - * substitution. See the methods of this class and of {@link MethodHandles}.) + * These transformations are quite general, and include such patterns as + * {@linkplain #asType conversion}, + * {@linkplain #bindTo insertion}, + * {@linkplain java.dyn.MethodHandles#dropArguments deletion}, + * and {@linkplain java.dyn.MethodHandles#filterArguments substitution}. + * <p> + * <em>Note: The super-class of MethodHandle is Object. + * Any other super-class visible in the Reference Implementation + * 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 {@link MethodType method type}. + * the method handle's own {@linkplain MethodType method type}. * <p> - * Every method handle confesses its type via the {@code type} accessor. + * 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 * the return type of the method (or {@code void.class} if none). * <p> * Every method handle appears as an object containing a method named - * {@code invoke}, whose signature exactly matches + * {@link #invokeExact invokeExact}, whose signature exactly matches * the method handle's type. * A Java method call expression, which compiles to an * {@code invokevirtual} instruction, @@ -61,15 +70,29 @@ * (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 invoke} of the intended method type. + * 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 invoke} + * 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}. * <p> + * Each individual method handle also contains a method named + * {@link #invokeGeneric invokeGeneric}, whose type is the same + * as {@code invokeExact}, and is therefore also reported by + * the {@link #type type} accessor. + * A call to {@code invokeGeneric} works the same as a call to + * {@code invokeExact}, if the signature specified by the caller + * 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. + * 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 @@ -77,31 +100,47 @@ * 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. - * They should not be passed to untrusted code. + * They should not be passed to untrusted code unless their use from + * the untrusted code would be harmless. * <p> - * Bytecode in an extended JVM can directly call a method handle's - * {@code invoke} from an {@code invokevirtual} instruction. + * 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 invoke}. The signature of the invocation + * must be {@code invokeExact}. The signature of the invocation * (after resolving symbolic type names) must exactly match the method type * of the target method. + * Similarly, bytecode can directly call a method handle's {@code invokeGeneric} + * method. The signature of the invocation (after resolving symbolic type names) + * must either exactly match the method type or be a valid argument to + * the target's {@link #asType asType} method. * <p> - * Every {@code invoke} method always throws {@link Exception}, + * 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 Exception}, or else must catch all checked exceptions locally. + * 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 an extended JVM can directly obtain a method handle + * Bytecode in the JVM can directly obtain a method handle * for any accessible method from a {@code ldc} instruction - * which refers to a {@code CONSTANT_Methodref} or - * {@code CONSTANT_InterfaceMethodref} constant pool entry. + * which refers to a {@code CONSTANT_MethodHandle} constant pool entry. + * (Each such entry refers directly to a {@code CONSTANT_Methodref}, + * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref} + * constant pool entry. + * For more details, see the <a href="package-summary.html#mhcon">package summary</a>.) * <p> - * All JVMs can also use a reflective API called {@code MethodHandles} + * Java code can also use a reflective API called + * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup} * for creating and calling method handles. + * For example, a static method handle can be obtained + * from {@link java.dyn.MethodHandles.Lookup#findStatic Lookup.findStatic}. + * There are also bridge methods from Core Reflection API objects, + * such as {@link java.dyn.MethodHandles.Lookup#unreflect Lookup.ureflect}. * <p> * A method reference may refer either to a static or non-static method. * In the non-static case, the method handle type includes an explicit @@ -128,10 +167,10 @@ mt = MethodType.methodType(String.class, char.class, char.class); mh = lookup.findVirtual(String.class, "replace", mt); // (Ljava/lang/String;CC)Ljava/lang/String; -s = mh.<String>invokeExact("daddy",'d','n'); +s = (String) mh.invokeExact("daddy",'d','n'); assert(s.equals("nanny")); // weakly typed invocation (using MHs.invoke) -s = (String) mh.invokeVarargs("sappy", 'p', 'v'); +s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); assert(s.equals("savvy")); // mt is {Object[] => List} mt = MethodType.methodType(java.util.List.class, Object[].class); @@ -147,14 +186,22 @@ mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); // (Ljava/util/List;)I -i = mh.<int>invokeExact(java.util.Arrays.asList(1,2,3)); +i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); assert(i == 3); +mt = MethodType.methodType(void.class, String.class); +mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); +mh.invokeExact(System.out, "Hello, world."); +// (Ljava/io/PrintStream;Ljava/lang/String;)V * </pre></blockquote> * Each of the above calls generates a single invokevirtual instruction * with the name {@code invoke} and the type descriptors indicated in the comments. * The argument types are taken directly from the actual arguments, - * while the return type is taken from the type parameter. - * (This type parameter may be a primitive, and it defaults to {@code Object}.) + * while the return type is taken from the cast immediately applied to the call. + * This cast may be to a primitive. + * If it is missing, the type defaults to {@code Object} if the call + * occurs in a context which uses the return value. + * If the call occurs as a statement, a cast is impossible, + * and there is no return type; the call is {@code void}. * <p> * <em>A note on generic typing:</em> Method handles do not represent * their function types in terms of Java parameterized (generic) types, @@ -162,7 +209,7 @@ * Java types. * <ol> * <li>Method types range over all possible arities, - * from no arguments to an arbitrary number of arguments. + * 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> * <li>Method types can specify arguments of primitive types, * which Java generic types cannot range over.</li> @@ -180,6 +227,19 @@ * fields, methods, and constructors can be represented directly * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. * Loading such a constant causes the component classes of its type to be loaded as necessary. + * <p> + * Method handles cannot be subclassed by the user. + * Implementations may (or may not) create internal subclasses of {@code MethodHandle} + * which may be visible via the {@code java.lang.Object#getClass Object.getClass} + * operation. The programmer should not draw conclusions about a method handle + * from its specific class, as the method handle class hierarchy (if any) + * may change from time to time or across implementations from different vendors. + * <p> + * With respect to the Java Memory Model, any method handle will behave + * as if all of its fields are final variables. This means that any method + * handle made visible to the application will always be fully formed. + * This is true even if the method handle is published through a shared + * variables in a data race. * * @see MethodType * @see MethodHandles @@ -189,7 +249,6 @@ // Note: This is an implementation inheritance hack, and will be removed // with a JVM change which moves the required hidden state onto this class. extends MethodHandleImpl - implements MethodHandleProvider { private static Access IMPL_TOKEN = Access.getToken(); @@ -208,7 +267,7 @@ /** * Report the type of this method handle. - * Every invocation of this method handle must exactly match this type. + * Every invocation of this method handle via {@code invokeExact} must exactly match this type. * @return the method handle type */ public final MethodType type() { @@ -216,12 +275,16 @@ } /** - * The constructor for MethodHandle may only be called by privileged code. - * Subclasses may be in other packages, but must possess - * a token which they obtained from MH with a security check. - * @param token non-null object which proves access permission - * @param type type (permanently assigned) of the new method handle + * <em>CONSTRUCTOR WILL BE REMOVED FOR PFD:</em> + * Temporary constructor in early versions of the Reference Implementation. + * Method handle inheritance (if any) will be contained completely within + * the {@code java.dyn} package. */ + // The constructor for MethodHandle may only be called by privileged code. + // Subclasses may be in other packages, but must possess + // a token which they obtained from MH with a security check. + // @param token non-null object which proves access permission + // @param type type (permanently assigned) of the new method handle protected MethodHandle(Access token, MethodType type) { super(token); Access.check(token); @@ -243,92 +306,116 @@ }); } - /** The string of a direct method handle is the simple name of its target method. - * The string of an adapter or bound method handle is the string of its - * target method handle. - * The string of a Java method handle is the string of its entry point method, - * unless the Java method handle overrides the toString method. + /** + * 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); } - //// This is the "Method Handle Kernel API" discussed at the JVM Language Summit, 9/2009. - //// Implementations here currently delegate to statics in MethodHandles. Some of those statics - //// will be deprecated. Others will be kept as "algorithms" to supply degrees of freedom - //// not present in the Kernel API. - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Invoke the method handle, allowing any caller signature, but requiring an exact signature match. * The signature at the call site of {@code invokeExact} must - * exactly match this method handle's {@code type}. + * 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 + * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call */ - public final native @PolymorphicSignature <R,A> R invokeExact(A... args) throws Throwable; - - // FIXME: remove this transitional form - /** @deprecated transitional form defined in EDR but removed in PFD */ - public final native @PolymorphicSignature <R,A> R invoke(A... args) throws Throwable; + public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable; /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Invoke the method handle, allowing any caller signature, - * and performing simple conversions for arguments and return types. - * The signature at the call site of {@code invokeGeneric} must - * have the same arity as this method handle's {@code type}. + * and optionally performing conversions for arguments and return types. * <p> - * If the call site signature exactly matches this method handle's {@code type}, - * the call proceeds as if by {@link #invokeExact}. + * If the call site signature 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 - * adjusted by calling {@link #asType} to adjust this method handle + * adjusted by calling {@link #asType asType} to adjust this method handle * to the required type, and then the call proceeds as if by - * {@link #invokeExact} on the adjusted method handle. + * {@link #invokeExact invokeExact} on the adjusted method handle. + * <p> + * There is no guarantee that the {@code asType} call is actually made. + * If the JVM can predict the results of making the call, it may perform + * 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. + * 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 + * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call */ - public final native @PolymorphicSignature <R,A> R invokeGeneric(A... args) throws Throwable; - - // ?? public final native @PolymorphicSignature <R,A,V> R invokeVarargs(A args, V[] varargs) throws Throwable; + public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable; /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Perform a varargs invocation, passing the arguments in the given array - * to the method handle, as if via {@link #invokeGeneric} from a call site + * to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site * which mentions only the type {@code Object}, and whose arity is the length * of the argument array. * <p> - * The length of the arguments array must equal the parameter count - * of the target's type. - * The arguments array is spread into separate arguments. + * Specifically, execution proceeds as if by the following steps, + * although the methods are not guaranteed to be called if the JVM + * can predict their effects. + * <ul> + * <li>Determine the length of the argument array as {@code N}. + * For a null reference, {@code N=0}. </li> + * <li>Determine the generic type {@code TN} of {@code N} arguments as + * as {@code TN=MethodType.genericMethodType(N)}.</li> + * <li>Force the original target method handle {@code MH0} to the + * required type, as {@code MH1 = MH0.asType(TN)}. </li> + * <li>Spread the array into {@code N} separate arguments {@code A0, ...}. </li> + * <li>Invoke the type-adjusted method handle on the unpacked arguments: + * MH1.invokeExact(A0, ...). </li> + * <li>Take the return value as an {@code Object} reference. </li> + * </ul> * <p> - * In order to match the type of the target, the following argument + * Because of the action of the {@code asType} step, the following argument * conversions are applied as necessary: * <ul> * <li>reference casting * <li>unboxing + * <li>widening primitive conversions * </ul> - * The following conversions are not applied: - * <ul> - * <li>primitive conversions (e.g., {@code byte} to {@code int} - * <li>varargs conversions other than the initial spread - * <li>any application-specific conversions (e.g., string to number) - * </ul> + * <p> * The result returned by the call is boxed if it is a primitive, * or forced to null if the return type is void. * <p> * This call is equivalent to the following code: * <p><blockquote><pre> - * MethodHandle invoker = MethodHandles.genericInvoker(this.type(), 0, true); - * Object result = invoker.invokeExact(this, arguments); + * MethodHandle invoker = MethodHandles.varargsInvoker(this.type(), 0); + * Object result = invoker.invokeExact(this, arguments); * </pre></blockquote> * @param arguments the arguments to pass to the target * @return the result returned by the target - * @see MethodHandles#genericInvoker + * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments + * @throws Throwable anything thrown by the target method invocation + * @see MethodHandles#varargsInvoker */ - public final Object invokeVarargs(Object... arguments) throws Throwable { + public final Object invokeWithArguments(Object... arguments) throws Throwable { int argc = arguments == null ? 0 : arguments.length; MethodType type = type(); + if (type.parameterCount() != argc) { + // simulate invokeGeneric + return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments); + } if (argc <= 10) { MethodHandle invoker = MethodHandles.invokers(type).genericInvoker(); switch (argc) { @@ -372,99 +459,70 @@ MethodHandle invoker = MethodHandles.invokers(type).varargsInvoker(0); return invoker.invokeExact(this, arguments); } - /** Equivalent to {@code invokeVarargs(arguments.toArray())}. */ + /** 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 { - return invokeVarargs(arguments.toArray()); + return invokeWithArguments(arguments.toArray()); } - /* --- this is intentionally NOT a javadoc yet --- - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + /** * Produce an adapter method handle which adapts the type of the - * current 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 confess a type + * current method handle to a new type + * The resulting method handle is guaranteed to report a type * which is equal to the desired new type. * <p> * If the original type and new type are equal, returns {@code this}. * <p> - * The following conversions are applied as needed both to - * arguments and return types. Let T0 and T1 be the differing - * new and old parameter types (or old and new return types) - * for corresponding values passed by the new and old method types. - * Given those types T0, T1, one of the following conversions is applied - * if possible: - * <ul> - * <li>If T0 and T1 are references, and T1 is not an interface type, - * then a cast to T1 is applied. - * (The types do not need to be related in any particular way.) - * <li>If T0 and T1 are references, and T1 is an interface type, - * then the value of type T0 is passed as a T1 without a cast. - * (This treatment of interfaces follows the usage of the bytecode verifier.) - * <li>If T0 and T1 are primitives, then a Java casting - * conversion (JLS 5.5) is applied, if one exists. - * <li>If T0 and T1 are primitives and one is boolean, - * the boolean is treated as a one-bit unsigned integer. - * (This treatment follows the usage of the bytecode verifier.) - * A conversion from another primitive type behaves as if - * it first converts to byte, and then masks all but the low bit. - * <li>If T0 is a primitive and T1 a reference, a boxing - * conversion is applied if one exists, possibly followed by - * an reference conversion to a superclass. - * T1 must be a wrapper class or a supertype of one. - * If T1 is a wrapper class, T0 is converted if necessary - * to T1's primitive type by one of the preceding conversions. - * Otherwise, T0 is boxed, and its wrapper converted to T1. - * <li>If T0 is a reference and T1 a primitive, an unboxing - * conversion is applied if one exists, possibly preceded by - * a reference conversion to a wrapper class. - * T0 must be a wrapper class or a supertype of one. - * If T0 is a wrapper class, its primitive value is converted - * if necessary to T1 by one of the preceding conversions. - * Otherwise, T0 is converted directly to the wrapper type for T1, - * which is then unboxed. - * <li>If the return type T1 is void, any returned value is discarded - * <li>If the return type T0 is void and T1 a reference, a null value is introduced. - * <li>If the return type T0 is void and T1 a primitive, a zero value is introduced. - * </ul> + * This method provides the crucial behavioral difference between + * {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}. The two methods + * perform the same steps when the caller's type descriptor is identical + * with the callee's, but when the types differ, {@link #invokeGeneric invokeGeneric} + * also calls {@code asType} (or some internal equivalent) in order + * to match up the caller's and callee's types. * <p> - */ - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * Produce an adapter method handle which adapts the type of the - * current 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 confess a type - * which is equal to the desired new type. + * 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> - * If the original type and new type are equal, returns {@code this}. - * <p> - * This method is equivalent to {@link MethodHandles#convertArguments}. + * 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. + * * @param newType the expected type of the new method handle * @return a method handle which delegates to {@code this} after performing * any necessary argument conversions, and arranges for any * necessary return value conversions - * @throws IllegalArgumentException if the conversion cannot be made + * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandles#convertArguments */ - public final MethodHandle asType(MethodType newType) { + public MethodHandle asType(MethodType newType) { return MethodHandles.convertArguments(this, newType); } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce a method handle which 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 all but the first - * {@code keepPosArgs} parameters of the target's type are replaced - * by a single array parameter of type {@code Object[]}. - * Thus, if {@code keepPosArgs} is zero, the adapter will take all - * arguments in a single object array. + * 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, + * the original target is adapted to take the array elements directly, + * as if by a call to {@link #asType asType}. * <p> * When called, the adapter replaces a trailing array argument * by the array's elements, each as its own argument to the target. * (The order of the arguments is preserved.) * They are converted pairwise by casting and/or unboxing - * (as if by {@link MethodHandles#convertArguments}) * to the types of the trailing parameters of the target. * Finally the target is called. * What the target eventually returns is returned unchanged by the adapter. @@ -473,54 +531,67 @@ * contains exactly enough elements to provide a correct argument count * to the target method handle. * (The array may also be null when zero elements are required.) - * @param keepPosArgs the number of leading positional arguments to preserve - * @return a new method handle which spreads its final argument, + * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments + * @param arrayLength the number of arguments to spread from an incoming array argument + * @return a new method handle which spreads its final array argument, * before calling the original method handle + * @throws IllegalArgumentException if {@code arrayType} is not an array type * @throws IllegalArgumentException if target does not have at least - * {@code keepPosArgs} parameter types + * {@code arrayLength} parameter types + * @throws WrongMethodTypeException if the implied {@code asType} call fails */ - public final MethodHandle asSpreader(int keepPosArgs) { + public final MethodHandle asSpreader(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 < arrayLength) throw newIllegalArgumentException("bad spread array length"); + int keepPosArgs = nargs - arrayLength; MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs); - newType = newType.insertParameterTypes(keepPosArgs, Object[].class); + newType = newType.insertParameterTypes(keepPosArgs, arrayType); return MethodHandles.spreadArguments(this, newType); } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce a method handle which 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 - * array parameter of type {@code Object[]} is replaced by - * {@code spreadArrayArgs} parameters of type {@code Object}. + * parameter (usually of type {@code arrayType}) is replaced by + * {@code arrayLength} parameters whose type is element type of {@code arrayType}. * <p> - * When called, the adapter replaces its trailing {@code spreadArrayArgs} - * arguments by a single new {@code Object} array, whose elements + * If the array type differs from the final argument type on original target, + * the original target is adapted to take the array type directly, + * as if by a call to {@link #asType asType}. + * <p> + * When called, the adapter replaces its trailing {@code arrayLength} + * arguments by a single new array of type {@code arrayType}, whose elements * comprise (in order) the replaced arguments. * Finally the target is called. * What the target eventually returns is returned unchanged by the adapter. * <p> - * (The array may also be a shared constant when {@code spreadArrayArgs} is zero.) - * @param spreadArrayArgs the number of arguments to spread from the trailing array + * (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 + * @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 the last argument of the target - * is not {@code Object[]} - * @throws IllegalArgumentException if {@code spreadArrayArgs} is not + * @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 - * @deprecated Provisional and unstable; use {@link MethodHandles#collectArguments}. + * @throws WrongMethodTypeException if the implied {@code asType} call fails */ - public final MethodHandle asCollector(int spreadArrayArgs) { + public final 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(); MethodType newType = oldType.dropParameterTypes(nargs-1, nargs); - newType = newType.insertParameterTypes(nargs-1, MethodType.genericMethodType(spreadArrayArgs).parameterArray()); + newType = newType.insertParameterTypes(nargs-1, + java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement)); return MethodHandles.collectArguments(this, newType); } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce a method handle which binds the given argument * to the current method handle as <i>target</i>. * The type of the bound handle will be @@ -541,15 +612,81 @@ * leading parameter type that is a reference type * @throws ClassCastException if {@code x} cannot be converted * to the leading parameter type of the target - * @deprecated Provisional and unstable; use {@link MethodHandles#insertArguments}. + * @see MethodHandles#insertArguments */ public final MethodHandle bindTo(Object x) { return MethodHandles.insertArguments(this, 0, x); } - /** Implementation of {@link MethodHandleProvider}, which returns {@code this}. */ - public final MethodHandle asMethodHandle() { return this; } + /** + * <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: + * <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; + * </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); - /** Implementation of {@link MethodHandleProvider}, which returns {@code this.asType(type)}. */ - public final MethodHandle asMethodHandle(MethodType type) { return this.asType(type); } +assertEquals("[]", makeAnyList.invokeGeneric().toString()); +assertEquals("[1]", makeAnyList.invokeGeneric(1).toString()); +assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString()); + * <pre><blockquote> + */ + public MethodHandle withTypeHandler(MethodHandle typeHandler) { + return MethodHandles.withTypeHandler(this, typeHandler); + } }
--- a/src/share/classes/java/dyn/MethodHandleProvider.java Tue Dec 21 15:27:55 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2009, 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; - -/** - * An interface for an object to provide a target {@linkplain MethodHandle method handle} to a {@code invokedynamic} instruction. - * There are many function-like objects in various Java APIs. - * This interface provides a standard way for such function-like objects to be bound - * to a dynamic call site, by providing a view of their behavior in the form of a low-level method handle. - * <p> - * The type {@link MethodHandle} is a concrete class whose implementation - * hierarchy (if any) may be tightly coupled to the underlying JVM implementation. - * It cannot also serve as a base type for user-defined functional APIs. - * For this reason, {@code MethodHandle} cannot be subclassed to add new - * behavior to method handles. But this interface can be used to provide - * a link between a user-defined function and the {@code invokedynamic} - * instruction and the method handle API. - */ -public interface MethodHandleProvider { - /** Produce a method handle which will serve as a behavioral proxy for the current object. - * The type and invocation behavior of the proxy method handle are user-defined, - * and should have some relation to the intended meaning of the original object itself. - * <p> - * The current object may have a changeable behavior. - * For example, {@link CallSite} has a {@code setTarget} method which changes its invocation. - * In such a case, it is <em>incorrect</em> for {@code asMethodHandle} to return - * a method handle whose behavior may diverge from that of the current object. - * Rather, the returned method handle must stably and permanently access - * the behavior of the current object, even if that behavior is changeable. - * <p> - * The reference identity of the proxy method handle is not guaranteed to - * have any particular relation to the reference identity of the object. - * In particular, several objects with the same intended meaning could - * share a common method handle, or the same object could return different - * method handles at different times. In the latter case, the different - * method handles should have the same type and invocation behavior, - * and be usable from any thread at any time. - * In particular, if a MethodHandleProvider is bound to an <code>invokedynamic</code> - * call site, the proxy method handle extracted at the time of binding - * will be used for an unlimited time, until the call site is rebound. - * <p> - * The type {@link MethodHandle} itself implements {@code MethodHandleProvider}, and - * for this method simply returns {@code this}. - */ - public MethodHandle asMethodHandle(); - - /** Produce a method handle of a given type which will serve as a behavioral proxy for the current object. - * As for the no-argument version {@link #asMethodHandle()}, the invocation behavior of the - * proxy method handle is user-defined. But the type must be the given type, - * or else a {@link WrongMethodTypeException} must be thrown. - * <p> - * If the current object somehow represents a variadic or overloaded behavior, - * the method handle returned for a given type might represent only a subset of - * the current object's repertoire of behaviors, which correspond to that type. - */ - public MethodHandle asMethodHandle(MethodType type) throws WrongMethodTypeException; -}
--- a/src/share/classes/java/dyn/MethodHandles.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/MethodHandles.java Tue Dec 21 18:45:45 2010 -0800 @@ -29,6 +29,7 @@ import sun.dyn.Access; import sun.dyn.MemberName; import sun.dyn.MethodHandleImpl; +import sun.dyn.util.ValueConversions; import sun.dyn.util.VerifyAccess; import sun.dyn.util.Wrapper; import java.util.List; @@ -135,28 +136,58 @@ * 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}. - * This rule is applied even if the Java compiler might have created + * <p> + * In some cases, this access 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 lookup object created for a nested class {@code C.D} + * For example, a nested class {@code C.D} * can access private members within other related classes such as - * {@code C}, {@code C.D.E}, or {@code C.B}. + * {@code C}, {@code C.D.E}, or {@code C.B}, + * but the Java compiler may need to generate wrapper methods in + * those related classes. In such cases, a {@code Lookup} object on + * {@code C.E} would be unable to those private members. + * 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. */ public static final class Lookup { /** The class on behalf of whom the lookup is being performed. */ private final Class<?> lookupClass; - /** The allowed sorts of members which may be looked up (public, etc.), with STATIC for package. */ + /** The allowed sorts of members which may be looked up (PUBLIC, etc.). */ private final int allowedModes; - private static final int - PUBLIC = Modifier.PUBLIC, - PACKAGE = Modifier.STATIC, - PROTECTED = Modifier.PROTECTED, - PRIVATE = Modifier.PRIVATE, - ALL_MODES = (PUBLIC | PACKAGE | PROTECTED | PRIVATE), - TRUSTED = -1; + /** A single-bit mask representing {@code public} access, + * which may contribute to the result of {@link #lookupModes lookupModes}. + * The value, {@code 0x01}, happens to be the same as the value of the + * {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}. + */ + public static final int PUBLIC = Modifier.PUBLIC; + + /** A single-bit mask representing {@code private} access, + * which may contribute to the result of {@link #lookupModes lookupModes}. + * The value, {@code 0x02}, happens to be the same as the value of the + * {@code private} {@linkplain java.lang.reflect.Modifier#PRIVATE modifier bit}. + */ + public static final int PRIVATE = Modifier.PRIVATE; + + /** A single-bit mask representing {@code protected} access, + * which may contribute to the result of {@link #lookupModes lookupModes}. + * The value, {@code 0x04}, happens to be the same as the value of the + * {@code protected} {@linkplain java.lang.reflect.Modifier#PROTECTED modifier bit}. + */ + public static final int PROTECTED = Modifier.PROTECTED; + + /** A single-bit mask representing {@code package} access (default access), + * which may contribute to the result of {@link #lookupModes lookupModes}. + * The value is {@code 0x08}, which does not correspond meaningfully to + * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. + */ + public static final int PACKAGE = Modifier.STATIC; + + private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE); + private static final int TRUSTED = -1; private static int fixmods(int mods) { mods &= (ALL_MODES - PACKAGE); @@ -181,13 +212,21 @@ } /** Which types of members can this lookup object produce? - * The result is a bit-mask of the {@link Modifier} bits - * {@linkplain Modifier#PUBLIC PUBLIC (0x01)}, - * {@linkplain Modifier#PROTECTED PROTECTED (0x02)}, - * {@linkplain Modifier#PRIVATE PRIVATE (0x04)}, - * and {@linkplain Modifier#STATIC STATIC (0x08)}. - * The modifier bit {@code STATIC} stands in for the package protection mode, - * which does not have an explicit modifier bit. + * The result is a bit-mask of the bits + * {@linkplain #PUBLIC PUBLIC (0x01)}, + * {@linkplain #PRIVATE PRIVATE (0x02)}, + * {@linkplain #PROTECTED PROTECTED (0x04)}, + * and {@linkplain #PACKAGE PACKAGE (0x08)}. + * <p> + * A freshly-created lookup object + * on the {@linkplain java.dyn.MethodHandles#lookup() caller's class} + * has all possible bits set, since the caller class can access all its own members. + * A lookup object on a new lookup class + * {@linkplain java.dyn.MethodHandles.Lookup#in created from a previous lookup object} + * may have some mode bits set to zero. + * The purpose of this is to restrict access via the new lookup object, + * so that it can access only names which can be reached by the original + * lookup object, and also by the new lookup class. */ public int lookupModes() { return allowedModes & ALL_MODES; @@ -220,18 +259,21 @@ /** * Create a lookup on the specified new lookup class. * The resulting object will report the specified - * class as its own {@link #lookupClass}. + * class as its own {@link #lookupClass lookupClass}. * <p> * However, the resulting {@code Lookup} object is guaranteed * to have no more access capabilities than the original. - * In particular:<ul> + * In particular, access capabilities can be lost as follows:<ul> * <li>If the new lookup class differs from the old one, * protected members will not be accessible by virtue of inheritance. + * (Protected members may continue to be accessible because of package sharing.) * <li>If the new lookup class is in a different package * than the old one, protected and default (package) members will not be accessible. * <li>If the new lookup class is not within the same package member * as the old one, private members will not be accessible. - * <li>In all cases, public members will continue to be accessible. + * <li>If the new lookup class is not accessible to the old lookup class, + * then no members, not even public members, will be accessible. + * (In all other cases, public members will continue to be accessible.) * </ul> */ public Lookup in(Class<?> requestedLookupClass) { @@ -245,10 +287,17 @@ && !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) { newModes &= ~(PACKAGE|PRIVATE); } + // Allow nestmate lookups to be created without special privilege: if ((newModes & PRIVATE) != 0 && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) { newModes &= ~PRIVATE; } + if (newModes == PUBLIC + && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass)) { + // The requested class it not accessible from the lookup class. + // No permissions. + newModes = 0; + } checkUnprivilegedlookupClass(requestedLookupClass); return new Lookup(requestedLookupClass, newModes); } @@ -272,35 +321,43 @@ throw newIllegalArgumentException("illegal lookupClass: "+lookupClass); } - /** Display the name of the class. - * If there are restrictions on the access permitted to this lookup, - * display those also. + /** + * Display the name of the class from which lookups are to be made. + * (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.) + * If there are restrictions on the access permitted to this lookup, + * this is indicated by adding a suffix to the class name, consisting + * of a slash and a keyword. The keyword is chosen as follows: + * <ul> + * <li>If no access is allowed, the suffix is "/noaccess". + * <li>If only public access is allowed, the suffix is "/public". + * <li>If only public and package access are allowed, the suffix is "/package". + * <li>If only public, package, and private access are allowed, the suffix is "/private". + * </ul> + * If none of the above cases apply, it is the case that full + * access (public, package, private, and protected) is allowed. + * In this case, no suffix is added. + * This is true only of an object obtained originally from + * {@link java.dyn.MethodHandles#lookup() MethodHandles.lookup}. + * Objects created by {@link java.dyn.MethodHandles.Lookup#in() Lookup#in} + * always have restricted access, and will display a suffix. */ @Override public String toString() { - String modestr; String cname = lookupClass.getName(); switch (allowedModes) { case TRUSTED: - return "/trusted"; + return "/trusted"; // internal only case PUBLIC: - modestr = "/public"; - if (lookupClass == Object.class) - return modestr; - break; + return cname + "/public"; case PUBLIC|PACKAGE: return cname + "/package"; - case 0: // should not happen - return cname + "/empty"; + case 0: // no privileges + return cname + "/noaccess"; case ALL_MODES: return cname; + default: + return cname + "/private"; } - StringBuilder buf = new StringBuilder(cname); - if ((allowedModes & PUBLIC) != 0) buf.append("/public"); - if ((allowedModes & PACKAGE) != 0) buf.append("/package"); - if ((allowedModes & PROTECTED) != 0) buf.append("/protected"); - if ((allowedModes & PRIVATE) != 0) buf.append("/private"); - return buf.toString(); } // call this from an entry point method in Lookup with extraFrames=0. @@ -326,7 +383,6 @@ * @param name the name of the method * @param type the type of the method * @return the desired method handle - * @exception SecurityException <em>TBD</em> * @exception NoAccessException if the method does not exist or access checking fails */ public @@ -342,13 +398,6 @@ * with the receiver type (usually {@code refc}) prepended. * The method and all its argument types must be accessible to the lookup class. * <p> - * (<em>BUG NOTE:</em> The type {@code Object} may be prepended instead - * of the receiver type, if the receiver type is not on the boot class path. - * This is due to a temporary JVM limitation, in which MethodHandle - * claims to be unable to access such classes. To work around this - * bug, use {@code convertArguments} to normalize the type of the leading - * argument to a type on the boot class path, such as {@code Object}.) - * <p> * When called, the handle will treat the first argument as a receiver * and dispatch on the receiver's type to determine which method * implementation to enter. @@ -358,7 +407,6 @@ * @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 SecurityException <em>TBD</em> * @exception NoAccessException if the method does not exist or access checking fails */ public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoAccessException { @@ -382,7 +430,6 @@ * @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 SecurityException <em>TBD</em> * @exception NoAccessException if the method does not exist or access checking fails */ public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoAccessException { @@ -409,13 +456,13 @@ * {@code invokespecial} instruction.) * <p> * If the explicitly specified caller class is not identical with the - * lookup class, a security check TBD is performed. + * lookup class, or if this lookup object does not have private access + * privileges, the access fails. * @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 SecurityException <em>TBD</em> * @exception NoAccessException if the method does not exist or access checking fails */ public MethodHandle findSpecial(Class<?> refc, String name, MethodType type, @@ -428,7 +475,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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. @@ -445,7 +491,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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 @@ -462,7 +507,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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. @@ -478,7 +522,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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 @@ -515,7 +558,6 @@ * @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 SecurityException <em>TBD</em> * @exception NoAccessException if the method does not exist or access checking fails */ public MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException { @@ -530,7 +572,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Make a direct method handle to <i>m</i>, if the lookup class has permission. * If <i>m</i> is non-static, the receiver argument is treated as an initial argument. * If <i>m</i> is virtual, overriding is respected on every call. @@ -554,7 +595,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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}. @@ -579,7 +619,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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. @@ -602,7 +641,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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. @@ -620,7 +658,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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 @@ -681,7 +718,7 @@ int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; int mods = m.getModifiers(); - if (Modifier.isPublic(mods) && Modifier.isPublic(refc.getModifiers())) + if (Modifier.isPublic(mods) && Modifier.isPublic(refc.getModifiers()) && allowedModes != 0) return; // common case int requestedModes = fixmods(mods); // adjust 0 => PACKAGE if ((requestedModes & allowedModes) != 0 @@ -706,6 +743,8 @@ 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)) @@ -713,9 +752,14 @@ return "member is private to package"; } + private static final boolean ALLOW_NESTMATE_ACCESS = false; + void checkSpecialCaller(Class<?> specialCaller) throws NoAccessException { if (allowedModes == TRUSTED) return; - if (!VerifyAccess.isSamePackageMember(specialCaller, lookupClass())) + if ((allowedModes & PRIVATE) == 0 + || (specialCaller != lookupClass() + && !(ALLOW_NESTMATE_ACCESS && + VerifyAccess.isSamePackageMember(specialCaller, lookupClass())))) throw newNoAccessException("no private access for invokespecial", new MemberName(specialCaller), lookupClass()); } @@ -725,7 +769,9 @@ // on itself or a subclass. Enforce that restriction, from JVMS 5.4.4, etc. if (!method.isProtected() || method.isStatic() || allowedModes == TRUSTED - || VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass())) + || method.getDeclaringClass() == lookupClass() + || (ALLOW_NESTMATE_ACCESS && + VerifyAccess.isSamePackageMember(method.getDeclaringClass(), lookupClass()))) return mh; else return restrictReceiver(method, mh, lookupClass()); @@ -765,7 +811,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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, @@ -780,7 +825,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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. @@ -796,25 +840,6 @@ /// method handle invocation (reflective style) /** - * @deprecated Alias for MethodHandle.invokeVarargs. - */ - @Deprecated - public static - Object invokeVarargs(MethodHandle target, Object... arguments) throws Throwable { - return target.invokeVarargs(arguments); - } - - /** - * @deprecated Alias for MethodHandle.invokeVarargs. - */ - @Deprecated - public static - Object invoke(MethodHandle target, Object... arguments) throws Throwable { - return target.invokeVarargs(arguments); - } - - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * 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 @@ -823,18 +848,28 @@ * <li>a single {@code MethodHandle} target * <li>zero or more {@code Object} values (one for each argument in {@code type}) * </ul> - * The invoker will apply reference casts as necessary and unbox primitive arguments, - * as if by {@link #convertArguments}. + * <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 = exactInvoker(type); + * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type); * MethodType genericType = type.generic(); * genericType = genericType.insertParameterType(0, MethodHandle.class); - * return convertArguments(invoker, genericType); + * 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 @@ -845,9 +880,8 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce a method handle which will invoke any method handle of the - * given type on a standard set of {@code Object} type arguments + * 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 * arguments: @@ -856,18 +890,31 @@ * <li>zero or more {@code Object} values (counted by {@code objectArgCount}) * <li>an {@code Object[]} array containing more arguments * </ul> - * The invoker will spread the varargs array, apply - * reference casts as necessary, and unbox primitive arguments. + * <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}, except for + * the last parameter type, which will be the array type {@code Object[]}. + * <p> + * Before invoking its target, the invoker will spread the varargs array, apply + * reference casts as necessary, and unbox and widen primitive arguments. * 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 = exactInvoker(type); - * MethodType vaType = MethodType.makeGeneric(objectArgCount, true); + * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type); + * MethodType vaType = MethodType.genericMethodType(objectArgCount, true); * vaType = vaType.insertParameterType(0, MethodHandle.class); - * return spreadArguments(invoker, vaType); + * int spreadArgCount = type.parameterCount - objectArgCount; + * invoker = invoker.asSpreader(Object.class, spreadArgCount); + * return invoker.asType(vaType); * </pre></blockquote> * @param type the desired target type * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments @@ -881,7 +928,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * 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 * exactly equal to the desired type, except that it will accept @@ -889,7 +935,7 @@ * <p> * This method is equivalent to the following code (though it may be more efficient): * <p><blockquote><pre> - * lookup().findVirtual(MethodHandle.class, "invoke", type); + * lookup().findVirtual(MethodHandle.class, "invokeExact", type); * </pre></blockquote> * @param type the desired target type * @return a method handle suitable for invoking any method handle of the given type @@ -899,39 +945,6 @@ return invokers(type).exactInvoker(); } - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * Produce a method handle equivalent to an invokedynamic instruction - * which has been linked to the given call site. - * Along with {@link Lookup#findVirtual}, {@link Lookup#findStatic}, - * and {@link Lookup#findSpecial}, this completes the emulation - * of the JVM's {@code invoke} instructions. - * <p>This method is equivalent to the following code: - * <p><blockquote><pre> - * MethodHandle getTarget, invoker, result; - * getTarget = lookup().bind(site, "getTarget", methodType(MethodHandle.class)); - * invoker = exactInvoker(site.type()); - * result = foldArguments(invoker, getTarget) - * </pre></blockquote> - * @return a method handle which always invokes the call site's target - */ - public static - MethodHandle dynamicInvoker(CallSite site) throws NoAccessException { - MethodHandle getCSTarget = GET_TARGET; - if (getCSTarget == null) { - try { - GET_TARGET = getCSTarget = Lookup.IMPL_LOOKUP. - findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); - } catch (NoAccessException ex) { - throw new InternalError(); - } - } - MethodHandle getTarget = MethodHandleImpl.bindReceiver(IMPL_TOKEN, getCSTarget, site); - MethodHandle invoker = exactInvoker(site.type()); - return foldArguments(invoker, getTarget); - } - private static MethodHandle GET_TARGET = null; // link this lazily, not eagerly - static Invokers invokers(MethodType type) { return MethodTypeImpl.invokers(IMPL_TOKEN, type); } @@ -974,23 +987,23 @@ if (t0.isPrimitive()) return Wrapper.asPrimitiveType(t1).cast(value); else - return Wrapper.OBJECT.cast(value, t1); + return Wrapper.OBJECT.convert(value, t1); } boolean prim0 = t0.isPrimitive(), prim1 = t1.isPrimitive(); if (!prim0) { // check contract with caller - Wrapper.OBJECT.cast(value, t0); + Wrapper.OBJECT.convert(value, t0); if (!prim1) { - return Wrapper.OBJECT.cast(value, t1); + return Wrapper.OBJECT.convert(value, t1); } // convert reference to primitive by unboxing Wrapper w1 = Wrapper.forPrimitiveType(t1); - return w1.cast(value, t1); + return w1.convert(value, t1); } // check contract with caller: Wrapper.asWrapperType(t0).cast(value); Wrapper w1 = Wrapper.forPrimitiveType(t1); - return w1.cast(value, t1); + return w1.convert(value, t1); } static @@ -1011,7 +1024,7 @@ * Produce a method handle which adapts the type of the * given method handle to a new type by pairwise argument conversion. * The original type and new type must have the same number of arguments. - * The resulting method handle is guaranteed to confess a type + * The resulting method handle is guaranteed to report a type * which is equal to the desired new type. * <p> * If the original type and new type are equal, returns target. @@ -1023,34 +1036,21 @@ * Given those types T0, T1, one of the following conversions is applied * if possible: * <ul> - * <li>If T0 and T1 are references, and T1 is not an interface type, - * then a cast to T1 is applied. + * <li>If T0 and T1 are references, then a cast to T1 is applied. * (The types do not need to be related in any particular way.) - * <li>If T0 and T1 are references, and T1 is an interface type, - * then the value of type T0 is passed as a T1 without a cast. - * (This treatment of interfaces follows the usage of the bytecode verifier.) - * <li>If T0 and T1 are primitives, then a Java casting - * conversion (JLS 5.5) is applied, if one exists. - * <li>If T0 and T1 are primitives and one is boolean, - * the boolean is treated as a one-bit unsigned integer. - * (This treatment follows the usage of the bytecode verifier.) - * A conversion from another primitive type behaves as if - * it first converts to byte, and then masks all but the low bit. + * <li>If T0 and T1 are primitives, then a Java method invocation + * conversion (JLS 5.3) is applied, if one exists. * <li>If T0 is a primitive and T1 a reference, a boxing * conversion is applied if one exists, possibly followed by - * an reference conversion to a superclass. + * a reference conversion to a superclass. * T1 must be a wrapper class or a supertype of one. - * If T1 is a wrapper class, T0 is converted if necessary - * to T1's primitive type by one of the preceding conversions. - * Otherwise, T0 is boxed, and its wrapper converted to T1. * <li>If T0 is a reference and T1 a primitive, an unboxing - * conversion is applied if one exists, possibly preceded by - * a reference conversion to a wrapper class. + * conversion will be applied at runtime, possibly followed + * by a Java method invocation conversion (JLS 5.3) + * on the primitive value. (These are the widening conversions.) * T0 must be a wrapper class or a supertype of one. - * If T0 is a wrapper class, its primitive value is converted - * if necessary to T1 by one of the preceding conversions. - * Otherwise, T0 is converted directly to the wrapper type for T1, - * which is then unboxed. + * (In the case where T0 is Object, these are the conversions + * allowed by java.lang.reflect.Method.invoke.) * <li>If the return type T1 is void, any returned value is discarded * <li>If the return type T0 is void and T1 a reference, a null value is introduced. * <li>If the return type T0 is void and T1 a primitive, a zero value is introduced. @@ -1060,26 +1060,109 @@ * @return a method handle which delegates to {@code target} after performing * any necessary argument conversions, and arranges for any * necessary return value conversions - * @throws IllegalArgumentException if the conversion cannot be made + * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandle#asType + * @see MethodHandles#explicitCastArguments */ public static MethodHandle convertArguments(MethodHandle target, MethodType newType) { MethodType oldType = target.type(); if (oldType.equals(newType)) return target; - MethodHandle res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target, - newType, oldType, null); + MethodHandle res = null; + try { + res = MethodHandleImpl.convertArguments(IMPL_TOKEN, target, + newType, oldType, null); + } catch (IllegalArgumentException ex) { + } if (res == null) - throw newIllegalArgumentException("cannot convert to "+newType+": "+target); + throw new WrongMethodTypeException("cannot convert to "+newType+": "+target); return res; } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * Produce a method handle which adapts the type of the + * given method handle to a new type by pairwise argument conversion. + * The original type and new type must have the same number of arguments. + * The resulting method handle is guaranteed to report a type + * which is equal to the desired new type. + * <p> + * If the original type and new type are equal, returns target. + * <p> + * The same conversions are allowed as for {@link #convertArguments convertArguments}, + * and some additional conversions are also applied if those conversions fail. + * Given types T0, T1, one of the following conversions is applied + * in addition, if the conversions specified for {@code convertArguments} + * would be insufficient: + * <ul> + * <li>If T0 and T1 are references, and T1 is an interface type, + * then the value of type T0 is passed as a T1 without a cast. + * (This treatment of interfaces follows the usage of the bytecode verifier.) + * <li>If T0 and T1 are primitives and one is boolean, + * the boolean is treated as a one-bit unsigned integer. + * (This treatment follows the usage of the bytecode verifier.) + * A conversion from another primitive type behaves as if + * it first converts to byte, and then masks all but the low bit. + * <li>If a primitive value would be converted by {@code convertArguments} + * using Java method invocation conversion (JLS 5.3), + * Java casting conversion (JLS 5.5) may be used also. + * This allows primitives to be narrowed as well as widened. + * </ul> + * @param target the method handle to invoke after arguments are retyped + * @param newType the expected type of the new method handle + * @return a method handle which delegates to {@code target} after performing + * any necessary argument conversions, and arranges for any + * necessary return value conversions + * @throws WrongMethodTypeException if the conversion cannot be made + * @see MethodHandle#asType + * @see MethodHandles#convertArguments + */ + public static + MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { + return convertArguments(target, newType); // FIXME! + } + + /* + FIXME: Reconcile javadoc with 10/22/2010 EG notes on conversion: + + Both converters arrange for their method handles to convert arguments + and return values. The conversion rules are the same for arguments + and return values, and depend only on source and target types, S and + T. The conversions allowed by castConvertArguments are a strict + superset of those performed by convertArguments. + + In all cases, if S and T are references, a simple checkcast is done. + If neither S nor T is a primitive, no attempt is made to unbox and + box. A failed conversion throws ClassCastException. + + If T is void, the value is dropped. + + For compatibility with reflection, if S is void and T is a reference, + a null value is produced. + + For compatibility with reflection, if S is a reference and T is a + primitive, S is first unboxed and then undergoes primitive conversion. + In the case of 'convertArguments', only assignment conversion is + performed (no narrowing primitive conversion). + + If S is a primitive, S is boxed, and then the above rules are applied. + If S and T are both primitives, the boxing will be undetectable; only + the primitive conversions will be apparent to the user. The key point + is that if S is a primitive type, the implementation may box it and + treat is as Object, without loss of information, or it may use a "fast + path" which does not use boxing. + + Notwithstanding the rules above, for compatibility with the verifier, + if T is an interface, it is treated as if it were Object. [KEEP THIS?] + + Also, for compatibility with the verifier, a boolean may be undergo + widening or narrowing conversion to any other primitive type. [KEEP THIS?] + */ + + /** * Produce 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 confess a type + * The resulting method handle is guaranteed to report a type * which is equal to the desired new type. * <p> * The given array controls the reordering. @@ -1092,22 +1175,42 @@ * outgoing argument will be taken from the {@code I}-th incoming * argument, where {@code I} is {@code reorder[N]}. * <p> + * No argument or return value conversions are applied. + * The type of each incoming argument, as determined by {@code newType}, + * must be identical to the type of the corresponding outgoing argument + * or arguments in the target method handle. + * The return type of {@code newType} must be identical to the return + * type of the original target. + * <p> * The reordering array need not specify an actual permutation. * An incoming argument will be duplicated if its index appears * more than once in the array, and an incoming argument will be dropped * if its index does not appear in the array. - * <p> - * Pairwise conversions are applied as needed to arguments and return - * values, as with {@link #convertArguments}. + * As in the case of {@link #dropArguments(MethodHandle,int,List) dropArguments}, + * incoming arguments which are not mentioned in the reordering array + * are may be any type, as determined only by {@code newType}. + * <blockquote><pre> +MethodType intfn1 = MethodType.methodType(int.class, int.class); +MethodType intfn2 = MethodType.methodType(int.class, int.class, int.class); +MethodHandle sub = ... {int x, int y => x-y} ...; +assert(sub.type().equals(intfn2)); +MethodHandle sub1 = MethodHandles.permuteArguments(sub, intfn2, 0, 1); +MethodHandle rsub = MethodHandles.permuteArguments(sub, intfn2, 1, 0); +assert((int)rsub.invokeExact(1, 100) == 99); +MethodHandle add = ... {int x, int y => x+y} ...; +assert(add.type().equals(intfn2)); +MethodHandle twice = MethodHandles.permuteArguments(add, intfn1, 0, 0); +assert(twice.type().equals(intfn1)); +assert((int)twice.invokeExact(21) == 42); + * </pre></blockquote> * @param target the method handle to invoke after arguments are reordered * @param newType the expected type of the new method handle * @param reorder a string which controls the reordering - * @return a method handle which delegates to {@code target} after performing - * any necessary argument motion and conversions, and arranges for any - * necessary return value conversions + * @return a method handle which delegates to {@code target} after it + * drops unused arguments and moves and/or duplicates the other arguments */ public static - MethodHandle permuteArguments(MethodHandle target, MethodType newType, int[] reorder) { + MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { MethodType oldType = target.type(); checkReorder(reorder, newType, oldType); return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, @@ -1130,33 +1233,21 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * Produce a method handle which adapts the type of the - * given method handle to a new type, by spreading the final argument. - * The resulting method handle is guaranteed to confess a type - * which is equal to the desired new type. - * <p> - * The final parameter type of the new type must be an array type T[]. - * This is the type of what is called the <i>spread</i> argument. - * All other arguments of the new type are called <i>ordinary</i> arguments. - * <p> - * The ordinary arguments of the new type are pairwise converted - * to the initial parameter types of the old type, according to the - * rules in {@link #convertArguments}. - * Any additional arguments in the old type - * are converted from the array element type T, - * again according to the rules in {@link #convertArguments}. - * The return value is converted according likewise. - * <p> - * The call verifies that the spread argument is in fact an array - * of exactly the type length, i.e., the excess number of - * arguments in the old type over the ordinary arguments in the new type. - * If there are no excess arguments, the spread argument is also - * allowed to be null. - * @param target the method handle to invoke after the argument is prepended + * <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); + * int spreadCount = target.type().parameterCount() - spreadPos; + * MethodHandle adapter = target.asSpreader(spreadType, spreadCount); + * adapter = adapter.asType(newType); + * return adapter; + * </pre></blockquote> + * @param target the method handle to invoke after argument spreading * @param newType the expected type of the new method handle - * @return a new method handle which spreads its final argument, + * @return a method handle which spreads its final argument, * before calling the original method handle + * @deprecated Use {@link MethodHandle#asSpreader} */ public static MethodHandle spreadArguments(MethodHandle target, MethodType newType) { @@ -1176,21 +1267,22 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> - * Produce a method handle which adapts the type of the - * given method handle to a new type, by collecting a series of - * trailing arguments as elements to a single argument array. - * <p> - * This method may be used as an inverse to {@link #spreadArguments}. - * The final parameter type of the old type must be an array type T[], - * which is the type of what is called the <i>spread</i> argument. - * The trailing arguments of the new type which correspond to - * the spread argument are all converted to type T and collected - * into an array before the original method is called. - * @param target the method handle to invoke after the argument is prepended + * <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); + * if (!collectType.isArray()) collectType = Object[].class; + * int collectCount = newType.parameterCount() - collectPos; + * MethodHandle adapter = target.asCollector(collectType, collectCount); + * adapter = adapter.asType(newType); + * return adapter; + * </pre></blockquote> + * @param target the method handle to invoke after argument collection * @param newType the expected type of the new method handle - * @return a new method handle which collects some trailing argument + * @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 MethodHandle collectArguments(MethodHandle target, MethodType newType) { @@ -1209,7 +1301,88 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * Produce a method handle of the requested return type which returns the given + * constant value every time it is invoked. + * <p> + * Before the method handle is returned, the passed-in value is converted to the requested type. + * If the requested type is primitive, widening primitive conversions are attempted, + * else reference conversions are attempted. + * <p>The returned method handle is equivalent to {@code identity(type).bindTo(value)}, + * unless the type is {@code void}, in which case it is {@code identity(type)}. + * @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 + */ + public static + MethodHandle constant(Class<?> type, Object value) { + if (type.isPrimitive()) { + if (type == void.class) return identity(type); + Wrapper w = Wrapper.forPrimitiveType(type); + return identity(type).bindTo(w.convert(value, type)); + } else { + return identity(type).bindTo(type.cast(value)); + } + } + + /** + * 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 + */ + 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) { + 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}, * after inserting the given argument(s) at the given position. * The formal parameters to {@code target} which will be supplied by those @@ -1229,8 +1402,9 @@ * @param target the method handle to invoke after the argument is inserted * @param pos where to insert the argument (zero for the first) * @param values the series of arguments to insert - * @return a new method handle which inserts an additional argument, + * @return a method handle which inserts an additional argument, * before calling the original method handle + * @see MethodHandle#bindTo */ public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values) { @@ -1263,14 +1437,7 @@ return result; } - @Deprecated // "use MethodHandles.insertArguments instead" - public static - MethodHandle insertArgument(MethodHandle target, int pos, Object value) { - return insertArguments(target, pos, value); - } - /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Produce 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 @@ -1283,25 +1450,25 @@ * <p> * <b>Example:</b> * <p><blockquote><pre> - * import static java.dyn.MethodHandles.*; - * import static java.dyn.MethodType.*; - * ... - * MethodHandle cat = lookup().findVirtual(String.class, - * "concat", methodType(String.class, String.class)); - * System.out.println((String) cat.invokeExact("x", "y")); // xy - * MethodHandle d0 = dropArguments(cat, 0, String.class); - * System.out.println((String) d0.invokeExact("x", "y", "z")); // yz - * MethodHandle d1 = dropArguments(cat, 1, String.class); - * System.out.println((String) d1.invokeExact("x", "y", "z")); // xz - * MethodHandle d2 = dropArguments(cat, 2, String.class); - * System.out.println((String) d2.invokeExact("x", "y", "z")); // xy - * MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); - * System.out.println((String) d12.invokeExact("x", 12, true, "z")); // xz +import static java.dyn.MethodHandles.*; +import static java.dyn.MethodType.*; +... +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +assertEquals("xy", (String) cat.invokeExact("x", "y")); +MethodHandle d0 = dropArguments(cat, 0, String.class); +assertEquals("yz", (String) d0.invokeExact("x", "y", "z")); +MethodHandle d1 = dropArguments(cat, 1, String.class); +assertEquals("xz", (String) d1.invokeExact("x", "y", "z")); +MethodHandle d2 = dropArguments(cat, 2, String.class); +assertEquals("xy", (String) d2.invokeExact("x", "y", "z")); +MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); +assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); * </pre></blockquote> - * @param target the method handle to invoke after the argument is dropped - * @param valueTypes the type(s) of the argument to drop - * @param pos which argument to drop (zero for the first) - * @return a new method handle which drops an argument of the given type, + * @param target the method handle to invoke after the arguments are dropped + * @param valueTypes the type(s) of the argument(s) to drop + * @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 */ public static @@ -1319,23 +1486,36 @@ return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos); } + /** + * Produce 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. + * This method is equivalent to the following code: + * <code> + * {@link #dropArguments(MethodHandle,int,List) dropArguments}(target, pos, Arrays.asList(valueTypes)) + * </code> + * @param target the method handle to invoke after the arguments are dropped + * @param valueTypes the type(s) of the argument(s) to drop + * @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 + */ public static MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) { return dropArguments(target, pos, Arrays.asList(valueTypes)); } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Adapt a target method handle {@code target} by pre-processing * one or more of its arguments, each with its own unary filter function, * and then calling the target with each pre-processed argument * replaced by the result of its corresponding filter function. * <p> * The pre-processing is performed by one or more method handles, - * specified in the non-null elements of the {@code filters} array. - * (If there are no such elements, the original target is returned.) - * Each filter (that is, each non-null element of {@code filters}) - * is applied to the corresponding argument of the adapter. + * specified in the elements of the {@code filters} array. + * (If there are no 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 * the method handle, then {@code F} must be a method handle which @@ -1345,46 +1525,49 @@ * The return type of {@code F} must be identical to the corresponding * parameter type of the target. * <p> - * It is an error if there are non-null elements of {@code filters} + * It is an error if there are elements of {@code filters} * which do not correspond to argument positions in the target. - * The actual length of the target array may be any number, it need - * not be the same as the parameter count of the target type. - * (This provides an easy way to filter just the first argument or two - * of a target method handle.) - * <p> Here is pseudocode for the resulting adapter: - * <blockquote><pre> - * // there are N arguments in the A sequence - * T target(A[N]...); - * [i<N] V[i] filter[i](B[i]) = filters[i] ?: identity; - * T adapter(B[N]... b) { - * A[N] a...; - * [i<N] a[i] = filter[i](b[i]); - * return target(a...); - * } + * <b>Example:</b> + * <p><blockquote><pre> +import static java.dyn.MethodHandles.*; +import static java.dyn.MethodType.*; +... +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +MethodHandle upcase = lookup().findVirtual(String.class, + "toUpperCase", methodType(String.class)); +assertEquals("xy", (String) cat.invokeExact("x", "y")); +MethodHandle f0 = filterArguments(cat, 0, upcase); +assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy +MethodHandle f1 = filterArguments(cat, 1, upcase); +assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY +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 a non-null element of {@code filters} - * does not match a corresponding argument type of {@code target} + * @throws IllegalArgumentException if an element of {@code filters} is null or + * does not match a corresponding argument type of {@code target} as described above */ public static - MethodHandle filterArguments(MethodHandle target, MethodHandle... filters) { + MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { MethodType targetType = target.type(); MethodHandle adapter = target; MethodType adapterType = targetType; - int pos = -1, maxPos = targetType.parameterCount(); + int maxPos = targetType.parameterCount(); + int curPos = pos; for (MethodHandle filter : filters) { - pos += 1; - if (filter == null) continue; - if (pos >= maxPos) + if (curPos >= maxPos) throw newIllegalArgumentException("too many filters"); MethodType filterType = filter.type(); if (filterType.parameterCount() != 1 - || filterType.returnType() != targetType.parameterType(pos)) + || filterType.returnType() != targetType.parameterType(curPos)) throw newIllegalArgumentException("target and filter types do not match"); - adapterType = adapterType.changeParameterType(pos, filterType.parameterType(0)); - adapter = MethodHandleImpl.filterArgument(IMPL_TOKEN, adapter, pos, filter); + adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0)); + adapter = MethodHandleImpl.filterArgument(IMPL_TOKEN, adapter, curPos, filter); + curPos += 1; } MethodType midType = adapter.type(); if (midType != adapterType) @@ -1393,7 +1576,48 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * Adapt a target method handle {@code target} by post-processing + * its return value with a unary filter function. + * <p> + * If a filter {@code F} applies to the return value of + * the target method handle, then {@code F} must be a method handle which + * takes exactly one argument. The return type of {@code F} + * replaces the return type of the target + * in the resulting adapted method handle. + * The argument type of {@code F} must be identical to the + * return type of the target. + * <b>Example:</b> + * <p><blockquote><pre> +import static java.dyn.MethodHandles.*; +import static java.dyn.MethodType.*; +... +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +MethodHandle length = lookup().findVirtual(String.class, + "length", methodType(int.class)); +System.out.println((String) cat.invokeExact("x", "y")); // xy +MethodHandle f0 = filterReturnValue(cat, length); +System.out.println((int) f0.invokeExact("x", "y")); // 2 + * </pre></blockquote> + * @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 + * does not match the return type of {@code target} as described above + */ + public static + MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { + MethodType targetType = target.type(); + MethodType filterType = filter.type(); + if (filterType.parameterCount() != 1 + || filterType.parameterType(0) != targetType.returnType()) + throw newIllegalArgumentException("target and filter types do not match"); + // FIXME: Too many nodes here. + MethodHandle returner = dropArguments(filter, 0, targetType.parameterList()); + return foldArguments(returner, exactInvoker(target.type()).bindTo(target)); + } + + /** * Adapt a target method handle {@code target} by pre-processing * some of its arguments, and then calling the target with * the result of the pre-processing, plus all original arguments. @@ -1410,10 +1634,10 @@ * The resulting adapter is the same type as the target, except that the * initial argument type of the target is dropped. * <p> - * (Note that {@link #dropArguments} can be used to remove any arguments + * (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 #collectArguments} instead, since those + * consider using {@link MethodHandle#asCollector} instead, since those * arguments will not need to be live on the stack on entry to the * target.) * <p> @@ -1434,7 +1658,7 @@ * @return method handle which incorporates the specified argument folding logic * @throws IllegalArgumentException if the first argument type of * {@code target} is not the same as {@code combiner}'s return type, - * or if the next {@code foldArgs} argument types of {@code target} + * or if the following argument types of {@code target} * are not identical with the argument types of {@code combiner} */ public static @@ -1443,6 +1667,10 @@ MethodType combinerType = combiner.type(); int foldArgs = combinerType.parameterCount(); boolean ok = (targetType.parameterCount() >= 1 + foldArgs); + if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1))) + ok = false; + if (ok && !combinerType.returnType().equals(targetType.parameterType(0))) + ok = false; if (!ok) throw misMatchedTypes("target and combiner types", targetType, combinerType); MethodType newType = targetType.dropParameterTypes(0, 1); @@ -1450,7 +1678,6 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Make a method handle which adapts a target method handle, * by guarding it with a test, a boolean-valued method handle. * If the guard fails, a fallback handle is called instead. @@ -1470,6 +1697,9 @@ * return fallback(a..., b...); * } * </pre></blockquote> + * Note that the test arguments ({@code a...} in the pseudocode) cannot + * be modified by execution of the test, and so are passed unchanged + * from the caller to the target or fallback as appropriate. * @param test method handle used for test, must return boolean * @param target method handle to call if test passes * @param fallback method handle to call if test fails @@ -1485,40 +1715,19 @@ MethodType gtype = test.type(); MethodType ttype = target.type(); MethodType ftype = fallback.type(); - if (ttype != ftype) + if (!ttype.equals(ftype)) throw misMatchedTypes("target and fallback types", ttype, ftype); - MethodType gtype2 = ttype.changeReturnType(boolean.class); - if (gtype2 != gtype) { - if (gtype.returnType() != boolean.class) - throw newIllegalArgumentException("guard type is not a predicate "+gtype); - int gpc = gtype.parameterCount(), tpc = ttype.parameterCount(); - if (gpc < tpc) { - test = dropArguments(test, gpc, ttype.parameterList().subList(gpc, tpc)); - gtype = test.type(); - } - if (gtype2 != gtype) + if (gtype.returnType() != boolean.class) + throw newIllegalArgumentException("guard type is not a predicate "+gtype); + List<Class<?>> targs = ttype.parameterList(); + List<Class<?>> gargs = gtype.parameterList(); + if (!targs.equals(gargs)) { + int gpc = gargs.size(), tpc = targs.size(); + if (gpc >= tpc || !targs.subList(0, gpc).equals(gargs)) throw misMatchedTypes("target and test types", ttype, gtype); + test = dropArguments(test, gpc, targs.subList(gpc, tpc)); + gtype = test.type(); } - /* { - MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type()); - static MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) { - return z ? t : f; - } - static MethodHandle compose(MethodHandle f, MethodHandle g) { - Class<?> initargs = g.type().parameterArray(); - f = dropArguments(f, 1, initargs); // ignore 2nd copy of args - return combineArguments(f, g); - } - // choose = \z.(z ? target : fallback) - MethodHandle choose = findVirtual(MethodHandles.class, "choose", - MethodType.methodType(boolean.class, MethodHandle.class, MethodHandle.class)); - choose = appendArgument(choose, target); - choose = appendArgument(choose, fallback); - MethodHandle dispatch = compose(choose, test); - // dispatch = \(a...).(test(a...) ? target : fallback) - return combineArguments(invoke, dispatch, 0); - // return \(a...).((test(a...) ? target : fallback).invokeExact(a...)) - } */ return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback); } @@ -1527,29 +1736,38 @@ } /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Make a method handle which adapts a target method handle, * by running it inside an exception handler. * If the target returns normally, the adapter returns that value. * If an exception matching the specified type is thrown, the fallback * handle is called instead on the exception, plus the original arguments. * <p> - * The handler must have leading parameter of {@code exType} or a supertype, - * followed by arguments which correspond <em>(how? TBD)</em> to - * all the parameters of the target. - * The target and handler must return the same type. + * The target and handler must have the same corresponding + * argument and return types, except that handler may omit trailing arguments + * (similarly to the predicate in {@link #guardWithTest guardWithTest}). + * Also, the handler must have an extra leading parameter of {@code exType} or a supertype. * <p> Here is pseudocode for the resulting adapter: * <blockquote><pre> - * T target(A...); + * T target(A..., B...); * T handler(ExType, A...); - * T adapter(A... a) { + * T adapter(A... a, B... b) { * try { - * return target(a...); + * return target(a..., b...); * } catch (ExType ex) { * return handler(ex, a...); * } * } * </pre></blockquote> + * Note that the saved arguments ({@code a...} in the pseudocode) cannot + * be modified by execution of the target, and so are passed unchanged + * from the caller to the handler, if the handler is invoked. + * <p> + * The target and handler must return the same type, even if the handler + * always throws. (This might happen, for instance, because the handler + * is simulating a {@code finally} clause). + * To create such a throwing handler, compose the handler creation logic + * with {@link #throwException throwException}, + * in order to create a method handle of the correct return type. * @param target method handle to call * @param exType the type of exception which the handler will catch * @param handler method handle to call if a matching exception is thrown @@ -1563,16 +1781,23 @@ MethodHandle catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler) { - MethodType targetType = target.type(); - MethodType handlerType = handler.type(); - boolean ok = (targetType.parameterCount() == - handlerType.parameterCount() - 1); -// for (int i = 0; ok && i < numExArgs; i++) { -// if (targetType.parameterType(i) != handlerType.parameterType(1+i)) -// ok = false; -// } - if (!ok) - throw newIllegalArgumentException("target and handler types do not match"); + MethodType ttype = target.type(); + MethodType htype = handler.type(); + if (htype.parameterCount() < 1 || + !htype.parameterType(0).isAssignableFrom(exType)) + throw newIllegalArgumentException("handler does not accept exception type "+exType); + if (htype.returnType() != ttype.returnType()) + throw misMatchedTypes("target and handler return types", ttype, htype); + List<Class<?>> targs = ttype.parameterList(); + List<Class<?>> hargs = htype.parameterList(); + hargs = hargs.subList(1, hargs.size()); // omit leading parameter from handler + if (!targs.equals(hargs)) { + int hpc = hargs.size(), tpc = targs.size(); + if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs)) + throw misMatchedTypes("target and handler types", ttype, htype); + handler = dropArguments(handler, hpc, hargs.subList(hpc, tpc)); + htype = handler.type(); + } return MethodHandleImpl.makeGuardWithCatch(IMPL_TOKEN, target, exType, handler); } @@ -1590,10 +1815,11 @@ } /** - * Produce a wrapper instance of the given "SAM" type which redirects its calls to the given method handle. - * A SAM type is a type which declares a single abstract method. - * Additionally, it must have either no constructor (as an interface) - * or have a public or protected constructor of zero arguments (as a class). + * <em>PROVISIONAL API, WORK IN PROGRESS:</em> + * Produce a wrapper instance of the given "SAM" interface which redirects + * its calls to the given method handle. + * A SAM interface is an interface which declares a single abstract method. + * The type must be public. (No additional access checks are performed.) * <p> * The resulting instance of the required SAM type will respond to * invocation of the SAM type's single abstract method by calling @@ -1605,9 +1831,9 @@ * The method handle may throw an <em>undeclared exception</em>, * which means any checked exception (or other checked throwable) * not declared by the SAM type's single abstract method. - * If this happens, the throwable will be wrapped in an instance - * of {@link UndeclaredThrowableException} and thrown in that - * wrapped form. + * If this happens, the throwable will be wrapped in an instance of + * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException} + * and thrown in that wrapped form. * <p> * The wrapper instance is guaranteed to be of a non-public * implementation class C in a package containing no classes @@ -1618,19 +1844,36 @@ * <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 MethodHandleProvider} and its methods + * <li>{@link java.dyn.AsInstanceObject AsInstanceObject} and its methods</li> * </ul> * <p> + * (Note: When determining the unique abstract method of a SAM interface, + * the public {@code Object} methods ({@code toString}, {@code equals}, {@code hashCode}) + * are disregarded. For example, {@link java.util.Comparator} is a SAM interface, + * even though it re-declares the {@code Object.equals} method.) + * <p> * No stable mapping is promised between the SAM type and * the implementation class C. Over time, several implementation * classes might be used for the same SAM type. * <p> * This method is not guaranteed to return a distinct - * wrapper object for each separate call. If the JVM is able - * to prove that a wrapper has already been created for a given + * wrapper object for each separate call. If the implementation is able + * to prove that a wrapper of the required SAM type + * has already been created for a given * method handle, or for another method handle with the - * same behavior, the JVM may return that wrapper in place of + * same behavior, the implementation may return that wrapper in place of * a new wrapper. + * <p> + * This method is designed to apply to common use cases + * where a single method handle must interoperate with + * a type (class or interface) that implements a function-like + * API. Additional variations, such as SAM classes with + * private constructors, or interfaces with multiple but related + * entry points, must be covered by hand-written or automatically + * generated adapter classes. In those cases, consider implementing + * {@link java.dyn.MethodHandles.AsInstanceObject AsInstanceObject} + * in the adapters, so that generic code can extract the underlying + * method handle without knowing where the SAM adapter came from. * @param target the method handle to invoke from the wrapper * @param samType the desired type of the wrapper, a SAM type * @return a correctly-typed wrapper for the given {@code target} @@ -1639,38 +1882,93 @@ */ // ISSUE: Should we delegate equals/hashCode to the targets? // Not useful unless there is a stable equals/hashCode behavior - // for MethodHandle, and for MethodHandleProvider.asMethodHandle. + // for MethodHandle, but there isn't. public static - <T> T asInstance(MethodHandle target, Class<T> samType) { + <T> T asInstance(final MethodHandle target, final Class<T> samType) { // POC implementation only; violates the above contract several ways final Method sam = getSamMethod(samType); 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"); - final MethodHandle mh = target; + throw new IllegalArgumentException("wrong method type: "+target+" should match "+sam); return samType.cast(Proxy.newProxyInstance( samType.getClassLoader(), - new Class[]{ samType, MethodHandleProvider.class }, + new Class[]{ samType, AsInstanceObject.class }, new InvocationHandler() { + private Object getArg(String name) { + if ((Object)name == "getAsInstanceTarget") return target; + if ((Object)name == "getAsInstanceType") return samType; + throw new AssertionError(); + } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getDeclaringClass() == MethodHandleProvider.class) { - return method.invoke(mh, args); - } - assert method.equals(sam) : method; - return mh.invokeVarargs(args); + if (method.getDeclaringClass() == AsInstanceObject.class) + return getArg(method.getName()); + if (method.equals(sam)) + return target.invokeVarargs(args); + if (isObjectMethod(method)) + return callObjectMethod(this, method, args); + throw new InternalError(); } })); } + /** + * <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. + */ + 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(); + } + + private static + boolean isObjectMethod(Method m) { + switch (m.getName()) { + case "toString": + return (m.getReturnType() == String.class + && m.getParameterTypes().length == 0); + case "hashCode": + return (m.getReturnType() == int.class + && m.getParameterTypes().length == 0); + case "equals": + return (m.getReturnType() == boolean.class + && m.getParameterTypes().length == 1 + && m.getParameterTypes()[0] == Object.class); + } + return false; + } + + private static + Object callObjectMethod(Object self, Method m, Object[] args) { + assert(isObjectMethod(m)) : m; + switch (m.getName()) { + case "toString": + return self.getClass().getName() + "@" + Integer.toHexString(self.hashCode()); + case "hashCode": + return System.identityHashCode(self); + case "equals": + return (self == args[0]); + } + return null; + } + private static Method getSamMethod(Class<?> samType) { Method sam = null; for (Method m : samType.getMethods()) { int mod = m.getModifiers(); if (Modifier.isAbstract(mod)) { - if (sam != null) + if (sam != null && !isObjectMethod(sam)) return null; // too many abstract methods sam = m; } @@ -1691,4 +1989,9 @@ } return null; } + + /*non-public*/ + static MethodHandle withTypeHandler(MethodHandle target, MethodHandle typeHandler) { + return MethodHandleImpl.withTypeHandler(IMPL_TOKEN, target, typeHandler); + } }
--- a/src/share/classes/java/dyn/MethodType.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/MethodType.java Tue Dec 21 18:45:45 2010 -0800 @@ -56,21 +56,33 @@ * <p> * This type can be created only by factory methods. * All factory methods may cache values, though caching is not guaranteed. + * Some factory methods are static, while others are virtual methods which + * modify precursor method types, e.g., by changing a selected parameter. + * <p> + * Factory methods which operate on groups of parameter types + * are systematically presented in two versions, so that both Java arrays and + * Java lists can be used to work with groups of parameter types. + * The query methods {@code parameterArray} and {@code parameterList} + * also provide a choice between arrays and lists. * <p> * {@code MethodType} objects are sometimes derived from bytecode instructions * such as {@code invokedynamic}, specifically from the type descriptor strings associated * with the instructions in a class file's constant pool. - * When this occurs, any classes named in the descriptor strings must be loaded. - * (But they need not be initialized.) - * This loading may occur at any time before the {@code MethodType} object is first derived. * <p> - * Like classes and strings, method types can be represented directly - * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes. - * Loading such a constant causes its component classes to be loaded as necessary. + * Like classes and strings, method types can also be represented directly + * in a class file's constant pool as constants. The may be loaded by an {@code ldc} + * instruction which refers to a suitable {@code CONSTANT_MethodType} constant pool entry. + * The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string. + * For more details, see the <a href="package-summary.html#mtcon">package summary</a>. + * <p> + * When the JVM materializes a {@code MethodType} from a descriptor string, + * all classes named in the descriptor must be accessible, and will be loaded. + * (But the classes need not be initialized, as is the case with a {@code CONSTANT_Class}.) + * This loading may occur at any time before the {@code MethodType} object is first derived. * @author John Rose, JSR 292 EG */ public final -class MethodType implements java.lang.reflect.Type { +class MethodType { private final Class<?> rtype; private final Class<?>[] ptypes; private MethodTypeForm form; // erased form, plus cached data about primitives @@ -119,7 +131,7 @@ for (Class<?> ptype : ptypes) { ptype.equals(ptype); // null check if (ptype == void.class) - throw newIllegalArgumentException("void parameter: "+this); + throw newIllegalArgumentException("parameter type cannot be void"); } } @@ -139,10 +151,6 @@ MethodType methodType(Class<?> rtype, Class<?>[] ptypes) { return makeImpl(rtype, ptypes, false); } - @Deprecated public static - MethodType make(Class<?> rtype, Class<?>[] ptypes) { - return methodType(rtype, ptypes); - } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. */ public static @@ -150,10 +158,6 @@ boolean notrust = false; // random List impl. could return evil ptypes array return makeImpl(rtype, ptypes.toArray(NO_PTYPES), notrust); } - @Deprecated public static - MethodType make(Class<?> rtype, List<? extends Class<?>> ptypes) { - return methodType(rtype, ptypes); - } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * The leading parameter type is prepended to the remaining array. @@ -165,10 +169,6 @@ System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length); return makeImpl(rtype, ptypes1, true); } - @Deprecated public static - MethodType make(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) { - return methodType(rtype, ptype0, ptypes); - } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * The resulting method has no parameter types. @@ -177,10 +177,6 @@ MethodType methodType(Class<?> rtype) { return makeImpl(rtype, NO_PTYPES, true); } - @Deprecated public static - MethodType make(Class<?> rtype) { - return methodType(rtype); - } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * The resulting method has the single given parameter type. @@ -189,10 +185,6 @@ MethodType methodType(Class<?> rtype, Class<?> ptype0) { return makeImpl(rtype, new Class<?>[]{ ptype0 }, true); } - @Deprecated public static - MethodType make(Class<?> rtype, Class<?> ptype0) { - return methodType(rtype, ptype0); - } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * The resulting method has the same parameter types as {@code ptypes}, @@ -202,10 +194,6 @@ MethodType methodType(Class<?> rtype, MethodType ptypes) { return makeImpl(rtype, ptypes.ptypes, true); } - @Deprecated public static - MethodType make(Class<?> rtype, MethodType ptypes) { - return methodType(rtype, ptypes); - } /** * Sole factory method to find or create an interned method type. @@ -275,10 +263,6 @@ } return mt; } - @Deprecated public static - MethodType makeGeneric(int objectArgCount, boolean varargs) { - return genericMethodType(objectArgCount, varargs); - } /** * All parameters and the return type will be Object. @@ -290,10 +274,6 @@ MethodType genericMethodType(int objectArgCount) { return genericMethodType(objectArgCount, false); } - @Deprecated public static - MethodType makeGeneric(int objectArgCount) { - return genericMethodType(objectArgCount); - } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * @param num the index (zero-based) of the parameter type to change @@ -307,18 +287,6 @@ return makeImpl(rtype, nptypes, true); } - /** Convenience method for {@link #insertParameterTypes}. - * @deprecated Use {@link #insertParameterTypes} instead. - */ - @Deprecated - public MethodType insertParameterType(int num, Class<?> nptype) { - int len = ptypes.length; - Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1); - System.arraycopy(nptypes, num, nptypes, num+1, len-num); - nptypes[num] = nptype; - return makeImpl(rtype, nptypes, true); - } - /** 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 @@ -337,6 +305,22 @@ } /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + * @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list + * @return the same type, except with the selected parameter(s) appended + */ + public MethodType appendParameterTypes(Class<?>... ptypesToInsert) { + return insertParameterTypes(parameterCount(), ptypesToInsert); + } + + /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. + * @param ptypesToInsert zero or more a new parameter types to insert after the end of the parameter list + * @return the same type, except with the selected parameter(s) appended + */ + public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) { + return insertParameterTypes(parameterCount(), ptypesToInsert); + } + + /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * @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 @@ -377,14 +361,6 @@ return makeImpl(rtype, nptypes, true); } - /** Convenience method for {@link #dropParameterTypes}. - * @deprecated Use {@link #dropParameterTypes} instead. - */ - @Deprecated - public MethodType dropParameterType(int num) { - return dropParameterTypes(num, num+1); - } - /** Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[])}. * @param nrtype a return parameter type to replace the old one with * @return the same type, except with the return type change @@ -552,7 +528,9 @@ * parenthesis enclosed, comma separated list of type names, * followed immediately by the return type. * <p> - * If a type name is array, it the base type followed + * Each type is represented by its + * {@link java.lang.Class#getSimpleName simple name}. + * If a type name name is array, it the base type followed * by [], rather than the Class.getName of the array type. */ @Override @@ -561,35 +539,13 @@ sb.append("("); for (int i = 0; i < ptypes.length; i++) { if (i > 0) sb.append(","); - putName(sb, ptypes[i]); + sb.append(ptypes[i].getSimpleName()); } sb.append(")"); - putName(sb, rtype); + sb.append(rtype.getSimpleName()); return sb.toString(); } - static void putName(StringBuilder sb, Class<?> cls) { - int brackets = 0; - while (cls.isArray()) { - cls = cls.getComponentType(); - brackets++; - } - String n = cls.getName(); - /* - if (n.startsWith("java.lang.")) { - String nb = n.substring("java.lang.".length()); - if (nb.indexOf('.') < 0) n = nb; - } else if (n.indexOf('.') < 0) { - n = "."+n; // anonymous package - } - */ - sb.append(n); - while (brackets > 0) { - sb.append("[]"); - brackets--; - } - } - /// Queries which have to do with the bytecode architecture /** The number of JVM stack slots required to invoke a method @@ -690,14 +646,4 @@ public String toMethodDescriptorString() { return BytecodeDescriptor.unparse(this); } - - /** Temporary alias for toMethodDescriptorString; delete after M3. */ - public String toBytecodeString() { - return toMethodDescriptorString(); - } - /** Temporary alias for fromMethodDescriptorString; delete after M3. */ - public static MethodType fromBytecodeString(String descriptor, ClassLoader loader) - throws IllegalArgumentException, TypeNotPresentException { - return fromMethodDescriptorString(descriptor, loader); - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/MutableCallSite.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.dyn; + +import sun.dyn.*; +import sun.dyn.empty.Empty; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A {@code MutableCallSite} is a {@link CallSite} whose target variable + * behaves like an ordinary field. + * An {@code invokedynamic} instruction linked to a {@code MutableCallSite} delegates + * all calls to the site's current target. + * The {@linkplain CallSite#dynamicInvoker dynamic invoker} of a mutable call site + * also delegates each call to the site's current target. + * <p> + * Here is an example of a mutable call site which introduces a + * state variable into a method handle chain. + * <blockquote><pre> +MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class)); +MethodHandle MH_name = name.dynamicInvoker(); +MethodType MT_str2 = MethodType.methodType(String.class, String.class); +MethodHandle MH_upcase = MethodHandles.lookup() + .findVirtual(String.class, "toUpperCase", MT_str2); +MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase); +name.setTarget(MethodHandles.constant(String.class, "Rocky")); +assertEquals("ROCKY", (String) worker1.invokeExact()); +name.setTarget(MethodHandles.constant(String.class, "Fred")); +assertEquals("FRED", (String) worker1.invokeExact()); +// (mutation can be continued indefinitely) + * </pre></blockquote> + * <p> + * The same call site may be used in several places at once. + * <blockquote><pre> +MethodHandle MH_dear = MethodHandles.lookup() + .findVirtual(String.class, "concat", MT_str2).bindTo(", dear?"); +MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear); +assertEquals("Fred, dear?", (String) worker2.invokeExact()); +name.setTarget(MethodHandles.constant(String.class, "Wilma")); +assertEquals("WILMA", (String) worker1.invokeExact()); +assertEquals("Wilma, dear?", (String) worker2.invokeExact()); + * </pre></blockquote> + * <p> + * <em>Non-synchronization of target values:</em> + * A write to a mutable call site's target does not force other threads + * to become aware of the updated value. Threads which do not perform + * suitable synchronization actions relative to the updated call site + * may cache the old target value and delay their use of the new target + * value indefinitely. + * (This is a normal consequence of the Java Memory Model as applied + * to object fields.) + * <p> + * The {@link #sync sync} operation provides a way to force threads + * to accept a new target value, even if there is no other synchronization. + * <p> + * For target values which will be frequently updated, consider using + * a {@linkplain VolatileCallSite volatile call site} instead. + * @author John Rose, JSR 292 EG + */ +public class MutableCallSite extends CallSite { + /** + * Make a blank call site object with the given method type. + * An initial target method is supplied which will throw + * an {@link IllegalStateException} if called. + * <p> + * Before this {@code CallSite} object is returned from a bootstrap method, + * it is usually provided with a more useful target method, + * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}. + * @throws NullPointerException if the proposed type is null + */ + public MutableCallSite(MethodType type) { + super(type); + } + + /** + * Make a blank call site object, possibly equipped with an initial target method handle. + * @param target the method handle which will be the initial target of the call site + * @throws NullPointerException if the proposed target is null + */ + public MutableCallSite(MethodHandle target) { + super(target); + } + + /** + * Perform a synchronization operation on each call site in the given array, + * forcing all other threads to throw away any cached values previously + * loaded from the target of any of the call sites. + * <p> + * This operation does not reverse any calls that have already started + * on an old target value. + * (Java supports {@linkplain java.lang.Object#wait() forward time travel} only.) + * <p> + * The overall effect is to force all future readers of each call site's target + * to accept the most recently stored value. + * ("Most recently" is reckoned relative to the {@code sync} itself.) + * Conversely, the {@code sync} call may block until all readers have + * (somehow) decached all previous versions of each call site's target. + * <p> + * To avoid race conditions, calls to {@code setTarget} and {@code sync} + * should generally be performed under some sort of mutual exclusion. + * Note that reader threads may observe an updated target as early + * as the {@code setTarget} call that install the value + * (and before the {@code sync} that confirms the value). + * On the other hand, reader threads may observe previous versions of + * the target until the {@code sync} call returns + * (and after the {@code setTarget} that attempts to convey the updated version). + * <p> + * In terms of the Java Memory Model, this operation performs a synchronization + * action which is comparable in effect to the writing of a volatile variable + * by the current thread, and an eventual volatile read by every other thread + * that may access one of the affected call sites. + * <p> + * The following effects are apparent, for each individual call site {@code S}: + * <ul> + * <li>A new volatile variable {@code V} is created, and written by the current thread. + * As defined by the JMM, this write is a global synchronization event. + * <li>As is normal with thread-local ordering of write events, + * every action already performed by the current thread is + * taken to happen before the volatile write to {@code V}. + * (In some implementations, this means that the current thread + * performs a global release operation.) + * <li>Specifically, the write to the current target of {@code S} is + * taken to happen before the volatile write to {@code V}. + * <li>The volatile write to {@code V} is placed + * (in an implementation specific manner) + * in the global synchronization order. + * <li>Consider an arbitrary thread {@code T} (other than the current thread). + * If {@code T} executes a synchronization action {@code A} + * after the volatile write to {@code V} (in the global synchronization order), + * it is therefore required to see either the current target + * of {@code S}, or a later write to that target, + * if it executes a read on the target of {@code S}. + * (This constraint is called "synchronization-order consistency".) + * <li>The JMM specifically allows optimizing compilers to elide + * reads or writes of variables that are known to be useless. + * Such elided reads and writes have no effect on the happens-before + * relation. Regardless of this fact, the volatile {@code V} + * will not be elided, even though its written value is + * indeterminate and its read value is not used. + * </ul> + * Because of the last point, the implementation behaves as if a + * volatile read of {@code V} were performed by {@code T} + * immediately after its action {@code A}. In the local ordering + * of actions in {@code T}, this read happens before any future + * read of the target of {@code S}. It is as if the + * implementation arbitrarily picked a read of {@code S}'s target + * by {@code T}, and forced a read of {@code V} to precede it, + * thereby ensuring communication of the new target value. + * <p> + * As long as the constraints of the Java Memory Model are obeyed, + * implementations may delay the completion of a {@code sync} + * operation while other threads ({@code T} above) continue to + * use previous values of {@code S}'s target. + * However, implementations are (as always) encouraged to avoid + * livelock, and to eventually require all threads to take account + * of the updated target. + * <p> + * This operation is likely to be expensive and should be used sparingly. + * If possible, it should be buffered for batch processing on sets of call sites. + * <p style="font-size:smaller;"> + * (This is a static method on a set of call sites, not a + * virtual method on a single call site, for performance reasons. + * Some implementations may incur a large fixed overhead cost + * for processing one or more synchronization operations, + * but a small incremental cost for each additional call site. + * In any case, this operation is likely to be costly, since + * other threads may have to be somehow interrupted + * in order to make them notice the updated target value. + * However, it may be observed that a single call to synchronize + * several sites has the same formal effect as many calls, + * each on just one of the sites.) + * <p> + * Simple implementations of {@code MutableCallSite} may use + * a volatile variable for the target of a mutable call site. + * In such an implementation, the {@code sync} method can be a no-op, + * and yet it will conform to the JMM behavior documented above. + */ + public static void sync(MutableCallSite[] sites) { + STORE_BARRIER.lazySet(0); + // FIXME: NYI + } + private static final AtomicInteger STORE_BARRIER = new AtomicInteger(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/Switcher.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.dyn; + +/** + * <p> + * A {@code Switcher} is an object which can publish state transitions to other threads. + * A switcher is initially in the <em>valid</em> state, but may at any time be + * changed to the <em>invalid</em> state. Invalidation cannot be reversed. + * <p> + * A single switcher may be used to create any number of guarded method handle pairs. + * Each guarded pair is wrapped in a new method handle {@code M}, + * which is permanently associated with the switcher that created it. + * Each pair consists of a target {@code T} and a fallback {@code F}. + * While the switcher is valid, invocations to {@code M} are delegated to {@code T}. + * After it is invalidated, invocations are delegated to {@code F}. + * <p> + * Invalidation is global and immediate, as if the switcher contained a + * volatile boolean variable consulted on every call to {@code M}. + * The invalidation is also permanent, which means the switcher + * can change state only once. + * <p> + * Here is an example of a switcher in action: + * <blockquote><pre> +MethodType MT_str2 = MethodType.methodType(String.class, String.class); +MethodHandle MH_strcat = MethodHandles.lookup() + .findVirtual(String.class, "concat", MT_str2); +Switcher switcher = new Switcher(); +// the following steps may be repeated to re-use the same switcher: +MethodHandle worker1 = strcat; +MethodHandle worker2 = MethodHandles.permuteArguments(strcat, MT_str2, 1, 0); +MethodHandle worker = switcher.guardWithTest(worker1, worker2); +assertEquals("method", (String) worker.invokeExact("met", "hod")); +switcher.invalidate(); +assertEquals("hodmet", (String) worker.invokeExact("met", "hod")); + * </pre></blockquote> + * <p> + * <em>Implementation Note:</em> + * A switcher behaves as if implemented on top of {@link MutableCallSite}, + * approximately as follows: + * <blockquote><pre> +public class Switcher { + private static final MethodHandle + K_true = MethodHandles.constant(boolean.class, true), + K_false = MethodHandles.constant(boolean.class, false); + private final MutableCallSite mcs; + private final MethodHandle mcsInvoker; + public Switcher() { + this.mcs = new MutableCallSite(K_true); + this.mcsInvoker = mcs.dynamicInvoker(); + } + public MethodHandle guardWithTest( + MethodHandle target, MethodHandle fallback) { + // Note: mcsInvoker is of type boolean(). + // Target and fallback may take any arguments, but must have the same type. + return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback); + } + public static void invalidateAll(Switcher[] switchers) { + List<MutableCallSite> mcss = new ArrayList<>(); + for (Switcher s : switchers) mcss.add(s.mcs); + for (MutableCallSite mcs : mcss) mcs.setTarget(K_false); + MutableCallSite.sync(mcss.toArray(new MutableCallSite[0])); + } +} + * </pre></blockquote> + * @author Remi Forax, JSR 292 EG + */ +public class Switcher { + private static final MethodHandle + K_true = MethodHandles.constant(boolean.class, true), + K_false = MethodHandles.constant(boolean.class, false); + + private final MutableCallSite mcs; + private final MethodHandle mcsInvoker; + + /** Create a switcher. */ + public Switcher() { + this.mcs = new MutableCallSite(K_true); + this.mcsInvoker = mcs.dynamicInvoker(); + } + + /** + * Return a method handle which always delegates either to the target or the fallback. + * The method handle will delegate to the target exactly as long as the switcher is valid. + * After that, it will permanently delegate to the fallback. + * <p> + * The target and fallback must be of exactly the same method type, + * and the resulting combined method handle will also be of this type. + * @see MethodHandles#guardWithTest + */ + public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) { + if (mcs.getTarget() == K_false) + return fallback; // already invalid + return MethodHandles.guardWithTest(mcsInvoker, target, fallback); + } + + /** Set all of the given switchers into the invalid state. */ + public static void invalidateAll(Switcher[] switchers) { + MutableCallSite[] sites = new MutableCallSite[switchers.length]; + int fillp = 0; + for (Switcher switcher : switchers) { + sites[fillp++] = switcher.mcs; + switcher.mcs.setTarget(K_false); + } + MutableCallSite.sync(sites); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/dyn/VolatileCallSite.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,79 @@ +/* + * 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; + +import java.util.List; + +/** + * A {@code VolatileCallSite} is a {@link CallSite} whose target acts like a volatile variable. + * An {@code invokedynamic} instruction linked to a {@code VolatileCallSite} sees updates + * to its call site target immediately, even if the update occurs in another thread. + * There may be a performance penalty for such tight coupling between threads. + * <p> + * Unlike {@code MutableCallSite}, there is no + * {@linkplain MutableCallSite#sync sync operation} on volatile + * call sites, since every write to a volatile variable is implicitly + * synchronized with reader threads. + * <p> + * In other respects, a {@code VolatileCallSite} is interchangeable + * with {@code MutableCallSite}. + * @see MutableCallSite + * @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}. + * @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. + * @throws NullPointerException if the proposed target is null + */ + public VolatileCallSite(MethodHandle target) { + super(target); + } + + /** Internal override to nominally final getTarget. */ + @Override + MethodHandle getTarget0() { + 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. + */ + @Override public void setTarget(MethodHandle newTarget) { + checkTargetChange(getTargetVolatile(), newTarget); + setTargetVolatile(newTarget); + } +}
--- a/src/share/classes/java/dyn/package-info.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/java/dyn/package-info.java Tue Dec 21 18:45:45 2010 -0800 @@ -24,7 +24,6 @@ */ /** - * <em>PROVISIONAL API, WORK IN PROGRESS:</em> * This package contains dynamic language support provided directly by * the Java core class libraries and virtual machine. * <p> @@ -42,13 +41,6 @@ * argument and return value conversions are applied. * </li> * - * <li>In source code, the class {@link java.dyn.InvokeDynamic InvokeDynamic} appears to accept - * any static method invocation, of any name and any signature. - * But instead of emitting - * an {@code invokestatic} instruction for such a call, the Java compiler emits - * an {@code invokedynamic} instruction with the given name and signature. - * </li> - * * <li>The JVM bytecode format supports immediate constants of * the classes {@link java.dyn.MethodHandle MethodHandle} and {@link java.dyn.MethodType MethodType}. * </li> @@ -56,51 +48,68 @@ * * <h2><a name="jvm_mods"></a>Corresponding JVM bytecode format changes</h2> * <em>The following low-level information is presented here as a preview of - * changes being made to the Java Virtual Machine specification for JSR 292.</em> + * changes being made to the Java Virtual Machine specification for JSR 292. + * This information will be incorporated in a future version of the JVM specification.</em> * - * <h3>{@code invokedynamic} instruction format</h3> + * <h3><a name="indyinsn"></a>{@code invokedynamic} instruction format</h3> * In bytecode, an {@code invokedynamic} instruction is formatted as five bytes. * The first byte is the opcode 186 (hexadecimal {@code BA}). * The next two bytes are a constant pool index (in the same format as for the other {@code invoke} instructions). * 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 17). See below for its format. - * The entry specifies the bootstrap method (a {@link java.dyn.MethodHandle MethodHandle} constant), - * the dynamic invocation name, and the argument types and return type of the call. + * 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>optionally, a sequence of additional <em>static arguments</em> to the bootstrap method ({@code ldc}-type constants)</li> + * </ul> * <p> * Each instance of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>. * Multiple instances of an {@code invokedynamic} instruction can share a single * {@code CONSTANT_InvokeDynamic} entry. * In any case, distinct call sites always have distinct linkage state. * <p> - * Moreover, for the purpose of distinguishing dynamic call sites, - * the JVM is allowed (but not required) to make internal copies - * of {@code invokedynamic} instructions, each one - * constituting a separate dynamic call site with its own linkage state. - * Such copying, if it occurs, cannot be observed except indirectly via - * execution of bootstrap methods and target methods. - * <p> * A dynamic call site is originally in an unlinked state. In this state, there is * no target method for the call site to invoke. * A dynamic call site is linked by means of a bootstrap method, * as <a href="#bsm">described below</a>. - * <p> - * <em>(Historic Note: Some older JVMs may allow the index of a {@code CONSTANT_NameAndType} + * + * <p style="font-size:smaller;"> + * (Historic Note: 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.)</em> + * bootstrap method was specified dynamically, in a per-class basis, during class initialization.) * - * <h3>constant pool entries for {@code invokedynamic} instructions</h3> - * If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 17), - * it must contain exactly four more bytes. - * The first two bytes after the tag must be an index to a {@code CONSTANT_MethodHandle} - * entry, and the second two bytes must be an index to a {@code CONSTANT_NameAndType}. + * <h3><a name="indycon"></a>constant pool entries for {@code invokedynamic} instructions</h3> + * If a constant pool entry has the tag {@code CONSTANT_InvokeDynamic} (decimal 18), + * it must contain exactly four more bytes after the tag. + * These bytes are interpreted as two 16-bit indexes, in the usual {@code u2} format. + * The first pair of bytes after the tag must be an index into a side table called the + * <em>bootstrap method table</em>, which is stored in the {@code BootstrapMethods} + * attribute as <a href="#bsmattr">described below</a>. + * The second pair of bytes must be an index to a {@code CONSTANT_NameAndType}. + * 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 {@code CONSTANT_Class} reference in a {@code CONSTANT_Methodref} entry - * is replaced by a bootstrap method reference. + * 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. * - * <h3>constant pool entries for {@code MethodType}s</h3> + * <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. @@ -113,7 +122,7 @@ * Access checking and error reporting is performed exactly as it is for * references by {@code ldc} instructions to {@code CONSTANT_Class} constants. * - * <h3>constant pool entries for {@code MethodHandle}s</h3> + * <h3><a name="mhcon"></a>constant pool entries for {@linkplain java.dyn.MethodHandle method handles}</h3> * If a constant pool entry has the tag {@code CONSTANT_MethodHandle} (decimal 15), * it must contain exactly three more bytes. The first byte after the tag is a subtag * value which must be in the range 1 through 9, and the last two must be an index to a @@ -129,7 +138,7 @@ * <p> * As with {@code CONSTANT_Class} and {@code CONSTANT_MethodType} constants, * the {@code Class} or {@code MethodType} object which reifies the field or method's - * type is created. Any classes mentioned in this reificaiton will be loaded if necessary, + * 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> * The method handle itself will have a type and behavior determined by the subtag as follows: @@ -148,16 +157,45 @@ * </table> * </code> * <p> - * The special names {@code <init>} and {@code <clinit>} are not allowed except for subtag 8 as shown. + * The special name {@code <clinit>} is not allowed. + * The special name {@code <init>} is not allowed except for subtag 8 as shown. * <p> - * The verifier applies the same access checks and restrictions for these references as for the hypothetical + * The JVM verifier and linker apply the same access checks and restrictions for these references as for the hypothetical * bytecode instructions specified in the last column of the table. In particular, method handles to * private and protected members can be created in exactly those classes for which the corresponding * normal accesses are legal. * <p> - * None of these constant types force class initialization. - * Method handles for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic} + * 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 + * 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. + * Method handle constants for subtags {@code REF_getStatic}, {@code REF_putStatic}, and {@code REF_invokeStatic} * may force class initialization on their first invocation, just like the corresponding bytecodes. + * <p> + * The rules of section 5.4.3 of the + * <a href="http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#73492">JVM Specification</a> + * apply to the resolution of {@code CONSTANT_MethodType}, {@code CONSTANT_MethodHandle}, + * and {@code CONSTANT_InvokeDynamic} constants, + * by the execution of {@code invokedynamic} and {@code ldc} instructions. + * (Roughly speaking, this means that every use of a constant pool entry + * must lead to the same outcome. + * If the resoultion succeeds, the same object reference is produced + * by every subsequent execution of the same instruction. + * If the resolution of the constant causes an error to occur, + * the same error will be re-thrown on every subsequent attempt + * to use this particular constant.) + * <p> + * Constants created by the resolution of these constant pool types are not necessarily + * interned. Except for {@link CONSTANT_Class} and {@link CONSTANT_String} entries, + * two distinct constant pool entries might not resolve to the same reference + * even if they contain the same symbolic reference. * * <h2><a name="bsm"></a>Bootstrap Methods</h2> * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction), @@ -181,24 +219,36 @@ * call site execution. * Linkage does not trigger class initialization. * <p> - * Next, the bootstrap method call is started, with four values being stacked: + * Next, the bootstrap method call is started, with four or five values being stacked: * <ul> * <li>a {@code MethodHandle}, the resolved bootstrap method itself </li> - * <li>a {@code Class}, the <em>caller class</em> in which dynamic call site occurs </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> * </ul> * The method handle is then applied to the other values as if by - * {@linkplain java.dyn.MethodHandle#invokeGeneric the <code>invokeGeneric</code> method}. - * The returned result must be a {@link java.dyn.CallSite CallSite}, a {@link java.dyn.MethodHandle MethodHandle}, - * or another {@link java.dyn.MethodHandleProvider MethodHandleProvider} value. - * The method {@linkplain java.dyn.MethodHandleProvider#asMethodHandle asMethodHandle} - * is then called on the returned value. The result of that second - * call is the {@code MethodHandle} which becomes the - * permanent binding for the dynamic call site. - * That method handle's type must be exactly equal to the type + * {@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 * the bootstrap method. + * The call site then becomes permanently linked to the dynamic call site. + * <p> + * As long as each bootstrap method can be correctly invoked + * by <code>invokeGeneric</code>, its detailed type is arbitrary. + * For example, the first argument could be {@code Object} + * instead of {@code MethodHandles.Lookup}, and the return type + * could also be {@code Object} instead of {@code CallSite}. + * <p> + * As with any method handle constant, a {@code varargs} modifier bit + * on the bootstrap method is ignored. + * <p> + * Note that the first argument of the bootstrap method cannot be + * a simple {@code Class} reference. (This is a change from earlier + * versions of this specification. If the caller class is needed, + * it is easy to {@linkplain java.dyn.MethodHandles.Lookup#lookupClass() extract it} + * from the {@code Lookup} object.) * <p> * After resolution, the linkage process may fail in a variety of ways. * All failures are reported by an {@link java.dyn.InvokeDynamicBootstrapError InvokeDynamicBootstrapError}, @@ -206,81 +256,209 @@ * site execution. * The following circumstances will cause this: * <ul> + * <li>the index to the bootstrap method specifier is out of range </li> + * <li>the bootstrap method cannot be resolved </li> + * <li>the {@code MethodType} to pass to the bootstrap method cannot be resolved </li> + * <li>a static argument to the bootstrap method cannot be resolved + * (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType}, + * or {@code CONSTANT_MethodHandle} argument cannot be linked) </li> + * <li>the bootstrap method has the wrong arity, + * causing {@code invokeGeneric} to throw {@code WrongMethodTypeException} </li> + * <li>the bootstrap method has a wrong argument or return type </li> * <li>the bootstrap method invocation completes abnormally </li> * <li>the result from the bootstrap invocation is not a reference to - * an object of type {@link java.dyn.MethodHandleProvider MethodHandleProvider} </li> - * <li>the call to {@code asMethodHandle} completes abnormally </li> - * <li>the call to {@code asMethodHandle} fails to return a reference to - * an object of type {@link java.dyn.MethodHandle MethodHandle} </li> - * <li>the method handle produced by {@code asMethodHandle} does not have + * an object of type {@link java.dyn.CallSite CallSite} </li> + * <li>the target of the {@code CallSite} does not have a target of * the expected {@code MethodType} </li> * </ul> - * <h3>timing of linkage</h3> + * + * <h3><a name="linktime"></a>timing of linkage</h3> * A dynamic call site is linked just before its first execution. * The bootstrap method call implementing the linkage occurs within * a thread that is attempting a first execution. * <p> - * If there are several such threads, the JVM picks one thread - * and runs the bootstrap method while the others wait for the - * invocation to terminate normally or abnormally. - * <p> - * After a bootstrap method is called and a method handle target - * successfully extracted, the JVM attempts to link the instruction - * being executed to the target method handle. - * This may fail if there has been intervening linkage - * or invalidation event for the same instruction. - * If such a failure occurs, the dynamic call site must be - * re-executed from the beginning, either re-linking it - * (if it has been invalidated) or invoking the target - * (if it the instruction has been linked by some other means). - * <p> - * If the instruction is linked successfully, the target method - * handle is invoked to complete the instruction execution. - * The state of linkage continues until the method containing the - * dynamic call site is garbage collected, or the dynamic call site - * is invalidated by an explicit request, - * such as {@link java.dyn.Linkage#invalidateCallerClass Linkage.invalidateCallerClass}. + * If there are several such threads, the bootstrap method may be + * invoked in several threads concurrently. + * Therefore, bootstrap methods which access global application + * data must take the usual precautions against race conditions. + * In any case, every {@code invokedynamic} instruction is either + * unlinked or linked to a unique {@code CallSite} object. * <p> * In an application which requires dynamic call sites with individually * mutable behaviors, their bootstrap methods should produce distinct * {@link java.dyn.CallSite CallSite} objects, one for each linkage request. - * <p> - * If a class containing {@code invokedynamic} instructions - * is {@linkplain java.dyn.Linkage#invalidateCallerClass(Class) invalidated}, - * subsequent execution of those {@code invokedynamic} instructions - * will require linking. - * It is as if they had never been executed in the first place. - * (However, invalidation does not cause constant pool entries to be - * resolved a second time.) - * <p> - * Invalidation events and bootstrap method calls for a particular - * dynamic call site are globally ordered relative to each other. - * When an invokedynamic instruction is invalidated, if there is - * simultaneously a bootstrap method invocation in process - * (in the same thread or a different thread), the result - * eventually returned must not be used to link the call site. - * Put another way, when a call site is invalidated, its - * subsequent linkage (if any) must be performed by a bootstrap method - * call initiated after the invalidation occurred. + * Alternatively, an application can link a single {@code CallSite} object + * to several {@code invokedynamic} instructions, in which case + * a change to the target method will become visible at each of + * the instructions. * <p> * If several threads simultaneously execute a bootstrap method for a single dynamic - * call site, the JVM must choose one target object and installs it visibly to + * call site, the JVM must choose one {@code CallSite} object and install it visibly to * all threads. Any other bootstrap method calls are allowed to complete, but their * results are ignored, and their dynamic call site invocations proceed with the originally * chosen target object. + * + * <p style="font-size:smaller;"> + * (Historic Note: 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.) + * + * <h3><a name="bsmattr">the {@code BootstrapMethods} attribute </h3> + * Each {@code CONSTANT_InvokeDynamic} entry contains an index which references + * a bootstrap method specifier; all such specifiers are contained in a separate array. + * This array is defined by a class attribute named {@code BootstrapMethods}. + * The body of this attribute consists of a sequence of byte pairs, all interpreted as + * as 16-bit counts or constant pool indexes, in the {@code u2} format. + * The attribute body starts with a count of bootstrap method specifiers, + * which is immediately followed by the sequence of specifiers. * <p> - * The JVM is free to duplicate dynamic call sites. - * This means that, even if a class contains just one {@code invokedynamic} - * instruction, its bootstrap method may be executed several times, - * once for each duplicate. Thus, bootstrap method code should not - * assume an exclusive one-to-one correspondence between particular occurrences - * of {@code invokedynamic} bytecodes in class files and linkage events. + * Each bootstrap method specifier contains an index to a + * {@code CONSTANT_MethodHandle} constant, which is the bootstrap + * method itself. + * This is followed by a count, and then a sequence (perhaps empty) of + * indexes to <a href="#args">additional static arguments</a> + * for the bootstrap method. + * <p> + * During class loading, the verifier must check the structure of the + * {@code BootstrapMethods} attribute. In particular, each constant + * pool index must be of the correct type. A bootstrap method index + * must refer to a {@code CONSTANT_MethodHandle} (tag 15). + * Every other index must refer to a valid operand of an + * {@code ldc_w} or {@code ldc2_w} instruction (tag 3..8 or 15..16). + * + * <h3><a name="args">static arguments to the bootstrap method</h3> + * An {@code invokedynamic} instruction specifies at least three arguments + * to pass to its bootstrap method: + * The caller class (expressed as a {@link java.dyn.MethodHandles.Lookup Lookup object}, + * the name (extracted from the {@code CONSTANT_NameAndType} entry), + * and the type (also extracted from the {@code CONSTANT_NameAndType} entry). + * The {@code invokedynamic} instruction may specify additional metadata values + * to pass to its bootstrap method. + * Collectively, these values are called <em>static arguments</em> to the + * {@code invokedynamic} instruction, because they are used once at link + * time to determine the instruction's behavior on subsequent sets of + * <em>dynamic arguments</em>. + * <p> + * Static arguments are used to communicate application-specific meta-data + * to the bootstrap method. + * Drawn from the constant pool, they may include references to classes, method handles, + * strings, or numeric data that may be relevant to the task of linking that particular call site. + * <p> + * Static arguments are specified constant pool indexes stored in the {@code BootstrapMethods} attribute. + * Before the bootstrap method is invoked, each index is used to compute an {@code Object} + * reference to the indexed value in the constant pool. + * 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"> + * <tr><th>entry type</th><th>argument type</th><th>argument value</th></tr> + * <tr><td>CONSTANT_String</td><td><code>java.lang.String</code></td><td>the indexed string literal</td></tr> + * <tr><td>CONSTANT_Class</td><td><code>java.lang.Class</code></td><td>the indexed class, resolved</td></tr> + * <tr><td>CONSTANT_Integer</td><td><code>java.lang.Integer</code></td><td>the indexed int value</td></tr> + * <tr><td>CONSTANT_Long</td><td><code>java.lang.Long</code></td><td>the indexed long value</td></tr> + * <tr><td>CONSTANT_Float</td><td><code>java.lang.Float</code></td><td>the indexed float value</td></tr> + * <tr><td>CONSTANT_Double</td><td><code>java.lang.Double</code></td><td>the indexed double value</td></tr> + * <tr><td>CONSTANT_MethodHandle</td><td><code>java.dyn.MethodHandle</code></td><td>the indexed method handle constant</td></tr> + * <tr><td>CONSTANT_MethodType</td><td><code>java.dyn.MethodType</code></td><td>the indexed method type constant</td></tr> + * </table> + * </code> + * <p> + * If a given {@code invokedynamic} instruction specifies no static arguments, + * 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. + * <code> + * <table border=1 cellpadding=5 summary="Static argument types"> + * <tr><th>N</th><th>sample bootstrap method</th></tr> + * <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type)</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> + * </table> + * </code> * <p> - * In principle, each individual execution of an {@code invokedynamic} - * instruction could be deemed (by a conforming implementation) to be a separate - * duplicate, requiring its own execution of the bootstrap method. - * However, implementations are expected to perform code duplication - * (if at all) in order to improve performance, not make it worse. + * 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 + * the {@code CONSTANT_InvokeDynamic} entry. + * 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. + * <p> + * Extra bootstrap method arguments are intended to allow language implementors + * to safely and compactly encode metadata. + * In principle, the name and extra arguments are redundant, + * 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 { + u1 tag = 15; + u1 reference_kind; // 1..8 (one of REF_invokeVirtual, etc.) + u2 reference_index; // index to CONSTANT_Fieldref or *Methodref +} +struct CONSTANT_MethodType_info { + u1 tag = 16; + u2 descriptor_index; // index to CONSTANT_Utf8, as in NameAndType +} +struct CONSTANT_InvokeDynamic_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 + u2 name_and_type_index; // index to CONSTANT_NameAndType, as in Methodref +} +struct BootstrapMethods_attr { + u2 name; // CONSTANT_Utf8 = "BootstrapMethods" + u4 size; + u2 bootstrap_method_count; + struct bootstrap_method_specifier { + u2 bootstrap_method_ref; // index to CONSTANT_MethodHandle + u2 bootstrap_argument_count; + u2 bootstrap_arguments[bootstrap_argument_count]; // constant pool indexes + } bootstrap_methods[bootstrap_method_count]; +} + * </pre></blockquote> + * <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/src/share/classes/sun/dyn/AdapterMethodHandle.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java Tue Dec 21 18:45:45 2010 -0800 @@ -478,6 +478,39 @@ return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)); } + static MethodHandle makeTypeHandler(Access token, + MethodHandle target, MethodHandle typeHandler) { + Access.check(token); + return new WithTypeHandler(target, typeHandler); + } + + static class WithTypeHandler extends AdapterMethodHandle { + final MethodHandle target, typeHandler; + WithTypeHandler(MethodHandle target, MethodHandle typeHandler) { + super(target, target.type(), makeConv(OP_RETYPE_ONLY)); + this.target = target; + this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE); + } + + public MethodHandle asType(MethodType newType) { + if (this.type() == newType) + return this; + 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); + } + } + private static final MethodType TYPE_HANDLER_TYPE + = MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class); + } + /** Can a checkcast adapter validly convert the target to newType? * The JVM supports all kind of reference casts, even silly ones. */
--- a/src/share/classes/sun/dyn/BoundMethodHandle.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/BoundMethodHandle.java Tue Dec 21 18:45:45 2010 -0800 @@ -103,21 +103,20 @@ super(Access.TOKEN, type); this.argument = argument; this.vmargslot = vmargslot; - assert(this.getClass() == AdapterMethodHandle.class); + assert(this instanceof AdapterMethodHandle); } - /** Initialize the current object as a Java method handle, binding it + /** Initialize the current object as a self-bound method handle, binding it * as the first argument of the method handle {@code entryPoint}. * The invocation type of the resulting method handle will be the * same as {@code entryPoint}, except that the first argument * type will be dropped. */ - protected BoundMethodHandle(MethodHandle entryPoint) { - super(Access.TOKEN, entryPoint.type().dropParameterTypes(0, 1)); + protected BoundMethodHandle(Access token, MethodHandle entryPoint) { + super(token, entryPoint.type().dropParameterTypes(0, 1)); this.argument = this; // kludge; get rid of this.vmargslot = this.type().parameterSlotDepth(0); initTarget(entryPoint, 0); - assert(this instanceof JavaMethodHandle); } /** Make sure the given {@code argument} can be used as {@code argnum}-th @@ -173,6 +172,11 @@ @Override public String toString() { + return MethodHandleImpl.addTypeString(baseName(), this); + } + + /** Component of toString() before the type string. */ + protected String baseName() { MethodHandle mh = this; while (mh instanceof BoundMethodHandle) { Object info = MethodHandleNatives.getTargetInfo(mh); @@ -185,12 +189,16 @@ if (name != null) return name; else - return super.toString(); // <unknown>, probably + return noParens(super.toString()); // "invoke", probably } assert(mh != this); - if (mh instanceof JavaMethodHandle) - break; // access JMH.toString(), not BMH.toString() } - return mh.toString(); + return noParens(mh.toString()); + } + + private static String noParens(String str) { + int paren = str.indexOf('('); + if (paren >= 0) str = str.substring(0, paren); + return str; } }
--- a/src/share/classes/sun/dyn/CallSiteImpl.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/CallSiteImpl.java Tue Dec 21 18:45:45 2010 -0800 @@ -41,31 +41,45 @@ Object info, // Caller information: MemberName callerMethod, int callerBCI) { - Class<?> caller = callerMethod.getDeclaringClass(); + Class<?> callerClass = callerMethod.getDeclaringClass(); + Object caller; + if (bootstrapMethod.type().parameterType(0) == Class.class) + caller = callerClass; // remove for PFD + else + caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass); if (bootstrapMethod == null) { // If there is no bootstrap method, throw IncompatibleClassChangeError. // This is a valid generic error type for resolution (JLS 12.3.3). throw new IncompatibleClassChangeError - ("Class "+caller.getName()+" has not declared a bootstrap method for invokedynamic"); + ("Class "+callerClass.getName()+" has not declared a bootstrap method for invokedynamic"); } CallSite site; try { Object binding; - if (false) // switch when invokeGeneric works - binding = bootstrapMethod.invokeGeneric(caller, name, type); - else - binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type }); + if (info == null) { + if (false) // switch when invokeGeneric works + binding = bootstrapMethod.invokeGeneric(caller, name, type); + else + binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type }); + } else { + info = maybeReBox(info); + if (false) // switch when invokeGeneric works + binding = bootstrapMethod.invokeGeneric(caller, name, type, info); + else + binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type, info }); + } //System.out.println("BSM for "+name+type+" => "+binding); if (binding instanceof CallSite) { site = (CallSite) binding; - } else if (binding instanceof MethodHandleProvider) { - MethodHandle target = ((MethodHandleProvider) binding).asMethodHandle(); + } else if (binding instanceof MethodHandle) { + // Transitional! + MethodHandle target = (MethodHandle) binding; site = new ConstantCallSite(target); } else { throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite"); } - PRIVATE_INITIALIZE_CALL_SITE.<void>invokeExact(site, name, type, - callerMethod, callerBCI); + PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type, + callerMethod, callerBCI); assert(site.getTarget() != null); assert(site.getTarget().type().equals(type)); } catch (Throwable ex) { @@ -79,6 +93,24 @@ return site; } + private static Object maybeReBox(Object x) { + if (x instanceof Integer) { + int xi = (int) x; + if (xi == (byte) xi) + x = xi; // must rebox; see JLS 5.1.7 + return x; + } else if (x instanceof Object[]) { + Object[] xa = (Object[]) x; + for (int i = 0; i < xa.length; i++) { + if (xa[i] instanceof Integer) + xa[i] = maybeReBox(xa[i]); + } + return xa; + } else { + return x; + } + } + // This method is private in CallSite because it touches private fields in CallSite. // These private fields (vmmethod, vmindex) are specific to the JVM. private static final MethodHandle PRIVATE_INITIALIZE_CALL_SITE;
--- a/src/share/classes/sun/dyn/FilterGeneric.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/FilterGeneric.java Tue Dec 21 18:45:45 2010 -0800 @@ -115,7 +115,7 @@ static MethodHandle make(Kind kind, int pos, MethodHandle filter, MethodHandle target) { FilterGeneric fgen = of(kind, pos, filter.type(), target.type()); - return fgen.makeInstance(kind, pos, filter, target).asMethodHandle(); + return fgen.makeInstance(kind, pos, filter, target); } /** Return the adapter information for this target and filter type. */ @@ -225,13 +225,13 @@ * The invoker is kept separate from the target because it can be * generated once per type erasure family, and reused across adapters. */ - static abstract class Adapter extends JavaMethodHandle { + static abstract class Adapter extends BoundMethodHandle { protected final MethodHandle filter; // transforms one or more arguments protected final MethodHandle target; // ultimate target @Override public String toString() { - return target.toString(); + return MethodHandleImpl.addTypeString(target, this); } protected boolean isPrototype() { return target == null; } @@ -246,7 +246,7 @@ protected Adapter(MethodHandle entryPoint, MethodHandle filter, MethodHandle target) { - super(entryPoint); + super(Access.TOKEN, entryPoint); this.filter = filter; this.target = target; } @@ -303,7 +303,7 @@ protected Object invoke_C0(Object a0) { return target.invokeExact(filter.invokeExact(a0)); } protected Object invoke_C1(Object a0) { return target.invokeExact(a0, filter.invokeExact()); } protected Object invoke_Y0(Object a0) { Object[] av = { a0 }; - filter.<void>invokeExact(av); return target.invokeExact(av[0]); } + filter.invokeExact(av); return target.invokeExact(av[0]); } } static class F2X extends Adapter { protected F2X(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -320,7 +320,7 @@ protected Object invoke_C1(Object a0, Object a1) { return target.invokeExact(a0, filter.invokeExact(a1)); } protected Object invoke_C2(Object a0, Object a1) { return target.invokeExact(a0, a1, filter.invokeExact()); } protected Object invoke_Y0(Object a0, Object a1) { Object[] av = { a0, a1 }; - filter.<void>invokeExact(av); return target.invokeExact(av[0], av[1]); } + filter.invokeExact(av); return target.invokeExact(av[0], av[1]); } } // */ @@ -337,7 +337,7 @@ return target.invokeExact(filter.invokeExact()); } static final Object[] NO_ARGS = { }; protected Object invoke_Y0() throws Throwable { - filter.<void>invokeExact(NO_ARGS); // make the flyby + filter.invokeExact(NO_ARGS); // make the flyby return target.invokeExact(); } } @@ -375,7 +375,7 @@ " return target.invokeExact(@av@, filter.invokeExact()); }", " protected Object invoke_Y0(@Tvav@) throws Throwable {", " Object[] av = { @av@ };", - " filter.<void>invokeExact(av); // make the flyby", + " filter.invokeExact(av); // make the flyby", " return target.invokeExact(@av[i]@); }", " }", } }; @@ -518,7 +518,7 @@ return target.invokeExact(a0, filter.invokeExact()); } protected Object invoke_Y0(Object a0) throws Throwable { Object[] av = { a0 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0]); } } static class F2 extends Adapter { @@ -548,7 +548,7 @@ return target.invokeExact(a0, a1, filter.invokeExact()); } protected Object invoke_Y0(Object a0, Object a1) throws Throwable { Object[] av = { a0, a1 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1]); } } static class F3 extends Adapter { @@ -585,7 +585,7 @@ return target.invokeExact(a0, a1, a2, filter.invokeExact()); } protected Object invoke_Y0(Object a0, Object a1, Object a2) throws Throwable { Object[] av = { a0, a1, a2 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2]); } } static class F4 extends Adapter { @@ -629,7 +629,7 @@ return target.invokeExact(a0, a1, a2, a3, filter.invokeExact()); } protected Object invoke_Y0(Object a0, Object a1, Object a2, Object a3) throws Throwable { Object[] av = { a0, a1, a2, a3 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3]); } } static class F5 extends Adapter { @@ -698,7 +698,7 @@ protected Object invoke_Y0(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { Object[] av = { a0, a1, a2, a3, a4 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4]); } } static class F6 extends Adapter { @@ -777,7 +777,7 @@ protected Object invoke_Y0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5]); } } static class F7 extends Adapter { @@ -866,7 +866,7 @@ protected Object invoke_Y0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6]); } } static class F8 extends Adapter { @@ -965,7 +965,7 @@ protected Object invoke_Y0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7]); } } static class F9 extends Adapter { @@ -1104,7 +1104,7 @@ Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8]); } } static class F10 extends Adapter { @@ -1256,7 +1256,7 @@ Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9]); } } static class F11 extends Adapter { @@ -1442,7 +1442,7 @@ Object a4, Object a5, Object a6, Object a7, Object a8, Object a9, Object a10) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10]); } } static class F12 extends Adapter { @@ -1644,7 +1644,7 @@ Object a4, Object a5, Object a6, Object a7, Object a8, Object a9, Object a10, Object a11) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11]); } } static class F13 extends Adapter { @@ -1904,7 +1904,7 @@ Object a8, Object a9, Object a10, Object a11, Object a12) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12]); } } static class F14 extends Adapter { @@ -2183,7 +2183,7 @@ Object a8, Object a9, Object a10, Object a11, Object a12, Object a13) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13]); } } static class F15 extends Adapter { @@ -2481,7 +2481,7 @@ Object a8, Object a9, Object a10, Object a11, Object a12, Object a13, Object a14) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13], av[14]); } } static class F16 extends Adapter { @@ -2798,7 +2798,7 @@ Object a8, Object a9, Object a10, Object a11, Object a12, Object a13, Object a14, Object a15) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13], av[14], av[15]); } } static class F17 extends Adapter { @@ -3188,7 +3188,7 @@ Object a12, Object a13, Object a14, Object a15, Object a16) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13], av[14], av[15], av[16]); } } static class F18 extends Adapter { @@ -3600,7 +3600,7 @@ Object a12, Object a13, Object a14, Object a15, Object a16, Object a17) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13], av[14], av[15], av[16], av[17]); } } static class F19 extends Adapter { @@ -4034,7 +4034,7 @@ Object a12, Object a13, Object a14, Object a15, Object a16, Object a17, Object a18) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13], av[14], av[15], av[16], av[17], av[18]); } } static class F20 extends Adapter { @@ -4490,7 +4490,7 @@ Object a12, Object a13, Object a14, Object a15, Object a16, Object a17, Object a18, Object a19) throws Throwable { Object[] av = { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19 }; - filter.<void>invokeExact(av); // make the flyby + filter.invokeExact(av); // make the flyby return target.invokeExact(av[0], av[1], av[2], av[3], av[4], av[5], av[6], av[7], av[8], av[9], av[10], av[11], av[12], av[13], av[14], av[15], av[16], av[17], av[18], av[19]); } } }
--- a/src/share/classes/sun/dyn/FilterOneArgument.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/FilterOneArgument.java Tue Dec 21 18:45:45 2010 -0800 @@ -36,7 +36,7 @@ * final method type is the responsibility of a JVM-level adapter. * @author jrose */ -public class FilterOneArgument extends JavaMethodHandle { +public class FilterOneArgument extends BoundMethodHandle { protected final MethodHandle filter; // Object -> Object protected final MethodHandle target; // Object -> Object @@ -62,7 +62,7 @@ } protected FilterOneArgument(MethodHandle filter, MethodHandle target) { - super(INVOKE); + super(Access.TOKEN, INVOKE); this.filter = filter; this.target = target; }
--- a/src/share/classes/sun/dyn/FromGeneric.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/FromGeneric.java Tue Dec 21 18:45:45 2010 -0800 @@ -241,7 +241,7 @@ * The invoker is kept separate from the target because it can be * generated once per type erasure family, and reused across adapters. */ - static abstract class Adapter extends JavaMethodHandle { + static abstract class Adapter extends BoundMethodHandle { /* * class X<<R,int N>> extends Adapter { * (MH, Object**N)=>raw(R) invoker; @@ -256,7 +256,7 @@ @Override public String toString() { - return target.toString(); + return MethodHandleImpl.addTypeString(target, this); } protected boolean isPrototype() { return target == null; } @@ -271,7 +271,7 @@ protected Adapter(MethodHandle entryPoint, MethodHandle invoker, MethodHandle convert, MethodHandle target) { - super(entryPoint); + super(Access.TOKEN, entryPoint); this.invoker = invoker; this.convert = convert; this.target = target; @@ -283,11 +283,11 @@ // { return new ThisType(entryPoint, convert, target); } /// Conversions on the value returned from the target. - protected Object convert_L(Object result) throws Throwable { return convert.<Object>invokeExact(result); } - protected Object convert_I(int result) throws Throwable { return convert.<Object>invokeExact(result); } - protected Object convert_J(long result) throws Throwable { return convert.<Object>invokeExact(result); } - protected Object convert_F(float result) throws Throwable { return convert.<Object>invokeExact(result); } - protected Object convert_D(double result) throws Throwable { return convert.<Object>invokeExact(result); } + protected Object convert_L(Object result) throws Throwable { return convert.invokeExact(result); } + protected Object convert_I(int result) throws Throwable { return convert.invokeExact(result); } + protected Object convert_J(long result) throws Throwable { return convert.invokeExact(result); } + protected Object convert_F(float result) throws Throwable { return convert.invokeExact(result); } + protected Object convert_D(double result) throws Throwable { return convert.invokeExact(result); } static private final String CLASS_PREFIX; // "sun.dyn.FromGeneric$" static { @@ -316,11 +316,11 @@ { super(e, i, c, t); } protected xA2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new xA2(e, i, c, t); } - protected Object invoke_L2(Object a0, Object a1) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1)); } - protected Object invoke_I2(Object a0, Object a1) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1)); } - protected Object invoke_J2(Object a0, Object a1) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1)); } - protected Object invoke_F2(Object a0, Object a1) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1)); } - protected Object invoke_D2(Object a0, Object a1) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1)); } + protected Object invoke_L2(Object a0, Object a1) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1)); } + protected Object invoke_I2(Object a0, Object a1) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1)); } + protected Object invoke_J2(Object a0, Object a1) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1)); } + protected Object invoke_F2(Object a0, Object a1) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1)); } + protected Object invoke_D2(Object a0, Object a1) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1)); } } // */ @@ -329,7 +329,8 @@ //{{{ import java.util.*; class genclasses { - static String[] TYPES = { "Object", "int ", "long ", "float ", "double" }; + static String[] TYPES = { "Object", "int ", "long ", "float ", "double" }; + static String[] WRAPS = { " ", "(Integer)", "(Long) ", "(Float) ", "(Double) " }; static String[] TCHARS = { "L", "I", "J", "F", "D", "A" }; static String[][] TEMPLATES = { { "@for@ arity=0..10 rcat<=4 nrefs<=99 nints=0 nlongs=0", @@ -341,13 +342,13 @@ " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t)", " { return new @cat@(e, i, c, t); }", " //@each-R@", - " protected Object invoke_@catN@(@Tvav@) throws Throwable { return convert_@Rc@(invoker.<@R@>invokeExact(target@av@)); }", + " protected Object invoke_@catN@(@Tvav@) throws Throwable { return convert_@Rc@((@R@)@W@invoker.invokeExact(target@av@)); }", " //@end-R@", " }", } }; static final String NEWLINE_INDENT = "\n "; enum VAR { - cat, catN, R, Rc, av, Tvav, Ovav; + cat, catN, R, Rc, W, av, Tvav, Ovav; public final String pattern = "@"+toString().replace('_','.')+"@"; public String binding; static void makeBindings(boolean topLevel, int rcat, int nrefs, int nints, int nlongs) { @@ -357,6 +358,7 @@ VAR.catN.binding = catstr(rcat, nrefs, nints, nlongs); VAR.R.binding = TYPES[rcat]; VAR.Rc.binding = TCHARS[rcat]; + VAR.W.binding = WRAPS[rcat]; String[] Tv = new String[nargs]; String[] av = new String[nargs]; String[] Tvav = new String[nargs]; @@ -497,11 +499,11 @@ { super(e, i, c, t); } protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A0(e, i, c, t); } - protected Object invoke_L0() throws Throwable { return convert_L(invoker.<Object>invokeExact(target)); } - protected Object invoke_I0() throws Throwable { return convert_I(invoker.<int >invokeExact(target)); } - protected Object invoke_J0() throws Throwable { return convert_J(invoker.<long >invokeExact(target)); } - protected Object invoke_F0() throws Throwable { return convert_F(invoker.<float >invokeExact(target)); } - protected Object invoke_D0() throws Throwable { return convert_D(invoker.<double>invokeExact(target)); } + protected Object invoke_L0() throws Throwable { return convert_L((Object)invoker.invokeExact(target)); } + protected Object invoke_I0() throws Throwable { return convert_I((int) invoker.invokeExact(target)); } + protected Object invoke_J0() throws Throwable { return convert_J((long) invoker.invokeExact(target)); } + protected Object invoke_F0() throws Throwable { return convert_F((float) invoker.invokeExact(target)); } + protected Object invoke_D0() throws Throwable { return convert_D((double)invoker.invokeExact(target)); } } static class A1 extends Adapter { protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -509,11 +511,11 @@ { super(e, i, c, t); } protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); } - protected Object invoke_L1(Object a0) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0)); } - protected Object invoke_I1(Object a0) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0)); } - protected Object invoke_J1(Object a0) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0)); } - protected Object invoke_F1(Object a0) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0)); } - protected Object invoke_D1(Object a0) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0)); } + protected Object invoke_L1(Object a0) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0)); } + protected Object invoke_I1(Object a0) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0)); } + protected Object invoke_J1(Object a0) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0)); } + protected Object invoke_F1(Object a0) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0)); } + protected Object invoke_D1(Object a0) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0)); } } static class A2 extends Adapter { protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -521,11 +523,11 @@ { super(e, i, c, t); } protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A2(e, i, c, t); } - protected Object invoke_L2(Object a0, Object a1) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1)); } - protected Object invoke_I2(Object a0, Object a1) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1)); } - protected Object invoke_J2(Object a0, Object a1) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1)); } - protected Object invoke_F2(Object a0, Object a1) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1)); } - protected Object invoke_D2(Object a0, Object a1) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1)); } + protected Object invoke_L2(Object a0, Object a1) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1)); } + protected Object invoke_I2(Object a0, Object a1) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1)); } + protected Object invoke_J2(Object a0, Object a1) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1)); } + protected Object invoke_F2(Object a0, Object a1) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1)); } + protected Object invoke_D2(Object a0, Object a1) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1)); } } static class A3 extends Adapter { protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -533,11 +535,11 @@ { super(e, i, c, t); } protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A3(e, i, c, t); } - protected Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2)); } - protected Object invoke_I3(Object a0, Object a1, Object a2) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2)); } - protected Object invoke_J3(Object a0, Object a1, Object a2) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2)); } - protected Object invoke_F3(Object a0, Object a1, Object a2) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2)); } - protected Object invoke_D3(Object a0, Object a1, Object a2) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2)); } + protected Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2)); } + protected Object invoke_I3(Object a0, Object a1, Object a2) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2)); } + protected Object invoke_J3(Object a0, Object a1, Object a2) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2)); } + protected Object invoke_F3(Object a0, Object a1, Object a2) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2)); } + protected Object invoke_D3(Object a0, Object a1, Object a2) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2)); } } static class A4 extends Adapter { protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -545,11 +547,11 @@ { super(e, i, c, t); } protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A4(e, i, c, t); } - protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3)); } - protected Object invoke_I4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3)); } - protected Object invoke_J4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3)); } - protected Object invoke_F4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3)); } - protected Object invoke_D4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3)); } + protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3)); } + protected Object invoke_I4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3)); } + protected Object invoke_J4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3)); } + protected Object invoke_F4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3)); } + protected Object invoke_D4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3)); } } static class A5 extends Adapter { protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -557,11 +559,11 @@ { super(e, i, c, t); } protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A5(e, i, c, t); } - protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4)); } - protected Object invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3, a4)); } - protected Object invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3, a4)); } - protected Object invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3, a4)); } - protected Object invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3, a4)); } + protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3, a4)); } + protected Object invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3, a4)); } + protected Object invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3, a4)); } + protected Object invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3, a4)); } + protected Object invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3, a4)); } } static class A6 extends Adapter { protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -569,11 +571,11 @@ { super(e, i, c, t); } protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A6(e, i, c, t); } - protected Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5)); } - protected Object invoke_I6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3, a4, a5)); } - protected Object invoke_J6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3, a4, a5)); } - protected Object invoke_F6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3, a4, a5)); } - protected Object invoke_D6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_I6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_J6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_F6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5)); } + protected Object invoke_D6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5)); } } static class A7 extends Adapter { protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -581,11 +583,11 @@ { super(e, i, c, t); } protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A7(e, i, c, t); } - protected Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } - protected Object invoke_I7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } - protected Object invoke_J7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } - protected Object invoke_F7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } - protected Object invoke_D7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_I7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_J7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_F7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } + protected Object invoke_D7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6)); } } static class A8 extends Adapter { protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -593,11 +595,11 @@ { super(e, i, c, t); } protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A8(e, i, c, t); } - protected Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } - protected Object invoke_I8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } - protected Object invoke_J8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } - protected Object invoke_F8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } - protected Object invoke_D8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_I8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_J8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_F8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } + protected Object invoke_D8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7)); } } static class A9 extends Adapter { protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -605,11 +607,11 @@ { super(e, i, c, t); } protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A9(e, i, c, t); } - protected Object invoke_L9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } - protected Object invoke_I9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } - protected Object invoke_J9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } - protected Object invoke_F9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } - protected Object invoke_D9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_L9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_I9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_J9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_F9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } + protected Object invoke_D9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8)); } } static class A10 extends Adapter { protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype @@ -617,10 +619,10 @@ { super(e, i, c, t); } protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A10(e, i, c, t); } - protected Object invoke_L10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_L(invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } - protected Object invoke_I10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_I(invoker.<int >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } - protected Object invoke_J10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_J(invoker.<long >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } - protected Object invoke_F10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_F(invoker.<float >invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } - protected Object invoke_D10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_D(invoker.<double>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_L10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_L((Object)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_I10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_I((int) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_J10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_J((long) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_F10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_F((float) invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } + protected Object invoke_D10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return convert_D((double)invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/dyn/InvokeGeneric.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2009, 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 sun.dyn; + +import java.dyn.*; +import java.lang.reflect.*; +import sun.dyn.util.*; + +/** + * Adapters which manage MethodHanndle.invokeGeneric calls. + * The JVM calls one of these when the exact type match fails. + * @author jrose + */ +class InvokeGeneric { + // erased type for the call, which originates from an invokeGeneric site + private final MethodType erasedCallerType; + // an invoker of type (MT, MH; A...) -> R + private final MethodHandle initialInvoker; + + /** 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 { + this.erasedCallerType = erasedCallerType; + this.initialInvoker = makeInitialInvoker(); + assert initialInvoker.type().equals(erasedCallerType + .insertParameterTypes(0, MethodType.class, MethodHandle.class)) + : initialInvoker.type(); + } + + private static MethodHandles.Lookup lookup() { + return MethodHandleImpl.IMPL_LOOKUP; + } + + /** Return the adapter information for this type's erasure. */ + static MethodHandle genericInvokerOf(MethodType type) { + MethodTypeImpl form = MethodTypeImpl.of(type); + MethodHandle genericInvoker = form.genericInvoker; + if (genericInvoker == null) { + try { + InvokeGeneric gen = new InvokeGeneric(form.erasedType()); + form.genericInvoker = genericInvoker = gen.initialInvoker; + } catch (NoAccessException ex) { + throw new RuntimeException(ex); + } + } + return genericInvoker; + } + + private MethodHandle makeInitialInvoker() throws NoAccessException { + // postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)} + MethodHandle postDispatch = makePostDispatchInvoker(); + MethodHandle invoker; + if (returnConversionPossible()) { + invoker = MethodHandles.foldArguments(postDispatch, + dispatcher("dispatchWithConversion")); + } else { + invoker = MethodHandles.foldArguments(postDispatch, dispatcher("dispatch")); + } + return invoker; + } + + private static final Class<?>[] EXTRA_ARGS = { MethodType.class, MethodHandle.class }; + 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); + } + 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 { + return lookup().bind(this, dispatchName, + MethodType.methodType(MethodHandle.class, + MethodType.class, MethodHandle.class)); + } + + static final boolean USE_AS_TYPE_PATH = true; + + /** Return a method handle to invoke on the callerType, target, and remaining arguments. + * The method handle must finish the call. + * This is the first look at the caller type and target. + */ + private MethodHandle dispatch(MethodType callerType, MethodHandle target) { + MethodType targetType = target.type(); + if (USE_AS_TYPE_PATH || target instanceof AdapterMethodHandle.WithTypeHandler) { + MethodHandle newTarget = target.asType(callerType); + targetType = callerType; + Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType); + MethodHandle invoker = invokers.erasedInvokerWithDrops; + if (invoker == null) { + invokers.erasedInvokerWithDrops = invoker = + dropDispatchArguments(invokers.erasedInvoker()); + } + return invoker.bindTo(newTarget); + } + throw new RuntimeException("NYI"); + } + + private MethodHandle dispatchWithConversion(MethodType callerType, MethodHandle target) { + MethodHandle finisher = dispatch(callerType, target); + if (returnConversionNeeded(callerType, target)) + finisher = addReturnConversion(finisher, callerType.returnType()); //FIXME: slow + return finisher; + } + + private boolean returnConversionPossible() { + Class<?> needType = erasedCallerType.returnType(); + return !needType.isPrimitive(); + } + private boolean returnConversionNeeded(MethodType callerType, MethodHandle target) { + Class<?> needType = callerType.returnType(); + if (needType == erasedCallerType.returnType()) + return false; // no conversions possible, since must be primitive or Object + Class<?> haveType = target.type().returnType(); + if (VerifyType.isNullConversion(haveType, needType)) + return false; + return true; + } + private MethodHandle addReturnConversion(MethodHandle target, Class<?> type) { + if (true) throw new RuntimeException("NYI"); + // FIXME: This is slow because it creates a closure node on every call that requires a return cast. + MethodType targetType = target.type(); + MethodHandle caster = ValueConversions.identity(type); + caster = caster.asType(MethodType.methodType(type, targetType.returnType())); + // Drop irrelevant arguments, because we only care about the return value: + caster = MethodHandles.dropArguments(caster, 1, targetType.parameterList()); + MethodHandle result = MethodHandles.foldArguments(caster, target); + return result.asType(target.type()); + } + + public String toString() { + return "InvokeGeneric"+erasedCallerType; + } +}
--- a/src/share/classes/sun/dyn/Invokers.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/Invokers.java Tue Dec 21 18:45:45 2010 -0800 @@ -26,6 +26,7 @@ package sun.dyn; import java.dyn.*; +import sun.dyn.empty.Empty; /** * Construction and caching of often-used invokers. @@ -38,12 +39,19 @@ // exact invoker for the outgoing call private /*lazy*/ MethodHandle exactInvoker; + // erased (partially untyped but with primitives) invoker for the outgoing call + private /*lazy*/ MethodHandle erasedInvoker; + /*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric + // generic (untyped) invoker for the outgoing call private /*lazy*/ MethodHandle genericInvoker; // generic (untyped) invoker for the outgoing call; accepts a single Object[] private final /*lazy*/ MethodHandle[] varargsInvokers; + // invoker for an unbound callsite + private /*lazy*/ MethodHandle uninitializedCallSite; + /** Compute and cache information common to all collecting adapters * that implement members of the erasure-family of the given erased type. */ @@ -80,6 +88,19 @@ return invoker; } + public MethodHandle erasedInvoker() { + MethodHandle invoker1 = exactInvoker(); + MethodHandle invoker = erasedInvoker; + if (invoker != null) return invoker; + MethodType erasedType = targetType.erase(); + if (erasedType == targetType.generic()) + invoker = genericInvoker(); + else + invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType)); + erasedInvoker = invoker; + return invoker; + } + public MethodHandle varargsInvoker(int objectArgCount) { MethodHandle vaInvoker = varargsInvokers[objectArgCount]; if (vaInvoker != null) return vaInvoker; @@ -90,6 +111,35 @@ return vaInvoker; } + private static MethodHandle THROW_UCS = null; + + public MethodHandle uninitializedCallSite() { + MethodHandle invoker = uninitializedCallSite; + if (invoker != null) return invoker; + if (targetType.parameterCount() > 0) { + MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount()); + Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0); + invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(), + 0, targetType.parameterList()); + assert(invoker.type().equals(targetType)); + uninitializedCallSite = invoker; + return invoker; + } + if (THROW_UCS == null) { + try { + THROW_UCS = MethodHandleImpl.IMPL_LOOKUP + .findStatic(CallSite.class, "uninitializedCallSite", + MethodType.methodType(Empty.class)); + } catch (NoAccessException ex) { + throw new RuntimeException(ex); + } + } + invoker = AdapterMethodHandle.makeRetypeRaw(Access.TOKEN, targetType, THROW_UCS); + assert(invoker.type().equals(targetType)); + uninitializedCallSite = invoker; + return invoker; + } + public String toString() { return "Invokers"+targetType; }
--- a/src/share/classes/sun/dyn/JavaMethodHandle.java Tue Dec 21 15:27:55 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 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.*; -import sun.dyn.Access; - -/** - * A Java method handle is a deprecated proposal for extending - * the basic method handle type with additional - * programmer defined methods and fields. - * Its behavior as a method handle is determined at instance creation time, - * by providing the new instance with an "entry point" method handle - * to handle calls. This entry point must accept a leading argument - * whose type is the Java method handle itself or a supertype, and the - * entry point is always called with the Java method handle itself as - * the first argument. This is similar to ordinary virtual methods, which also - * accept the receiver object {@code this} as an implicit leading argument. - * The {@code MethodType} of the Java method handle is the same as that - * of the entry point method handle, with the leading parameter type - * omitted. - * <p> - * Here is an example of usage, creating a hybrid object/functional datum: - * <p><blockquote><pre> - * class Greeter extends JavaMethodHandle { - * private String greeting = "hello"; - * public void setGreeting(String s) { greeting = s; } - * public void run() { System.out.println(greeting+", "+greetee); } - * private final String greetee; - * Greeter(String greetee) { - * super(RUN); // alternatively, super("run") - * this.greetee = greetee; - * } - * // the entry point function is computed once: - * private static final MethodHandle RUN - * = MethodHandles.lookup().findVirtual(Greeter.class, "run", - * MethodType.make(void.class)); - * } - * // class Main { public static void main(String... av) { ... - * Greeter greeter = new Greeter("world"); - * greeter.run(); // prints "hello, world" - * // Statically typed method handle invocation (most direct): - * MethodHandle mh = greeter; - * mh.<void>invokeExact(); // also prints "hello, world" - * // Dynamically typed method handle invocation: - * MethodHandles.invokeExact(greeter); // also prints "hello, world" - * greeter.setGreeting("howdy"); - * mh.invokeExact(); // prints "howdy, world" (object-like mutable behavior) - * </pre></blockquote> - * <p> - * In the example of {@code Greeter}, the method {@code run} provides the entry point. - * The entry point need not be a constant value; it may be independently - * computed in each call to the constructor. The entry point does not - * even need to be a method on the {@code Greeter} class, though - * that is the typical case. - * <p> - * The entry point may also be provided symbolically, in which case the the - * {@code JavaMethodHandle} constructor performs the lookup of the entry point. - * This makes it possible to use {@code JavaMethodHandle} to create an anonymous - * inner class: - * <p><blockquote><pre> - * // We can also do this with symbolic names and/or inner classes: - * MethodHandles.invokeExact(new JavaMethodHandle("yow") { - * void yow() { System.out.println("yow, world"); } - * }); - * </pre></blockquote> - * <p> - * Here is similar lower-level code which works in terms of a bound method handle. - * <p><blockquote><pre> - * class Greeter { - * public void run() { System.out.println("hello, "+greetee); } - * private final String greetee; - * Greeter(String greetee) { this.greetee = greetee; } - * // the entry point function is computed once: - * private static final MethodHandle RUN - * = MethodHandles.findVirtual(Greeter.class, "run", - * MethodType.make(void.class)); - * } - * // class Main { public static void main(String... av) { ... - * Greeter greeter = new Greeter("world"); - * greeter.run(); // prints "hello, world" - * MethodHandle mh = MethodHanndles.insertArgument(Greeter.RUN, 0, greeter); - * mh.invokeExact(); // also prints "hello, world" - * </pre></blockquote> - * Note that the method handle must be separately created as a view on the base object. - * This increases footprint, complexity, and dynamic indirections. - * <p> - * Here is a pure functional value expressed most concisely as an anonymous inner class: - * <p><blockquote><pre> - * // class Main { public static void main(String... av) { ... - * final String greetee = "world"; - * MethodHandle greeter = new JavaMethodHandle("run") { - * private void run() { System.out.println("hello, "+greetee); } - * } - * greeter.invokeExact(); // prints "hello, world" - * </pre></blockquote> - * <p> - * Here is an abstract parameterized lvalue, efficiently expressed as a subtype of MethodHandle, - * and instantiated as an anonymous class. The data structure is a handle to 1-D array, - * with a specialized index type (long). It is created by inner class, and uses - * signature-polymorphic APIs throughout. - * <p><blockquote><pre> - * abstract class AssignableMethodHandle extends JavaMethodHandle { - * private final MethodHandle setter; - * public MethodHandle setter() { return setter; } - * public AssignableMethodHandle(String get, String set) { - * super(get); - * MethodType getType = this.type(); - * MethodType setType = getType.insertParameterType(getType.parameterCount(), getType.returnType()).changeReturnType(void.class); - * this.setter = MethodHandles.publicLookup().bind(this, set, setType); - * } - * } - * // class Main { public static void main(String... av) { ... - * final Number[] stuff = { 123, 456 }; - * AssignableMethodHandle stuffPtr = new AssignableMethodHandle("get", "set") { - * public Number get(long i) { return stuff[(int)i]; } - * public void set(long i, Object x) { stuff[(int)i] = x; } - * } - * int x = (Integer) stuffPtr.<Number>invokeExact(1L); // 456 - * stuffPtr.setter().<void>invokeExact(0L, (Number) 789); // replaces 123 with 789 - * </pre></blockquote> - * @see MethodHandle - * @deprecated The JSR 292 EG intends to replace {@code JavaMethodHandle} with - * an interface-based API for mixing method handle behavior with other classes. - * @author John Rose, JSR 292 EG - */ -public abstract class JavaMethodHandle - // Note: This is an implementation inheritance hack, and will be removed - // with a JVM change which moves the required hidden behavior onto this class. - extends sun.dyn.BoundMethodHandle -{ - private static final Access IMPL_TOKEN = Access.getToken(); - - /** - * When creating a {@code JavaMethodHandle}, the actual method handle - * invocation behavior will be delegated to the specified {@code entryPoint}. - * This may be any method handle which can take the newly constructed object - * as a leading parameter. - * <p> - * The method handle type of {@code this} (i.e, the fully constructed object) - * will be {@code entryPoint}, minus the leading argument. - * The leading argument will be bound to {@code this} on every method - * handle invocation. - * @param entryPoint the method handle to handle calls - */ - protected JavaMethodHandle(MethodHandle entryPoint) { - super(entryPoint); - } -}
--- a/src/share/classes/sun/dyn/MethodHandleImpl.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/MethodHandleImpl.java Tue Dec 21 18:45:45 2010 -0800 @@ -199,7 +199,7 @@ return allocator; } - static final class AllocateObject<C> extends JavaMethodHandle { + static final class AllocateObject<C> extends BoundMethodHandle { private static final Unsafe unsafe = Unsafe.getUnsafe(); private final Class<C> allocateClass; @@ -207,7 +207,7 @@ private AllocateObject(MethodHandle invoker, Class<C> allocateClass, MethodHandle rawConstructor) { - super(invoker); + super(Access.TOKEN, invoker); this.allocateClass = allocateClass; this.rawConstructor = rawConstructor; } @@ -237,7 +237,7 @@ } @Override public String toString() { - return allocateClass.getSimpleName(); + return addTypeString(allocateClass.getSimpleName(), this); } @SuppressWarnings("unchecked") private C allocate() throws InstantiationException { @@ -245,52 +245,52 @@ } private C invoke_V(Object... av) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, av); + rawConstructor.invokeExact((Object)obj, av); return obj; } private C invoke_L0() throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj); + rawConstructor.invokeExact((Object)obj); return obj; } private C invoke_L1(Object a0) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0); + rawConstructor.invokeExact((Object)obj, a0); return obj; } private C invoke_L2(Object a0, Object a1) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1); + rawConstructor.invokeExact((Object)obj, a0, a1); return obj; } private C invoke_L3(Object a0, Object a1, Object a2) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1, a2); + rawConstructor.invokeExact((Object)obj, a0, a1, a2); return obj; } private C invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1, a2, a3); + rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3); return obj; } private C invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1, a2, a3, a4); + rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4); return obj; } private C invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1, a2, a3, a4, a5); + rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5); return obj; } private C invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6); + rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6); return obj; } private C invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { C obj = allocate(); - rawConstructor.<void>invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6, a7); + rawConstructor.invokeExact((Object)obj, a0, a1, a2, a3, a4, a5, a6, a7); return obj; } static MethodHandle[] makeInvokes() { @@ -369,19 +369,19 @@ return mhs[isSetter ? 1 : 0]; } - static final class FieldAccessor<C,V> extends JavaMethodHandle { + static final class FieldAccessor<C,V> extends BoundMethodHandle { private static final Unsafe unsafe = Unsafe.getUnsafe(); final Object base; // for static refs only final long offset; final String name; public FieldAccessor(Access token, MemberName field, boolean isSetter) { - super(fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic())); + super(Access.TOKEN, fhandle(field.getDeclaringClass(), field.getFieldType(), isSetter, field.isStatic())); this.offset = (long) field.getVMIndex(token); this.name = field.getName(); this.base = staticBase(field); } - public String toString() { return name; } + public String toString() { return addTypeString(name, this); } int getFieldI(C obj) { return unsafe.getInt(obj, offset); } void setFieldI(C obj, int x) { unsafe.putInt(obj, offset, x); } @@ -560,7 +560,9 @@ MethodHandle bindReceiver(Access token, MethodHandle target, Object receiver) { Access.check(token); - if (target instanceof AdapterMethodHandle) { + if (target instanceof AdapterMethodHandle && + ((AdapterMethodHandle)target).conversionOp() == MethodHandleNatives.Constants.OP_RETYPE_ONLY + ) { Object info = MethodHandleNatives.getTargetInfo(target); if (info instanceof DirectMethodHandle) { DirectMethodHandle dmh = (DirectMethodHandle) info; @@ -908,11 +910,11 @@ throw new UnsupportedOperationException("NYI"); } - private static class GuardWithTest extends JavaMethodHandle { + private static class GuardWithTest extends BoundMethodHandle { private final MethodHandle test, target, fallback; private GuardWithTest(MethodHandle invoker, MethodHandle test, MethodHandle target, MethodHandle fallback) { - super(invoker); + super(Access.TOKEN, invoker); this.test = test; this.target = target; this.fallback = fallback; @@ -946,57 +948,57 @@ } @Override public String toString() { - return target.toString(); + return addTypeString(target, this); } private Object invoke_V(Object... av) throws Throwable { - if (test.<boolean>invokeExact(av)) - return target.<Object>invokeExact(av); - return fallback.<Object>invokeExact(av); + if ((boolean) test.invokeExact(av)) + return target.invokeExact(av); + return fallback.invokeExact(av); } private Object invoke_L0() throws Throwable { - if (test.<boolean>invokeExact()) - return target.<Object>invokeExact(); - return fallback.<Object>invokeExact(); + if ((boolean) test.invokeExact()) + return target.invokeExact(); + return fallback.invokeExact(); } private Object invoke_L1(Object a0) throws Throwable { - if (test.<boolean>invokeExact(a0)) - return target.<Object>invokeExact(a0); - return fallback.<Object>invokeExact(a0); + if ((boolean) test.invokeExact(a0)) + return target.invokeExact(a0); + return fallback.invokeExact(a0); } private Object invoke_L2(Object a0, Object a1) throws Throwable { - if (test.<boolean>invokeExact(a0, a1)) - return target.<Object>invokeExact(a0, a1); - return fallback.<Object>invokeExact(a0, a1); + if ((boolean) test.invokeExact(a0, a1)) + return target.invokeExact(a0, a1); + return fallback.invokeExact(a0, a1); } private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { - if (test.<boolean>invokeExact(a0, a1, a2)) - return target.<Object>invokeExact(a0, a1, a2); - return fallback.<Object>invokeExact(a0, a1, a2); + if ((boolean) test.invokeExact(a0, a1, a2)) + return target.invokeExact(a0, a1, a2); + return fallback.invokeExact(a0, a1, a2); } private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { - if (test.<boolean>invokeExact(a0, a1, a2, a3)) - return target.<Object>invokeExact(a0, a1, a2, a3); - return fallback.<Object>invokeExact(a0, a1, a2, a3); + if ((boolean) test.invokeExact(a0, a1, a2, a3)) + return target.invokeExact(a0, a1, a2, a3); + return fallback.invokeExact(a0, a1, a2, a3); } private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { - if (test.<boolean>invokeExact(a0, a1, a2, a3, a4)) - return target.<Object>invokeExact(a0, a1, a2, a3, a4); - return fallback.<Object>invokeExact(a0, a1, a2, a3, a4); + if ((boolean) test.invokeExact(a0, a1, a2, a3, a4)) + return target.invokeExact(a0, a1, a2, a3, a4); + return fallback.invokeExact(a0, a1, a2, a3, a4); } private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { - if (test.<boolean>invokeExact(a0, a1, a2, a3, a4, a5)) - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5); - return fallback.<Object>invokeExact(a0, a1, a2, a3, a4, a5); + if ((boolean) test.invokeExact(a0, a1, a2, a3, a4, a5)) + return target.invokeExact(a0, a1, a2, a3, a4, a5); + return fallback.invokeExact(a0, a1, a2, a3, a4, a5); } private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { - if (test.<boolean>invokeExact(a0, a1, a2, a3, a4, a5, a6)) - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6); - return fallback.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6); + if ((boolean) test.invokeExact(a0, a1, a2, a3, a4, a5, a6)) + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); + return fallback.invokeExact(a0, a1, a2, a3, a4, a5, a6); } private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { - if (test.<boolean>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7)) - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); - return fallback.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); + if ((boolean) test.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7)) + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); + return fallback.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); } static MethodHandle[] makeInvokes() { ArrayList<MethodHandle> invokes = new ArrayList<MethodHandle>(); @@ -1036,7 +1038,7 @@ return GuardWithTest.make(token, test, target, fallback); } - private static class GuardWithCatch extends JavaMethodHandle { + private static class GuardWithCatch extends BoundMethodHandle { private final MethodHandle target; private final Class<? extends Throwable> exType; private final MethodHandle catcher; @@ -1045,93 +1047,93 @@ } public GuardWithCatch(MethodHandle invoker, MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { - super(invoker); + super(Access.TOKEN, invoker); this.target = target; this.exType = exType; this.catcher = catcher; } @Override public String toString() { - return target.toString(); + return addTypeString(target, this); } private Object invoke_V(Object... av) throws Throwable { try { - return target.<Object>invokeExact(av); + return target.invokeExact(av); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, av); + return catcher.invokeExact(t, av); } } private Object invoke_L0() throws Throwable { try { - return target.<Object>invokeExact(); + return target.invokeExact(); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t); + return catcher.invokeExact(t); } } private Object invoke_L1(Object a0) throws Throwable { try { - return target.<Object>invokeExact(a0); + return target.invokeExact(a0); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0); + return catcher.invokeExact(t, a0); } } private Object invoke_L2(Object a0, Object a1) throws Throwable { try { - return target.<Object>invokeExact(a0, a1); + return target.invokeExact(a0, a1); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1); + return catcher.invokeExact(t, a0, a1); } } private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { try { - return target.<Object>invokeExact(a0, a1, a2); + return target.invokeExact(a0, a1, a2); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1, a2); + return catcher.invokeExact(t, a0, a1, a2); } } private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { try { - return target.<Object>invokeExact(a0, a1, a2, a3); + return target.invokeExact(a0, a1, a2, a3); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1, a2, a3); + return catcher.invokeExact(t, a0, a1, a2, a3); } } private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { try { - return target.<Object>invokeExact(a0, a1, a2, a3, a4); + return target.invokeExact(a0, a1, a2, a3, a4); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1, a2, a3, a4); + return catcher.invokeExact(t, a0, a1, a2, a3, a4); } } private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { try { - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5); + return target.invokeExact(a0, a1, a2, a3, a4, a5); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1, a2, a3, a4, a5); + return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); } } private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { try { - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6); + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1, a2, a3, a4, a5, a6); + return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); } } private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { try { - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); } catch (Throwable t) { if (!exType.isInstance(t)) throw t; - return catcher.<Object>invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); + return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); } } static MethodHandle[] makeInvokes() { @@ -1217,21 +1219,24 @@ if (target != null) name = MethodHandleNatives.getMethodName(target); if (name == null) - return "<unknown>"; - return name.getName(); + return "invoke" + target.type(); + return name.getName() + target.type(); } - public static String addTypeString(MethodHandle target) { - if (target == null) return "null"; - return target.toString() + target.type(); + static String addTypeString(Object obj, MethodHandle target) { + String str = String.valueOf(obj); + if (target == null) return str; + int paren = str.indexOf('('); + if (paren >= 0) str = str.substring(0, paren); + return str + target.type(); } - public static void checkSpreadArgument(Object av, int n) { + static void checkSpreadArgument(Object av, int n) { if (av == null ? n != 0 : ((Object[])av).length != n) throw newIllegalArgumentException("Array is not of length "+n); } - public static void raiseException(int code, Object actual, Object required) { + static void raiseException(int code, Object actual, Object required) { String message; // disregard the identity of the actual object, if it is not a class: if (!(actual instanceof Class) && !(actual instanceof MethodType)) @@ -1257,4 +1262,9 @@ Access.check(token); return MethodHandleNatives.getBootstrap(callerClass); } + + public static MethodHandle withTypeHandler(Access token, MethodHandle target, MethodHandle typeHandler) { + Access.check(token); + return AdapterMethodHandle.makeTypeHandler(token, target, typeHandler); + } }
--- a/src/share/classes/sun/dyn/MethodHandleNatives.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/MethodHandleNatives.java Tue Dec 21 18:45:45 2010 -0800 @@ -317,6 +317,20 @@ } /** + * The JVM wants to use a MethodType with invokeGeneric. Give the runtime fair warning. + */ + static void notifyGenericMethodType(MethodType type) { + try { + // Trigger adapter creation. + InvokeGeneric.genericInvokerOf(type); + } catch (Exception ex) { + Error err = new InternalError("Exception while resolving invokeGeneric"); + err.initCause(ex); + throw err; + } + } + + /** * The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help. * It will make an up-call to this method. (Do not change the name or signature.) */
--- a/src/share/classes/sun/dyn/MethodTypeImpl.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/MethodTypeImpl.java Tue Dec 21 18:45:45 2010 -0800 @@ -48,6 +48,7 @@ final long primCounts; // packed prim & double counts final int vmslots; // total number of parameter slots final MethodType erasedType; // the canonical erasure + /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers /*lazy*/ MethodType primArgsAsBoxes; // wrap args only; make raw return /*lazy*/ MethodType primsAsInts; // replace prims by int/long @@ -59,6 +60,7 @@ /*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with /*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many /*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly + /*lazy*/ MethodHandle genericInvoker; // hook for invokeGeneric public MethodType erasedType() { return erasedType;
--- a/src/share/classes/sun/dyn/SpreadGeneric.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/SpreadGeneric.java Tue Dec 21 18:45:45 2010 -0800 @@ -208,7 +208,7 @@ * The invoker is kept separate from the target because it can be * generated once per type erasure family, and reused across adapters. */ - static abstract class Adapter extends JavaMethodHandle { + static abstract class Adapter extends BoundMethodHandle { /* * class X<<R,int M,int N>> extends Adapter { * (Object**N)=>R target; @@ -221,21 +221,21 @@ @Override public String toString() { - return target.toString(); + return MethodHandleImpl.addTypeString(target, this); } static final MethodHandle NO_ENTRY = ValueConversions.identity(); protected boolean isPrototype() { return target == null; } protected Adapter(SpreadGeneric outer) { - super(NO_ENTRY); + super(Access.TOKEN, NO_ENTRY); this.outer = outer; this.target = null; assert(isPrototype()); } protected Adapter(SpreadGeneric outer, MethodHandle target) { - super(outer.entryPoint); + super(Access.TOKEN, outer.entryPoint); this.outer = outer; this.target = target; } @@ -277,12 +277,12 @@ protected xS2(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected xS2 makeInstance(SpreadGeneric outer, MethodHandle t) { return new xS2(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object av) throws Throwable { av = super.check(av,0); - return target.<Object>invokeExact(a0, a1)); } + return target.invokeExact(a0, a1)); } protected Object invoke_S1(Object a0, Object av) throws Throwable { av = super.check(av,1); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0)); } protected Object invoke_S2(Object a0, Object av) throws Throwable { av = super.check(av,1); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1)); } } // */ @@ -300,10 +300,10 @@ " protected @cat@(SpreadGeneric outer, MethodHandle t) { super(outer, t); }", " protected @cat@ makeInstance(SpreadGeneric outer, MethodHandle t) { return new @cat@(outer, t); }", " protected Object invoke_S0(@Tvav,@Object av) throws Throwable { av = super.check(av, 0);", - " return target.<Object>invokeExact(@av@); }", + " return target.invokeExact(@av@); }", " //@each-S@", " protected Object invoke_S@S@(@Tvav,@Object av) throws Throwable { av = super.check(av, @S@);", - " return target.<Object>invokeExact(@av,@@sv@); }", + " return target.invokeExact(@av,@@sv@); }", " //@end-S@", " }", } }; @@ -414,16 +414,16 @@ protected S0(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S0 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S0(outer, t); } protected Object invoke_S0(Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(); } + return target.invokeExact(); } } static class S1 extends Adapter { protected S1(SpreadGeneric outer) { super(outer); } // to build prototype protected S1(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S1 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S1(outer, t); } protected Object invoke_S0(Object a0, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0); } + return target.invokeExact(a0); } protected Object invoke_S1(Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0)); } } static class S2 extends Adapter { @@ -431,12 +431,12 @@ protected S2(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S2 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S2(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1); } + return target.invokeExact(a0, a1); } protected Object invoke_S1(Object a0, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0)); } protected Object invoke_S2(Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1)); } } static class S3 extends Adapter { @@ -444,15 +444,15 @@ protected S3(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S3 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S3(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2); } + return target.invokeExact(a0, a1, a2); } protected Object invoke_S1(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0)); } protected Object invoke_S2(Object a0, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2)); } } static class S4 extends Adapter { @@ -460,18 +460,18 @@ protected S4(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S4 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S4(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3); } + return target.invokeExact(a0, a1, a2, a3); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } } static class S5 extends Adapter { @@ -479,21 +479,21 @@ protected S5(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S5 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S5(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object a4, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3, a4); } + return target.invokeExact(a0, a1, a2, a3, a4); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, a3, + return target.invokeExact(a0, a1, a2, a3, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object a0, Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } protected Object invoke_S5(Object av) throws Throwable { av = super.check(av, 5); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4)); } } @@ -502,25 +502,25 @@ protected S6(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S6 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S6(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5); } + return target.invokeExact(a0, a1, a2, a3, a4, a5); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object a3, Object a4, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, + return target.invokeExact(a0, a1, a2, a3, a4, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, a2, a3, + return target.invokeExact(a0, a1, a2, a3, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } protected Object invoke_S5(Object a0, Object av) throws Throwable { av = super.check(av, 5); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4)); } protected Object invoke_S6(Object av) throws Throwable { av = super.check(av, 6); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5)); } } @@ -529,29 +529,29 @@ protected S7(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S7 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S7(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6); } + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, + return target.invokeExact(a0, a1, a2, a3, a4, a5, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object a2, Object a3, Object a4, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, + return target.invokeExact(a0, a1, a2, a3, a4, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, a1, a2, a3, + return target.invokeExact(a0, a1, a2, a3, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } protected Object invoke_S5(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 5); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4)); } protected Object invoke_S6(Object a0, Object av) throws Throwable { av = super.check(av, 6); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5)); } protected Object invoke_S7(Object av) throws Throwable { av = super.check(av, 7); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6)); } } @@ -560,33 +560,33 @@ protected S8(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S8 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S8(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); } + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, + return target.invokeExact(a0, a1, a2, a3, a4, a5, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object a1, Object a2, Object a3, Object a4, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, + return target.invokeExact(a0, a1, a2, a3, a4, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact(a0, a1, a2, a3, + return target.invokeExact(a0, a1, a2, a3, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } protected Object invoke_S5(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 5); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4)); } protected Object invoke_S6(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 6); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5)); } protected Object invoke_S7(Object a0, Object av) throws Throwable { av = super.check(av, 7); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6)); } protected Object invoke_S8(Object av) throws Throwable { av = super.check(av, 8); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6), super.select(av,7)); } } @@ -595,37 +595,37 @@ protected S9(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S9 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S9(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, a8); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, + return target.invokeExact(a0, a1, a2, a3, a4, a5, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object a0, Object a1, Object a2, Object a3, Object a4, Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, + return target.invokeExact(a0, a1, a2, a3, a4, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } protected Object invoke_S5(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 5); - return target.<Object>invokeExact(a0, a1, a2, a3, + return target.invokeExact(a0, a1, a2, a3, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4)); } protected Object invoke_S6(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 6); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5)); } protected Object invoke_S7(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 7); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6)); } protected Object invoke_S8(Object a0, Object av) throws Throwable { av = super.check(av, 8); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6), super.select(av,7)); } protected Object invoke_S9(Object av) throws Throwable { av = super.check(av, 9); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6), super.select(av,7), super.select(av,8)); } @@ -635,42 +635,42 @@ protected S10(SpreadGeneric outer, MethodHandle t) { super(outer, t); } protected S10 makeInstance(SpreadGeneric outer, MethodHandle t) { return new S10(outer, t); } protected Object invoke_S0(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9, Object av) throws Throwable { av = super.check(av, 0); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected Object invoke_S1(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object av) throws Throwable { av = super.check(av, 1); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, a8, + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, a8, super.select(av,0)); } protected Object invoke_S2(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object av) throws Throwable { av = super.check(av, 2); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7, super.select(av,0), super.select(av,1)); } protected Object invoke_S3(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object av) throws Throwable { av = super.check(av, 3); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, a6, + return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, super.select(av,0), super.select(av,1), super.select(av,2)); } protected Object invoke_S4(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object av) throws Throwable { av = super.check(av, 4); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, a5, + return target.invokeExact(a0, a1, a2, a3, a4, a5, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3)); } protected Object invoke_S5(Object a0, Object a1, Object a2, Object a3, Object a4, Object av) throws Throwable { av = super.check(av, 5); - return target.<Object>invokeExact(a0, a1, a2, a3, a4, + return target.invokeExact(a0, a1, a2, a3, a4, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4)); } protected Object invoke_S6(Object a0, Object a1, Object a2, Object a3, Object av) throws Throwable { av = super.check(av, 6); - return target.<Object>invokeExact(a0, a1, a2, a3, + return target.invokeExact(a0, a1, a2, a3, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5)); } protected Object invoke_S7(Object a0, Object a1, Object a2, Object av) throws Throwable { av = super.check(av, 7); - return target.<Object>invokeExact(a0, a1, a2, + return target.invokeExact(a0, a1, a2, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6)); } protected Object invoke_S8(Object a0, Object a1, Object av) throws Throwable { av = super.check(av, 8); - return target.<Object>invokeExact(a0, a1, + return target.invokeExact(a0, a1, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6), super.select(av,7)); } protected Object invoke_S9(Object a0, Object av) throws Throwable { av = super.check(av, 9); - return target.<Object>invokeExact(a0, + return target.invokeExact(a0, super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6), super.select(av,7), super.select(av,8)); } protected Object invoke_S10(Object av) throws Throwable { av = super.check(av, 10); - return target.<Object>invokeExact( + return target.invokeExact( super.select(av,0), super.select(av,1), super.select(av,2), super.select(av,3), super.select(av,4), super.select(av,5), super.select(av,6), super.select(av,7), super.select(av,8), super.select(av,9)); }
--- a/src/share/classes/sun/dyn/ToGeneric.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/ToGeneric.java Tue Dec 21 18:45:45 2010 -0800 @@ -323,7 +323,7 @@ * via another method handle {@code convert}, which is responsible for * converting the object result into the raw return value. */ - static abstract class Adapter extends JavaMethodHandle { + static abstract class Adapter extends BoundMethodHandle { /* * class X<<R,A...>> extends Adapter { * Object...=>Object target; @@ -337,13 +337,13 @@ @Override public String toString() { - return target == null ? "prototype:"+convert : target.toString(); + return target == null ? "prototype:"+convert : MethodHandleImpl.addTypeString(target, this); } protected boolean isPrototype() { return target == null; } /* Prototype constructor. */ protected Adapter(MethodHandle entryPoint) { - super(entryPoint); + super(Access.TOKEN, entryPoint); this.invoker = null; this.convert = entryPoint; this.target = null; @@ -355,7 +355,7 @@ } protected Adapter(MethodHandle entryPoint, MethodHandle invoker, MethodHandle convert, MethodHandle target) { - super(entryPoint); + super(Access.TOKEN, entryPoint); this.invoker = invoker; this.convert = convert; this.target = target; @@ -367,33 +367,33 @@ // { return new ThisType(entryPoint, convert, target); } // Code to run when the arguments (<= 4) have all been boxed. - protected Object target() throws Throwable { return invoker.<Object>invokeExact(target); } - protected Object target(Object a0) throws Throwable { return invoker.<Object>invokeExact(target, a0); } + protected Object target() throws Throwable { return invoker.invokeExact(target); } + protected Object target(Object a0) throws Throwable { return invoker.invokeExact(target, a0); } protected Object target(Object a0, Object a1) - throws Throwable { return invoker.<Object>invokeExact(target, a0, a1); } + throws Throwable { return invoker.invokeExact(target, a0, a1); } protected Object target(Object a0, Object a1, Object a2) - throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2); } + throws Throwable { return invoker.invokeExact(target, a0, a1, a2); } protected Object target(Object a0, Object a1, Object a2, Object a3) - throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3); } + throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3); } /* - protected Object target_0(Object... av) throws Throwable { return invoker.<Object>invokeExact(target, av); } + protected Object target_0(Object... av) throws Throwable { return invoker.invokeExact(target, av); } protected Object target_1(Object a0, Object... av) - throws Throwable { return invoker.<Object>invokeExact(target, a0, (Object)av); } + throws Throwable { return invoker.invokeExact(target, a0, (Object)av); } protected Object target_2(Object a0, Object a1, Object... av) - throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, (Object)av); } + throws Throwable { return invoker.invokeExact(target, a0, a1, (Object)av); } protected Object target_3(Object a0, Object a1, Object a2, Object... av) - throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, (Object)av); } + throws Throwable { return invoker.invokeExact(target, a0, a1, a2, (Object)av); } protected Object target_4(Object a0, Object a1, Object a2, Object a3, Object... av) - throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, (Object)av); } + throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, (Object)av); } // */ // (For more than 4 arguments, generate the code in the adapter itself.) // Code to run when the generic target has finished and produced a value. - protected Object return_L(Object res) throws Throwable { return convert.<Object>invokeExact(res); } - protected int return_I(Object res) throws Throwable { return convert.<int >invokeExact(res); } - protected long return_J(Object res) throws Throwable { return convert.<long >invokeExact(res); } - protected float return_F(Object res) throws Throwable { return convert.<float >invokeExact(res); } - protected double return_D(Object res) throws Throwable { return convert.<double>invokeExact(res); } + protected Object return_L(Object res) throws Throwable { return (Object)convert.invokeExact(res); } + protected int return_I(Object res) throws Throwable { return (int) convert.invokeExact(res); } + protected long return_J(Object res) throws Throwable { return (long) convert.invokeExact(res); } + protected float return_F(Object res) throws Throwable { return (float) convert.invokeExact(res); } + protected double return_D(Object res) throws Throwable { return (double)convert.invokeExact(res); } static private final String CLASS_PREFIX; // "sun.dyn.ToGeneric$" static { @@ -420,7 +420,7 @@ protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); } - protected Object target(Object a0) throws Throwable { return invoker.<Object>invokeExact(target, a0); } + protected Object target(Object a0) throws Throwable { return invoker.invokeExact(target, a0); } protected Object targetA1(Object a0) throws Throwable { return target(a0); } protected Object targetA1(int a0) throws Throwable { return target(a0); } protected Object targetA1(long a0) throws Throwable { return target(a0); } @@ -458,7 +458,7 @@ " protected @cat@(MethodHandle entryPoint) { super(entryPoint); } // to build prototype", " protected @cat@(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); }", " protected @cat@ makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new @cat@(e, i, c, t); }", - " protected Object target(@Ovav@) throws Throwable { return invoker.<Object>invokeExact(target, @av@); }", + " protected Object target(@Ovav@) throws Throwable { return invoker.invokeExact(target, @av@); }", " //@each-Tv@", " protected Object target@cat@(@Tvav@) throws Throwable { return target(@av@); }", " //@end-Tv@", @@ -618,7 +618,7 @@ protected A0(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A0(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A0 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A0(e, i, c, t); } - protected Object target() throws Throwable { return invoker.<Object>invokeExact(target); } + protected Object target() throws Throwable { return invoker.invokeExact(target); } protected Object targetA0() throws Throwable { return target(); } protected Object invoke_L() throws Throwable { return return_L(targetA0()); } protected int invoke_I() throws Throwable { return return_I(targetA0()); } @@ -630,7 +630,7 @@ protected A1(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A1(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A1 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A1(e, i, c, t); } - protected Object target(Object a0) throws Throwable { return invoker.<Object>invokeExact(target, a0); } + protected Object target(Object a0) throws Throwable { return invoker.invokeExact(target, a0); } protected Object targetA1(Object a0) throws Throwable { return target(a0); } protected Object targetA1(int a0) throws Throwable { return target(a0); } protected Object targetA1(long a0) throws Throwable { return target(a0); } @@ -654,7 +654,7 @@ protected A2(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A2(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A2 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A2(e, i, c, t); } - protected Object target(Object a0, Object a1) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1); } + protected Object target(Object a0, Object a1) throws Throwable { return invoker.invokeExact(target, a0, a1); } protected Object targetA2(Object a0, Object a1) throws Throwable { return target(a0, a1); } protected Object targetA2(Object a0, int a1) throws Throwable { return target(a0, a1); } protected Object targetA2(int a0, int a1) throws Throwable { return target(a0, a1); } @@ -690,7 +690,7 @@ protected A3(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A3(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A3 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A3(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2); } + protected Object target(Object a0, Object a1, Object a2) throws Throwable { return invoker.invokeExact(target, a0, a1, a2); } protected Object targetA3(Object a0, Object a1, Object a2) throws Throwable { return target(a0, a1, a2); } protected Object targetA3(Object a0, Object a1, int a2) throws Throwable { return target(a0, a1, a2); } protected Object targetA3(Object a0, int a1, int a2) throws Throwable { return target(a0, a1, a2); } @@ -739,7 +739,7 @@ protected A4(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A4(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A4 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A4(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3); } + protected Object target(Object a0, Object a1, Object a2, Object a3) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3); } protected Object targetA4(Object a0, Object a1, Object a2, Object a3) throws Throwable { return target(a0, a1, a2, a3); } protected Object targetA4(Object a0, Object a1, Object a2, int a3) throws Throwable { return target(a0, a1, a2, a3); } protected Object targetA4(Object a0, Object a1, int a2, int a3) throws Throwable { return target(a0, a1, a2, a3); } @@ -781,7 +781,7 @@ protected A5(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A5(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A5 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A5(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, a4); } protected Object targetA5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { return target(a0, a1, a2, a3, a4); } protected Object targetA5(Object a0, Object a1, Object a2, Object a3, int a4) throws Throwable { return target(a0, a1, a2, a3, a4); } protected Object targetA5(Object a0, Object a1, Object a2, int a3, int a4) throws Throwable { return target(a0, a1, a2, a3, a4); } @@ -832,7 +832,7 @@ protected A6(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A6(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A6 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A6(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, a4, a5); } protected Object targetA6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { return target(a0, a1, a2, a3, a4, a5); } protected Object targetA6(Object a0, Object a1, Object a2, Object a3, Object a4, long a5) throws Throwable { return target(a0, a1, a2, a3, a4, a5); } protected Object targetA6(Object a0, Object a1, Object a2, Object a3, long a4, long a5) throws Throwable { return target(a0, a1, a2, a3, a4, a5); } @@ -866,7 +866,7 @@ protected A7(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A7(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A7 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A7(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6); } protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6); } protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6); } protected Object targetA7(Object a0, Object a1, Object a2, Object a3, Object a4, long a5, long a6) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6); } @@ -904,7 +904,7 @@ protected A8(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A8(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A8 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A8(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7); } protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7); } protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7); } protected Object targetA8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, long a6, long a7) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7); } @@ -946,7 +946,7 @@ protected A9(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A9(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A9 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A9(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8); } protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } protected Object targetA9(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, long a7, long a8) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8); } @@ -992,7 +992,7 @@ protected A10(MethodHandle entryPoint) { super(entryPoint); } // to build prototype protected A10(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { super(e, i, c, t); } protected A10 makeInstance(MethodHandle e, MethodHandle i, MethodHandle c, MethodHandle t) { return new A10(e, i, c, t); } - protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return invoker.<Object>invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + protected Object target(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return invoker.invokeExact(target, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, long a9) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } protected Object targetA10(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, long a8, long a9) throws Throwable { return target(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
--- a/src/share/classes/sun/dyn/util/ValueConversions.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/util/ValueConversions.java Tue Dec 21 18:45:45 2010 -0800 @@ -377,7 +377,7 @@ REBOX_CONVERSIONS = newWrapperCaches(2); /** - * Becase we normalize primitive types to reduce the number of signatures, + * Because we normalize primitive types to reduce the number of signatures, * primitives are sometimes manipulated under an "erased" type, * either int (for types other than long/double) or long (for all types). * When the erased primitive value is then boxed into an Integer or Long, @@ -475,10 +475,10 @@ } private static final EnumMap<Wrapper, MethodHandle>[] - ZERO_CONSTANT_FUNCTIONS = newWrapperCaches(1); + CONSTANT_FUNCTIONS = newWrapperCaches(2); public static MethodHandle zeroConstantFunction(Wrapper wrap) { - EnumMap<Wrapper, MethodHandle> cache = ZERO_CONSTANT_FUNCTIONS[0]; + EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[0]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -544,6 +544,24 @@ } /** + * Identity function on ints. + * @param x an arbitrary int value + * @return the same value x + */ + static int identity(int x) { + return x; + } + + /** + * Identity function on longs. + * @param x an arbitrary long value + * @return the same value x + */ + static long identity(long x) { + return x; + } + + /** * Identity function, with reference cast. * @param t an arbitrary reference type * @param x an arbitrary reference value @@ -553,7 +571,7 @@ return t.cast(x); } - private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY; + private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY; static { try { MethodType idType = MethodType.genericMethodType(1); @@ -562,6 +580,8 @@ MethodType ignoreType = idType.changeReturnType(void.class); MethodType zeroObjectType = MethodType.genericMethodType(0); IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType); + IDENTITY_I = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(int.class, int.class)); + IDENTITY_J = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(long.class, long.class)); //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType); ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType); @@ -613,8 +633,53 @@ return IDENTITY; } + public static MethodHandle identity(Class<?> type) { + if (type == Object.class) + return IDENTITY; + else if (!type.isPrimitive()) + return retype(MethodType.methodType(type, type), IDENTITY); + else + return identity(Wrapper.forPrimitiveType(type)); + } + + static MethodHandle identity(Wrapper wrap) { + EnumMap<Wrapper, MethodHandle> cache = CONSTANT_FUNCTIONS[1]; + MethodHandle mh = cache.get(wrap); + if (mh != null) { + return mh; + } + // slow path + MethodType type = MethodType.methodType(wrap.primitiveType()); + if (wrap != Wrapper.VOID) + type = type.appendParameterTypes(wrap.primitiveType()); + try { + mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type); + } catch (NoAccessException ex) { + mh = null; + } + if (mh == null && wrap == Wrapper.VOID) { + mh = EMPTY; // #(){} : #()void + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + + // use a raw conversion + if (wrap.isSingleWord() && wrap != Wrapper.INT) { + mh = retype(type, identity(Wrapper.INT)); + } else if (wrap.isDoubleWord() && wrap != Wrapper.LONG) { + mh = retype(type, identity(Wrapper.LONG)); + } + if (mh != null) { + cache.put(wrap, mh); + return mh; + } + throw new IllegalArgumentException("cannot find identity for " + wrap); + } + private static MethodHandle retype(MethodType type, MethodHandle mh) { - return AdapterMethodHandle.makeRetypeOnly(IMPL_TOKEN, type, mh); + return AdapterMethodHandle.makeRetypeRaw(IMPL_TOKEN, type, mh); } private static final Object[] NO_ARGS_ARRAY = {};
--- a/src/share/classes/sun/dyn/util/VerifyAccess.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/util/VerifyAccess.java Tue Dec 21 18:45:45 2010 -0800 @@ -25,7 +25,6 @@ package sun.dyn.util; -import java.dyn.LinkagePermission; import java.dyn.NoAccessException; import java.lang.reflect.Modifier; import sun.dyn.MemberName; @@ -43,6 +42,7 @@ private static final int PACKAGE_ONLY = 0; private static final int ALL_ACCESS_MODES = (PUBLIC|PRIVATE|PROTECTED|PACKAGE_ONLY); + private static final boolean ALLOW_NESTMATE_ACCESS = false; /** * Evaluate the JVM linkage rules for access to the given method @@ -102,6 +102,8 @@ // a superclass of the lookup class. } } + if (defc == lookupClass) + return true; // easy check; all self-access is OK switch (mods & ALL_ACCESS_MODES) { case PUBLIC: if (refc != defc) return true; // already checked above @@ -112,7 +114,8 @@ return isSamePackage(defc, lookupClass); case PRIVATE: // Loosened rules for privates follows access rules for inner classes. - return isSamePackageMember(defc, lookupClass); + return (ALLOW_NESTMATE_ACCESS && + isSamePackageMember(defc, lookupClass)); default: throw new IllegalArgumentException("bad modifiers: "+Modifier.toString(mods)); } @@ -206,24 +209,4 @@ } return false; } - - /** - * Ensure the requesting class have privileges to perform invokedynamic - * linkage operations on subjectClass. True if requestingClass is - * Access.class (meaning the request originates from the JVM) or if the - * classes are in the same package and have consistent class loaders. - * (The subject class loader must be identical with or be a child of - * the requesting class loader.) - * @param requestingClass - * @param subjectClass - */ - public static void checkBootstrapPrivilege(Class requestingClass, Class subjectClass, - String permissionName) { - if (requestingClass == null) return; - if (requestingClass == subjectClass) return; - SecurityManager security = System.getSecurityManager(); - if (security == null) return; // open season - if (isSamePackage(requestingClass, subjectClass)) return; - security.checkPermission(new LinkagePermission(permissionName, requestingClass)); - } }
--- a/src/share/classes/sun/dyn/util/Wrapper.java Tue Dec 21 15:27:55 2010 -0800 +++ b/src/share/classes/sun/dyn/util/Wrapper.java Tue Dec 21 18:45:45 2010 -0800 @@ -26,17 +26,19 @@ package sun.dyn.util; public enum Wrapper { - INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), - LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), + BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), + // These must be in the order defined for widening primitive conversions in JLS 5.1.2 BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)), SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)), CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)), - BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), + INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), + LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)), DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)), - VOID(Void.class, void.class, 'V', null, Format.other(0)), //NULL(Null.class, null.class, 'N', null, Format.other(1)), OBJECT(Object.class, Object.class, 'L', null, Format.other(1)), + // VOID must be the last type, since it is "assignable" from any other type: + VOID(Void.class, void.class, 'V', null, Format.other(0)), ; private final Class<?> wrapperType; @@ -76,9 +78,11 @@ false); return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT); } - static int + static final int INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), + SHORT = SIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT), BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT), + CHAR = UNSIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT), FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT), NUM_MASK = (-1) << SIZE_SHIFT; @@ -111,6 +115,29 @@ /** Is the wrapped type either float or double? */ public boolean isFloating() { return format >= Format.FLOAT; } + /** Does the JVM verifier allow a variable of this wrapper's + * primitive type to be assigned from a value of the given wrapper's primitive type? + * Cases: + * <ul> + * <li>unboxing followed by widening primitive conversion + * <li>any type converted to {@code void} + * <li>boxing conversion followed by widening reference conversion to {@code Object} + * <li>conversion of {@code boolean} to any type + * </ul> + */ + public boolean isConvertibleFrom(Wrapper source) { + if (this == source) return true; + if (this.compareTo(source) < 0) { + // At best, this is a narrowing conversion. + return false; + } + if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) { + assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT); + return false; + } + return true; + } + /** Produce a zero value for the given wrapper type. * This will be a numeric zero for a number or character, * false for a boolean, and null for a reference or void. @@ -122,10 +149,10 @@ public Object zero() { return zero; } /** Produce a zero value for the given wrapper type T. - * The optinoal argument must a type compatible with this wrapper. + * The optional argument must a type compatible with this wrapper. * Equivalent to {@code this.cast(this.zero(), type)}. */ - public <T> T zero(Class<T> type) { return cast(zero, type); } + public <T> T zero(Class<T> type) { return convert(zero, type); } // /** Produce a wrapper for the given wrapper or primitive type. */ // public static Wrapper valueOf(Class<?> type) { @@ -264,7 +291,11 @@ exampleType.isInterface()) { return forceType(wrapperType, exampleType); } - throw new ClassCastException(exampleType + " not <:" + wrapperType); + throw newClassCastException(exampleType, primitiveType); + } + + private static ClassCastException newClassCastException(Class<?> actual, Class<?> expected) { + return new ClassCastException(actual + " is not compatible with " + expected); } /** If {@code type} is a primitive type, return the corresponding @@ -325,17 +356,55 @@ // } /** Cast a wrapped value to the given type, which may be either a primitive or wrapper type. + * The given target type must be this wrapper's primitive or wrapper type. + * If this wrapper is OBJECT, the target type may also be an interface, perform no runtime check. * Performs standard primitive conversions, including truncation and float conversions. * The given type must be compatible with this wrapper. That is, it must either * be the wrapper type (or a subtype, in the case of {@code OBJECT}) or else * it must be the wrapper's primitive type. + * Primitive conversions are only performed if the given type is itself a primitive. * @throws ClassCastException if the given type is not compatible with this wrapper */ public <T> T cast(Object x, Class<T> type) { + return convert(x, type, true); + } + + /** Convert a wrapped value to the given type. + * The given target type must be this wrapper's primitive or wrapper type. + * This is equivalent to {@link #cast}, except that it refuses to perform + * narrowing primitive conversions. + */ + public <T> T convert(Object x, Class<T> type) { + return convert(x, type, false); + } + + private <T> T convert(Object x, Class<T> type, boolean isCast) { + if (this == OBJECT) { + // If the target wrapper is OBJECT, just do a reference cast. + // If the target type is an interface, perform no runtime check. + // (This loophole is safe, and is allowed by the JVM verifier.) + // If the target type is a primitive, change it to a wrapper. + @SuppressWarnings("unchecked") + T result = (T) x; // unchecked warning is expected here + return result; + } Class<T> wtype = wrapperType(type); - if (wtype.isInstance(x)) - return wtype.cast(x); - return wtype.cast(wrap(x)); + if (wtype.isInstance(x)) { + @SuppressWarnings("unchecked") + T result = (T) x; // unchecked warning is expected here + return result; + } + Class<?> sourceType = x.getClass(); // throw NPE if x is null + if (!isCast) { + Wrapper source = findWrapperType(sourceType); + if (source == null || !this.isConvertibleFrom(source)) { + throw newClassCastException(wtype, sourceType); + } + } + @SuppressWarnings("unchecked") + T result = (T) wrap(x); // unchecked warning is expected here + assert result.getClass() == wtype; + return result; } /** Cast a reference type to another reference type.
--- a/test/java/dyn/ClassValueTest.java Tue Dec 21 15:27:55 2010 -0800 +++ b/test/java/dyn/ClassValueTest.java Tue Dec 21 18:45:45 2010 -0800 @@ -53,12 +53,13 @@ return "CV1:" + type.getName(); } static int countForCV1; - static final ClassValue<String> CV1 = new ClassValue<String>() { + static final ClassValue<String> CV1 = new CV1(); + private static class CV1 extends ClassValue<String> { protected String computeValue(Class<?> type) { countForCV1++; return nameForCV1(type); } - }; + } static final Class[] CLASSES = { String.class,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/dyn/InvokeDynamicPrintArgs.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,172 @@ +/* + * 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. + * + * 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. + */ + +/* @test + * @summary smoke test for invokedynamic instructions + * @library 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 + */ + +import java.util.*; +import java.io.*; + +import java.dyn.*; +import static java.dyn.MethodHandles.*; +import static java.dyn.MethodType.*; + +public class InvokeDynamicPrintArgs { + public static void main(String... av) throws Throwable { + if (av.length > 0) openBuf(); // --check-output mode + System.out.println("Printing some argument lists, starting with a empty one:"); + INDY_nothing().invokeExact(); // BSM specifier #0 = {bsm} + INDY_bar().invokeExact("bar arg", 1); // BSM specifier #1 = {bsm2, Void.class, "void type"} + INDY_bar2().invokeExact("bar2 arg", 222); // BSM specifier #1 = (same) + INDY_baz().invokeExact("baz arg", 2, 3.14); // BSM specifier #2 = {bsm2, 1234.5} + INDY_foo().invokeExact("foo arg"); // BSM specifier #0 = (same) + // Hence, BSM specifier count should be 3. See "--verify-specifier-count=3" above. + System.out.println("Done printing argument lists."); + closeBuf(); + } + + private static PrintStream oldOut; + private static ByteArrayOutputStream buf; + private static void openBuf() { + oldOut = System.out; + buf = new ByteArrayOutputStream(); + System.setOut(new PrintStream(buf)); + } + private static void closeBuf() { + if (buf == null) return; + System.out.flush(); + System.setOut(oldOut); + String[] haveLines = new String(buf.toByteArray()).split("[\n\r]+"); + for (String line : haveLines) System.out.println(line); + Iterator<String> iter = Arrays.asList(haveLines).iterator(); + for (String want : EXPECT_OUTPUT) { + String have = iter.hasNext() ? iter.next() : "[EOF]"; + if (want.equals(have)) continue; + System.err.println("want line: "+want); + System.err.println("have line: "+have); + throw new AssertionError("unexpected output: "+have); + } + if (iter.hasNext()) + throw new AssertionError("unexpected output: "+iter.next()); + } + 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]", + "Done printing argument lists." + }; + + private static void printArgs(Object bsmInfo, Object... args) { + System.out.println(bsmInfo+Arrays.deepToString(args)); + } + private static MethodHandle MH_printArgs() throws ReflectiveOperationException { + shouldNotCallThis(); + return lookup().findStatic(lookup().lookupClass(), + "printArgs", methodType(void.class, Object.class, Object[].class)); + } + + private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException { + // ignore caller and name, but match the type: + Object bsmInfo = Arrays.asList(caller, name, type); + return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type)); + } + private static MethodType MT_bsm() { + shouldNotCallThis(); + return methodType(CallSite.class, Lookup.class, String.class, MethodType.class); + } + private static MethodHandle MH_bsm() throws ReflectiveOperationException { + shouldNotCallThis(); + return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm()); + } + + 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); + 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); + } + private static MethodHandle MH_bsm2() throws ReflectiveOperationException { + shouldNotCallThis(); + return lookup().findStatic(lookup().lookupClass(), "bsm2", MT_bsm2()); + } + + private static MethodHandle INDY_nothing() throws Throwable { + shouldNotCallThis(); + return ((CallSite) MH_bsm().invokeGeneric(lookup(), + "nothing", methodType(void.class) + )).dynamicInvoker(); + } + private static MethodHandle INDY_foo() throws Throwable { + shouldNotCallThis(); + return ((CallSite) MH_bsm().invokeGeneric(lookup(), + "foo", methodType(void.class, String.class) + )).dynamicInvoker(); + } + private static MethodHandle INDY_bar() throws Throwable { + shouldNotCallThis(); + return ((CallSite) MH_bsm2().invokeGeneric(lookup(), + "bar", methodType(void.class, String.class, int.class) + , new Object[] { Void.class, "void type!", + 1, 234.5F, 67.5, (long)89 } + )).dynamicInvoker(); + } + private static MethodHandle INDY_bar2() throws Throwable { + shouldNotCallThis(); + return ((CallSite) MH_bsm2().invokeGeneric(lookup(), + "bar2", methodType(void.class, String.class, int.class) + , new Object[] { Void.class, "void type!", + 1, 234.5F, 67.5, (long)89 } + )).dynamicInvoker(); + } + private static MethodHandle INDY_baz() throws Throwable { + shouldNotCallThis(); + return ((CallSite) MH_bsm2().invokeGeneric(lookup(), + "baz", methodType(void.class, String.class, int.class, double.class) + , 1234.5 + )).dynamicInvoker(); + } + + private static void shouldNotCallThis() { + // if this gets called, the transformation has not taken place + if (System.getProperty("InvokeDynamicPrintArgs.allow-untransformed") != null) return; + throw new AssertionError("this code should be statically transformed away by Indify"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/dyn/InvokeGenericTest.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* @test + * @summary unit tests for java.dyn.MethodHandle.invokeGeneric + * @compile -XDallowTransitionalJSR292=no -target 7 InvokeGenericTest.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.InvokeGenericTest + */ + +package test.java.dyn; + +import java.dyn.*; +import static java.dyn.MethodHandles.*; +import static java.dyn.MethodType.*; +import java.lang.reflect.*; +import java.util.*; +import org.junit.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + + +/** + * + * @author jrose + */ +public class InvokeGenericTest { + // How much output? + static int verbosity = 0; + static { + String vstr = System.getProperty("test.java.dyn.InvokeGenericTest.verbosity"); + if (vstr != null) verbosity = Integer.parseInt(vstr); + } + + @Test + public void testFirst() throws Throwable { + verbosity += 9; try { + // left blank for debugging + } finally { printCounts(); verbosity -= 9; } + } + + public InvokeGenericTest() { + } + + @Before + public void checkImplementedPlatform() { + boolean platformOK = false; + Properties properties = System.getProperties(); + String vers = properties.getProperty("java.vm.version"); + String name = properties.getProperty("java.vm.name"); + String arch = properties.getProperty("os.arch"); + if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") || + arch.equals("sparc") || arch.equals("sparcv9")) && + (name.contains("Client") || name.contains("Server")) + ) { + platformOK = true; + } else { + System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); + } + assumeTrue(platformOK); + } + + String testName; + static int allPosTests, allNegTests; + int posTests, negTests; + @After + public void printCounts() { + if (verbosity >= 2 && (posTests | negTests) != 0) { + System.out.println(); + if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run"); + if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run"); + allPosTests += posTests; + allNegTests += negTests; + posTests = negTests = 0; + } + } + void countTest(boolean positive) { + if (positive) ++posTests; + else ++negTests; + } + void countTest() { countTest(true); } + void startTest(String name) { + if (testName != null) printCounts(); + if (verbosity >= 1) + System.out.println(name); + posTests = negTests = 0; + testName = name; + } + + @BeforeClass + public static void setUpClass() throws Exception { + calledLog.clear(); + calledLog.add(null); + nextArgVal = INITIAL_ARG_VAL; + } + + @AfterClass + public static void tearDownClass() throws Exception { + int posTests = allPosTests, negTests = allNegTests; + if (verbosity >= 2 && (posTests | negTests) != 0) { + System.out.println(); + if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases"); + if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases"); + } + } + + static List<Object> calledLog = new ArrayList<Object>(); + static Object logEntry(String name, Object... args) { + return Arrays.asList(name, Arrays.asList(args)); + } + static Object called(String name, Object... args) { + Object entry = logEntry(name, args); + calledLog.add(entry); + return entry; + } + static void assertCalled(String name, Object... args) { + Object expected = logEntry(name, args); + Object actual = calledLog.get(calledLog.size() - 1); + if (expected.equals(actual) && verbosity < 9) return; + System.out.println("assertCalled "+name+":"); + System.out.println("expected: "+expected); + System.out.println("actual: "+actual); + System.out.println("ex. types: "+getClasses(expected)); + System.out.println("act. types: "+getClasses(actual)); + assertEquals("previous method call", expected, actual); + } + static void printCalled(MethodHandle target, String name, Object... args) { + if (verbosity >= 3) + System.out.println("calling MH="+target+" to "+name+Arrays.toString(args)); + } + + static Object castToWrapper(Object value, Class<?> dst) { + Object wrap = null; + if (value instanceof Number) + wrap = castToWrapperOrNull(((Number)value).longValue(), dst); + if (value instanceof Character) + wrap = castToWrapperOrNull((char)(Character)value, dst); + if (wrap != null) return wrap; + return dst.cast(value); + } + + static Object castToWrapperOrNull(long value, Class<?> dst) { + if (dst == int.class || dst == Integer.class) + return (int)(value); + if (dst == long.class || dst == Long.class) + return (long)(value); + if (dst == char.class || dst == Character.class) + return (char)(value); + if (dst == short.class || dst == Short.class) + return (short)(value); + if (dst == float.class || dst == Float.class) + return (float)(value); + if (dst == double.class || dst == Double.class) + return (double)(value); + if (dst == byte.class || dst == Byte.class) + return (byte)(value); + if (dst == boolean.class || dst == boolean.class) + return ((value % 29) & 1) == 0; + return null; + } + + static final int ONE_MILLION = (1000*1000), // first int value + TEN_BILLION = (10*1000*1000*1000), // scale factor to reach upper 32 bits + INITIAL_ARG_VAL = ONE_MILLION << 1; // <<1 makes space for sign bit; + static long nextArgVal; + static long nextArg(boolean moreBits) { + long val = nextArgVal++; + long sign = -(val & 1); // alternate signs + val >>= 1; + if (moreBits) + // Guarantee some bits in the high word. + // In any case keep the decimal representation simple-looking, + // with lots of zeroes, so as not to make the printed decimal + // strings unnecessarily noisy. + val += (val % ONE_MILLION) * TEN_BILLION; + return val ^ sign; + } + static int nextArg() { + // Produce a 32-bit result something like ONE_MILLION+(smallint). + // Example: 1_000_042. + return (int) nextArg(false); + } + static long nextArg(Class<?> kind) { + if (kind == long.class || kind == Long.class || + kind == double.class || kind == Double.class) + // produce a 64-bit result something like + // ((TEN_BILLION+1) * (ONE_MILLION+(smallint))) + // Example: 10_000_420_001_000_042. + return nextArg(true); + return (long) nextArg(); + } + + static Object randomArg(Class<?> param) { + Object wrap = castToWrapperOrNull(nextArg(param), param); + if (wrap != null) { + return wrap; + } +// import sun.dyn.util.Wrapper; +// Wrapper wrap = Wrapper.forBasicType(dst); +// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst)) +// wrap = Wrapper.forWrapperType(dst); +// if (wrap != Wrapper.OBJECT) +// return wrap.wrap(nextArg++); + if (param.isInterface()) { + for (Class<?> c : param.getClasses()) { + if (param.isAssignableFrom(c) && !c.isInterface()) + { param = c; break; } + } + } + if (param.isInterface() || param.isAssignableFrom(String.class)) + return "#"+nextArg(); + else + try { + return param.newInstance(); + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + return null; // random class not Object, String, Integer, etc. + } + static Object[] randomArgs(Class<?>... params) { + Object[] args = new Object[params.length]; + for (int i = 0; i < args.length; i++) + args[i] = randomArg(params[i]); + return args; + } + static Object[] randomArgs(int nargs, Class<?> param) { + Object[] args = new Object[nargs]; + for (int i = 0; i < args.length; i++) + args[i] = randomArg(param); + return args; + } + + static final Object ANON_OBJ = new Object(); + static Object zeroArg(Class<?> param) { + Object x = castToWrapperOrNull(0L, param); + if (x != null) return x; + if (param.isInterface() || param.isAssignableFrom(String.class)) return "\"\""; + if (param == Object.class) return ANON_OBJ; + if (param.getComponentType() != null) return Array.newInstance(param.getComponentType(), 0); + return null; + } + static Object[] zeroArgs(Class<?>... params) { + Object[] args = new Object[params.length]; + for (int i = 0; i < args.length; i++) + args[i] = zeroArg(params[i]); + return args; + } + static Object[] zeroArgs(List<Class<?>> params) { + return zeroArgs(params.toArray(new Class<?>[0])); + } + + static <T, E extends T> T[] array(Class<T[]> atype, E... a) { + return Arrays.copyOf(a, a.length, atype); + } + static <T> T[] cat(T[] a, T... b) { + int alen = a.length, blen = b.length; + if (blen == 0) return a; + T[] c = Arrays.copyOf(a, alen + blen); + System.arraycopy(b, 0, c, alen, blen); + return c; + } + static Integer[] boxAll(int... vx) { + Integer[] res = new Integer[vx.length]; + for (int i = 0; i < res.length; i++) { + res[i] = vx[i]; + } + return res; + } + static Object getClasses(Object x) { + if (x == null) return x; + if (x instanceof String) return x; // keep the name + if (x instanceof List) { + // recursively report classes of the list elements + Object[] xa = ((List)x).toArray(); + for (int i = 0; i < xa.length; i++) + xa[i] = getClasses(xa[i]); + return Arrays.asList(xa); + } + return x.getClass().getSimpleName(); + } + + static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) { + return changeArgTypes(target, 0, 999, argType); + } + static MethodHandle changeArgTypes(MethodHandle target, + int beg, int end, Class<?> argType) { + MethodType targetType = target.type(); + end = Math.min(end, targetType.parameterCount()); + ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList()); + Collections.fill(argTypes.subList(beg, end), argType); + MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes); + return MethodHandles.convertArguments(target, ttype2); + } + + // This lookup is good for all members in and under InvokeGenericTest. + static final Lookup LOOKUP = MethodHandles.lookup(); + + Map<List<Class<?>>, MethodHandle> CALLABLES = new HashMap<List<Class<?>>, MethodHandle>(); + MethodHandle callable(List<Class<?>> params) { + MethodHandle mh = CALLABLES.get(params); + if (mh == null) { + mh = collectArguments(collector_MH, methodType(Object.class, params)); + CALLABLES.put(params, mh); + } + return mh; + } + MethodHandle callable(Class<?>... params) { + return callable(Arrays.asList(params)); + } + private static Object collector(Object... args) { + return Arrays.asList(args); + } + private static final MethodHandle collector_MH; + static { + try { + collector_MH + = LOOKUP.findStatic(LOOKUP.lookupClass(), + "collector", + methodType(Object.class, Object[].class)); + } catch (NoAccessException ex) { + throw new RuntimeException(ex); + } + } + + @Test + public void testSimple() throws Throwable { + startTest("testSimple"); + countTest(); + String[] args = { "one", "two" }; + MethodHandle mh = callable(Object.class, String.class); + Object res; List resl; + res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]); + //System.out.println(res); + assertEquals(Arrays.asList(args), res); + } + + @Test + public void testWrongArgumentCount() throws Throwable { + startTest("testWrongArgumentCount"); + for (int i = 0; i <= 10; i++) { + testWrongArgumentCount(Collections.<Class<?>>nCopies(i, Integer.class)); + if (i <= 4) { + testWrongArgumentCount(Collections.<Class<?>>nCopies(i, int.class)); + testWrongArgumentCount(Collections.<Class<?>>nCopies(i, long.class)); + } + } + } + public void testWrongArgumentCount(List<Class<?>> params) throws Throwable { + int max = params.size(); + for (int i = 0; i < max; i++) { + List<Class<?>> params2 = params.subList(0, i); + for (int k = 0; k <= 2; k++) { + if (k == 1) params = methodType(Object.class, params).generic().parameterList(); + if (k == 2) params2 = methodType(Object.class, params2).generic().parameterList(); + testWrongArgumentCount(params, params2); + testWrongArgumentCount(params2, params); + } + } + } + public void testWrongArgumentCount(List<Class<?>> expect, List<Class<?>> observe) throws Throwable { + countTest(false); + if (expect.equals(observe)) + assert(false); + MethodHandle target = callable(expect); + Object[] args = zeroArgs(observe); + Object junk; + try { + switch (args.length) { + case 0: + junk = target.invokeGeneric(); break; + case 1: + junk = target.invokeGeneric(args[0]); break; + case 2: + junk = target.invokeGeneric(args[0], args[1]); break; + case 3: + junk = target.invokeGeneric(args[0], args[1], args[2]); break; + case 4: + junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break; + default: + junk = target.invokeWithArguments(args); break; + } + } catch (WrongMethodTypeException ex) { + return; + } catch (Exception ex) { + throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex); + } + throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args)); + } + + /** Make a list of all combinations of the given types, with the given arities. + * A void return type is possible iff the first type is void.class. + */ + static List<MethodType> allMethodTypes(int minargc, int maxargc, Class<?>... types) { + ArrayList<MethodType> result = new ArrayList<MethodType>(); + if (types.length > 0) { + ArrayList<MethodType> argcTypes = new ArrayList<MethodType>(); + // build arity-zero types first + for (Class<?> rtype : types) { + argcTypes.add(MethodType.methodType(rtype)); + } + if (types[0] == void.class) + // void is not an argument type + types = Arrays.copyOfRange(types, 1, types.length); + for (int argc = 0; argc <= maxargc; argc++) { + if (argc >= minargc) + result.addAll(argcTypes); + if (argc >= maxargc) + break; + ArrayList<MethodType> prevTypes = argcTypes; + argcTypes = new ArrayList<MethodType>(); + for (MethodType prevType : prevTypes) { + for (Class<?> ptype : types) { + argcTypes.add(prevType.insertParameterTypes(argc, ptype)); + } + } + } + } + return Collections.unmodifiableList(result); + } + static List<MethodType> allMethodTypes(int argc, Class<?>... types) { + return allMethodTypes(argc, argc, types); + } + + interface RandomInterface { } + + MethodHandle toString_MH; + + @Test + public void testReferenceConversions() throws Throwable { + startTest("testReferenceConversions"); + toString_MH = LOOKUP. + findVirtual(Object.class, "toString", MethodType.methodType(String.class)); + String[] args = { "one", "two" }; + for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) { + testReferenceConversions(type, args); + } + } + public void testReferenceConversions(MethodType type, Object... args) throws Throwable { + countTest(); + if (verbosity > 3) System.out.println("target type: "+type); + MethodHandle mh = callable(type.parameterList()); + MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList()); + mh = MethodHandles.foldArguments(tsdrop, mh); + mh = mh.asType(type); + Object res = mh.invokeGeneric((String)args[0], (Object)args[1]); + //System.out.println(res); + assertEquals(Arrays.asList(args).toString(), res); + } + + + @Test @Ignore("known failure pending 6939861") + public void testBoxConversions() throws Throwable { + startTest("testBoxConversions"); + countTest(); + Integer[] args = { 1, 2 }; + MethodHandle mh = callable(Object.class, int.class); + Object res; List resl; + res = resl = (List) mh.invokeGeneric((int)args[0], (Object)args[1]); + //System.out.println(res); + assertEquals(Arrays.asList(args), res); + } + +}
--- a/test/java/dyn/JavaDocExamples.java Tue Dec 21 15:27:55 2010 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2009, 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. - */ - -/* @test - * @summary example code used in javadoc for java.dyn API - * @compile -XDallowTransitionalJSR292=no JavaDocExamples.java - * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.JavaDocExamples - */ - -/* ----- To run outside jtreg: -$ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \ - $DAVINCI/sources/jdk/test/java/dyn/JavaDocExamples.java -$ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \ - -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles \ - -Dtest.java.dyn.JavaDocExamples.verbosity=1 \ - test.java.dyn.JavaDocExamples ----- -*/ - -package test.java.dyn; - -import java.dyn.*; -import static java.dyn.MethodHandles.*; -import static java.dyn.MethodType.*; - -import java.lang.reflect.*; -import java.util.*; - -import org.junit.*; -import static org.junit.Assert.*; -import static org.junit.Assume.*; - - -/** - * @author jrose - */ -public class JavaDocExamples { - /** Wrapper for running the JUnit tests in this module. - * Put JUnit on the classpath! - */ - public static void main(String... ignore) { - org.junit.runner.JUnitCore.runClasses(JavaDocExamples.class); - } - // How much output? - static int verbosity = Integer.getInteger("test.java.dyn.JavaDocExamples.verbosity", 0); - -{} -static final private Lookup LOOKUP = lookup(); -// static final private MethodHandle CONCAT_1 = LOOKUP.findVirtual(String.class, -// "concat", methodType(String.class, String.class)); -// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class, -// "hashCode", methodType(int.class)); - -// form required if NoAccessException is intercepted: -static final private MethodHandle CONCAT_2, HASHCODE_2; -static { - try { - CONCAT_2 = LOOKUP.findVirtual(String.class, - "concat", methodType(String.class, String.class)); - HASHCODE_2 = LOOKUP.findVirtual(Object.class, - "hashCode", methodType(int.class)); - } catch (NoAccessException ex) { - throw new RuntimeException(ex); - } -} -{} - - @Test public void testFindVirtual() throws Throwable { -{} -MethodHandle CONCAT_3 = LOOKUP.findVirtual(String.class, - "concat", methodType(String.class, String.class)); -MethodHandle HASHCODE_3 = LOOKUP.findVirtual(Object.class, - "hashCode", methodType(int.class)); -//assertEquals("xy", (String) CONCAT_1.invokeExact("x", "y")); -assertEquals("xy", (String) CONCAT_2.<String>invokeExact("x", "y")); -assertEquals("xy", (String) CONCAT_3.<String>invokeExact("x", "y")); -//assertEquals("xy".hashCode(), (int) HASHCODE_1.<int>invokeExact((Object)"xy")); -assertEquals("xy".hashCode(), (int) HASHCODE_2.<int>invokeExact((Object)"xy")); -assertEquals("xy".hashCode(), (int) HASHCODE_3.<int>invokeExact((Object)"xy")); -{} - } - @Test public void testDropArguments() throws Throwable { - {{ -{} /// JAVADOC -MethodHandle cat = lookup().findVirtual(String.class, - "concat", methodType(String.class, String.class)); -cat = cat.asType(methodType(Object.class, String.class, String.class)); /*(String)*/ -assertEquals("xy", /*(String)*/ cat.invokeExact("x", "y")); -MethodHandle d0 = dropArguments(cat, 0, String.class); -assertEquals("yz", /*(String)*/ d0.invokeExact("x", "y", "z")); -MethodHandle d1 = dropArguments(cat, 1, String.class); -assertEquals("xz", /*(String)*/ d1.invokeExact("x", "y", "z")); -MethodHandle d2 = dropArguments(cat, 2, String.class); -assertEquals("xy", /*(String)*/ d2.invokeExact("x", "y", "z")); -MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); -assertEquals("xz", /*(String)*/ d12.invokeExact("x", 12, true, "z")); - }} - } - - static void assertEquals(Object exp, Object act) { - if (verbosity > 0) - System.out.println("result: "+act); - Assert.assertEquals(exp, act); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/dyn/JavaDocExamplesTest.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* @test + * @summary example code used in javadoc for java.dyn API + * @compile -XDallowTransitionalJSR292=no JavaDocExamplesTest.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.JavaDocExamplesTest + */ + +/* +---- To run outside jtreg: +$ $JAVA7X_HOME/bin/javac -cp $JUNIT4_JAR -d /tmp/Classes \ + $DAVINCI/sources/jdk/test/java/dyn/JavaDocExamplesTest.java +$ $JAVA7X_HOME/bin/java -cp $JUNIT4_JAR:/tmp/Classes \ + -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles \ + -Dtest.java.dyn.JavaDocExamplesTest.verbosity=1 \ + test.java.dyn.JavaDocExamplesTest +---- +*/ + +package test.java.dyn; + +import java.dyn.*; +import static java.dyn.MethodHandles.*; +import static java.dyn.MethodType.*; + +import java.lang.reflect.*; +import java.util.*; + +import org.junit.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + + +/** + * @author jrose + */ +public class JavaDocExamplesTest { + /** Wrapper for running the JUnit tests in this module. + * Put JUnit on the classpath! + */ + public static void main(String... ignore) { + org.junit.runner.JUnitCore.runClasses(JavaDocExamplesTest.class); + } + // How much output? + static int verbosity = Integer.getInteger("test.java.dyn.JavaDocExamplesTest.verbosity", 0); + +{} +static final private Lookup LOOKUP = lookup(); +// static final private MethodHandle CONCAT_1 = LOOKUP.findVirtual(String.class, +// "concat", methodType(String.class, String.class)); +// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class, +// "hashCode", methodType(int.class)); + +// form required if NoAccessException is intercepted: +static final private MethodHandle CONCAT_2, HASHCODE_2; +static { + try { + CONCAT_2 = LOOKUP.findVirtual(String.class, + "concat", methodType(String.class, String.class)); + HASHCODE_2 = LOOKUP.findVirtual(Object.class, + "hashCode", methodType(int.class)); + } catch (NoAccessException ex) { + throw new RuntimeException(ex); + } +} +{} + + @Test public void testFindVirtual() throws Throwable { +{} +MethodHandle CONCAT_3 = LOOKUP.findVirtual(String.class, + "concat", methodType(String.class, String.class)); +MethodHandle HASHCODE_3 = LOOKUP.findVirtual(Object.class, + "hashCode", methodType(int.class)); +//assertEquals("xy", (String) CONCAT_1.invokeExact("x", "y")); +assertEquals("xy", (String) CONCAT_2.invokeExact("x", "y")); +assertEquals("xy", (String) CONCAT_3.invokeExact("x", "y")); +//assertEquals("xy".hashCode(), (int) HASHCODE_1.invokeExact((Object)"xy")); +assertEquals("xy".hashCode(), (int) HASHCODE_2.invokeExact((Object)"xy")); +assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy")); +{} + } + @Test public void testDropArguments() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +assertEquals("xy", (String) cat.invokeExact("x", "y")); +MethodHandle d0 = dropArguments(cat, 0, String.class); +assertEquals("yz", (String) d0.invokeExact("x", "y", "z")); +MethodHandle d1 = dropArguments(cat, 1, String.class); +assertEquals("xz", (String) d1.invokeExact("x", "y", "z")); +MethodHandle d2 = dropArguments(cat, 2, String.class); +assertEquals("xy", (String) d2.invokeExact("x", "y", "z")); +MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class); +assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z")); + }} + } + + @Test public void testFilterArguments() throws Throwable { + {{ +{} /// JAVADOC +MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); +MethodHandle upcase = lookup().findVirtual(String.class, + "toUpperCase", methodType(String.class)); +assertEquals("xy", (String) cat.invokeExact("x", "y")); +MethodHandle f0 = filterArguments(cat, 0, upcase); +assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy +MethodHandle f1 = filterArguments(cat, 1, upcase); +assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY +MethodHandle f2 = filterArguments(cat, 0, upcase, upcase); +assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY + }} + } + + static void assertEquals(Object exp, Object act) { + if (verbosity > 0) + System.out.println("result: "+act); + Assert.assertEquals(exp, act); + } + +static MethodHandle asList; + @Test public void testWithTypeHandler() 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()); + }} + } + +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; +} + +}
--- a/test/java/dyn/MethodHandlesTest.java Tue Dec 21 15:27:55 2010 -0800 +++ b/test/java/dyn/MethodHandlesTest.java Tue Dec 21 18:45:45 2010 -0800 @@ -25,8 +25,8 @@ /* @test * @summary unit tests for java.dyn.MethodHandles - * @compile -XDinvokedynamic MethodHandlesTest.java - * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic test.java.dyn.MethodHandlesTest + * @compile -source 7 -target 7 -XDallowTransitionalJSR292=no MethodHandlesTest.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.dyn.MethodHandlesTest */ package test.java.dyn; @@ -62,7 +62,6 @@ // lookups, without exercising the actual method handle. static boolean DO_MORE_CALLS = true; - @Test public void testFirst() throws Throwable { verbosity += 9; try { @@ -458,7 +457,7 @@ Exception noAccess = null; try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); - target = lookup.findStatic(defc, name, type); + target = lookup.in(defc).findStatic(defc, name, type); } catch (NoAccessException ex) { noAccess = ex; } @@ -469,16 +468,22 @@ assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); if (!positive) return; // negative test failed as expected assertEquals(type, target.type()); - assertTrue(target.toString().contains(name)); // rough check + assertNameStringContains(target, name); if (!DO_MORE_CALLS && lookup != PRIVATE) return; Object[] args = randomArgs(params); printCalled(target, name, args); - target.invokeVarargs(args); + target.invokeWithArguments(args); assertCalled(name, args); if (verbosity >= 1) System.out.print(':'); } + // rough check of name string + static void assertNameStringContains(Object x, String s) { + if (x.toString().contains(s)) return; + assertEquals(s, x); + } + @Test public void testFindVirtual() throws Throwable { if (CAN_SKIP_WORKING) return; @@ -522,7 +527,7 @@ Exception noAccess = null; try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); - target = lookup.findVirtual(defc, methodName, type); + target = lookup.in(defc).findVirtual(defc, methodName, type); } catch (NoAccessException ex) { noAccess = ex; } @@ -535,12 +540,12 @@ Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); assertEquals(typeWithSelf, target.type()); - assertTrue(target.toString().contains(methodName)); // rough check + assertNameStringContains(target, methodName); if (!DO_MORE_CALLS && lookup != PRIVATE) return; Object[] argsWithSelf = randomArgs(paramsWithSelf); if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); printCalled(target, name, argsWithSelf); - target.invokeVarargs(argsWithSelf); + target.invokeWithArguments(argsWithSelf); assertCalled(name, argsWithSelf); if (verbosity >= 1) System.out.print(':'); @@ -576,7 +581,8 @@ Exception noAccess = null; try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); - target = lookup.findSpecial(defc, name, type, specialCaller); + if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller)); + target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller); } catch (NoAccessException ex) { noAccess = ex; } @@ -591,11 +597,11 @@ assertEquals(type, target.type().dropParameterTypes(0,1)); Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params); MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); - assertTrue(target.toString().contains(name)); // rough check + assertNameStringContains(target, name); if (!DO_MORE_CALLS && lookup != PRIVATE && lookup != EXAMPLE) return; Object[] args = randomArgs(paramsWithSelf); printCalled(target, name, args); - target.invokeVarargs(args); + target.invokeWithArguments(args); assertCalled(name, args); } @@ -632,7 +638,7 @@ Exception noAccess = null; try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); - target = lookup.bind(receiver, methodName, type); + target = lookup.in(defc).bind(receiver, methodName, type); } catch (NoAccessException ex) { noAccess = ex; } @@ -645,7 +651,7 @@ assertEquals(type, target.type()); Object[] args = randomArgs(params); printCalled(target, name, args); - target.invokeVarargs(args); + target.invokeWithArguments(args); Object[] argsWithReceiver = cat(array(Object[].class, receiver), args); assertCalled(name, argsWithReceiver); if (verbosity >= 1) @@ -705,9 +711,9 @@ try { if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); if (isSpecial) - target = lookup.unreflectSpecial(rmethod, specialCaller); + target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller); else - target = lookup.unreflect(rmethod); + target = lookup.in(defc).unreflect(rmethod); } catch (NoAccessException ex) { noAccess = ex; } @@ -737,7 +743,7 @@ } Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf); printCalled(target, name, argsMaybeWithSelf); - target.invokeVarargs(argsMaybeWithSelf); + target.invokeWithArguments(argsMaybeWithSelf); assertCalled(name, argsMaybeWithSelf); if (verbosity >= 1) System.out.print(':'); @@ -875,7 +881,7 @@ if (isStatic) expType = expType.dropParameterTypes(0, 1); MethodHandle mh = lookup.unreflectGetter(f); assertSame(mh.type(), expType); - assertEquals(mh.toString(), fname); + assertNameStringContains(mh, fname); HasFields fields = new HasFields(); Object sawValue; Class<?> rtype = type; @@ -885,12 +891,12 @@ for (int i = 0; i <= 1; i++) { if (isStatic) { if (type == int.class) - sawValue = mh.<int>invokeExact(); // do these exactly + sawValue = (int) mh.invokeExact(); // do these exactly else sawValue = mh.invokeExact(); } else { if (type == int.class) - sawValue = mh.<int>invokeExact((Object) fields); + sawValue = (int) mh.invokeExact((Object) fields); else sawValue = mh.invokeExact((Object) fields); } @@ -947,7 +953,7 @@ mh = lookup.findStaticSetter(fclass, fname, ftype); else throw new InternalError(); assertSame(mh.type(), expType); - assertEquals(mh.toString(), fname); + assertNameStringContains(mh, fname); HasFields fields = new HasFields(); Object sawValue; Class<?> vtype = type; @@ -959,14 +965,14 @@ Object putValue = randomArg(type); if (isStatic) { if (type == int.class) - mh.<void>invokeExact((int)(Integer)putValue); // do these exactly + mh.invokeExact((int)putValue); // do these exactly else - mh.<void>invokeExact(putValue); + mh.invokeExact(putValue); } else { if (type == int.class) - mh.<void>invokeExact((Object) fields, (int)(Integer)putValue); + mh.invokeExact((Object) fields, (int)putValue); else - mh.<void>invokeExact((Object) fields, putValue); + mh.invokeExact((Object) fields, putValue); } assertEquals(f.get(fields), putValue); } @@ -1032,11 +1038,11 @@ model.set(i, random); if (testSetter) { if (elemType == int.class) - mh.<void>invokeExact((int[]) array, i, (int)(Integer)random); + mh.invokeExact((int[]) array, i, (int)random); else if (elemType == boolean.class) - mh.<void>invokeExact((boolean[]) array, i, (boolean)(Boolean)random); + mh.invokeExact((boolean[]) array, i, (boolean)random); else - mh.<void>invokeExact(array, i, random); + mh.invokeExact(array, i, random); assertEquals(model, array2list(array)); } else { Array.set(array, i, random); @@ -1052,9 +1058,9 @@ if (!testSetter) { expValue = sawValue; if (elemType == int.class) - sawValue = mh.<int>invokeExact((int[]) array, i); + sawValue = (int) mh.invokeExact((int[]) array, i); else if (elemType == boolean.class) - sawValue = mh.<boolean>invokeExact((boolean[]) array, i); + sawValue = (boolean) mh.invokeExact((boolean[]) array, i); else sawValue = mh.invokeExact(array, i); assertEquals(sawValue, expValue); @@ -1102,6 +1108,18 @@ } } + 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; @@ -1115,10 +1133,29 @@ } void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { - testConvert(true, id, rtype, name, params); + testConvert(true, false, id, rtype, name, params); + testConvert(true, true, id, rtype, name, params); } - void testConvert(boolean positive, MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { + @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); MethodType idType = id.type(); if (rtype == null) rtype = idType.returnType(); @@ -1135,7 +1172,7 @@ if (src != dst) convArgs[i] = castToWrapper(convArgs[i], dst); } - Object convResult = id.invokeVarargs(convArgs); + Object convResult = id.invokeWithArguments(convArgs); { Class<?> dst = newType.returnType(); Class<?> src = idType.returnType(); @@ -1145,7 +1182,10 @@ MethodHandle target = null; RuntimeException error = null; try { - target = MethodHandles.convertArguments(id, newType); + if (useAsType) + target = MethodHandles.convertArguments(id, newType); + else + target = id.asType(newType); } catch (RuntimeException ex) { error = ex; } @@ -1157,7 +1197,7 @@ if (!positive) return; // negative test failed as expected assertEquals(newType, target.type()); printCalled(target, id.toString(), args); - Object result = target.invokeVarargs(args); + Object result = target.invokeWithArguments(args); assertCalled(name, convArgs); assertEquals(convResult, result); if (verbosity >= 1) @@ -1279,7 +1319,7 @@ MethodType outType = MethodType.methodType(Object.class, permTypes); MethodHandle target = MethodHandles.convertArguments(ValueConversions.varargsList(outargs), outType); MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); - Object result = newTarget.invokeVarargs(args); + Object result = newTarget.invokeWithArguments(args); Object expected = Arrays.asList(permArgs); assertEquals(expected, result); } @@ -1311,19 +1351,19 @@ Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: if (pos == 0 && nargs < 5) { - Object[] check = (Object[]) target.invokeVarargs(args); + Object[] check = (Object[]) target.invokeWithArguments(args); assertArrayEquals(args, check); switch (nargs) { case 0: - check = target.<Object[]>invokeExact(); + check = (Object[]) target.invokeExact(); assertArrayEquals(args, check); break; case 1: - check = target.<Object[]>invokeExact(args[0]); + check = (Object[]) target.invokeExact(args[0]); assertArrayEquals(args, check); break; case 2: - check = target.<Object[]>invokeExact(args[0], args[1]); + check = (Object[]) target.invokeExact(args[0], args[1]); assertArrayEquals(args, check); break; } @@ -1337,12 +1377,15 @@ MethodHandle result = MethodHandles.spreadArguments(target2, newType); Object[] returnValue; if (pos == 0) { - Object rawRetVal = result.invokeExact(args); - returnValue = (Object[]) rawRetVal; + // In the following line, the first cast implies + // normal Object return value for the MH call (Object[])->Object, + // while the second cast dynamically converts to an Object array. + // Such a double cast is typical of MH.invokeExact. + returnValue = (Object[]) (Object) result.invokeExact(args); } else { Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); args1[pos] = Arrays.copyOfRange(args, pos, args.length); - returnValue = (Object[]) result.invokeVarargs(args1); + returnValue = (Object[]) result.invokeWithArguments(args1); } assertArrayEquals(args, returnValue); } @@ -1379,7 +1422,7 @@ if (verbosity >= 3) System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]"); MethodHandle result = MethodHandles.collectArguments(target, newType); - Object[] returnValue = (Object[]) result.invokeVarargs(args); + Object[] returnValue = (Object[]) result.invokeWithArguments(args); // assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]); // returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]); // collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]); @@ -1412,7 +1455,7 @@ MethodHandle target2 = MethodHandles.insertArguments(target, pos, (Object[]) argsToInsert.toArray()); argsToInsert.clear(); // remove from argsToInsert - Object res2 = target2.invokeVarargs(argsToPass); + Object res2 = target2.invokeWithArguments(argsToPass); Object res2List = Arrays.asList((Object[])res2); if (verbosity >= 3) System.out.println("result: "+res2List); @@ -1440,14 +1483,12 @@ Object[] argsToPass = randomArgs(nargs, Object.class); if (verbosity >= 3) System.out.println("filter "+target+" at "+pos+" with "+filter); - MethodHandle[] filters = new MethodHandle[pos*2+1]; - filters[pos] = filter; - MethodHandle target2 = MethodHandles.filterArguments(target, filters); + MethodHandle target2 = MethodHandles.filterArguments(target, pos, filter); // Simulate expected effect of filter on arglist: Object[] filteredArgs = argsToPass.clone(); filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]); List<Object> expected = Arrays.asList(filteredArgs); - Object result = target2.invokeVarargs(argsToPass); + Object result = target2.invokeWithArguments(argsToPass); if (verbosity >= 3) System.out.println("result: "+result); if (!expected.equals(result)) @@ -1472,7 +1513,7 @@ if (pos != 0) return; // can fold only at pos=0 for now countTest(); MethodHandle target = ValueConversions.varargsList(1 + nargs); - MethodHandle combine = ValueConversions.varargsList(fold); + MethodHandle combine = ValueConversions.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); @@ -1482,9 +1523,9 @@ List<Object> argsToFold = expected.subList(pos, pos + fold); if (verbosity >= 3) System.out.println("fold: "+argsToFold+" into "+target2); - Object foldedArgs = combine.invokeVarargs(argsToFold); + Object foldedArgs = combine.invokeWithArguments(argsToFold); argsToFold.add(0, foldedArgs); - Object result = target2.invokeVarargs(argsToPass); + Object result = target2.invokeWithArguments(argsToPass); if (verbosity >= 3) System.out.println("result: "+result); if (!expected.equals(result)) @@ -1516,7 +1557,7 @@ for (int i = drop; i > 0; i--) { argsToDrop.add(pos, "blort#"+i); } - Object res2 = target2.invokeVarargs(argsToDrop); + Object res2 = target2.invokeWithArguments(argsToDrop); Object res2List = Arrays.asList((Object[])res2); //if (!resList.equals(res2List)) // System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List); @@ -1572,7 +1613,7 @@ countTest(); calledLog.clear(); inv = MethodHandles.exactInvoker(type); - result = inv.invokeVarargs(targetPlusArgs); + result = inv.invokeWithArguments(targetPlusArgs); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); // generic invoker @@ -1598,7 +1639,7 @@ assertCalled("invokee", args); } calledLog.clear(); - result = inv.invokeVarargs(targetPlusArgs); + result = inv.invokeWithArguments(targetPlusArgs); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); // varargs invoker #0 @@ -1640,17 +1681,29 @@ List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs); Object[] tail = tailList.toArray(); tailList.clear(); tailList.add(tail); - result = inv.invokeVarargs(targetPlusVarArgs); + result = inv.invokeWithArguments(targetPlusVarArgs); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); } + // dynamic invoker countTest(); - CallSite site = new CallSite(MethodHandlesTest.class, "foo", type); - inv = MethodHandles.dynamicInvoker(site); + CallSite site = new MutableCallSite(type); + inv = site.dynamicInvoker(); + + // see if we get the result of the original target: + try { + result = inv.invokeWithArguments(args); + assertTrue("should not reach here", false); + } catch (IllegalStateException ex) { + String msg = ex.getMessage(); + assertTrue(msg, msg.contains("site")); + } + + // set new target after invoker is created, to make sure we track target site.setTarget(target); calledLog.clear(); - result = inv.invokeVarargs(args); + result = inv.invokeWithArguments(args); if (testRetCode) assertEquals(code, result); assertCalled("invokee", args); } @@ -1734,7 +1787,7 @@ String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals"); if (verbosity >= 3) System.out.println(logEntry(willCall, argList)); - Object result = mh.invokeVarargs(argList); + Object result = mh.invokeWithArguments(argList); assertCalled(willCall, argList); } } @@ -1767,16 +1820,17 @@ MethodHandle throwOrReturn = PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn", MethodType.methodType(Object.class, Object.class, Throwable.class)); - MethodHandle thrower = throwOrReturn; + MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2)); while (thrower.type().parameterCount() < nargs) thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class); + MethodHandle catcher = ValueConversions.varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs)); MethodHandle target = MethodHandles.catchException(thrower, - thrown.getClass(), ValueConversions.varargsList(1+nargs)); + thrown.getClass(), catcher); assertEquals(thrower.type(), target.type()); //System.out.println("catching with "+target+" : "+throwOrReturn); Object[] args = randomArgs(nargs, Object.class); args[1] = (throwIt ? thrown : null); - Object returned = target.invokeVarargs(args); + Object returned = target.invokeWithArguments(args); //System.out.println("return from "+target+" : "+returned); if (!throwIt) { assertSame(args[0], returned); @@ -1828,13 +1882,10 @@ testCastFailure("unbox/return", 11000); } - static class Surprise implements MethodHandleProvider { + static class Surprise { public MethodHandle asMethodHandle() { return VALUE.bindTo(this); } - public MethodHandle asMethodHandle(MethodType type) { - return asMethodHandle().asType(type); - } Object value(Object x) { trace("value", x); if (boo != null) return boo; @@ -1896,8 +1947,8 @@ } if (callee != null) { callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1)); - surprise = MethodHandles.filterArguments(callee, surprise); - identity = MethodHandles.filterArguments(callee, identity); + surprise = MethodHandles.filterArguments(callee, 0, surprise); + identity = MethodHandles.filterArguments(callee, 0, identity); } } assertNotSame(mode, surprise, surprise0); @@ -1949,7 +2000,7 @@ assertEquals(mt, mh.type()); assertEquals(Example.class, mh.type().returnType()); args = randomArgs(mh.type().parameterArray()); - mh.invokeVarargs(args); + mh.invokeWithArguments(args); assertCalled(name, args); // Try a virtual method. @@ -1959,7 +2010,7 @@ assertEquals(mt, mh.type().dropParameterTypes(0,1)); assertTrue(mh.type().parameterList().contains(Example.class)); args = randomArgs(mh.type().parameterArray()); - mh.invokeVarargs(args); + mh.invokeWithArguments(args); assertCalled(name, args); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/dyn/indify/Indify.java Tue Dec 21 18:45:45 2010 -0800 @@ -0,0 +1,1861 @@ +/* + * 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 indify; + +import java.util.*; +import java.io.*; +import java.lang.reflect.Modifier; +import java.util.regex.*; + +/** + * Transform one or more class files to incorporate JSR 292 features, + * such as {@code invokedynamic}. + * <p> + * This is a standalone program in a single source file. + * In this form, it may be useful for test harnesses, small experiments, and javadoc examples. + * Copies of this file may show up in multiple locations for standalone usage. + * The primary maintained location of this file is as follows: + * <a href="http://kenai.com/projects/ninja/sources/indify-repo/content/src/indify/Indify.java"> + * http://kenai.com/projects/ninja/sources/indify-repo/content/src/indify/Indify.java</a> + * <p> + * Static private methods named MH_x and MT_x (where x is arbitrary) + * must be stereotyped generators of MethodHandle and MethodType + * constants. All calls to them are transformed to {@code CONSTANT_MethodHandle} + * and {@code CONSTANT_MethodType} "ldc" instructions. + * The stereotyped code must create method types by calls to {@code methodType} or + * {@code fromMethodDescriptorString}. The "lookup" argument must be created + * by calls to {@code java.dyn.MethodHandles#lookup MethodHandles.lookup}. + * The class and string arguments must be constant. + * The following methods of {@code java.dyn.MethodHandle.Lookup Lookup} are + * allowed for method handle creation: {@code findStatic}, {@code findVirtual}, + * {@code findConstructor}, {@code findSpecial}, + * {@code findGetter}, {@code findSetter}, + * {@code findStaticGetter}, or {@code findStaticSetter}. + * The call to one of these methods must be followed immediately + * by an {@code areturn} instruction. + * The net result of the call to the MH_x or MT_x method must be + * the creation of a constant method handle. Thus, replacing calls + * to MH_x or MT_x methods by {@code ldc} instructions should leave + * the meaning of the program unchanged. + * <p> + * Static private methods named INDY_x must be stereotyped generators + * of {@code invokedynamic} call sites. + * All calls to them must be immediately followed by + * {@code invokeExact} calls. + * All such pairs of calls are transformed to {@code invokedynamic} + * instructions. Each INDY_x method must begin with a call to a + * MH_x method, which is taken to be its bootstrap method. + * The method must be immediately invoked (via {@code invokeGeneric} + * on constant lookup, name, and type arguments. An object array of + * constants may also be appended to the {@code invokeGeneric call}. + * This call must be cast to {@code CallSite}, and the result must be + * immediately followed by a call to {@code dynamicInvoker}, with the + * resulting method handle returned. + * <p> + * The net result of all of these actions is equivalent to the JVM's + * execution of an {@code invokedynamic} instruction in the unlinked state. + * Running this code once should produce the same results as running + * the corresponding {@code invokedynamic} instruction. + * In order to model the caching behavior, the code of an INDY_x + * method is allowed to begin with getstatic, aaload, and if_acmpne + * instructions which load a static method handle value and return it + * if the value is non-null. + * <p> + * Example usage: + * <blockquote><pre> +$ JAVA_HOME=(some recent OpenJDK 7 build) +$ ant +$ $JAVA_HOME/bin/java -cp build/classes indify.Indify --overwrite --dest build/testout build/classes/indify/Example.class +$ $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic -cp build/classes indify.Example +MT = (java.lang.Object)java.lang.Object +MH = adder(int,int)java.lang.Integer +adder(1,2) = 3 +calling indy: 42 +$ $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic -cp build/testout indify.Example +(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). + * <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 + */ +public class Indify { + public static void main(String... av) throws IOException { + new Indify().run(av); + } + + public File dest; + public String[] classpath = {"."}; + public boolean keepgoing = false; + public boolean expandProperties = false; + public boolean overwrite = false; + public boolean quiet = false; + public boolean verbose = false; + public boolean transitionalJSR292 = true; // default to false later + public boolean all = false; + public int verifySpecifierCount = -1; + + public void run(String... av) throws IOException { + List<String> avl = new ArrayList<>(Arrays.asList(av)); + parseOptions(avl); + if (avl.isEmpty()) + throw new IllegalArgumentException("Usage: indify [--dest dir] [option...] file..."); + if ("--java".equals(avl.get(0))) { + avl.remove(0); + try { + runApplication(avl.toArray(new String[0])); + } catch (Exception ex) { + if (ex instanceof RuntimeException) throw (RuntimeException) ex; + throw new RuntimeException(ex); + } + return; + } + Exception err = null; + for (String a : avl) { + try { + indify(a); + } catch (Exception ex) { + if (err == null) err = ex; + System.err.println("failure on "+a); + if (!keepgoing) break; + } + } + if (err != null) { + if (err instanceof IOException) throw (IOException) err; + throw (RuntimeException) err; + } + } + + /** Execute the given application under a class loader which indifies all application classes. */ + public void runApplication(String... av) throws Exception { + List<String> avl = new ArrayList<>(Arrays.asList(av)); + String mainClassName = avl.remove(0); + av = avl.toArray(new String[0]); + Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader()); + java.lang.reflect.Method main = mainClass.getMethod("main", String[].class); + main.invoke(null, (Object) av); + } + + public void parseOptions(List<String> av) throws IOException { + for (; !av.isEmpty(); av.remove(0)) { + String a = av.get(0); + if (a.startsWith("-")) { + String a2 = null; + int eq = a.indexOf('='); + if (eq > 0) { + a2 = maybeExpandProperties(a.substring(eq+1)); + a = a.substring(0, eq+1); + } + switch (a) { + case "--java": + return; // keep this argument + case "-d": case "--dest": case "-d=": case "--dest=": + dest = new File(a2 != null ? a2 : maybeExpandProperties(av.remove(1))); + break; + case "-cp": case "--classpath": + classpath = maybeExpandProperties(av.remove(1)).split("["+File.pathSeparatorChar+"]"); + break; + case "-k": case "--keepgoing": case "--keepgoing=": + keepgoing = booleanOption(a2); // print errors but keep going + break; + case "--expand-properties": case "--expand-properties=": + expandProperties = booleanOption(a2); // expand property references in subsequent arguments + break; + case "--verify-specifier-count": case "--verify-specifier-count=": + verifySpecifierCount = Integer.valueOf(a2); + break; + case "--overwrite": case "--overwrite=": + overwrite = booleanOption(a2); // overwrite output files + break; + case "--all": case "--all=": + all = booleanOption(a2); // copy all classes, even if no patterns + break; + case "-q": case "--quiet": case "--quiet=": + quiet = booleanOption(a2); // less output + break; + case "-v": case "--verbose": case "--verbose=": + verbose = booleanOption(a2); // more output + break; + case "--transitionalJSR292": case "--transitionalJSR292=": + transitionalJSR292 = booleanOption(a2); // use older invokedynamic format + break; + default: + throw new IllegalArgumentException("unrecognized flag: "+a); + } + continue; + } else { + break; + } + } + if (dest == null && !overwrite) + throw new RuntimeException("no output specified; need --dest d or --overwrite"); + if (expandProperties) { + for (int i = 0; i < av.size(); i++) + av.set(i, maybeExpandProperties(av.get(i))); + } + } + + 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; + } + throw new IllegalArgumentException("unrecognized boolean flag="+s); + } + + private String maybeExpandProperties(String s) { + if (!expandProperties) return s; + Set<String> propsDone = new HashSet<>(); + while (s.contains("${")) { + int lbrk = s.indexOf("${"); + int rbrk = s.indexOf('}', lbrk); + if (rbrk < 0) break; + String prop = s.substring(lbrk+2, rbrk); + if (!propsDone.add(prop)) break; + String value = System.getProperty(prop); + if (verbose) System.err.println("expanding ${"+prop+"} => "+value); + if (value == null) break; + s = s.substring(0, lbrk) + value + s.substring(rbrk+1); + } + return s; + } + + public void indify(String a) throws IOException { + File f = new File(a); + String fn = f.getName(); + if (fn.endsWith(".class") && f.isFile()) + indifyFile(f, dest); + else if (fn.endsWith(".jar") && f.isFile()) + indifyJar(f, dest); + else if (f.isDirectory()) + indifyTree(f, dest); + else if (!keepgoing) + throw new RuntimeException("unrecognized file: "+a); + } + + private void ensureDirectory(File dir) { + if (dir.mkdirs() && !quiet) + System.err.println("created "+dir); + } + + public void indifyFile(File f, File dest) throws IOException { + if (verbose) System.err.println("reading "+f); + ClassFile cf = new ClassFile(f); + Logic logic = new Logic(cf); + boolean changed = logic.transform(); + logic.reportPatternMethods(quiet, keepgoing); + if (changed || all) { + File outfile; + if (dest != null) { + ensureDirectory(dest); + outfile = classPathFile(dest, cf.nameString()); + } else { + outfile = f; // overwrite input file, no matter where it is + } + cf.writeTo(outfile); + if (!quiet) System.err.println("wrote "+outfile); + } + } + + File classPathFile(File pathDir, String className) { + String qualname = className+".class"; + qualname = qualname.replace('/', File.separatorChar); + return new File(pathDir, qualname); + } + + public void indifyJar(File f, Object dest) throws IOException { + throw new UnsupportedOperationException("Not yet implemented"); + } + + public void indifyTree(File f, File dest) throws IOException { + if (verbose) System.err.println("reading directory: "+f); + for (File f2 : f.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + if (name.endsWith(".class")) return true; + if (name.contains(".")) return false; + // return true if it might be a package name: + return Character.isJavaIdentifierStart(name.charAt(0)); + }})) { + if (f2.getName().endsWith(".class")) + indifyFile(f2, dest); + else if (f2.isDirectory()) + indifyTree(f2, dest); + } + } + + public ClassLoader makeClassLoader() { + return new Loader(); + } + private class Loader extends ClassLoader { + Loader() { + this(Indify.class.getClassLoader()); + } + Loader(ClassLoader parent) { + super(parent); + } + public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + File f = findClassInPath(name); + if (f != null) { + try { + Class<?> c = transformAndLoadClass(f); + if (c != null) { + if (resolve) resolveClass(c); + return c; + } + } catch (Exception ex) { + if (ex instanceof IllegalArgumentException) + // pass error from reportPatternMethods + throw (IllegalArgumentException) ex; + } + } + return super.loadClass(name, resolve); + } + private File findClassInPath(String name) { + for (String s : classpath) { + File f = classPathFile(new File(s), name); + if (f.exists() && f.canRead()) { + return f; + } + } + return null; + } + protected Class<?> findClass(String name) throws ClassNotFoundException { + try { + return transformAndLoadClass(findClassInPath(name)); + } catch (IOException ex) { + throw new ClassNotFoundException("IO error", ex); + } + } + private Class<?> transformAndLoadClass(File f) throws ClassNotFoundException, IOException { + if (verbose) System.out.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)"); + logic.reportPatternMethods(!verbose, keepgoing); + byte[] bytes = cf.toByteArray(); + return defineClass(null, bytes, 0, bytes.length); + } + } + + private class Logic { + // Indify logic, per se. + ClassFile cf; + final char[] poolMarks; + final Map<Method,Constant> constants = new HashMap<>(); + final Map<Method,String> indySignatures = new HashMap<>(); + Logic(ClassFile cf) { + this.cf = cf; + poolMarks = new char[cf.pool.size()]; + } + boolean transform() { + if (!initializeMarks()) return false; + if (!findPatternMethods()) return false; + Pool pool = cf.pool; + //for (Constant c : cp) System.out.println(" # "+c); + for (Method m : cf.methods) { + if (constants.containsKey(m)) continue; // don't bother + // Transform references. + int blab = 0; + for (Instruction i = m.instructions(); i != null; i = i.next()) { + if (i.bc != opc_invokestatic) continue; + int methi = i.u2At(1); + if (poolMarks[methi] == 0) continue; + Short[] ref = pool.getMemberRef((short)methi); + Method conm = findMember(cf.methods, ref[1], ref[2]); + if (conm == null) continue; + Constant con = constants.get(conm); + if (con == null) continue; + if (blab++ == 0 && !quiet) + System.err.println("patching "+cf.nameString()+"."+m); + //if (blab == 1) { for (Instruction j = m.instructions(); j != null; j = j.next()) System.out.println(" |"+j); } + if (con.tag == CONSTANT_InvokeDynamic || + con.tag == CONSTANT_InvokeDynamic_17) { + // need to patch the following instruction too, + // but there are usually intervening argument pushes too + Instruction i2 = findPop(i); + Short[] ref2 = null; + short ref2i = 0; + if (i2 != null && i2.bc == opc_invokevirtual && + poolMarks[(char)(ref2i = (short) i2.u2At(1))] == 'D') + ref2 = pool.getMemberRef(ref2i); + if (ref2 == null || !"invokeExact".equals(pool.getString(ref2[1]))) { + System.err.println(m+": failed to create invokedynamic at "+i.pc); + continue; + } + String invType = pool.getString(ref2[2]); + String bsmType = indySignatures.get(conm); + if (!invType.equals(bsmType)) { + System.err.println(m+": warning: "+conm+" call type and local invoke type differ: " + +bsmType+", "+invType); + } + assert(i.len == 3 || i2.len == 3); + if (!quiet) System.err.println(i+" "+conm+";...; "+i2+" => invokedynamic "+con); + int start = i.pc + 3, end = i2.pc; + System.arraycopy(i.codeBase, start, i.codeBase, i.pc, end-start); + i.forceNext(0); // force revisit of new instruction + i2.u1AtPut(-3, opc_invokedynamic); + i2.u2AtPut(-2, con.index); + i2.u2AtPut(0, (short)0); + i2.u1AtPut(2, opc_nop); + //System.out.println(new Instruction(i.codeBase, i2.pc-3)); + } else { + if (!quiet) System.err.println(i+" "+conm+" => ldc "+con); + assert(i.len == 3); + i.u1AtPut(0, opc_ldc_w); + i.u2AtPut(1, con.index); + } + } + //if (blab >= 1) { for (Instruction j = m.instructions(); j != null; j = j.next()) System.out.println(" |"+j); } + } + cf.methods.removeAll(constants.keySet()); + return true; + } + + // Scan forward from the instruction to find where the stack p + // below the current sp at the instruction. + Instruction findPop(Instruction i) { + //System.out.println("findPop from "+i); + Pool pool = cf.pool; + JVMState jvm = new JVMState(); + decode: + for (i = i.clone().next(); i != null; i = i.next()) { + String pops = INSTRUCTION_POPS[i.bc]; + //System.out.println(" "+i+" "+jvm.stack+" : "+pops.replace("$", " => ")); + if (pops == null) break; + if (jvm.stackMotion(i.bc)) continue decode; + if (pops.indexOf('Q') >= 0) { + Short[] ref = pool.getMemberRef((short) i.u2At(1)); + String type = simplifyType(pool.getString(CONSTANT_Utf8, ref[2])); + switch (i.bc) { + case opc_getstatic: + case opc_getfield: + case opc_putstatic: + case opc_putfield: + pops = pops.replace("Q", type); + break; + default: + if (!type.startsWith("(")) + throw new InternalError(i.toString()); + pops = pops.replace("Q$Q", type.substring(1).replace(")","$")); + break; + } + //System.out.println("special type: "+type+" => "+pops); + } + int npops = pops.indexOf('$'); + if (npops < 0) throw new InternalError(); + if (npops > jvm.sp()) return i; + List<Object> args = jvm.args(npops); + int k = 0; + for (Object x : args) { + char have = (Character) x; + char want = pops.charAt(k++); + if (have == 'X' || want == 'X') continue; + if (have != want) break decode; + } + if (pops.charAt(k++) != '$') break decode; + args.clear(); + while (k < pops.length()) + args.add(pops.charAt(k++)); + } + System.err.println("*** bailout on jvm: "+jvm.stack+" "+i); + return null; + } + + boolean findPatternMethods() { + boolean found = false; + for (char mark : "THI".toCharArray()) { + for (Method m : cf.methods) { + if (!Modifier.isPrivate(m.access)) continue; + if (!Modifier.isStatic(m.access)) continue; + if (nameAndTypeMark(m.name, m.type) == mark) { + Constant con = scanPattern(m, mark); + if (con == null) continue; + constants.put(m, con); + found = true; + } + } + } + return found; + } + + void reportPatternMethods(boolean quietly, boolean allowMatchFailure) { + if (!quietly && !constants.keySet().isEmpty()) + System.err.println("pattern methods removed: "+constants.keySet()); + for (Method m : cf.methods) { + if (nameMark(cf.pool.getString(m.name)) != 0 && + constants.get(m) == null) { + String failure = "method has special name but fails to match pattern: "+m; + if (!allowMatchFailure) + throw new IllegalArgumentException(failure); + else if (!quietly) + System.err.println("warning: "+failure); + } + } + if (verifySpecifierCount >= 0) { + List<Object[]> specs = bootstrapMethodSpecifiers(false); + int specsLen = (specs == null ? 0 : specs.size()); + if (specsLen != verifySpecifierCount) { + throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount); + } + } + } + + // mark constant pool entries according to participation in patterns + boolean initializeMarks() { + boolean changed = false; + for (;;) { + boolean changed1 = false; + int cpindex = -1; + for (Constant e : cf.pool) { + ++cpindex; + if (e == null) continue; + char mark = poolMarks[cpindex]; + if (mark != 0) continue; + switch (e.tag) { + case CONSTANT_Utf8: + mark = nameMark(e.itemString()); break; + case CONSTANT_NameAndType: + mark = nameAndTypeMark(e.itemIndexes()); break; + case CONSTANT_Class: { + int n1 = e.itemIndex(); + char nmark = poolMarks[(char)n1]; + if ("DJ".indexOf(nmark) >= 0) + mark = nmark; + break; + } + case CONSTANT_Field: + case CONSTANT_Method: { + Short[] n12 = e.itemIndexes(); + short cl = n12[0]; + short nt = n12[1]; + char cmark = poolMarks[(char)cl]; + if (cmark != 0) { + mark = cmark; // it is a java.dyn.* or java.lang.* method + break; + } + String cls = cf.pool.getString(CONSTANT_Class, cl); + if (cls.equals(cf.nameString())) { + switch (poolMarks[(char)nt]) { + // it is a private MH/MT/INDY method + case 'T': case 'H': case 'I': + mark = poolMarks[(char)nt]; + break; + } + } + break; + } + default: break; + } + if (mark != 0) { + poolMarks[cpindex] = mark; + changed1 = true; + } + } + if (!changed1) + break; + changed = true; + } + return changed; + } + char nameMark(String s) { + if (s.startsWith("MT_")) return 'T'; + else if (s.startsWith("MH_")) return 'H'; + else if (s.startsWith("INDY_")) return 'I'; + else if (s.startsWith("java/dyn/")) return 'D'; + else if (s.startsWith("java/lang/")) return 'J'; + return 0; + } + char nameAndTypeMark(Short[] n12) { + return nameAndTypeMark(n12[0], n12[1]); + } + char nameAndTypeMark(short n1, short n2) { + char mark = poolMarks[(char)n1]; + if (mark == 0) return 0; + String descr = cf.pool.getString(CONSTANT_Utf8, n2); + String requiredType; + switch (poolMarks[(char)n1]) { + case 'H': requiredType = "()Ljava/dyn/MethodHandle;"; break; + case 'T': requiredType = "()Ljava/dyn/MethodType;"; break; + case 'I': requiredType = "()Ljava/dyn/MethodHandle;"; break; + default: return 0; + } + if (descr.equals(requiredType)) return mark; + return 0; + } + + private class JVMState { + final List<Object> stack = new ArrayList<>(); + int sp() { return stack.size(); } + void push(Object x) { stack.add(x); } + void push2(Object x) { stack.add(EMPTY_SLOT); stack.add(x); } + void pushAt(int pos, Object x) { stack.add(stack.size()+pos, x); } + Object pop() { return stack.remove(sp()-1); } + Object top() { return stack.get(sp()-1); } + List<Object> args(boolean hasRecv, String type) { + return args(argsize(type) + (hasRecv ? 1 : 0)); + } + List<Object> args(int argsize) { + return stack.subList(sp()-argsize, sp()); + } + boolean stackMotion(int bc) { + switch (bc) { + case opc_pop: pop(); break; + case opc_pop2: pop(); pop(); break; + case opc_swap: pushAt(-1, pop()); break; + case opc_dup: push(top()); break; + case opc_dup_x1: pushAt(-2, top()); break; + case opc_dup_x2: pushAt(-3, top()); break; + // ? also: dup2{,_x1,_x2} + default: return false; + } + return true; + } + } + private final String EMPTY_SLOT = "_"; + private void removeEmptyJVMSlots(List<Object> args) { + for (;;) { + int i = args.indexOf(EMPTY_SLOT); + if (i >= 0 && i+1 < args.size() + && (isConstant(args.get(i+1), CONSTANT_Long) || + isConstant(args.get(i+1), CONSTANT_Double))) + args.remove(i); + else break; + } + } + + private Constant scanPattern(Method m, char patternMark) { + if (verbose) System.err.println("scan "+m+" for pattern="+patternMark); + int wantTag; + switch (patternMark) { + case 'T': wantTag = CONSTANT_MethodType; break; + case 'H': wantTag = CONSTANT_MethodHandle; break; + case 'I': wantTag = CONSTANT_InvokeDynamic; break; + default: throw new InternalError(); + } + Instruction i = m.instructions(); + JVMState jvm = new JVMState(); + Pool pool = cf.pool; + int branchCount = 0; + Object arg; + List<Object> args; + List<Object> bsmArgs = null; // args to invokeGeneric + decode: + for (; i != null; i = i.next()) { + //System.out.println(jvm.stack+" "+i); + int bc = i.bc; + switch (bc) { + case opc_ldc: jvm.push(pool.get(i.u1At(1))); break; + case opc_ldc_w: jvm.push(pool.get(i.u2At(1))); break; + case opc_ldc2_w: jvm.push2(pool.get(i.u2At(1))); break; + case opc_aconst_null: jvm.push(null); break; + case opc_bipush: jvm.push((int)(byte) i.u1At(1)); break; + case opc_sipush: jvm.push((int)(short)i.u2At(1)); break; + + // these support creation of a restarg array + case opc_anewarray: + arg = jvm.pop(); + if (!(arg instanceof Integer)) break decode; + arg = Arrays.asList(new Object[(Integer)arg]); + jvm.push(arg); + break; + case opc_dup: + jvm.push(jvm.top()); break; + case opc_aastore: + args = jvm.args(3); // array, index, value + if (args.get(0) instanceof List && + args.get(1) instanceof Integer) { + ((List<Object>)args.get(0)).set( (Integer)args.get(1), args.get(2) ); + } + args.clear(); + break; + + case opc_getstatic: + { + // int.class compiles to getstatic Integer.TYPE + int fieldi = i.u2At(1); + char mark = poolMarks[fieldi]; + //System.err.println("getstatic "+fieldi+Arrays.asList(pool.getStrings(pool.getMemberRef((short)fieldi)))+mark); + if (mark == 'J') { + Short[] ref = pool.getMemberRef((short) fieldi); + String name = pool.getString(CONSTANT_Utf8, ref[1]); + if ("TYPE".equals(name)) { + String wrapperName = pool.getString(CONSTANT_Class, ref[0]).replace('/', '.'); + // a primitive type descriptor + Class<?> primClass; + try { + primClass = (Class<?>) Class.forName(wrapperName).getField(name).get(null); + } catch (Exception ex) { + throw new InternalError("cannot load "+wrapperName+"."+name); + } + jvm.push(primClass); + break; + } + } + // unknown field; keep going... + jvm.push(UNKNOWN_CON); + break; + } + case opc_putstatic: + { + if (patternMark != 'I') break decode; + jvm.pop(); + // unknown field; keep going... + break; + } + + case opc_invokestatic: + case opc_invokevirtual: + { + boolean hasRecv = (bc == opc_invokevirtual); + int methi = i.u2At(1); + char mark = poolMarks[methi]; + Short[] ref = pool.getMemberRef((short)methi); + String type = pool.getString(CONSTANT_Utf8, ref[2]); + //System.out.println("invoke "+pool.getString(CONSTANT_Utf8, ref[1])+" "+Arrays.asList(ref)+" : "+type); + args = jvm.args(hasRecv, type); + String intrinsic = null; + Constant con; + if (mark == 'D' || mark == 'J') { + intrinsic = pool.getString(CONSTANT_Utf8, ref[1]); + if (mark == 'J') { + String cls = pool.getString(CONSTANT_Class, ref[0]); + cls = cls.substring(1+cls.lastIndexOf('/')); + intrinsic = cls+"."+intrinsic; + } + //System.out.println("recognized intrinsic "+intrinsic); + byte refKind = -1; + switch (intrinsic) { + case "findGetter": refKind = REF_getField; break; + case "findStaticGetter": refKind = REF_getStatic; break; + case "findSetter": refKind = REF_putField; break; + case "findStaticSetter": refKind = REF_putStatic; break; + case "findVirtual": refKind = REF_invokeVirtual; break; + case "findStatic": refKind = REF_invokeStatic; break; + case "findSpecial": refKind = REF_invokeSpecial; break; + case "findConstructor": refKind = REF_newInvokeSpecial; break; + } + if (refKind >= 0 && (con = parseMemberLookup(refKind, args)) != null) { + args.clear(); args.add(con); + continue; + } + } + Method ownMethod = null; + if (mark == 'T' || mark == 'H' || mark == 'I') { + ownMethod = findMember(cf.methods, ref[1], ref[2]); + } + switch (intrinsic == null ? "" : intrinsic) { + case "fromMethodDescriptorString": + con = makeMethodTypeCon(args.get(0)); + args.clear(); args.add(con); + continue; + case "methodType": { + flattenVarargs(args); // there are several overloadings, some with varargs + StringBuilder buf = new StringBuilder(); + String rtype = null; + for (Object typeArg : args) { + if (typeArg instanceof Class) { + Class<?> argClass = (Class<?>) typeArg; + if (argClass.isPrimitive()) { + char tchar; + switch (argClass.getName()) { + case "void": tchar = 'V'; break; + case "boolean": tchar = 'Z'; break; + case "byte": tchar = 'B'; break; + case "char": tchar = 'C'; break; + case "short": tchar = 'S'; break; + case "int": tchar = 'I'; break; + case "long": tchar = 'J'; break; + case "float": tchar = 'F'; break; + case "double": tchar = 'D'; break; + default: throw new InternalError(argClass.toString()); + } + buf.append(tchar); + } else { + // should not happen, but... + buf.append('L').append(argClass.getName().replace('.','/')).append(';'); + } + } else if (typeArg instanceof Constant) { + Constant argCon = (Constant) typeArg; + if (argCon.tag == CONSTANT_Class) { + String cn = pool.get(argCon.itemIndex()).itemString(); + if (cn.endsWith(";")) + buf.append(cn); + else + buf.append('L').append(cn).append(';'); + } else { + break decode; + } + } else { + break decode; + } + if (rtype == null) { + // first arg is treated differently + rtype = buf.toString(); + buf.setLength(0); + buf.append('('); + } + } + buf.append(')').append(rtype); + con = con = makeMethodTypeCon(buf.toString()); + args.clear(); args.add(con); + continue; + } + case "lookup": + case "dynamicInvoker": + args.clear(); args.add(intrinsic); + continue; + case "lookupClass": + if (args.equals(Arrays.asList("lookup"))) { + // fold lookup().lookupClass() to the enclosing class + args.clear(); args.add(pool.get(cf.thisc)); + continue; + } + break; + case "invokeGeneric": + case "invokeWithArguments": + if (patternMark != 'I') break decode; + if ("invokeWithArguments".equals(intrinsic)) + flattenVarargs(args); + bsmArgs = new ArrayList(args); + args.clear(); args.add("invokeGeneric"); + continue; + case "Integer.valueOf": + case "Float.valueOf": + case "Long.valueOf": + case "Double.valueOf": + removeEmptyJVMSlots(args); + if (args.size() == 1) { + arg = args.remove(0); + assert(3456 == (CONSTANT_Integer*1000 + CONSTANT_Float*100 + CONSTANT_Long*10 + CONSTANT_Double)); + if (isConstant(arg, CONSTANT_Integer + "IFLD".indexOf(intrinsic.charAt(0))) + || arg instanceof Number) { + args.add(arg); continue; + } + } + break decode; + } + if (!hasRecv && ownMethod != null && patternMark != 0) { + con = constants.get(ownMethod); + if (con == null) break decode; + args.clear(); args.add(con); + continue; + } else if (type.endsWith(")V")) { + // allow calls like println("reached the pattern method") + args.clear(); + continue; + } + break decode; // bail out for most calls + } + case opc_areturn: + { + ++branchCount; + if (bsmArgs != null) { + // parse bsmArgs as (MH, lookup, String, MT, [extra]) + Constant indyCon = makeInvokeDynamicCon(bsmArgs); + if (indyCon != null) { + Constant typeCon = (Constant) bsmArgs.get(3); + indySignatures.put(m, pool.getString(typeCon.itemIndex())); + return indyCon; + } + System.err.println(m+": inscrutable bsm arguments: "+bsmArgs); + break decode; // bail out + } + arg = jvm.pop(); + if (branchCount == 2 && UNKNOWN_CON.equals(arg)) + break; // merge to next path + if (isConstant(arg, wantTag)) + return (Constant) arg; + break decode; // bail out + } + default: + if (jvm.stackMotion(i.bc)) break; + if (bc >= opc_nconst_MIN && bc <= opc_nconst_MAX) + { jvm.push(INSTRUCTION_CONSTANTS[bc - opc_nconst_MIN]); break; } + if (patternMark == 'I') { + // these support caching paths in INDY_x methods + if (bc == opc_aload || bc >= opc_aload_0 && bc <= opc_aload_MAX) + { jvm.push(UNKNOWN_CON); break; } + if (bc == opc_astore || bc >= opc_astore_0 && bc <= opc_astore_MAX) + { jvm.pop(); break; } + switch (bc) { + case opc_getfield: + case opc_aaload: + jvm.push(UNKNOWN_CON); break; + case opc_ifnull: + case opc_ifnonnull: + // ignore branch target + if (++branchCount != 1) break decode; + jvm.pop(); + break; + case opc_checkcast: + arg = jvm.top(); + if ("invokeWithArguments".equals(arg) || + "invokeGeneric".equals(arg)) + break; // assume it is a helpful cast + break decode; + default: + break decode; // bail out + } + continue decode; // go to next instruction + } + break decode; // bail out + } //end switch + } + System.err.println(m+": bailout on "+i+" jvm stack: "+jvm.stack); + return null; + } + private final String UNKNOWN_CON = "<unknown>"; + + private void flattenVarargs(List<Object> args) { + int size = args.size(); + if (size > 0 && args.get(size-1) instanceof List) + args.addAll((List<Object>) args.remove(size-1)); + } + + private boolean isConstant(Object x, int tag) { + return x instanceof Constant && ((Constant)x).tag == tag; + } + private Constant makeMethodTypeCon(Object x) { + short utfIndex; + if (x instanceof String) + utfIndex = (short) cf.pool.addConstant(CONSTANT_Utf8, x).index; + else if (isConstant(x, CONSTANT_String)) + utfIndex = ((Constant)x).itemIndex(); + else return null; + return cf.pool.addConstant(CONSTANT_MethodType, utfIndex); + } + private Constant parseMemberLookup(byte refKind, List<Object> args) { + // E.g.: lookup().findStatic(Foo.class, "name", MethodType) + if (args.size() != 4) return null; + int argi = 0; + if (!"lookup".equals(args.get(argi++))) return null; + short refindex, cindex, ntindex, nindex, tindex; + Object con; + if (!isConstant(con = args.get(argi++), CONSTANT_Class)) return null; + cindex = (short)((Constant)con).index; + if (!isConstant(con = args.get(argi++), CONSTANT_String)) return null; + nindex = ((Constant)con).itemIndex(); + if (isConstant(con = args.get(argi++), CONSTANT_MethodType) || + isConstant(con, CONSTANT_Class)) { + tindex = ((Constant)con).itemIndex(); + } else return null; + ntindex = (short) cf.pool.addConstant(CONSTANT_NameAndType, + new Short[]{ nindex, tindex }).index; + byte reftag = CONSTANT_Method; + if (refKind <= REF_putStatic) + reftag = CONSTANT_Field; + else if (refKind == REF_invokeInterface) + reftag = CONSTANT_InterfaceMethod; + Constant ref = cf.pool.addConstant(reftag, new Short[]{ cindex, ntindex }); + return cf.pool.addConstant(CONSTANT_MethodHandle, new Object[]{ refKind, (short)ref.index }); + } + private Constant makeInvokeDynamicCon(List<Object> args) { + // E.g.: MH_bsm.invokeGeneric(lookup(), "name", MethodType, "extraArg") + removeEmptyJVMSlots(args); + if (args.size() != 4 && args.size() != 5) return null; + int argi = 0; + short nindex, tindex, ntindex, bsmindex; + Object con; + if (!isConstant(con = args.get(argi++), CONSTANT_MethodHandle)) return null; + bsmindex = (short) ((Constant)con).index; + if (!"lookup".equals(args.get(argi++))) return null; + if (!isConstant(con = args.get(argi++), CONSTANT_String)) return null; + nindex = ((Constant)con).itemIndex(); + if (!isConstant(con = args.get(argi++), CONSTANT_MethodType)) return null; + tindex = ((Constant)con).itemIndex(); + ntindex = (short) cf.pool.addConstant(CONSTANT_NameAndType, + new Short[]{ nindex, tindex }).index; + if (transitionalJSR292) { + if (argi != args.size()) { + System.err.println("BSM specifier has extra arguments but transitionalJSR292=1"); + return null; + } + return cf.pool.addConstant(CONSTANT_InvokeDynamic_17, + new Short[]{ bsmindex, ntindex }); + } + List<Object> extraArgs = Collections.emptyList(); + if (argi < args.size()) { + Object arg = args.get(argi); + if (arg instanceof List) + extraArgs = (List<Object>) arg; + else + extraArgs = Arrays.asList(arg); + removeEmptyJVMSlots(args); + } + List<Short> extraArgIndexes = new CountedList<>(Short.class); + for (Object x : extraArgs) { + if (x instanceof Number) { + Object num = null; byte numTag = 0; + if (x instanceof Integer) { num = x; numTag = CONSTANT_Integer; } + if (x instanceof Float) { num = Float.floatToRawIntBits((Float)x); numTag = CONSTANT_Float; } + if (x instanceof Long) { num = x; numTag = CONSTANT_Long; } + if (x instanceof Double) { num = Double.doubleToRawLongBits((Double)x); numTag = CONSTANT_Double; } + if (num != null) x = cf.pool.addConstant(numTag, x); + } + if (!(x instanceof Constant)) return null; + extraArgIndexes.add((short) ((Constant)x).index); + } + List<Object[]> specs = bootstrapMethodSpecifiers(true); + int specindex = -1; + Object[] spec = new Object[]{ bsmindex, extraArgIndexes }; + for (Object[] spec1 : specs) { + if (Arrays.equals(spec1, spec)) { + specindex = specs.indexOf(spec1); + if (verbose) System.err.println("reusing BSM specifier: "+spec1[0]+spec1[1]); + break; + } + } + if (specindex == -1) { + specindex = (short) specs.size(); + specs.add(spec); + if (verbose) System.err.println("adding BSM specifier: "+spec[0]+spec[1]); + } + return cf.pool.addConstant(CONSTANT_InvokeDynamic, + new Short[]{ (short)specindex, ntindex }); + } + + List<Object[]> bootstrapMethodSpecifiers(boolean createIfNotFound) { + Attr bsms = cf.findAttr("BootstrapMethods"); + if (bsms == null) { + if (!createIfNotFound) return null; + bsms = new Attr(cf, "BootstrapMethods", new byte[]{0,0}); + assert(bsms == cf.findAttr("BootstrapMethods")); + } + if (bsms.item instanceof byte[]) { + // unflatten + List<Object[]> specs = new CountedList<>(Object[].class); + DataInputStream in = new DataInputStream(new ByteArrayInputStream((byte[]) bsms.item)); + try { + int len = (char) in.readShort(); + for (int i = 0; i < len; i++) { + short bsm = in.readShort(); + int argc = (char) in.readShort(); + List<Short> argv = new CountedList<>(Short.class); + for (int j = 0; j < argc; j++) + argv.add(in.readShort()); + specs.add(new Object[]{ bsm, argv }); + } + } catch (IOException ex) { throw new InternalError(); } + bsms.item = specs; + } + return (List<Object[]>) bsms.item; + } + } + + private DataInputStream openInput(File f) throws IOException { + return new DataInputStream(new BufferedInputStream(new FileInputStream(f))); + } + + private DataOutputStream openOutput(File f) throws IOException { + if (!overwrite && f.exists()) + throw new IOException("file already exists: "+f); + ensureDirectory(f.getParentFile()); + return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f))); + } + + static byte[] readRawBytes(DataInputStream in, int size) throws IOException { + byte[] bytes = new byte[size]; + int nr = in.read(bytes); + if (nr != size) + throw new InternalError("wrong size: "+nr); + return bytes; + } + + private interface Chunk { + void readFrom(DataInputStream in) throws IOException; + void writeTo(DataOutputStream out) throws IOException; + } + + private static class CountedList<T> extends ArrayList<T> implements Chunk { + final Class<? extends T> itemClass; + final int rowlen; + CountedList(Class<? extends T> itemClass, int rowlen) { + this.itemClass = itemClass; + this.rowlen = rowlen; + } + CountedList(Class<? extends T> itemClass) { this(itemClass, -1); } + public void readFrom(DataInputStream in) throws IOException { + int count = in.readUnsignedShort(); + while (size() < count) { + if (rowlen < 0) { + add(readInput(in, itemClass)); + } else { + Class<?> elemClass = itemClass.getComponentType(); + Object[] row = (Object[]) java.lang.reflect.Array.newInstance(elemClass, rowlen); + for (int i = 0; i < rowlen; i++) + row[i] = readInput(in, elemClass); + add(itemClass.cast(row)); + } + } + } + public void writeTo(DataOutputStream out) throws IOException { + out.writeShort((short)size()); + for (T item : this) { + writeOutput(out, item); + } + } + } + + private static <T> T readInput(DataInputStream in, Class<T> dataClass) throws IOException { + Object data; + if (dataClass == Integer.class) { + data = in.readInt(); + } else if (dataClass == Short.class) { + data = in.readShort(); + } else if (dataClass == Byte.class) { + data = in.readByte(); + } else if (dataClass == String.class) { + data = in.readUTF(); + } else if (Chunk.class.isAssignableFrom(dataClass)) { + T obj; + try { obj = dataClass.newInstance(); } + catch (Exception ex) { throw new RuntimeException(ex); } + ((Chunk)obj).readFrom(in); + data = obj; + } else { + throw new InternalError("bad input datum: "+dataClass); + } + return dataClass.cast(data); + } + private static <T> T readInput(byte[] bytes, Class<T> dataClass) { + try { + return readInput(new DataInputStream(new ByteArrayInputStream(bytes)), dataClass); + } catch (IOException ex) { + throw new InternalError(); + } + } + private static void readInputs(DataInputStream in, Object... data) throws IOException { + for (Object x : data) ((Chunk)x).readFrom(in); + } + + private static void writeOutput(DataOutputStream out, Object data) throws IOException { + if (data == null) { + return; + } if (data instanceof Integer) { + out.writeInt((Integer)data); + } else if (data instanceof Long) { + out.writeLong((Long)data); + } else if (data instanceof Short) { + out.writeShort((Short)data); + } else if (data instanceof Byte) { + out.writeByte((Byte)data); + } else if (data instanceof String) { + out.writeUTF((String)data); + } else if (data instanceof byte[]) { + out.write((byte[])data); + } else if (data instanceof Object[]) { + for (Object x : (Object[]) data) + writeOutput(out, x); + } else if (data instanceof Chunk) { + Chunk x = (Chunk) data; + x.writeTo(out); + } else if (data instanceof List) { + for (Object x : (List<?>) data) + writeOutput(out, x); + } else { + throw new InternalError("bad output datum: "+data+" : "+data.getClass().getName()); + } + } + private static void writeOutputs(DataOutputStream out, Object... data) throws IOException { + for (Object x : data) writeOutput(out, x); + } + + public static abstract class Outer { + public abstract List<? extends Inner> inners(); + protected void linkInners() { + for (Inner i : inners()) { + i.linkOuter(this); + if (i instanceof Outer) + ((Outer)i).linkInners(); + } + } + public <T extends Outer> T outer(Class<T> c) { + for (Outer walk = this;; walk = ((Inner)walk).outer()) { + if (c.isInstance(walk)) + return c.cast(walk); + //if (!(walk instanceof Inner)) return null; + } + } + + public abstract List<Attr> attrs(); + public Attr findAttr(String name) { + return findAttr(outer(ClassFile.class).pool.stringIndex(name, false)); + } + public Attr findAttr(int name) { + if (name == 0) return null; + for (Attr a : attrs()) { + if (a.name == name) return a; + } + return null; + } + } + public interface Inner { Outer outer(); void linkOuter(Outer o); } + public static abstract class InnerOuter extends Outer implements Inner { + public Outer outer; + public Outer outer() { return outer; } + public void linkOuter(Outer o) { assert(outer == null); outer = o; } + } + public static class Constant<T> implements Chunk { + public final byte tag; + public final T item; + public final int index; + public Constant(int index, byte tag, T item) { + this.index = index; + this.tag = tag; + this.item = item; + } + public Constant checkTag(byte tag) { + if (this.tag != tag) throw new InternalError(this.toString()); + return this; + } + public String itemString() { return (String)item; } + public Short itemIndex() { return (Short)item; } + public Short[] itemIndexes() { return (Short[])item; } + public void readFrom(DataInputStream in) throws IOException { + throw new InternalError("do not call"); + } + public void writeTo(DataOutputStream out) throws IOException { + writeOutputs(out, tag, item); + } + public boolean equals(Object x) { return (x instanceof Constant && equals((Constant)x)); } + public boolean equals(Constant that) { + return (this.tag == that.tag && this.itemAsComparable().equals(that.itemAsComparable())); + } + public int hashCode() { return (tag * 31) + this.itemAsComparable().hashCode(); } + public Object itemAsComparable() { + switch (tag) { + case CONSTANT_Double: return Double.longBitsToDouble((Long)item); + case CONSTANT_Float: return Float.intBitsToFloat((Integer)item); + } + return (item instanceof Object[] ? Arrays.asList((Object[])item) : item); + } + public String toString() { + String itstr = String.valueOf(itemAsComparable()); + return (index + ":" + tagName(tag) + (itstr.startsWith("[")?"":"=") + itstr); + } + private static String[] TAG_NAMES; + public static String tagName(byte tag) { // used for error messages + if (TAG_NAMES == null) + TAG_NAMES = ("None Utf8 Unicode Integer Float Long Double Class String" + +" Fieldref Methodref InterfaceMethodref NameAndType #13 #14" + +" MethodHandle MethodType InvokeDynamic#17 InvokeDynamic").split(" "); + if ((tag & 0xFF) >= TAG_NAMES.length) return "#"+(tag & 0xFF); + return TAG_NAMES[tag & 0xFF]; + } + } + + public static class Pool extends CountedList<Constant> implements Chunk { + private Map<String,Short> strings = new TreeMap<>(); + + public Pool() { + super(Constant.class); + } + public void readFrom(DataInputStream in) throws IOException { + int count = in.readUnsignedShort(); + add(null); // always ignore first item + while (size() < count) { + readConstant(in); + } + } + public <T> Constant<T> addConstant(byte tag, T item) { + Constant<T> con = new Constant<>(size(), tag, item); + int idx = indexOf(con); + if (idx >= 0) return get(idx); + add(con); + if (tag == CONSTANT_Utf8) strings.put((String)item, (short) con.index); + return con; + } + private void readConstant(DataInputStream in) throws IOException { + byte tag = in.readByte(); + int index = size(); + Object arg; + switch (tag) { + case CONSTANT_Utf8: + arg = in.readUTF(); + strings.put((String) arg, (short) size()); + break; + case CONSTANT_Integer: + case CONSTANT_Float: + arg = in.readInt(); break; + case CONSTANT_Long: + case CONSTANT_Double: + add(new Constant(index, tag, in.readLong())); + add(null); + return; + case CONSTANT_Class: + case CONSTANT_String: + arg = in.readShort(); break; + case CONSTANT_Field: + case CONSTANT_Method: + case CONSTANT_InterfaceMethod: + case CONSTANT_NameAndType: + case CONSTANT_InvokeDynamic_17: + case CONSTANT_InvokeDynamic: + // read an ordered pair + arg = new Short[] { in.readShort(), in.readShort() }; + break; + case CONSTANT_MethodHandle: + // read an ordered pair; first part is a u1 (not u2) + arg = new Object[] { in.readByte(), in.readShort() }; + break; + case CONSTANT_MethodType: + arg = in.readShort(); break; + default: + throw new InternalError("bad CP tag "+tag); + } + add(new Constant(index, tag, arg)); + } + + // Access: + public Constant get(int index) { + // extra 1-bits get into the shorts + return super.get((char) index); + } + String getString(byte tag, short index) { + get(index).checkTag(tag); + return getString(index); + } + String getString(short index) { + Object v = get(index).item; + if (v instanceof Short) + v = get((Short)v).checkTag(CONSTANT_Utf8).item; + return (String) v; + } + String[] getStrings(Short[] indexes) { + String[] res = new String[indexes.length]; + for (int i = 0; i < indexes.length; i++) + res[i] = getString(indexes[i]); + return res; + } + int stringIndex(String name, boolean createIfNotFound) { + Short x = strings.get(name); + if (x != null) return (char)(int) x; + if (!createIfNotFound) return 0; + return addConstant(CONSTANT_Utf8, name).index; + } + Short[] getMemberRef(short index) { + Short[] cls_nnt = get(index).itemIndexes(); + Short[] name_type = get(cls_nnt[1]).itemIndexes(); + return new Short[]{ cls_nnt[0], name_type[0], name_type[1] }; + } + } + + public class ClassFile extends Outer implements Chunk { + ClassFile(File f) throws IOException { + DataInputStream in = openInput(f); + try { + readFrom(in); + } finally { + if (in != null) in.close(); + } + } + + public int magic, version; // <min:maj> + public final Pool pool = new Pool(); + public short access, thisc, superc; + public final List<Short> interfaces = new CountedList<>(Short.class); + public final List<Field> fields = new CountedList<>(Field.class); + public final List<Method> methods = new CountedList<>(Method.class); + public final List<Attr> attrs = new CountedList<>(Attr.class); + + public final void readFrom(DataInputStream in) throws IOException { + magic = in.readInt(); version = in.readInt(); + if (magic != 0xCAFEBABE) throw new IOException("bad magic number"); + pool.readFrom(in); + Code_index = pool.stringIndex("Code", false); + access = in.readShort(); thisc = in.readShort(); superc = in.readShort(); + readInputs(in, interfaces, fields, methods, attrs); + if (in.read() >= 0) throw new IOException("junk after end of file"); + linkInners(); + } + + void writeTo(File f) throws IOException { + DataOutputStream out = openOutput(f); + try { + writeTo(out); + } finally { + out.close(); + } + } + + public void writeTo(DataOutputStream out) throws IOException { + writeOutputs(out, magic, version, pool, + access, thisc, superc, interfaces, + fields, methods, attrs); + } + + public byte[] toByteArray() { + try { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + writeTo(new DataOutputStream(buf)); + return buf.toByteArray(); + } catch (IOException ex) { + throw new InternalError(); + } + } + + public List<Inner> inners() { + List<Inner> inns = new ArrayList<>(); + inns.addAll(fields); inns.addAll(methods); inns.addAll(attrs); + return inns; + } + public List<Attr> attrs() { return attrs; } + + // derived stuff: + public String nameString() { return pool.getString(CONSTANT_Class, thisc); } + int Code_index; + } + + private static <T extends Member> T findMember(List<T> mems, int name, int type) { + if (name == 0 || type == 0) return null; + for (T m : mems) { + if (m.name == name && m.type == type) return m; + } + return null; + } + + public static class Member extends InnerOuter implements Chunk { + public short access, name, type; + public final List<Attr> attrs = new CountedList<>(Attr.class); + public void readFrom(DataInputStream in) throws IOException { + access = in.readShort(); name = in.readShort(); type = in.readShort(); + readInputs(in, attrs); + } + public void writeTo(DataOutputStream out) throws IOException { + writeOutputs(out, access, name, type, attrs); + } + public List<Attr> inners() { return attrs; } + public List<Attr> attrs() { return attrs; } + public ClassFile outer() { return (ClassFile) outer; } + public String nameString() { return outer().pool.getString(CONSTANT_Utf8, name); } + public String typeString() { return outer().pool.getString(CONSTANT_Utf8, type); } + public String toString() { + if (outer == null) return super.toString(); + return nameString() + (this instanceof Method ? "" : ":") + + simplifyType(typeString()); + } + } + public static class Field extends Member { + } + public static class Method extends Member { + public Code code() { + Attr a = findAttr("Code"); + if (a == null) return null; + return (Code) a.item; + } + public Instruction instructions() { + Code code = code(); + if (code == null) return null; + return code.instructions(); + } + } + + public static class Attr extends InnerOuter implements Chunk { + public short name; + public int size = -1; // no pre-declared size + public Object item; + + public Attr() {} + public Attr(Outer outer, String name, Object item) { + ClassFile cf = outer.outer(ClassFile.class); + linkOuter(outer); + this.name = (short) cf.pool.stringIndex(name, true); + this.item = item; + outer.attrs().add(this); + } + public void readFrom(DataInputStream in) throws IOException { + name = in.readShort(); + size = in.readInt(); + item = readRawBytes(in, size); + } + public void writeTo(DataOutputStream out) throws IOException { + out.writeShort(name); + // write the 4-byte size header and then the contents: + byte[] bytes; + int trueSize; + if (item instanceof byte[]) { + bytes = (byte[]) item; + out.writeInt(trueSize = bytes.length); + out.write(bytes); + } else { + trueSize = flatten(out); + } + if (trueSize != size && size >= 0) + System.err.println("warning: attribute size changed "+size+" to "+trueSize); + } + public void linkOuter(Outer o) { + super.linkOuter(o); + if (item instanceof byte[] && + outer instanceof Method && + ((Method)outer).outer().Code_index == name) { + item = readInput((byte[])item, Code.class); + } + } + public List<Inner> inners() { + if (item instanceof Inner) + return Collections.nCopies(1, (Inner)item); + return Collections.emptyList(); + } + public List<Attr> attrs() { return null; } // Code overrides this + public byte[] flatten() { + ByteArrayOutputStream buf = new ByteArrayOutputStream(size); + flatten(buf); + return buf.toByteArray(); + } + public int flatten(DataOutputStream out) throws IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size)); + int trueSize = flatten(buf); + out.writeInt(trueSize); + buf.writeTo(out); + return trueSize; + } + private int flatten(ByteArrayOutputStream buf) { + try { + writeOutput(new DataOutputStream(buf), item); + return buf.size(); + } catch (IOException ex) { + throw new InternalError(); + } + } + public String nameString() { + ClassFile cf = outer(ClassFile.class); + if (cf == null) return "#"+name; + return cf.pool.getString(name); + } + public String toString() { + return nameString()+(size < 0 ? "=" : "["+size+"]=")+item; + } + } + + public static class Code extends InnerOuter implements Chunk { + public short stacks, locals; + public byte[] bytes; + public final List<Short[]> etable = new CountedList<>(Short[].class, 4); + public final List<Attr> attrs = new CountedList<>(Attr.class); + // etable[N] = (N)*{ startpc, endpc, handlerpc, catchtype } + public void readFrom(DataInputStream in) throws IOException { + stacks = in.readShort(); locals = in.readShort(); + bytes = readRawBytes(in, in.readInt()); + readInputs(in, etable, attrs); + } + public void writeTo(DataOutputStream out) throws IOException { + writeOutputs(out, stacks, locals, bytes.length, bytes, etable, attrs); + } + public List<Attr> inners() { return attrs; } + public List<Attr> attrs() { return attrs; } + public Instruction instructions() { + return new Instruction(bytes, 0); + } + } + + // lots of constants + private static final byte + CONSTANT_Utf8 = 1, + CONSTANT_Integer = 3, + CONSTANT_Float = 4, + CONSTANT_Long = 5, + CONSTANT_Double = 6, + CONSTANT_Class = 7, + CONSTANT_String = 8, + CONSTANT_Field = 9, + CONSTANT_Method = 10, + CONSTANT_InterfaceMethod = 11, + CONSTANT_NameAndType = 12, + CONSTANT_MethodHandle = 15, // JSR 292 + CONSTANT_MethodType = 16, // JSR 292 + CONSTANT_InvokeDynamic_17 = 17, // JSR 292, only occurs in old class files + CONSTANT_InvokeDynamic = 18; // JSR 292 + private static final byte + REF_getField = 1, + REF_getStatic = 2, + REF_putField = 3, + REF_putStatic = 4, + REF_invokeVirtual = 5, + REF_invokeStatic = 6, + REF_invokeSpecial = 7, + REF_newInvokeSpecial = 8, + REF_invokeInterface = 9; + + private static final int + opc_nop = 0, + opc_aconst_null = 1, + opc_nconst_MIN = 2, // iconst_m1 + opc_nconst_MAX = 15, // dconst_1 + opc_bipush = 16, + opc_sipush = 17, + opc_ldc = 18, + opc_ldc_w = 19, + opc_ldc2_w = 20, + opc_aload = 25, + opc_aload_0 = 42, + opc_aload_MAX = 45, + opc_aaload = 50, + opc_astore = 58, + opc_astore_0 = 75, + opc_astore_MAX = 78, + opc_aastore = 83, + opc_pop = 87, + opc_pop2 = 88, + opc_dup = 89, + opc_dup_x1 = 90, + opc_dup_x2 = 91, + opc_dup2 = 92, + opc_dup2_x1 = 93, + opc_dup2_x2 = 94, + opc_swap = 95, + opc_tableswitch = 170, + opc_lookupswitch = 171, + opc_areturn = 176, + opc_getstatic = 178, + opc_putstatic = 179, + opc_getfield = 180, + opc_putfield = 181, + opc_invokevirtual = 182, + opc_invokespecial = 183, + opc_invokestatic = 184, + opc_invokeinterface = 185, + opc_invokedynamic = 186, + opc_anewarray = 189, + opc_checkcast = 192, + opc_ifnull = 198, + opc_ifnonnull = 199, + opc_wide = 196; + + private static final Object[] INSTRUCTION_CONSTANTS = { + -1, 0, 1, 2, 3, 4, 5, 0L, 1L, 0.0F, 1.0F, 2.0F, 0.0D, 1.0D + }; + + private static final String INSTRUCTION_FORMATS = + "nop$ aconst_null$L iconst_m1$I iconst_0$I iconst_1$I "+ + "iconst_2$I iconst_3$I iconst_4$I iconst_5$I lconst_0$J_ "+ + "lconst_1$J_ fconst_0$F fconst_1$F fconst_2$F dconst_0$D_ "+ + "dconst_1$D_ bipush=bx$I sipush=bxx$I ldc=bk$X ldc_w=bkk$X "+ + "ldc2_w=bkk$X_ iload=bl/wbll$I lload=bl/wbll$J_ fload=bl/wbll$F "+ + "dload=bl/wbll$D_ aload=bl/wbll$L iload_0$I iload_1$I "+ + "iload_2$I iload_3$I lload_0$J_ lload_1$J_ lload_2$J_ "+ + "lload_3$J_ fload_0$F fload_1$F fload_2$F fload_3$F dload_0$D_ "+ + "dload_1$D_ dload_2$D_ dload_3$D_ aload_0$L aload_1$L "+ + "aload_2$L aload_3$L iaload$LI$I laload$LI$J_ faload$LI$F "+ + "daload$LI$D_ aaload$LI$L baload$LI$I caload$LI$I saload$LI$I "+ + "istore=bl/wbll$I$ lstore=bl/wbll$J_$ fstore=bl/wbll$F$ "+ + "dstore=bl/wbll$D_$ astore=bl/wbll$L$ istore_0$I$ istore_1$I$ "+ + "istore_2$I$ istore_3$I$ lstore_0$J_$ lstore_1$J_$ "+ + "lstore_2$J_$ lstore_3$J_$ fstore_0$F$ fstore_1$F$ fstore_2$F$ "+ + "fstore_3$F$ dstore_0$D_$ dstore_1$D_$ dstore_2$D_$ "+ + "dstore_3$D_$ astore_0$L$ astore_1$L$ astore_2$L$ astore_3$L$ "+ + "iastore$LII$ lastore$LIJ_$ fastore$LIF$ dastore$LID_$ "+ + "aastore$LIL$ bastore$LII$ castore$LII$ sastore$LII$ pop$X$ "+ + "pop2$XX$ dup$X$XX dup_x1$XX$XXX dup_x2$XXX$XXXX dup2$XX$XXXX "+ + "dup2_x1$XXX$XXXXX dup2_x2$XXXX$XXXXXX swap$XX$XX "+ + "iadd$II$I ladd$J_J_$J_ fadd$FF$F dadd$D_D_$D_ isub$II$I "+ + "lsub$J_J_$J_ fsub$FF$F dsub$D_D_$D_ imul$II$I lmul$J_J_$J_ "+ + "fmul$FF$F dmul$D_D_$D_ idiv$II$I ldiv$J_J_$J_ fdiv$FF$F "+ + "ddiv$D_D_$D_ irem$II$I lrem$J_J_$J_ frem$FF$F drem$D_D_$D_ "+ + "ineg$I$I lneg$J_$J_ fneg$F$F dneg$D_$D_ ishl$II$I lshl$J_I$J_ "+ + "ishr$II$I lshr$J_I$J_ iushr$II$I lushr$J_I$J_ iand$II$I "+ + "land$J_J_$J_ ior$II$I lor$J_J_$J_ ixor$II$I lxor$J_J_$J_ "+ + "iinc=blx/wbllxx$ i2l$I$J_ i2f$I$F i2d$I$D_ l2i$J_$I l2f$J_$F "+ + "l2d$J_$D_ f2i$F$I f2l$F$J_ f2d$F$D_ d2i$D_$I d2l$D_$J_ "+ + "d2f$D_$F i2b$I$I i2c$I$I i2s$I$I lcmp fcmpl fcmpg dcmpl dcmpg "+ + "ifeq=boo ifne=boo iflt=boo ifge=boo ifgt=boo ifle=boo "+ + "if_icmpeq=boo if_icmpne=boo if_icmplt=boo if_icmpge=boo "+ + "if_icmpgt=boo if_icmple=boo if_acmpeq=boo if_acmpne=boo "+ + "goto=boo jsr=boo ret=bl/wbll tableswitch=* lookupswitch=* "+ + "ireturn lreturn freturn dreturn areturn return "+ + "getstatic=bkf$Q putstatic=bkf$Q$ getfield=bkf$L$Q "+ + "putfield=bkf$LQ$ invokevirtual=bkm$LQ$Q "+ + "invokespecial=bkm$LQ$Q invokestatic=bkm$Q$Q "+ + "invokeinterface=bkixx$LQ$Q invokedynamic=bkd__$Q$Q new=bkc$L "+ + "newarray=bx$I$L anewarray=bkc$I$L arraylength$L$I athrow "+ + "checkcast=bkc$L$L instanceof=bkc$L$I monitorenter$L "+ + "monitorexit$L wide=* multianewarray=bkcx ifnull=boo "+ + "ifnonnull=boo goto_w=boooo jsr_w=boooo "; + private static final String[] INSTRUCTION_NAMES; + private static final String[] INSTRUCTION_POPS; + private static final int[] INSTRUCTION_INFO; + static { + String[] insns = INSTRUCTION_FORMATS.split(" "); + assert(insns[opc_lookupswitch].startsWith("lookupswitch")); + assert(insns[opc_tableswitch].startsWith("tableswitch")); + assert(insns[opc_wide].startsWith("wide")); + assert(insns[opc_invokedynamic].startsWith("invokedynamic")); + int[] info = new int[256]; + String[] names = new String[256]; + String[] pops = new String[256]; + for (int i = 0; i < insns.length; i++) { + String insn = insns[i]; + int dl = insn.indexOf('$'); + if (dl > 0) { + String p = insn.substring(dl+1); + if (p.indexOf('$') < 0) p = "$" + p; + pops[i] = p; + insn = insn.substring(0, dl); + } + int eq = insn.indexOf('='); + if (eq < 0) { + info[i] = 1; + names[i] = insn; + continue; + } + names[i] = insn.substring(0, eq); + String fmt = insn.substring(eq+1); + if (fmt.equals("*")) { + info[i] = 0; + continue; + } + int sl = fmt.indexOf('/'); + if (sl < 0) { + info[i] = (char) fmt.length(); + } else { + String wfmt = fmt.substring(sl+1); + fmt = fmt.substring(0, sl); + info[i] = (char)( fmt.length() + (wfmt.length() * 16) ); + } + } + INSTRUCTION_INFO = info; + INSTRUCTION_NAMES = names; + INSTRUCTION_POPS = pops; + } + + public static class Instruction implements Cloneable { + byte[] codeBase; + int pc; + int bc; + int info; + int wide; + int len; + Instruction(byte[] codeBase, int pc) { + this.codeBase = codeBase; + init(pc); + } + public Instruction clone() { + try { + return (Instruction) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + private Instruction init(int pc) { + this.pc = pc; + this.bc = codeBase[pc] & 0xFF; + this.info = INSTRUCTION_INFO[bc]; + this.wide = 0; + this.len = (info & 0x0F); + if (len == 0) + computeLength(); + return this; + } + Instruction next() { + if (len == 0 && bc != 0) throw new InternalError(); + int npc = pc + len; + if (npc == codeBase.length) + return null; + return init(npc); + } + void forceNext(int newLen) { + bc = opc_nop; + len = newLen; + } + + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(pc).append(":").append(INSTRUCTION_NAMES[bc]); + switch (len) { + case 3: buf.append(" ").append(u2At(1)); break; + case 5: buf.append(" ").append(u2At(1)).append(" ").append(u2At(3)); break; + default: for (int i = 1; i < len; i++) buf.append(" ").append(u1At(1)); + } + return buf.toString(); + } + + // these are the hard parts + private void computeLength() { + int cases; + switch (bc) { + case opc_wide: + bc = codeBase[pc + 1]; + info = INSTRUCTION_INFO[bc]; + len = ((info >> 4) & 0x0F); + if (len == 0) throw new RuntimeException("misplaced wide bytecode: "+bc); + return; + + case opc_tableswitch: + cases = (u4At(alignedIntOffset(2)) - u4At(alignedIntOffset(1)) + 1); + len = alignedIntOffset(3 + cases*1); + return; + + case opc_lookupswitch: + cases = u4At(alignedIntOffset(1)); + len = alignedIntOffset(2 + cases*2); + return; + + default: + throw new RuntimeException("unknown bytecode: "+bc); + } + } + // switch code + // clget the Nth int (where 0 is the first after the opcode itself) + public int alignedIntOffset(int n) { + int pos = pc + 1; + pos += ((-pos) & 0x03); // align it + pos += (n * 4); + return pos - pc; + } + public int u1At(int pos) { + return (codeBase[pc+pos] & 0xFF); + } + public int u2At(int pos) { + return (u1At(pos+0)<<8) + u1At(pos+1); + } + public int u4At(int pos) { + return (u2At(pos+0)<<16) + u2At(pos+2); + } + public void u1AtPut(int pos, int x) { + codeBase[pc+pos] = (byte)x; + } + public void u2AtPut(int pos, int x) { + codeBase[pc+pos+0] = (byte)(x >> 8); + codeBase[pc+pos+1] = (byte)(x >> 0); + } + } + + static String simplifyType(String type) { + String simpleType = OBJ_SIGNATURE.matcher(type).replaceAll("L"); + assert(simpleType.matches("^\\([A-Z]*\\)[A-Z]$")); + // change (DD)D to (D_D_)D_ + simpleType = WIDE_SIGNATURE.matcher(simpleType).replaceAll("\\0_"); + return simpleType; + } + static int argsize(String type) { + return simplifyType(type).length()-3; + } + private static final Pattern OBJ_SIGNATURE = Pattern.compile("\\[*L[^;]*;|\\[+[A-Z]"); + private static final Pattern WIDE_SIGNATURE = Pattern.compile("[JD]"); +}