Merge
diff --git a/make/java/dyn/Makefile b/make/java/dyn/Makefile
index af7bec9..a85b516 100644
--- a/make/java/dyn/Makefile
+++ b/make/java/dyn/Makefile
@@ -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
diff --git a/src/share/classes/java/dyn/BootstrapMethod.java b/src/share/classes/java/dyn/BootstrapMethod.java
deleted file mode 100644
index a5c41b4..0000000
--- a/src/share/classes/java/dyn/BootstrapMethod.java
+++ /dev/null
@@ -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>
-&#064;BootstrapMethod(value=MyLanguageRuntime.class, name="bootstrapDynamic")
-String x = (String) InvokeDynamic.greet();
-//BSM => MyLanguageRuntime.bootstrapDynamic(Here.class, "greet", methodType(String.class))
-&#064;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};
-}
diff --git a/src/share/classes/java/dyn/CallSite.java b/src/share/classes/java/dyn/CallSite.java
index 7c4ed52..2290aa8 100644
--- a/src/share/classes/java/dyn/CallSite.java
+++ b/src/share/classes/java/dyn/CallSite.java
@@ -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 -->
-&#064;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 */
-    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.
+    /**
+     * 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
      */
-    private boolean isLinked() {
-        return vmmethod != null;
+    public MethodType type() {
+        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.)
+     * Report the current linkage state of the call site, a value which may change over time.
      * <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.
+     * 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>
-     * 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}.
-     * <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) {
-        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;
-        }
+        checkTargetChange(this.target, newTarget);
+        setTargetNormal(newTarget);
+    }
+
+    void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
         MethodType oldType = oldTarget.type();
-        if (!newTarget.type().equals(oldType))
+        MethodType newType = newTarget.type();  // null check!
+        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");
+    }
 
-    /** Implementation of {@link MethodHandleProvider}, which returns {@code this.dynamicInvoker().asType(type)}. */
-    public final MethodHandle asMethodHandle(MethodType type) { return dynamicInvoker().asType(type); }
+    // 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); }
+    }
+
+    /*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);
+    }
 }
diff --git a/src/share/classes/java/dyn/ClassValue.java b/src/share/classes/java/dyn/ClassValue.java
index 325d55e..7b00a72 100644
--- a/src/share/classes/java/dyn/ClassValue.java
+++ b/src/share/classes/java/dyn/ClassValue.java
@@ -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);
diff --git a/src/share/classes/java/dyn/ConstantCallSite.java b/src/share/classes/java/dyn/ConstantCallSite.java
index e03fa8d..585fdc7 100644
--- a/src/share/classes/java/dyn/ConstantCallSite.java
+++ b/src/share/classes/java/dyn/ConstantCallSite.java
@@ -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");
     }
 }
diff --git a/src/share/classes/java/dyn/InvokeDynamic.java b/src/share/classes/java/dyn/InvokeDynamic.java
index 4406363..9c3ede1 100644
--- a/src/share/classes/java/dyn/InvokeDynamic.java
+++ b/src/share/classes/java/dyn/InvokeDynamic.java
@@ -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 -->
-&#064;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
-}
-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
+ * This is a place-holder class.  Some HotSpot implementations need to see it.
  */
-@MethodHandle.PolymorphicSignature
-public final class InvokeDynamic {
+final class InvokeDynamic {
     private InvokeDynamic() { throw new InternalError(); }  // do not instantiate
-
-    // no statically defined static methods
 }
diff --git a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
index 3820e94..1fe1af0 100644
--- a/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
+++ b/src/share/classes/java/dyn/InvokeDynamicBootstrapError.java
@@ -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);
+    }
 }
diff --git a/src/share/classes/java/dyn/Linkage.java b/src/share/classes/java/dyn/Linkage.java
index 98eedbb..76abe24 100644
--- a/src/share/classes/java/dyn/Linkage.java
+++ b/src/share/classes/java/dyn/Linkage.java
@@ -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();
     }
 }
diff --git a/src/share/classes/java/dyn/LinkagePermission.java b/src/share/classes/java/dyn/LinkagePermission.java
deleted file mode 100644
index 9861843..0000000
--- a/src/share/classes/java/dyn/LinkagePermission.java
+++ /dev/null
@@ -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());
-    }
-}
diff --git a/src/share/classes/java/dyn/MethodHandle.java b/src/share/classes/java/dyn/MethodHandle.java
index 4904e97..25d0f80 100644
--- a/src/share/classes/java/dyn/MethodHandle.java
+++ b/src/share/classes/java/dyn/MethodHandle.java
@@ -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.&lt;String&gt;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[] =&gt; 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.&lt;int&gt;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
-     * 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>
-     * <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
+     * 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>
-     * This method is equivalent to {@link MethodHandles#convertArguments}.
+     * 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>
+     * This method is equivalent to {@link MethodHandles#convertArguments convertArguments},
+     * except for method handles produced by {@link #withTypeHandler withTypeHandler},
+     * in which case the specified type handler is used for calls to {@code asType}.
+     * <p>
+     * Note that the default behavior of {@code asType} only performs
+     * pairwise argument conversion and return value conversion.
+     * Because of this, unless the method handle has a type handler,
+     * the original type and new type must have the same number of arguments.
+     *
      * @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);
+    }
 }
diff --git a/src/share/classes/java/dyn/MethodHandleProvider.java b/src/share/classes/java/dyn/MethodHandleProvider.java
deleted file mode 100644
index 365b605..0000000
--- a/src/share/classes/java/dyn/MethodHandleProvider.java
+++ /dev/null
@@ -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;
-}
diff --git a/src/share/classes/java/dyn/MethodHandles.java b/src/share/classes/java/dyn/MethodHandles.java
index b6e8333..d2de0c2 100644
--- a/src/share/classes/java/dyn/MethodHandles.java
+++ b/src/share/classes/java/dyn/MethodHandles.java
@@ -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 "&lt;init&gt;")
          * @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&lt;N] V[i] filter[i](B[i]) = filters[i] ?: identity;
-     * T adapter(B[N]... b) {
-     *   A[N] a...;
-     *   [i&lt;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);
+    }
 }
diff --git a/src/share/classes/java/dyn/MethodType.java b/src/share/classes/java/dyn/MethodType.java
index e7e948b..9045c83 100644
--- a/src/share/classes/java/dyn/MethodType.java
+++ b/src/share/classes/java/dyn/MethodType.java
@@ -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);
-    }
 }
diff --git a/src/share/classes/java/dyn/MutableCallSite.java b/src/share/classes/java/dyn/MutableCallSite.java
new file mode 100644
index 0000000..b33c41c
--- /dev/null
+++ b/src/share/classes/java/dyn/MutableCallSite.java
@@ -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();
+}
diff --git a/src/share/classes/java/dyn/Switcher.java b/src/share/classes/java/dyn/Switcher.java
new file mode 100644
index 0000000..b083876
--- /dev/null
+++ b/src/share/classes/java/dyn/Switcher.java
@@ -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);
+    }
+}
diff --git a/src/share/classes/java/dyn/VolatileCallSite.java b/src/share/classes/java/dyn/VolatileCallSite.java
new file mode 100644
index 0000000..8c603b9
--- /dev/null
+++ b/src/share/classes/java/dyn/VolatileCallSite.java
@@ -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);
+    }
+}
diff --git a/src/share/classes/java/dyn/package-info.java b/src/share/classes/java/dyn/package-info.java
index 37555be..41d97b8 100644
--- a/src/share/classes/java/dyn/package-info.java
+++ b/src/share/classes/java/dyn/package-info.java
@@ -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}
- * 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>
  *
- * <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}.
+ * <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.)
+ *
+ * <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 &ldquo;causeless&rdquo; 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>
- * 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.
+ * 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>
+ * 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
  */
diff --git a/src/share/classes/sun/dyn/AdapterMethodHandle.java b/src/share/classes/sun/dyn/AdapterMethodHandle.java
index fb9b7fa..c23699b 100644
--- a/src/share/classes/sun/dyn/AdapterMethodHandle.java
+++ b/src/share/classes/sun/dyn/AdapterMethodHandle.java
@@ -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.
      */
diff --git a/src/share/classes/sun/dyn/BoundMethodHandle.java b/src/share/classes/sun/dyn/BoundMethodHandle.java
index f2ae32e..cfe1966 100644
--- a/src/share/classes/sun/dyn/BoundMethodHandle.java
+++ b/src/share/classes/sun/dyn/BoundMethodHandle.java
@@ -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;
     }
 }
diff --git a/src/share/classes/sun/dyn/CallSiteImpl.java b/src/share/classes/sun/dyn/CallSiteImpl.java
index f7c2d70..eb5abf7 100644
--- a/src/share/classes/sun/dyn/CallSiteImpl.java
+++ b/src/share/classes/sun/dyn/CallSiteImpl.java
@@ -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;
diff --git a/src/share/classes/sun/dyn/FilterGeneric.java b/src/share/classes/sun/dyn/FilterGeneric.java
index 1e7b594..b593588 100644
--- a/src/share/classes/sun/dyn/FilterGeneric.java
+++ b/src/share/classes/sun/dyn/FilterGeneric.java
@@ -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]); }
     }
 }
diff --git a/src/share/classes/sun/dyn/FilterOneArgument.java b/src/share/classes/sun/dyn/FilterOneArgument.java
index cc8ecf6..2712242 100644
--- a/src/share/classes/sun/dyn/FilterOneArgument.java
+++ b/src/share/classes/sun/dyn/FilterOneArgument.java
@@ -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;
     }
diff --git a/src/share/classes/sun/dyn/FromGeneric.java b/src/share/classes/sun/dyn/FromGeneric.java
index 24f40c0..4651d44 100644
--- a/src/share/classes/sun/dyn/FromGeneric.java
+++ b/src/share/classes/sun/dyn/FromGeneric.java
@@ -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)); }
     }
 }
diff --git a/src/share/classes/sun/dyn/InvokeGeneric.java b/src/share/classes/sun/dyn/InvokeGeneric.java
new file mode 100644
index 0000000..8137bb4
--- /dev/null
+++ b/src/share/classes/sun/dyn/InvokeGeneric.java
@@ -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;
+    }
+}
diff --git a/src/share/classes/sun/dyn/Invokers.java b/src/share/classes/sun/dyn/Invokers.java
index b3d2823..123dd53 100644
--- a/src/share/classes/sun/dyn/Invokers.java
+++ b/src/share/classes/sun/dyn/Invokers.java
@@ -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;
     }
diff --git a/src/share/classes/sun/dyn/JavaMethodHandle.java b/src/share/classes/sun/dyn/JavaMethodHandle.java
deleted file mode 100644
index 2ce44d1..0000000
--- a/src/share/classes/sun/dyn/JavaMethodHandle.java
+++ /dev/null
@@ -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.&lt;void&gt;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.&lt;Number&gt;invokeExact(1L);  // 456
- *     stuffPtr.setter().&lt;void&gt;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);
-    }
-}
diff --git a/src/share/classes/sun/dyn/MethodHandleImpl.java b/src/share/classes/sun/dyn/MethodHandleImpl.java
index caa96b9..24f7aae 100644
--- a/src/share/classes/sun/dyn/MethodHandleImpl.java
+++ b/src/share/classes/sun/dyn/MethodHandleImpl.java
@@ -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);
+    }
 }
diff --git a/src/share/classes/sun/dyn/MethodHandleNatives.java b/src/share/classes/sun/dyn/MethodHandleNatives.java
index 47a9a2d..3d0629d 100644
--- a/src/share/classes/sun/dyn/MethodHandleNatives.java
+++ b/src/share/classes/sun/dyn/MethodHandleNatives.java
@@ -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.)
      */
diff --git a/src/share/classes/sun/dyn/MethodTypeImpl.java b/src/share/classes/sun/dyn/MethodTypeImpl.java
index 0a31814..059f269 100644
--- a/src/share/classes/sun/dyn/MethodTypeImpl.java
+++ b/src/share/classes/sun/dyn/MethodTypeImpl.java
@@ -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;
diff --git a/src/share/classes/sun/dyn/SpreadGeneric.java b/src/share/classes/sun/dyn/SpreadGeneric.java
index 2b12b06..d95f3b9 100644
--- a/src/share/classes/sun/dyn/SpreadGeneric.java
+++ b/src/share/classes/sun/dyn/SpreadGeneric.java
@@ -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)); }
diff --git a/src/share/classes/sun/dyn/ToGeneric.java b/src/share/classes/sun/dyn/ToGeneric.java
index 65acc6f..c639645 100644
--- a/src/share/classes/sun/dyn/ToGeneric.java
+++ b/src/share/classes/sun/dyn/ToGeneric.java
@@ -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); }
diff --git a/src/share/classes/sun/dyn/util/ValueConversions.java b/src/share/classes/sun/dyn/util/ValueConversions.java
index f5ee5cb..8cb5ece 100644
--- a/src/share/classes/sun/dyn/util/ValueConversions.java
+++ b/src/share/classes/sun/dyn/util/ValueConversions.java
@@ -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 = {};
diff --git a/src/share/classes/sun/dyn/util/VerifyAccess.java b/src/share/classes/sun/dyn/util/VerifyAccess.java
index 13987da..0382bea 100644
--- a/src/share/classes/sun/dyn/util/VerifyAccess.java
+++ b/src/share/classes/sun/dyn/util/VerifyAccess.java
@@ -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));
-    }
 }
diff --git a/src/share/classes/sun/dyn/util/Wrapper.java b/src/share/classes/sun/dyn/util/Wrapper.java
index 5229c80..a3e34dd 100644
--- a/src/share/classes/sun/dyn/util/Wrapper.java
+++ b/src/share/classes/sun/dyn/util/Wrapper.java
@@ -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.
diff --git a/test/java/dyn/ClassValueTest.java b/test/java/dyn/ClassValueTest.java
index cf7bcff..f917e95 100644
--- a/test/java/dyn/ClassValueTest.java
+++ b/test/java/dyn/ClassValueTest.java
@@ -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,
diff --git a/test/java/dyn/InvokeDynamicPrintArgs.java b/test/java/dyn/InvokeDynamicPrintArgs.java
new file mode 100644
index 0000000..1bf1b73
--- /dev/null
+++ b/test/java/dyn/InvokeDynamicPrintArgs.java
@@ -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");
+    }
+}
diff --git a/test/java/dyn/InvokeGenericTest.java b/test/java/dyn/InvokeGenericTest.java
new file mode 100644
index 0000000..1267066
--- /dev/null
+++ b/test/java/dyn/InvokeGenericTest.java
@@ -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);
+    }
+
+}
diff --git a/test/java/dyn/JavaDocExamples.java b/test/java/dyn/JavaDocExamples.java
deleted file mode 100644
index db5a5ef..0000000
--- a/test/java/dyn/JavaDocExamples.java
+++ /dev/null
@@ -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);
-    }
-}
diff --git a/test/java/dyn/JavaDocExamplesTest.java b/test/java/dyn/JavaDocExamplesTest.java
new file mode 100644
index 0000000..9cb41f9
--- /dev/null
+++ b/test/java/dyn/JavaDocExamplesTest.java
@@ -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;
+}
+
+}
diff --git a/test/java/dyn/MethodHandlesTest.java b/test/java/dyn/MethodHandlesTest.java
index 62b0c90..e92bc6d 100644
--- a/test/java/dyn/MethodHandlesTest.java
+++ b/test/java/dyn/MethodHandlesTest.java
@@ -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);
     }
 
diff --git a/test/java/dyn/indify/Indify.java b/test/java/dyn/indify/Indify.java
new file mode 100644
index 0000000..0896f0e
--- /dev/null
+++ b/test/java/dyn/indify/Indify.java
@@ -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]");
+}