changeset 438:51b63e67f83e

meth-lfi: refactor LF.Template to IBG.CodePattern and do cleanups; also assign some bug numbers
author jrose
date Wed, 17 Oct 2012 21:25:59 -0700
parents d925ea8227c0
children c3c151c6d7d0
files anno-stable-8001107.patch anno-stable.patch meth-lfi-8001106.patch meth-lfi.patch meth.patch series
diffstat 6 files changed, 1776 insertions(+), 1668 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/anno-stable-8001107.patch	Wed Oct 17 21:25:59 2012 -0700
@@ -0,0 +1,303 @@
+8001107: @Stable annotation for constant folding of lazily evaluated variables
+
+diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
+--- a/src/share/classes/java/lang/invoke/LambdaForm.java
++++ b/src/share/classes/java/lang/invoke/LambdaForm.java
+@@ -34,6 +34,7 @@
+ import java.util.HashMap;
+ import java.util.concurrent.ConcurrentHashMap;
+ import sun.invoke.util.Wrapper;
++import sun.invoke.Stable;
+ import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+ import java.lang.reflect.Field;
+@@ -120,7 +121,7 @@
+ class LambdaForm {
+     final int arity;
+     final int result;
+-    final Name[] names;
++    @Stable final Name[] names;
+     final String debugName;
+     MemberName vmentry;   // low-level behavior, or null if not yet prepared
+     private boolean isCompiled;
+@@ -969,8 +970,8 @@
+ 
+     static class NamedFunction {
+         final MemberName member;
+-        MethodHandle resolvedHandle;
+-        MethodHandle invoker;
++        @Stable MethodHandle resolvedHandle;
++        @Stable MethodHandle invoker;
+ 
+         NamedFunction(MethodHandle resolvedHandle) {
+             this(resolvedHandle.internalMemberName(), resolvedHandle);
+@@ -1265,7 +1266,7 @@
+         final char type;
+         private short index;
+         final NamedFunction function;
+-        final Object[] arguments;
++        @Stable final Object[] arguments;
+ 
+         private Name(int index, char type, NamedFunction function, Object[] arguments) {
+             this.index = (short)index;
+diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
+--- a/src/share/classes/java/lang/invoke/MethodHandle.java
++++ b/src/share/classes/java/lang/invoke/MethodHandle.java
+@@ -419,6 +419,7 @@
+ 
+     private final MethodType type;
+     /*private*/ final LambdaForm form;
++    //@@ FIXME: MAKE @Stable
+     // form is not private so that invokers can easily fetch it
+ 
+     /**
+diff --git a/src/share/classes/java/lang/invoke/MethodType.java b/src/share/classes/java/lang/invoke/MethodType.java
+--- a/src/share/classes/java/lang/invoke/MethodType.java
++++ b/src/share/classes/java/lang/invoke/MethodType.java
+@@ -33,6 +33,7 @@
+ import java.util.List;
+ import sun.invoke.util.BytecodeDescriptor;
+ import static java.lang.invoke.MethodHandleStatics.*;
++import sun.invoke.Stable;
+ import sun.invoke.util.VerifyType;
+ 
+ /**
+@@ -91,9 +92,9 @@
+     private final Class<?>[] ptypes;
+ 
+     // The remaining fields are caches of various sorts:
+-    private MethodTypeForm form; // erased form, plus cached data about primitives
+-    private MethodType wrapAlt;  // alternative wrapped/unwrapped version
+-    private Invokers invokers;   // cache of handy higher-order adapters
++    private @Stable MethodTypeForm form; // erased form, plus cached data about primitives
++    private @Stable MethodType wrapAlt;  // alternative wrapped/unwrapped version
++    private @Stable Invokers invokers;   // cache of handy higher-order adapters
+ 
+     /**
+      * Check the given parameters for validity and store them into the final fields.
+diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java
+--- a/src/share/classes/java/lang/invoke/MethodTypeForm.java
++++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java
+@@ -28,6 +28,7 @@
+ import sun.invoke.util.Wrapper;
+ import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
++import sun.invoke.Stable;
+ 
+ /**
+  * Shared information for a group of method types, which differ
+@@ -51,12 +52,13 @@
+     final MethodType basicType;         // the canonical erasure, with primitives simplified
+ 
+     // Cached adapter information:
+-    /*lazy*/ MethodHandle genericInvoker; // JVM hook for inexact invoke
+-    /*lazy*/ MethodHandle basicInvoker;   // cached instance of MH.invokeBasic
+-    /*lazy*/ MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
++    @Stable String typeString;           // argument type signature characters
++    @Stable MethodHandle genericInvoker; // JVM hook for inexact invoke
++    @Stable MethodHandle basicInvoker;   // cached instance of MH.invokeBasic
++    @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
+ 
+     // Cached lambda form information, for basic types only:
+-    final LambdaForm[] lambdaForms;
++    final @Stable LambdaForm[] lambdaForms;
+     // Indexes into lambdaForms:
+     static final int
+             LF_INVVIRTUAL     =  0,  // DMH invokeVirtual
+diff --git a/src/share/classes/sun/invoke/Stable.java b/src/share/classes/sun/invoke/Stable.java
+new file mode 100644
+--- /dev/null
++++ b/src/share/classes/sun/invoke/Stable.java
+@@ -0,0 +1,74 @@
++/*
++ * Copyright (c) 2012, 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.invoke;
++
++import java.lang.annotation.*;
++
++/**
++ * Internal marker for some methods in the JSR 292 implementation.
++ * A field may be annotated as stable if all of its component variables
++ * changes value at most once.
++ * A field's value counts as its component value.
++ * If the field is typed as an array, then all the non-null components
++ * of the array, of depth up to the rank of the field's array type,
++ * also count as component values.
++ * By extension, any variable (either array or field) which has annotated
++ * as stable is called a stable variable, and its non-null or non-zero
++ * value is called a stable value.
++ * <p>
++ * Since all fields begin with a default value of null for references
++ * (resp., zero for primitives), it follows that this annotation indicates
++ * that the first non-null (resp., non-zero) value stored in the field
++ * will never be changed.
++ * <p>
++ * If the field is not of an array type, there are no array elements,
++ * then the value indicated as stable is simply the value of the field.
++ * If the dynamic type of the field value is an array but the static type
++ * is not, the components of the array are <em>not</em> regarded as stable.
++ * <p>
++ * If the field is an array type, then both the field value and
++ * all the components of the field value (if the field value is non-null)
++ * are indicated to be stable.
++ * If the field type is an array type with rank {@code N &gt; 1},
++ * then each component of the field value (if the field value is non-null),
++ * is regarded as a stable array of rank {@code N-1}.
++ * <p>
++ * Fields which are declared {@code final} may also be annotated as stable.
++ * Since final fields already behave as stable values, such an annotation
++ * indicates no additional information, unless the type of the field is
++ * an array type.
++ * <p>
++ * It is (currently) undefined what happens if a field annotated as stable
++ * is given a third value.  In practice, if the JVM relies on this annotation
++ * to promote a field reference to a constant, it may be that the Java memory
++ * model would appear to be broken, if such a constant (the second value of the field)
++ * is used as the value of the field even after the field value has changed.
++ */
++public
++@Target(ElementType.FIELD)
++@Retention(RetentionPolicy.RUNTIME)
++@interface Stable {
++}
+diff --git a/test/sun/invoke/StableValueTest.java b/test/sun/invoke/StableValueTest.java
+new file mode 100644
+--- /dev/null
++++ b/test/sun/invoke/StableValueTest.java
+@@ -0,0 +1,113 @@
++/*
++ * Copyright (c) 2012, 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 test inlining of elements of stable arrays
++ * @compile StableValueTest.java
++ * @run main/othervm
++ *        -Xbatch
++ *        -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
++ *        -XX:CompileCommand=compileonly,*StableValueTest::test
++ *        -XX:-TieredCompilation -XX:-UseTypeProfile
++ *        -XX:+FoldStableValues
++ *        -XX:+PrintCompilation -XX:+PrintInlining
++ *          test.sun.invoke.StableValueTest
++ */
++
++package test.sun.invoke;
++import sun.invoke.Stable;
++
++import java.util.*;
++
++public class StableValueTest {
++    static class Node {
++        final Object tree;
++        @Stable String label;
++        @Stable Node[] children;
++        Node(Object tree) {
++            this.tree = tree;
++        }
++        Node expand() {
++            if (children != null)
++                return this;
++            if (tree instanceof Object[]) {
++                Object[] a = (Object[]) tree;
++                Node[] children = new Node[a.length];
++                for (int i = 0; i < a.length; i++) {
++                    Object t = a[i];
++                    children[i] = (t instanceof Node) ? (Node)t : new Node(t);
++                }
++                this.children = children;
++            } else {
++                label = tree.toString();
++                children = new Node[0];
++            }
++            return this;
++        }
++        Node[] children() {
++            if (children == null) expand();
++            return children;
++        }
++        public String toString() {
++            expand();
++            return (label != null) ? label : "N"+Arrays.toString(children);
++        }
++    }
++
++    @Stable static Object[][] NODE_TABLE;
++    static @Stable int VERBOSITY;
++
++    public static void main(String... av) {
++        VERBOSITY = (av.length + 1);  // non-zero value!
++        NODE_TABLE = new Object[3][5];
++        ArrayList<Node> nodes = new ArrayList<>();
++        for (int i = 0; i < 3*5; i++) {
++            Node n = new Node(i);
++            if (i % 2 == 0) {
++                nodes.add(n);
++                n = new Node(nodes.toArray());
++            }
++            NODE_TABLE[i/5][i%5] = n;
++            if (VERBOSITY > 1)  System.out.println(n.toString());
++        }
++        if (VERBOSITY > 1)  System.out.println(NODE_TABLE[2][4].toString());
++        for (int i = 0; i < 100; i++) {
++            test(1000);
++        }
++    }
++
++    static String test(int count) {
++        String x = null;
++        for (int i = 0; i < count; i++) {
++            x = NODE_TABLE[2][4].toString();
++        }
++        return x;
++    }
++
++    @org.junit.Test public void run() {
++        main();
++    }
++}
--- a/anno-stable.patch	Wed Oct 17 21:02:38 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,303 +0,0 @@
-Summary: @Stable annotation for constant folding of lazily evaluated variables.
-
-diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
---- a/src/share/classes/java/lang/invoke/LambdaForm.java
-+++ b/src/share/classes/java/lang/invoke/LambdaForm.java
-@@ -34,6 +34,7 @@
- import java.util.HashMap;
- import java.util.concurrent.ConcurrentHashMap;
- import sun.invoke.util.Wrapper;
-+import sun.invoke.Stable;
- import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
- import java.lang.reflect.Field;
-@@ -120,7 +121,7 @@
- class LambdaForm {
-     final int arity;
-     final int result;
--    final Name[] names;
-+    @Stable final Name[] names;
-     final String debugName;
-     MemberName vmentry;   // low-level behavior, or null if not yet prepared
-     private boolean isCompiled;
-@@ -969,8 +970,8 @@
- 
-     static class NamedFunction {
-         final MemberName member;
--        MethodHandle resolvedHandle;
--        MethodHandle invoker;
-+        @Stable MethodHandle resolvedHandle;
-+        @Stable MethodHandle invoker;
- 
-         NamedFunction(MethodHandle resolvedHandle) {
-             this(resolvedHandle.internalMemberName(), resolvedHandle);
-@@ -1265,7 +1266,7 @@
-         final char type;
-         private short index;
-         final NamedFunction function;
--        final Object[] arguments;
-+        @Stable final Object[] arguments;
- 
-         private Name(int index, char type, NamedFunction function, Object[] arguments) {
-             this.index = (short)index;
-diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
---- a/src/share/classes/java/lang/invoke/MethodHandle.java
-+++ b/src/share/classes/java/lang/invoke/MethodHandle.java
-@@ -419,6 +419,7 @@
- 
-     private final MethodType type;
-     /*private*/ final LambdaForm form;
-+    //@@ FIXME: MAKE @Stable
-     // form is not private so that invokers can easily fetch it
- 
-     /**
-diff --git a/src/share/classes/java/lang/invoke/MethodType.java b/src/share/classes/java/lang/invoke/MethodType.java
---- a/src/share/classes/java/lang/invoke/MethodType.java
-+++ b/src/share/classes/java/lang/invoke/MethodType.java
-@@ -33,6 +33,7 @@
- import java.util.List;
- import sun.invoke.util.BytecodeDescriptor;
- import static java.lang.invoke.MethodHandleStatics.*;
-+import sun.invoke.Stable;
- import sun.invoke.util.VerifyType;
- 
- /**
-@@ -91,9 +92,9 @@
-     private final Class<?>[] ptypes;
- 
-     // The remaining fields are caches of various sorts:
--    private MethodTypeForm form; // erased form, plus cached data about primitives
--    private MethodType wrapAlt;  // alternative wrapped/unwrapped version
--    private Invokers invokers;   // cache of handy higher-order adapters
-+    private @Stable MethodTypeForm form; // erased form, plus cached data about primitives
-+    private @Stable MethodType wrapAlt;  // alternative wrapped/unwrapped version
-+    private @Stable Invokers invokers;   // cache of handy higher-order adapters
- 
-     /**
-      * Check the given parameters for validity and store them into the final fields.
-diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java
---- a/src/share/classes/java/lang/invoke/MethodTypeForm.java
-+++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java
-@@ -28,6 +28,7 @@
- import sun.invoke.util.Wrapper;
- import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
-+import sun.invoke.Stable;
- 
- /**
-  * Shared information for a group of method types, which differ
-@@ -51,12 +52,13 @@
-     final MethodType basicType;         // the canonical erasure, with primitives simplified
- 
-     // Cached adapter information:
--    /*lazy*/ MethodHandle genericInvoker; // JVM hook for inexact invoke
--    /*lazy*/ MethodHandle basicInvoker;   // cached instance of MH.invokeBasic
--    /*lazy*/ MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
-+    @Stable String typeString;           // argument type signature characters
-+    @Stable MethodHandle genericInvoker; // JVM hook for inexact invoke
-+    @Stable MethodHandle basicInvoker;   // cached instance of MH.invokeBasic
-+    @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
- 
-     // Cached lambda form information, for basic types only:
--    final LambdaForm[] lambdaForms;
-+    final @Stable LambdaForm[] lambdaForms;
-     // Indexes into lambdaForms:
-     static final int
-             LF_INVVIRTUAL     =  0,  // DMH invokeVirtual
-diff --git a/src/share/classes/sun/invoke/Stable.java b/src/share/classes/sun/invoke/Stable.java
-new file mode 100644
---- /dev/null
-+++ b/src/share/classes/sun/invoke/Stable.java
-@@ -0,0 +1,74 @@
-+/*
-+ * Copyright (c) 2012, 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.invoke;
-+
-+import java.lang.annotation.*;
-+
-+/**
-+ * Internal marker for some methods in the JSR 292 implementation.
-+ * A field may be annotated as stable if all of its component variables
-+ * changes value at most once.
-+ * A field's value counts as its component value.
-+ * If the field is typed as an array, then all the non-null components
-+ * of the array, of depth up to the rank of the field's array type,
-+ * also count as component values.
-+ * By extension, any variable (either array or field) which has annotated
-+ * as stable is called a stable variable, and its non-null or non-zero
-+ * value is called a stable value.
-+ * <p>
-+ * Since all fields begin with a default value of null for references
-+ * (resp., zero for primitives), it follows that this annotation indicates
-+ * that the first non-null (resp., non-zero) value stored in the field
-+ * will never be changed.
-+ * <p>
-+ * If the field is not of an array type, there are no array elements,
-+ * then the value indicated as stable is simply the value of the field.
-+ * If the dynamic type of the field value is an array but the static type
-+ * is not, the components of the array are <em>not</em> regarded as stable.
-+ * <p>
-+ * If the field is an array type, then both the field value and
-+ * all the components of the field value (if the field value is non-null)
-+ * are indicated to be stable.
-+ * If the field type is an array type with rank {@code N &gt; 1},
-+ * then each component of the field value (if the field value is non-null),
-+ * is regarded as a stable array of rank {@code N-1}.
-+ * <p>
-+ * Fields which are declared {@code final} may also be annotated as stable.
-+ * Since final fields already behave as stable values, such an annotation
-+ * indicates no additional information, unless the type of the field is
-+ * an array type.
-+ * <p>
-+ * It is (currently) undefined what happens if a field annotated as stable
-+ * is given a third value.  In practice, if the JVM relies on this annotation
-+ * to promote a field reference to a constant, it may be that the Java memory
-+ * model would appear to be broken, if such a constant (the second value of the field)
-+ * is used as the value of the field even after the field value has changed.
-+ */
-+public
-+@Target(ElementType.FIELD)
-+@Retention(RetentionPolicy.RUNTIME)
-+@interface Stable {
-+}
-diff --git a/test/sun/invoke/StableValueTest.java b/test/sun/invoke/StableValueTest.java
-new file mode 100644
---- /dev/null
-+++ b/test/sun/invoke/StableValueTest.java
-@@ -0,0 +1,113 @@
-+/*
-+ * Copyright (c) 2012, 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 test inlining of elements of stable arrays
-+ * @compile StableValueTest.java
-+ * @run main/othervm
-+ *        -Xbatch
-+ *        -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
-+ *        -XX:CompileCommand=compileonly,*StableValueTest::test
-+ *        -XX:-TieredCompilation -XX:-UseTypeProfile
-+ *        -XX:+FoldStableValues
-+ *        -XX:+PrintCompilation -XX:+PrintInlining
-+ *          test.sun.invoke.StableValueTest
-+ */
-+
-+package test.sun.invoke;
-+import sun.invoke.Stable;
-+
-+import java.util.*;
-+
-+public class StableValueTest {
-+    static class Node {
-+        final Object tree;
-+        @Stable String label;
-+        @Stable Node[] children;
-+        Node(Object tree) {
-+            this.tree = tree;
-+        }
-+        Node expand() {
-+            if (children != null)
-+                return this;
-+            if (tree instanceof Object[]) {
-+                Object[] a = (Object[]) tree;
-+                Node[] children = new Node[a.length];
-+                for (int i = 0; i < a.length; i++) {
-+                    Object t = a[i];
-+                    children[i] = (t instanceof Node) ? (Node)t : new Node(t);
-+                }
-+                this.children = children;
-+            } else {
-+                label = tree.toString();
-+                children = new Node[0];
-+            }
-+            return this;
-+        }
-+        Node[] children() {
-+            if (children == null) expand();
-+            return children;
-+        }
-+        public String toString() {
-+            expand();
-+            return (label != null) ? label : "N"+Arrays.toString(children);
-+        }
-+    }
-+
-+    @Stable static Object[][] NODE_TABLE;
-+    static @Stable int VERBOSITY;
-+
-+    public static void main(String... av) {
-+        VERBOSITY = (av.length + 1);  // non-zero value!
-+        NODE_TABLE = new Object[3][5];
-+        ArrayList<Node> nodes = new ArrayList<>();
-+        for (int i = 0; i < 3*5; i++) {
-+            Node n = new Node(i);
-+            if (i % 2 == 0) {
-+                nodes.add(n);
-+                n = new Node(nodes.toArray());
-+            }
-+            NODE_TABLE[i/5][i%5] = n;
-+            if (VERBOSITY > 1)  System.out.println(n.toString());
-+        }
-+        if (VERBOSITY > 1)  System.out.println(NODE_TABLE[2][4].toString());
-+        for (int i = 0; i < 100; i++) {
-+            test(1000);
-+        }
-+    }
-+
-+    static String test(int count) {
-+        String x = null;
-+        for (int i = 0; i < count; i++) {
-+            x = NODE_TABLE[2][4].toString();
-+        }
-+        return x;
-+    }
-+
-+    @org.junit.Test public void run() {
-+        main();
-+    }
-+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/meth-lfi-8001106.patch	Wed Oct 17 21:25:59 2012 -0700
@@ -0,0 +1,1441 @@
+8001106: Bytecodes should be shared between similar lambda forms.
+Assumes @Stable arrays for optimization.
+
+diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
++++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+@@ -251,14 +251,14 @@
+         lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
+         LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
+         // This is a tricky bit of code.  Don't send it through the LF interpreter.
+-        lform.compileToBytecode();
++        lform.compileToBytecode(true);
+         return lform;
+     }
+ 
+     private static void maybeCompile(LambdaForm lform, MemberName m) {
+         if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
+             // Help along bootstrapping...
+-            lform.compileToBytecode();
++            lform.compileToBytecode(true);
+     }
+ 
+     /** Static wrapper for DirectMethodHandle.internalMemberName. */
+diff --git a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+--- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
++++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+@@ -27,7 +27,6 @@
+ 
+ import sun.invoke.util.VerifyAccess;
+ import java.lang.invoke.LambdaForm.Name;
+-import java.lang.invoke.MethodHandles.Lookup;
+ 
+ import sun.invoke.util.Wrapper;
+ 
+@@ -35,12 +34,12 @@
+ import java.util.*;
+ 
+ import com.sun.xml.internal.ws.org.objectweb.asm.*;
++import java.lang.invoke.LambdaForm.NamedFunction;
+ 
+ import java.lang.reflect.*;
+ import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+-import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+-import sun.invoke.util.ValueConversions;
++import java.util.concurrent.ConcurrentHashMap;
+ import sun.invoke.util.VerifyType;
+ 
+ /**
+@@ -51,13 +50,13 @@
+ class InvokerBytecodeGenerator {
+     /** Define class names for convenience. */
+     private static final String MH      = "java/lang/invoke/MethodHandle";
+-    private static final String BMH     = "java/lang/invoke/BoundMethodHandle";
+     private static final String LF      = "java/lang/invoke/LambdaForm";
+     private static final String LFN     = "java/lang/invoke/LambdaForm$Name";
+     private static final String CLS     = "java/lang/Class";
+     private static final String OBJ     = "java/lang/Object";
+     private static final String OBJARY  = "[Ljava/lang/Object;";
+ 
++    private static final String OBJ_SIG = "L" + OBJ + ";";
+     private static final String LF_SIG  = "L" + LF + ";";
+     private static final String LFN_SIG = "L" + LFN + ";";
+     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
+@@ -72,9 +71,11 @@
+     private final String sourceFile;
+ 
+     private final LambdaForm lambdaForm;
++    private final CodePattern codePattern;
+     private final String     invokerName;
+     private final MethodType invokerType;
+     private final int[] localsMap;
++    private final Object[] localsModel;
+ 
+     /** ASM bytecode generation. */
+     private ClassWriter cw;
+@@ -83,7 +84,7 @@
+     private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
+     private static final Class<?> HOST_CLASS = LambdaForm.class;
+ 
+-    private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
++    private InvokerBytecodeGenerator(CodePattern codePattern, LambdaForm form, int localsMapSize,
+                                      String className, String invokerName, MethodType invokerType) {
+         if (invokerName.contains(".")) {
+             int p = invokerName.indexOf(".");
+@@ -95,14 +96,16 @@
+         }
+         this.className  = superName + "$" + className;
+         this.sourceFile = "LambdaForm$" + className;
+-        this.lambdaForm = lambdaForm;
++        this.codePattern = codePattern;
++        this.lambdaForm = form;
+         this.invokerName = invokerName;
+         this.invokerType = invokerType;
+         this.localsMap = new int[localsMapSize];
++        this.localsModel = new Object[localsMapSize];
+     }
+ 
+     private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
+-        this(null, invokerType.parameterCount(),
++        this(null, null, invokerType.parameterCount(),
+              className, invokerName, invokerType);
+         // Create an array to map name indexes to locals indexes.
+         for (int i = 0; i < localsMap.length; i++) {
+@@ -110,17 +113,26 @@
+         }
+     }
+ 
+-    private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
+-        this(form, form.names.length,
+-             className, form.debugName, invokerType);
++    private InvokerBytecodeGenerator(String className, CodePattern cp) {
++        this(cp, cp.form, cp.form.names.length + SCRATCH_LOCALS,
++             className, cp.form.debugName, cp.form.methodType());
+         // Create an array to map name indexes to locals indexes.
+-        Name[] names = form.names;
++        Name[] names = cp.form.names;
+         for (int i = 0, index = 0; i < localsMap.length; i++) {
+             localsMap[i] = index;
+-            index += Wrapper.forBasicType(names[i].type).stackSlots();
++            char type = (i < names.length ? names[i].type : 'L');
++            index += Wrapper.forBasicType(type).stackSlots();
+         }
++        // final value of index is number of locals, but we don't need it
+     }
+ 
++    private static final int SCRATCH_LOCALS = 2;  // LambdaForm, Name.arguments
++    private int localThisLF() {
++        return localsMap.length-2;
++    }
++    private int localThisArgList() {
++        return localsMap.length-1;
++    }
+ 
+     /** instance counters for dumped classes */
+     private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
+@@ -146,6 +158,251 @@
+         }
+     }
+ 
++    /** Use this for making equivalence classes of LFs. */
++    static class CodePattern {
++        // compileLevel values
++        static final int CL_NONE = 0, CL_FACTORED = 1, CL_CUSTOM = 2;
++
++        final LambdaForm form;
++        final int compileLevel;
++        final MemberName vmentry;  // product of compilation, or null if none
++        CodePattern(LambdaForm form, boolean z) {
++            this(form, CL_NONE);
++        }
++        CodePattern(LambdaForm form, int compileLevel) {
++            this.form = form;
++            this.compileLevel = compileLevel;
++            this.vmentry = null;
++        }
++        CodePattern(CodePattern cp, MemberName vmentry) {
++            this.form = cp.form;
++            this.compileLevel = cp.compileLevel;
++            this.vmentry = vmentry;
++        }
++        boolean canBeStrongRoot() {
++            // full custom compilations contain random pointers,
++            // which could leak storage if cached with a strong reference
++            return compileLevel == CL_FACTORED;
++        }
++        boolean satisfiesCompileLevel(int requiredLevel) {
++            return compileLevel >= requiredLevel;
++        }
++
++        @Override public String toString() {
++            return form.toString(this);
++        }
++
++        @Override public int hashCode() {
++            final int M1 = 31;
++            int hc = (compileLevel * M1 + form.result) * M1 + form.arity;
++            for (Name name : form.names) {
++                hc *= M1;
++                hc += nameHashCode(name);
++            }
++            return hc;
++        }
++        public boolean equals(CodePattern that) {
++            if (this == that)  return true;
++            if (this.compileLevel != that.compileLevel)  return false;
++            if (this.form == that.form)  return true;
++            boolean z = sameFormAfterFactoring(this.form, that.form);
++            assert(!(z && this.hashCode() != that.hashCode()));
++            return z;
++        }
++        @Override public boolean equals(Object x) {
++            return x instanceof CodePattern && equals((CodePattern)x);
++        }
++
++        boolean sameFormAfterFactoring(LambdaForm form1, LambdaForm form2) {
++            if (form1.arity  != form2.arity)   return false;
++            if (form1.result != form2.result)  return false;
++            Name[] names1 = form1.names;
++            Name[] names2 = form2.names;
++            int length = names1.length;
++            if (length != names2.length)  return false;
++            for (int i = 0; i < length; i++) {
++                if (!sameNameAfterFactoring(names1[i], names2[i]))  return false;
++                assert(nameHashCode(names1[i]) == nameHashCode(names2[i]));
++            }
++            return true;
++        }
++
++        int nameHashCode(Name name) {
++            final int M2 = 127;
++            int nhc = name.type;
++            if (name.isParam())
++                return nhc;
++            nhc *= M2;
++            if (shouldBeFactored(name, -1))
++                nhc += name.function.basicTypeSignature().hashCode();
++            else
++                nhc += name.function.hashCode();
++            int j = -1;
++            for (Object a : name.arguments) {
++                ++j;  // index of name
++                int ahc;
++                if (a instanceof Name)
++                    ahc = ((Name)a).indexAndType();
++                else if (shouldBeFactored(name, j))
++                    ahc = 0;  // this value will be factored; suppress
++                else
++                    ahc = a.hashCode();
++                nhc = (nhc * M2) + ahc;
++            }
++            return nhc;
++        }
++
++        boolean sameNameAfterFactoring(Name n1, Name n2) {
++            if (n1 == n2)  return true;
++            if (n1.indexAndType() != n2.indexAndType())  return false;
++            if (n1.isParam())  return n2.isParam();
++            if (n2.isParam())  return false;
++            int argc = n1.arguments.length;
++            if (argc != n2.arguments.length)  return false;
++            String sig = n1.function.basicTypeSignature();
++            if (!sig.equals(n2.function.basicTypeSignature()))  return false;
++            if (!shouldBeFactored(n1, -1)) {
++                // check identity of functions
++                if (!n1.function.equals(n2.function))  return false;
++            }
++            assert(shouldBeFactored(n1, -1) == shouldBeFactored(n2, -1));
++            for (int j = 0; j < argc; j++) {
++                Object a1 = n1.arguments[j];
++                Object a2 = n2.arguments[j];
++                if (a1 == a2)  continue;
++                if (a1 instanceof Name) {
++                    if (a2 instanceof Name && ((Name)a1).index() == ((Name)a2).index())
++                        continue;
++                    return false;
++                }
++                if (a2 instanceof Name)  return false;
++                if (!shouldBeFactored(n1, j)) {
++                    // recheck identity of arguments
++                    if (sig.charAt(j) != 'L') {
++                        if (a1.equals(a2))  a1 = a2;  // rebox
++                    }
++                    if (a1 != a2)  return false;
++                }
++                assert(shouldBeFactored(n1, j) == shouldBeFactored(n2, j));
++            }
++            return true;
++        }
++
++        boolean shouldBeFactored(Name name, int argIndex) {
++            if (compileLevel == CL_CUSTOM)  return false;
++            assert(compileLevel == CL_FACTORED);
++            int BSL = (BYTECODE_SHARE_LEVEL != null ? (int)BYTECODE_SHARE_LEVEL : 255);
++            if (BSL == 0) {
++                return false;
++            }
++            if (argIndex < 0) {
++                // Factor the head of the function application?
++                if ((BSL & 1) == 0)  return false;
++                if (isFactoredPlaceholder(name.function))  return true;  // already decided
++                MethodType mt = name.function.methodType();
++                if (mt.parameterSlotCount() >= 255)  return false;  // edge case: no invokeBasic
++                MemberName m = name.function.member;
++                if (m == null)  return true;
++                // Factor if the declaring class is not on the BCP.
++                // See also InvokerBytecodeGenerator.isStaticallyInvocable.
++                return m.getDeclaringClass().getClassLoader() != null;
++            }
++            Object arg = name.arguments[argIndex];
++            if (arg == null)
++                return false;
++            if (arg instanceof Name)
++                return false;
++            if ((BSL & 2) == 0) {
++                Class<?> ptype = name.function.methodType().parameterType(argIndex);
++                if (ptype.isPrimitive() || ptype == String.class) {
++                    return false;
++                }
++            }
++            return true;
++        }
++
++        /** Generate a fully factored lambda form. */
++        CodePattern copyFactored() {
++            Name[] names = form.names.clone();
++            boolean changed = false;
++            int startFixing = -1;
++            for (int i = form.arity; i < names.length; i++) {
++                Name n0 = names[i];
++                Name n1 = copyFactored(n0);
++                if (changed)
++                    // also edit occurrences of previously changed names
++                    n1 = n1.replaceNames(form.names, names, startFixing, i);
++                if (n1 == n0)  continue;
++                names[i] = n1;
++                if (!changed)  startFixing = i;
++                changed = true;
++            }
++            if (!changed)  return this;
++            LambdaForm lfact = new LambdaForm(form.debugName, form.arity, names, form.result);
++            CodePattern tfact = new CodePattern(lfact, compileLevel);
++            assert(tfact.equals(this)) : Arrays.asList(tfact, this, form);
++            return tfact;
++        }
++        NamedFunction copyFactored(NamedFunction fn) {
++            MemberName fake = new MemberName(CodePattern.class, "***", fn.methodType().basicType(), REF_invokeStatic);
++            return new NamedFunction(fake);
++        }
++        static boolean isFactoredPlaceholder(NamedFunction fn) {
++            MemberName fake = fn.member;
++            return fake != null && fake.getDeclaringClass() == CodePattern.class && fake.getName().equals("***");
++        }
++        private Name copyFactored(Name name) {
++            boolean changed = false;
++            LambdaForm.NamedFunction fn = name.function;
++            if (shouldBeFactored(name, -1)) {
++                fn = copyFactored(fn);
++                changed = true;
++            }
++            Object[] args = name.arguments.clone();
++            String sig = name.function.basicTypeSignature();
++            for (int j = 0; j < args.length; j++) {
++                if (!shouldBeFactored(name, j))  continue;
++                args[j] = copyFactoredArgument(sig.charAt(j), args[j]);
++                changed = true;
++            }
++            if (!changed)  return name;
++            return new Name(fn, args);
++        }
++        private Object copyFactoredArgument(char type, Object arg) {
++            if (type != 'L')
++                // This is required by the typesMatch assertion in Name.<init>.
++                return Wrapper.forBasicType(type).zero();
++            return "***";
++        }
++    }
++    static HashSet<CodePattern> DUMPED_PATTERNS = null;
++    void maybeDumpPattern(final String className) {
++        if (DUMP_CLASS_FILES) {
++            if (codePattern != null) {
++                if (DUMPED_PATTERNS == null)
++                    DUMPED_PATTERNS = new HashSet<>();
++                if (!DUMPED_PATTERNS.add(codePattern))
++                    return;
++                java.security.AccessController.doPrivileged(
++                new java.security.PrivilegedAction<Void>() {
++                    public Void run() {
++                        try {
++                            String dumpName = className;
++                            //dumpName = dumpName.replace('/', '-');
++                            File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+"."+codePattern.compileLevel+".pattern");
++                            dumpFile.getParentFile().mkdirs();
++                            FileOutputStream file = new FileOutputStream(dumpFile);
++                            file.write(codePattern.toString().getBytes());
++                            file.close();
++                            return null;
++                        } catch (IOException ex) {
++                            throw new InternalError(ex);
++                        }
++                    }
++                });
++            }
++        }
++    }
+     static void maybeDump(final String className, final byte[] classFile) {
+         if (DUMP_CLASS_FILES) {
+             System.out.println("dump: " + className);
+@@ -416,8 +673,8 @@
+      *
+      * @param type primitive type class to box.
+      */
+-    private void emitBoxing(Class<?> type) {
+-        Wrapper wrapper = Wrapper.forPrimitiveType(type);
++    private void emitBoxing(Wrapper wrapper) {
++        if (wrapper == Wrapper.OBJECT)  return;
+         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+         String name  = "valueOf";
+         String desc  = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
+@@ -429,8 +686,8 @@
+      *
+      * @param type wrapper type class to unbox.
+      */
+-    private void emitUnboxing(Class<?> type) {
+-        Wrapper wrapper = Wrapper.forWrapperType(type);
++    private void emitUnboxing(Wrapper wrapper) {
++        if (wrapper == Wrapper.OBJECT)  return;
+         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
+         String name  = wrapper.primitiveSimpleName() + "Value";
+         String desc  = "()" + wrapper.basicTypeChar();
+@@ -439,6 +696,27 @@
+     }
+ 
+     /**
++     * Emit a checkcast, if the reference type is non-trivial.
++     *
++     * @param type reference type required.
++     */
++    private void emitRefCast(Class<?> type) {
++        assert(!type.isPrimitive());
++        if (VerifyType.isNullConversion(Object.class, type))
++            return;
++        if (isStaticallyNameable(type)) {
++            mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(type));
++        } else {
++            mv.visitLdcInsn(constantPlaceholder(type));
++            mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
++            mv.visitInsn(Opcodes.SWAP);
++            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
++            if (type.isArray())
++                mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
++        }
++    }
++
++    /**
+      * Emit an implicit conversion.
+      *
+      * @param ptype type of value present on stack
+@@ -447,22 +725,11 @@
+     private void emitImplicitConversion(char ptype, Class<?> pclass) {
+         switch (ptype) {
+         case 'L':
+-            if (VerifyType.isNullConversion(Object.class, pclass))
+-                return;
+-            if (isStaticallyNameable(pclass)) {
+-                mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
+-            } else {
+-                mv.visitLdcInsn(constantPlaceholder(pclass));
+-                mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
+-                mv.visitInsn(Opcodes.SWAP);
+-                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
+-                if (pclass.isArray())
+-                    mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
+-            }
++            emitRefCast(pclass);
+             return;
+         case 'I':
+             if (!VerifyType.isNullConversion(int.class, pclass))
+-                emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
++                emitPrimCast(Wrapper.INT, Wrapper.forPrimitiveType(pclass));
+             return;
+         case 'J':
+             assert(pclass == long.class);
+@@ -500,16 +767,40 @@
+         return c.getName().replace('.', '/');
+     }
+ 
++    private static final ConcurrentHashMap<CodePattern,CodePattern> COMPILED_PATTERNS;
++    static {
++        int   capacity   = 512;    // expect many distinct signatures over time
++        float loadFactor = 0.75f;  // normal default
++        int   writers    = 1;
++        if (BYTECODE_SHARE_LEVEL != null && (int)BYTECODE_SHARE_LEVEL == 0)
++            COMPILED_PATTERNS = null;
++        else
++            COMPILED_PATTERNS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
++    }
++
+     /**
+-     * Generate customized bytecode for a given LambdaForm.
+-     *
+-     * @param form
+-     * @param invokerType
+-     * @return
++     * Generate factored or customized bytecode for a given LambdaForm.
+      */
+-    static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
+-        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
+-        return g.loadMethod(g.generateCustomizedCodeBytes());
++    static CodePattern generateCustomizedCode(CodePattern cp) {
++        boolean tryCache = (COMPILED_PATTERNS != null && cp.canBeStrongRoot());
++        if (tryCache) {
++            CodePattern cp2 = COMPILED_PATTERNS.get(cp);
++            if (cp2 != null)  return cp2;
++            // No hit.  Got to generate new code.
++        }
++        cp = cp.copyFactored();
++        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", cp);
++        MemberName vmentry = g.loadMethod(g.generateCustomizedCodeBytes());
++        cp = new CodePattern(cp, vmentry);
++        if (tryCache) {
++            CodePattern cp2 = COMPILED_PATTERNS.putIfAbsent(cp, cp);
++            if (cp2 != null)  cp = cp2;  // another racer won
++        }
++        return cp;
++    }
++
++    private boolean shouldBeFactored(Name name, int j) {
++        return codePattern != null && codePattern.shouldBeFactored(name, j);
+     }
+ 
+     /**
+@@ -530,7 +821,9 @@
+             Name name = lambdaForm.names[i];
+             MemberName member = name.function.member();
+ 
+-            if (isSelectAlternative(member)) {
++            if (shouldBeFactored(name, -1)) {
++                emitInvoke(name, true);
++            } else if (isSelectAlternative(member) && codePattern != null && name.arguments[0] instanceof Name) {
+                 // selectAlternative idiom
+                 // FIXME: make sure this idiom is really present!
+                 emitSelectAlternative(name, lambdaForm.names[i + 1]);
+@@ -538,7 +831,7 @@
+             } else if (isStaticallyInvocable(member)) {
+                 emitStaticInvoke(member, name);
+             } else {
+-                emitInvoke(name);
++                emitInvoke(name, false);
+             }
+ 
+             // store the result from evaluating to the target name in a local if required
+@@ -556,32 +849,69 @@
+         emitReturn();
+ 
+         classFileEpilogue();
+-        bogusMethod(lambdaForm);
++        bogusMethod(lambdaForm.toString(codePattern));
+ 
+         final byte[] classFile = cw.toByteArray();
+         maybeDump(className, classFile);
++        maybeDumpPattern(className);
+         return classFile;
+     }
+ 
++    void emitLoadThisLambdaForm() {
++        boolean cache = true;
++        int index = localThisLF();
++        if (localsModel[index] == lambdaForm) {
++            emitAloadInsn(index);
++        } else {
++            emitAloadInsn(0);
++            if (invokerType.parameterType(0) != MethodHandle.class)
++                mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
++            mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
++            if (cache) {
++                mv.visitInsn(Opcodes.DUP);
++                emitAstoreInsn(index);
++                localsModel[index] = lambdaForm;
++            }
++        }
++    }
++
++    void emitLoadThisArgList(Name name) {
++        boolean cache = true;
++        int index = localThisArgList();
++        Object[] arglist = name.arguments;
++        if (localsModel[index] == arglist) {
++            emitAloadInsn(index);
++        } else {
++            emitLoadThisLambdaForm();
++            mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", "[" + LFN_SIG);
++            emitIconstInsn(name.index());
++            mv.visitInsn(Opcodes.AALOAD);
++            mv.visitFieldInsn(Opcodes.GETFIELD, LFN, "arguments", "[" + OBJ_SIG);
++            if (cache) {
++                mv.visitInsn(Opcodes.DUP);
++                emitAstoreInsn(index);
++                localsModel[index] = arglist;
++            }
++        }
++    }
++
+     /**
+      * Emit an invoke for the given name.
+      *
+      * @param name
+      */
+-    void emitInvoke(Name name) {
+-        if (true) {
++    void emitInvoke(Name name, boolean shouldBeFactored) {
++        if (!shouldBeFactored) {
+             // push receiver
+             MethodHandle target = name.function.resolvedHandle;
+             assert(target != null) : name.exprString();
+             mv.visitLdcInsn(constantPlaceholder(target));
+             mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+         } else {
+-            // load receiver
+-            emitAloadInsn(0);
+-            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+-            mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
+-            mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
+-            // TODO more to come
++            // load receiver from the MH.form
++            emitLoadThisLambdaForm();
++            emitIconstInsn(name.index());
++            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "getResolvedHandleAt", "(I)L" + MH + ";");
+         }
+ 
+         // push arguments
+@@ -708,8 +1038,6 @@
+      * @param invokeBasicName
+      */
+     private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
+-        MethodType type = selectAlternativeName.function.methodType();
+-
+         Name receiver = (Name) invokeBasicName.arguments[0];
+ 
+         Label L_fallback = new Label();
+@@ -723,10 +1051,9 @@
+         mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
+ 
+         // invoke selectAlternativeName.arguments[1]
+-        MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
+         emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
+         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
+-        emitInvoke(invokeBasicName);
++        emitInvoke(invokeBasicName, shouldBeFactored(invokeBasicName, -1));
+ 
+         // goto L_done
+         mv.visitJumpInsn(Opcodes.GOTO, L_done);
+@@ -735,10 +1062,9 @@
+         mv.visitLabel(L_fallback);
+ 
+         // invoke selectAlternativeName.arguments[2]
+-        MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
+         emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
+         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
+-        emitInvoke(invokeBasicName);
++        emitInvoke(invokeBasicName, shouldBeFactored(invokeBasicName, -1));
+ 
+         // L_done:
+         mv.visitLabel(L_done);
+@@ -753,10 +1079,21 @@
+         Object arg = name.arguments[paramIndex];
+         char ptype = name.function.parameterType(paramIndex);
+         MethodType mtype = name.function.methodType();
++        if (shouldBeFactored(name, -1))
++            mtype = mtype.basicType();
++        Class<?> parameterType = mtype.parameterType(paramIndex);
+         if (arg instanceof Name) {
+             Name n = (Name) arg;
+             emitLoadInsn(n.type, n.index());
+-            emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
++            emitImplicitConversion(n.type, parameterType);
++        } else if (shouldBeFactored(name, paramIndex)) {
++            emitLoadThisArgList(name);
++            emitIconstInsn(paramIndex);
++            mv.visitInsn(Opcodes.AALOAD);
++            if (parameterType.isPrimitive())
++                emitUnboxing(Wrapper.forBasicType(ptype));
++            else
++                emitImplicitConversion('L', parameterType);
+         } else if ((arg == null || arg instanceof String) && ptype == 'L') {
+             emitConst(arg);
+         } else {
+@@ -764,7 +1101,7 @@
+                 emitConst(arg);
+             } else {
+                 mv.visitLdcInsn(constantPlaceholder(arg));
+-                emitImplicitConversion('L', mtype.parameterType(paramIndex));
++                emitImplicitConversion('L', parameterType);
+             }
+         }
+     }
+@@ -774,52 +1111,66 @@
+      */
+     private void emitReturn() {
+         // return statement
+-        if (lambdaForm.result == -1) {
++        Class<?> reqType = invokerType.returnType();
++        char reqbt = Wrapper.basicTypeChar(reqType);
++        if (reqType == void.class) {
+             // void
+-            mv.visitInsn(Opcodes.RETURN);
++        } else if (lambdaForm.result == -1) {
++            emitConst(Wrapper.forBasicType(reqbt).zero());
+         } else {
+             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
+-            char rtype = Wrapper.basicTypeChar(invokerType.returnType());
+ 
+             // put return value on the stack if it is not already there
+             if (lambdaForm.result != lambdaForm.names.length - 1) {
+                 emitLoadInsn(rn.type, lambdaForm.result);
+             }
+ 
++            char tosbt = rn.type();
++            Class<?> tosType = LambdaForm.typeClass(tosbt);
++            if (!rn.isParam())
++                tosType = rn.function.methodType().returnType();
++
+             // potentially generate cast
+             // rtype is the return type of the invoker - generated code must conform to this
+             // rn.type is the type of the result Name in the LF
+-            if (rtype != rn.type) {
++            if (tosbt != reqbt) {
+                 // need cast
+-                if (rtype == 'L') {
++                Wrapper w;
++                if (reqbt == 'L') {
+                     // possibly cast the primitive to the correct type for boxing
+-                    char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
+-                    if (boxedType != rn.type) {
+-                        emitPrimCast(rn.type, boxedType);
+-                    }
++                    if (reqType == Object.class || !Wrapper.isWrapperType(reqType))
++                        w = Wrapper.forPrimitiveType(tosType);
++                    else
++                        w = Wrapper.forWrapperType(reqType);
++                    if (tosType != w.primitiveType())
++                        emitPrimCast(Wrapper.forBasicType(tosbt), w);
+                     // cast primitive to reference ("boxing")
+-                    emitBoxing(invokerType.returnType());
+-                } else {
+-                    // to-primitive cast
+-                    if (rn.type != 'L') {
+-                        // prim-to-prim cast
+-                        emitPrimCast(rn.type, rtype);
+-                    } else {
+-                        // ref-to-prim cast ("unboxing")
+-                        throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
+-                    }
++                    emitBoxing(w);
++                    tosType = w.wrapperType();
++                } else if (tosbt == 'L') {
++                    w = Wrapper.forBasicType(reqbt);
++                    // ref-to-prim cast ("unboxing")
++                    emitUnboxing(w);
++                    tosType = w.primitiveType();
++                } else if (tosType != reqType) {
++                    // prim-to-prim cast
++                    emitPrimCast(Wrapper.forBasicType(tosbt), Wrapper.forBasicType(reqbt));
++                    tosType = reqType;
+                 }
+             }
+-
+-            // generate actual return statement
+-            emitReturnInsn(invokerType.returnType());
++            if (!VerifyType.isNullConversion(tosType, reqType)) {
++                assert(reqbt == 'L');
++                emitRefCast(reqType);
++            }
+         }
++        // generate actual return statement
++        emitReturnInsn(reqType);
+     }
+ 
+     /**
+      * Emit a type conversion bytecode casting from "from" to "to".
+      */
+-    private void emitPrimCast(char from, char to) {
++    private void emitPrimCast(Wrapper wfrom, Wrapper wto) {
+         // Here's how.
+         // -   indicates forbidden
+         // <-> indicates implicit
+@@ -832,12 +1183,11 @@
+         //      long        -     l2i,i2b   l2i,i2s  l2i,i2c    l2i      <->      l2f      l2d
+         //      float       -     f2i,i2b   f2i,i2s  f2i,i2c    f2i      f2l      <->      f2d
+         //      double      -     d2i,i2b   d2i,i2s  d2i,i2c    d2i      d2l      d2f      <->
+-        if (from == to) {
++        if (wfrom == wto) {
+             // no cast required, should be dead code anyway
+             return;
+         }
+-        Wrapper wfrom = Wrapper.forBasicType(from);
+-        Wrapper wto   = Wrapper.forBasicType(to);
++        char from = wfrom.basicTypeChar(), to = wto.basicTypeChar();
+         if (wfrom.isSubwordOrInt()) {
+             // cast from {byte,short,char,int} to anything
+             emitI2X(to);
+@@ -921,19 +1271,9 @@
+      * @param sig
+      * @return
+      */
+-    static MemberName generateLambdaFormInterpreterEntryPoint(String sig) {
+-        assert(LambdaForm.isValidSignature(sig));
+-        //System.out.println("generateExactInvoker "+sig);
+-        // compute method type
+-        // first parameter and return type
+-        char tret = LambdaForm.signatureReturn(sig);
+-        MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class);
+-        // other parameter types
+-        int arity = LambdaForm.signatureArity(sig);
+-        for (int i = 1; i < arity; i++) {
+-            type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i)));
+-        }
+-        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type);
++    static MemberName generateLambdaFormInterpreterEntryPoint(MethodType type) {
++        char rt = Wrapper.forBasicType(type.returnType()).basicTypeChar();
++        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+rt, type);
+         return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes());
+     }
+ 
+@@ -955,20 +1295,20 @@
+             emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
+             // box if primitive type
+             if (ptype.isPrimitive()) {
+-                emitBoxing(ptype);
++                emitBoxing(Wrapper.forPrimitiveType(ptype));
+             }
+             mv.visitInsn(Opcodes.AASTORE);
+         }
+         // invoke
+         emitAloadInsn(0);
+-        mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
++        mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
+         mv.visitInsn(Opcodes.SWAP);  // swap form and array; avoid local variable
+         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
+ 
+         // maybe unbox
+         Class<?> rtype = invokerType.returnType();
+         if (rtype.isPrimitive() && rtype != void.class) {
+-            emitUnboxing(Wrapper.asWrapperType(rtype));
++            emitUnboxing(Wrapper.forPrimitiveType(rtype));
+         }
+ 
+         // return statement
+@@ -1020,8 +1360,8 @@
+                 Class<?> sptype = dstType.basicType().wrap().parameterType(i);
+                 Wrapper dstWrapper = Wrapper.forBasicType(dptype);
+                 Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper;  // narrow subword from int
+-                emitUnboxing(srcWrapper.wrapperType());
+-                emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
++                emitUnboxing(srcWrapper);
++                emitPrimCast(srcWrapper, dstWrapper);
+             }
+         }
+ 
+@@ -1035,8 +1375,8 @@
+             Wrapper srcWrapper = Wrapper.forBasicType(rtype);
+             Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper;  // widen subword to int
+             // boolean casts not allowed
+-            emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
+-            emitBoxing(dstWrapper.primitiveType());
++            emitPrimCast(srcWrapper, dstWrapper);
++            emitBoxing(dstWrapper);
+         }
+ 
+         // If the return type is void we return a null reference.
+diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java
+--- a/src/share/classes/java/lang/invoke/Invokers.java
++++ b/src/share/classes/java/lang/invoke/Invokers.java
+@@ -336,7 +336,7 @@
+         names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
+         lform = new LambdaForm(debugName, INARG_LIMIT, names);
+         if (isLinker)
+-            lform.compileToBytecode();  // JVM needs a real methodOop
++            lform.compileToBytecode(true);  // JVM needs a real methodOop
+         if (isCached)
+             lform = mtype.form().setCachedLambdaForm(which, lform);
+         return lform;
+@@ -424,7 +424,7 @@
+         outArgs[PREPEND_MH] = names[CALL_MH];
+         names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs);
+         lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names);
+-        lform.compileToBytecode();  // JVM needs a real methodOop
++        lform.compileToBytecode(true);  // JVM needs a real methodOop
+         lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform);
+         return lform;
+     }
+diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
+--- a/src/share/classes/java/lang/invoke/LambdaForm.java
++++ b/src/share/classes/java/lang/invoke/LambdaForm.java
+@@ -26,19 +26,15 @@
+ package java.lang.invoke;
+ 
+ import java.lang.annotation.*;
++import java.lang.invoke.InvokerBytecodeGenerator.CodePattern;
+ import java.lang.reflect.Method;
+-import java.util.Map;
+ import java.util.List;
+ import java.util.Arrays;
+-import java.util.ArrayList;
+-import java.util.HashMap;
+-import java.util.concurrent.ConcurrentHashMap;
+ import sun.invoke.util.Wrapper;
+ import sun.invoke.Stable;
+ import static java.lang.invoke.MethodHandleStatics.*;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
+ import java.lang.reflect.Field;
+-import java.util.Objects;
+ 
+ /**
+  * The symbolic, non-executable form of a method handle's invocation semantics.
+@@ -124,7 +120,7 @@
+     @Stable final Name[] names;
+     final String debugName;
+     MemberName vmentry;   // low-level behavior, or null if not yet prepared
+-    private boolean isCompiled;
++    private CodePattern compiled;  // null if not compiled yet
+ 
+     // Caches for common structural transforms:
+     LambdaForm[] bindCache;
+@@ -163,40 +159,35 @@
+         return names;
+     }
+ 
+-    private LambdaForm(String sig) {
++    private LambdaForm(MethodType mt) {
+         // Make a blank lambda form, which returns a constant zero or null.
+         // It is used as a template for managing the invocation of similar forms that are non-empty.
+         // Called only from getPreparedForm.
+-        assert(isValidSignature(sig));
+-        this.arity = signatureArity(sig);
+-        this.result = (signatureReturn(sig) == 'V' ? -1 : arity);
+-        this.names = buildEmptyNames(arity, sig);
++        this.arity = mt.parameterCount();
++        this.result = (mt.returnType() == void.class ? -1 : arity);
++        this.names = buildEmptyNames(mt);
+         this.debugName = "LF.zero";
+         assert(nameRefsAreLegal());
+         assert(isEmpty());
+-        assert(sig.equals(basicTypeSignature()));
++        assert(mt.equals(methodType()));
+     }
+ 
+-    private static Name[] buildEmptyNames(int arity, String basicTypeSignature) {
+-        assert(isValidSignature(basicTypeSignature));
+-        int resultPos = arity + 1;  // skip '_'
+-        if (arity < 0 || basicTypeSignature.length() != resultPos+1)
+-            throw new IllegalArgumentException("bad arity for "+basicTypeSignature);
+-        int numRes = (basicTypeSignature.charAt(resultPos) == 'V' ? 0 : 1);
+-        Name[] names = arguments(numRes, basicTypeSignature.substring(0, arity));
+-        for (int i = 0; i < numRes; i++) {
+-            names[arity + i] = constantZero(arity + i, basicTypeSignature.charAt(resultPos + i));
+-        }
++    private static Name[] buildEmptyNames(MethodType mt) {
++        int arity = mt.parameterCount();
++        int numRes = (mt.returnType() == void.class ? 0 : 1);
++        Name[] names = arguments(numRes, mt);
++        if (numRes != 0)
++        names[arity] = constantZero(arity, basicType(mt.returnType()));
+         return names;
+     }
+ 
+     private static int fixResult(int result, Name[] names) {
+-        if (result >= 0) {
+-            if (names[result].type == 'V')
+-                return -1;
+-        } else if (result == LAST_RESULT) {
+-            return names.length - 1;
+-        }
++        // Normalize the result field to either one of non-void names or -1.
++        if (result == LAST_RESULT)
++          result = names.length - 1;
++        assert(result >= VOID_RESULT);
++        if (result != VOID_RESULT && names[result].type == 'V')
++            result = VOID_RESULT;
+         return result;
+     }
+ 
+@@ -312,9 +303,16 @@
+         return arity;
+     }
+ 
++    /** Report the lead argument type.
++     *  This is usually MethodHandle but could also be an interface wrapper type.
++     */
++    Class<?> receiver() {
++        return MethodHandle.class;
++    }
++
+     /** Return the method type corresponding to my basic type signature. */
+     MethodType methodType() {
+-        return signatureType(basicTypeSignature());
++        return signatureType(basicTypeSignature()).changeParameterType(0, receiver());
+     }
+     /** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
+     final String basicTypeSignature() {
+@@ -433,155 +431,48 @@
+      */
+     public void prepare() {
+         if (COMPILE_THRESHOLD == 0) {
+-            compileToBytecode();
++            compileToBytecode(false);
+         }
+-        if (this.vmentry != null) {
+-            // already prepared (e.g., a primitive DMH invoker form)
+-            return;
++        if (vmentry == null) {
++            vmentry = getInterpreterEntry();
+         }
+-        LambdaForm prep = getPreparedForm(basicTypeSignature());
+-        this.vmentry = prep.vmentry;
+-        // TO DO: Maybe add invokeGeneric, invokeWithArguments
++        // might be already prepared (e.g., a primitive DMH invoker form)
++    }
++
++    MemberName getInterpreterEntry() {
++        MethodType mt = methodType();
++        LambdaForm prep =  mt.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
++        if (prep != null)  return prep.vmentry;
++        prep = new LambdaForm(mt);
++        prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(mt);
++        prep = mt.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
++        return prep.vmentry;
+     }
+ 
+     /** Generate optimizable bytecode for this form. */
+-    MemberName compileToBytecode() {
+-        MethodType invokerType = methodType();
+-        assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
+-        if (vmentry != null && isCompiled) {
+-            return vmentry;  // already compiled somehow
++    MemberName compileToBytecode(boolean fullCustom) {
++        int requiredLevel = (fullCustom ? CodePattern.CL_CUSTOM : CodePattern.CL_FACTORED);
++        assert(vmentry == null || vmentry.getMethodType().basicType().equals(methodType().basicType()));
++        CodePattern cp = compiled;
++        if (cp != null && cp.satisfiesCompileLevel(requiredLevel)) {
++            return cp.vmentry;  // already compiled somehow
+         }
+         try {
+-            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
++            cp = new CodePattern(this, requiredLevel);
++            cp = InvokerBytecodeGenerator.generateCustomizedCode(cp);
++            CodePattern cp2 = compiled;  // race condition?
++            if (cp2 != null && cp2.satisfiesCompileLevel(requiredLevel))
++                return cp2.vmentry;
++            compiled = cp;
++            vmentry = cp.vmentry;
+             if (TRACE_INTERPRETER)
+-                traceInterpreter("compileToBytecode", this);
+-            isCompiled = true;
+-            return vmentry;
++                traceInterpreter("compileToBytecode" + (fullCustom ? "/fullCustom" : ""), this);
++            return cp.vmentry;
+         } catch (Error | Exception ex) {
+             throw new InternalError(this.toString(), ex);
+         }
+     }
+ 
+-    private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
+-    static {
+-        int   capacity   = 512;    // expect many distinct signatures over time
+-        float loadFactor = 0.75f;  // normal default
+-        int   writers    = 1;
+-        PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
+-    }
+-
+-    private static Map<String,LambdaForm> computeInitialPreparedForms() {
+-        // Find all predefined invokers and associate them with canonical empty lambda forms.
+-        HashMap<String,LambdaForm> forms = new HashMap<>();
+-        for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
+-            if (!m.isStatic() || !m.isPackage())  continue;
+-            MethodType mt = m.getMethodType();
+-            if (mt.parameterCount() > 0 &&
+-                mt.parameterType(0) == MethodHandle.class &&
+-                m.getName().startsWith("interpret_")) {
+-                String sig = basicTypeSignature(mt);
+-                assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
+-                LambdaForm form = new LambdaForm(sig);
+-                form.vmentry = m;
+-                mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
+-                // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
+-                forms.put(sig, form);
+-            }
+-        }
+-        //System.out.println("computeInitialPreparedForms => "+forms);
+-        return forms;
+-    }
+-
+-    // Set this false to disable use of the interpret_L methods defined in this file.
+-    private static final boolean USE_PREDEFINED_INTERPRET_METHODS = true;
+-
+-    // The following are predefined exact invokers.  The system must build
+-    // a separate invoker for each distinct signature.
+-    static Object interpret_L(MethodHandle mh) throws Throwable {
+-        Object[] av = {mh};
+-        String sig = null;
+-        assert(argumentTypesMatch(sig = "L_L", av));
+-        Object res = mh.form.interpretWithArguments(av);
+-        assert(returnTypesMatch(sig, av, res));
+-        return res;
+-    }
+-    static Object interpret_L(MethodHandle mh, Object x1) throws Throwable {
+-        Object[] av = {mh, x1};
+-        String sig = null;
+-        assert(argumentTypesMatch(sig = "LL_L", av));
+-        Object res = mh.form.interpretWithArguments(av);
+-        assert(returnTypesMatch(sig, av, res));
+-        return res;
+-    }
+-    static Object interpret_L(MethodHandle mh, Object x1, Object x2) throws Throwable {
+-        Object[] av = {mh, x1, x2};
+-        String sig = null;
+-        assert(argumentTypesMatch(sig = "LLL_L", av));
+-        Object res = mh.form.interpretWithArguments(av);
+-        assert(returnTypesMatch(sig, av, res));
+-        return res;
+-    }
+-    private static LambdaForm getPreparedForm(String sig) {
+-        MethodType mtype = signatureType(sig);
+-        //LambdaForm prep = PREPARED_FORMS.get(sig);
+-        LambdaForm prep =  mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
+-        if (prep != null)  return prep;
+-        assert(isValidSignature(sig));
+-        prep = new LambdaForm(sig);
+-        prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
+-        //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
+-        return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
+-    }
+-
+-    // The next few routines are called only from assert expressions
+-    // They verify that the built-in invokers process the correct raw data types.
+-    private static boolean argumentTypesMatch(String sig, Object[] av) {
+-        int arity = signatureArity(sig);
+-        assert(av.length == arity) : "av.length == arity: av.length=" + av.length + ", arity=" + arity;
+-        assert(av[0] instanceof MethodHandle) : "av[0] not instace of MethodHandle: " + av[0];
+-        MethodHandle mh = (MethodHandle) av[0];
+-        MethodType mt = mh.type();
+-        assert(mt.parameterCount() == arity-1);
+-        for (int i = 0; i < av.length; i++) {
+-            Class<?> pt = (i == 0 ? MethodHandle.class : mt.parameterType(i-1));
+-            assert(valueMatches(sig.charAt(i), pt, av[i]));
+-        }
+-        return true;
+-    }
+-    private static boolean valueMatches(char tc, Class<?> type, Object x) {
+-        // The following line is needed because (...)void method handles can use non-void invokers
+-        if (type == void.class)  tc = 'V';   // can drop any kind of value
+-        assert tc == basicType(type) : tc + " == basicType(" + type + ")=" + basicType(type);
+-        switch (tc) {
+-        case 'I': assert checkInt(type, x)   : "checkInt(" + type + "," + x +")";   break;
+-        case 'J': assert x instanceof Long   : "instanceof Long: " + x;             break;
+-        case 'F': assert x instanceof Float  : "instanceof Float: " + x;            break;
+-        case 'D': assert x instanceof Double : "instanceof Double: " + x;           break;
+-        case 'L': assert checkRef(type, x)   : "checkRef(" + type + "," + x + ")";  break;
+-        case 'V': break;  // allow anything here; will be dropped
+-        default:  assert(false);
+-        }
+-        return true;
+-    }
+-    private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
+-        MethodHandle mh = (MethodHandle) av[0];
+-        return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
+-    }
+-    private static boolean checkInt(Class<?> type, Object x) {
+-        assert(x instanceof Integer);
+-        if (type == int.class)  return true;
+-        Wrapper w = Wrapper.forBasicType(type);
+-        assert(w.isSubwordOrInt());
+-        Object x1 = Wrapper.INT.wrap(w.wrap(x));
+-        return x.equals(x1);
+-    }
+-    private static boolean checkRef(Class<?> type, Object x) {
+-        assert(!type.isPrimitive());
+-        if (x == null)  return true;
+-        if (type.isInterface())  return true;
+-        return type.isInstance(x);
+-    }
+-
+     /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
+     private static final int COMPILE_THRESHOLD;
+     static {
+@@ -627,22 +518,18 @@
+     private void checkInvocationCounter() {
+         if (COMPILE_THRESHOLD != 0 &&
+             invocationCounter < COMPILE_THRESHOLD) {
+-            invocationCounter++;  // benign race
+-            if (invocationCounter >= COMPILE_THRESHOLD) {
++            int ctr = ++invocationCounter;  // benign race
++            if (TRACE_INTERPRETER)
++                traceInterpreter("| invocationCounter", ctr);
++            if (ctr >= COMPILE_THRESHOLD) {
+                 // Replace vmentry with a bytecode version of this LF.
+-                compileToBytecode();
++                compileToBytecode(false);
+             }
+         }
+     }
+     Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
+         traceInterpreter("[ interpretWithArguments", this, argumentValues);
+-        if (invocationCounter < COMPILE_THRESHOLD) {
+-            int ctr = invocationCounter++;  // benign race
+-            traceInterpreter("| invocationCounter", ctr);
+-            if (invocationCounter >= COMPILE_THRESHOLD) {
+-                compileToBytecode();
+-            }
+-        }
++        checkInvocationCounter();
+         Object rval;
+         try {
+             assert(arityCheck(argumentValues));
+@@ -707,7 +594,16 @@
+     }
+ 
+     public String toString() {
+-        StringBuilder buf = new StringBuilder(debugName+"=Lambda(");
++        return toString((CodePattern)null);
++    }
++    String toString(CodePattern codePattern) {
++        if (codePattern != null && codePattern.compileLevel == CodePattern.CL_NONE)
++            codePattern = null;
++        StringBuilder buf = new StringBuilder();
++        if (codePattern != null)
++            buf.append("CodePattern/"+codePattern.compileLevel+"(");
++        else
++            buf.append(debugName).append("=Lambda(");
+         for (int i = 0; i < names.length; i++) {
+             if (i == arity)  buf.append(")=>{");
+             Name n = names[i];
+@@ -717,7 +613,7 @@
+                 if (i+1 < arity)  buf.append(",");
+                 continue;
+             }
+-            buf.append("=").append(n.exprString());
++            buf.append("=").append(n.exprString(codePattern));
+             buf.append(";");
+         }
+         buf.append(result < 0 ? "void" : names[result]).append("}");
+@@ -968,6 +864,13 @@
+         return true;
+     }
+ 
++
++    /** Get the function for the Nth name.  This is used by the bytecode compiler and can be constant folded. */
++    MethodHandle getResolvedHandleAt(int index) {
++        return names[index].function.resolvedHandle();
++    }
++
++    /** The head of a function application in a Name. */
+     static class NamedFunction {
+         final MemberName member;
+         @Stable MethodHandle resolvedHandle;
+@@ -1007,19 +910,26 @@
+         }
+ 
+         @Override
+-        public boolean equals(Object other) {
+-            if (this == other) return true;
+-            if (other == null) return false;
+-            if (!(other instanceof NamedFunction)) return false;
+-            NamedFunction that = (NamedFunction) other;
+-            return this.member != null && this.member.equals(that.member);
++        public boolean equals(Object x) {
++            return x instanceof NamedFunction && equals((NamedFunction)x);
++        }
++        public boolean equals(NamedFunction that) {
++            if (this == that) return true;
++            if (that == null) return false;
++            if (this.member != null)
++                return this.member.equals(that.member);
++            assert(this.resolvedHandle != null);
++            return (that.member == null &&
++                    this.resolvedHandle != null &&
++                    this.resolvedHandle == that.resolvedHandle);
+         }
+ 
+         @Override
+         public int hashCode() {
+             if (member != null)
+                 return member.hashCode();
+-            return super.hashCode();
++            else
++                return resolvedHandle.hashCode();
+         }
+ 
+         // Put the predefined NamedFunction invokers into the table.
+@@ -1251,15 +1161,9 @@
+         return btypes;
+     }
+     public static String basicTypeSignature(MethodType type) {
+-        char[] sig = new char[type.parameterCount() + 2];
+-        int sigp = 0;
+-        for (Class<?> pt : type.parameterList()) {
+-            sig[sigp++] = basicType(pt);
+-        }
+-        sig[sigp++] = '_';
+-        sig[sigp++] = basicType(type.returnType());
+-        assert(sigp == sig.length);
+-        return String.valueOf(sig);
++        String sig = type.basicType().form().typeString();
++        assert(sig.matches("[LIJFD]*_[LIJFDV]"));
++        return sig;
+     }
+ 
+     static final class Name {
+@@ -1392,13 +1296,27 @@
+             return (function == null) ? s : s + "=" + exprString();
+         }
+         public String exprString() {
++            return exprString((CodePattern)null);
++        }
++        public String exprString(CodePattern codePattern) {
+             if (function == null)  return "null";
+-            StringBuilder buf = new StringBuilder(function.toString());
++            StringBuilder buf = new StringBuilder();
++            if (codePattern != null && codePattern.shouldBeFactored(this, -1)
++                || CodePattern.isFactoredPlaceholder(function))
++                buf.append("***").append(function.basicTypeSignature());
++            else
++                buf.append(function.toString());
+             buf.append("(");
+             String cma = "";
++            int j = -1;
+             for (Object a : arguments) {
+                 buf.append(cma); cma = ",";
+-                if (a instanceof Name || a instanceof Integer)
++                ++j;
++                if (a instanceof Name)
++                    buf.append(a);
++                else if (codePattern != null && codePattern.shouldBeFactored(this, j))
++                    buf.append("***");
++                else if (a instanceof Integer)
+                     buf.append(a);
+                 else
+                     buf.append("(").append(a).append(")");
+@@ -1454,7 +1372,7 @@
+         public boolean equals(Name that) {
+             if (this == that)  return true;
+             if (isParam())
+-                // each parameter is a unique atom
++            // each parameter is a unique atom
+                 return false;  // this != that
+             return
+                 //this.index == that.index &&
+@@ -1466,10 +1384,13 @@
+         public boolean equals(Object x) {
+             return x instanceof Name && equals((Name)x);
+         }
++        int indexAndType() {
++            return (index + (type << 8));
++        }
+         @Override
+         public int hashCode() {
+             if (isParam())
+-                return index | (type << 8);
++                return indexAndType();
+             return function.hashCode() ^ Arrays.hashCode(arguments);
+         }
+     }
+@@ -1565,12 +1486,6 @@
+     private static double zeroD() { return 0; }
+     private static Object zeroL() { return null; }
+ 
+-    // Put this last, so that previous static inits can run before.
+-    static {
+-        if (USE_PREDEFINED_INTERPRET_METHODS)
+-            PREPARED_FORMS.putAll(computeInitialPreparedForms());
+-    }
+-
+     /**
+      * Internal marker for byte-compiled LambdaForms.
+      */
+diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
+--- a/src/share/classes/java/lang/invoke/MemberName.java
++++ b/src/share/classes/java/lang/invoke/MemberName.java
+@@ -151,7 +151,10 @@
+         MethodType itype = getMethodOrFieldType();
+         if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
+             return itype.changeReturnType(clazz);
+-        if (!isStatic())
++        //if (!isStatic())
++        assert(!isStatic() == MethodHandleNatives.refKindHasReceiver(getReferenceKind())
++                || !isResolved());
++        if (MethodHandleNatives.refKindHasReceiver(getReferenceKind()))
+             return itype.insertParameterTypes(0, clazz);
+         return itype;
+     }
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+@@ -424,7 +424,9 @@
+             Class<?> src = lambdaType.parameterType(i);
+             if (i == spreadArgPos) {
+                 // Spread the array.
+-                MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
++                Class<?> basicArrayType = spreadArgType;
++                if (Object[].class.isAssignableFrom(spreadArgType))  basicArrayType = Object[].class;
++                MethodHandle aload = ArrayAccessor.getAccessor(basicArrayType, false);
+                 Name array = names[argIndex];
+                 names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
+                 for (int j = 0; j < spreadArgCount; i++, j++) {
+diff --git a/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
+--- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java
++++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
+@@ -46,8 +46,9 @@
+     static final boolean TRACE_INTERPRETER;
+     static final boolean TRACE_METHOD_LINKAGE;
+     static final Integer COMPILE_THRESHOLD;
++    static final Integer BYTECODE_SHARE_LEVEL;
+     static {
+-        final Object[] values = { false, false, false, false, null };
++        final Object[] values = { false, false, false, false, null, null };
+         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                 public Void run() {
+                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
+@@ -55,6 +56,7 @@
+                     values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
+                     values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
+                     values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
++                    values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.BYTECODE_SHARE_LEVEL");
+                     return null;
+                 }
+             });
+@@ -63,6 +65,7 @@
+         TRACE_INTERPRETER         = (Boolean) values[2];
+         TRACE_METHOD_LINKAGE      = (Boolean) values[3];
+         COMPILE_THRESHOLD         = (Integer) values[4];
++        BYTECODE_SHARE_LEVEL      = (Integer) values[5];
+     }
+ 
+     /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
+diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java
+--- a/src/share/classes/java/lang/invoke/MethodTypeForm.java
++++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java
+@@ -85,6 +85,23 @@
+         return basicType;
+     }
+ 
++    public String typeString() {
++        if (typeString == null) {
++            MethodType type = erasedType;
++            char[] sig = new char[type.parameterCount() + 2];
++            int sigp = 0;
++            for (Class<?> pt : type.parameterList()) {
++                sig[sigp++] = Wrapper.basicTypeChar(pt);
++            }
++            sig[sigp++] = '_';
++            Class<?> rt = type.returnType();
++            sig[sigp++] = Wrapper.basicTypeChar(rt);
++            assert(sigp == sig.length);
++            return typeString = String.valueOf(sig);
++        }
++        return typeString;
++    }
++
+     public LambdaForm cachedLambdaForm(int which) {
+         return lambdaForms[which];
+     }
--- a/meth-lfi.patch	Wed Oct 17 21:02:38 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1323 +0,0 @@
-Lambda form interning.
-Allows greater sharing of bytecodes.
-Assumes @Stable arrays for optimization.
-
-diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
---- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
-+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
-@@ -251,14 +251,14 @@
-         lambdaName += "_" + LambdaForm.basicTypeSignature(mtype);
-         LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
-         // This is a tricky bit of code.  Don't send it through the LF interpreter.
--        lform.compileToBytecode();
-+        lform.compileToBytecode(true);
-         return lform;
-     }
- 
-     private static void maybeCompile(LambdaForm lform, MemberName m) {
-         if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class))
-             // Help along bootstrapping...
--            lform.compileToBytecode();
-+            lform.compileToBytecode(true);
-     }
- 
-     /** Static wrapper for DirectMethodHandle.internalMemberName. */
-diff --git a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
---- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
-+++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
-@@ -27,7 +27,6 @@
- 
- import sun.invoke.util.VerifyAccess;
- import java.lang.invoke.LambdaForm.Name;
--import java.lang.invoke.MethodHandles.Lookup;
- 
- import sun.invoke.util.Wrapper;
- 
-@@ -35,12 +34,12 @@
- import java.util.*;
- 
- import com.sun.xml.internal.ws.org.objectweb.asm.*;
-+import java.lang.invoke.LambdaForm.Template;
- 
- import java.lang.reflect.*;
- import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
--import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
--import sun.invoke.util.ValueConversions;
-+import java.util.concurrent.ConcurrentHashMap;
- import sun.invoke.util.VerifyType;
- 
- /**
-@@ -51,13 +50,13 @@
- class InvokerBytecodeGenerator {
-     /** Define class names for convenience. */
-     private static final String MH      = "java/lang/invoke/MethodHandle";
--    private static final String BMH     = "java/lang/invoke/BoundMethodHandle";
-     private static final String LF      = "java/lang/invoke/LambdaForm";
-     private static final String LFN     = "java/lang/invoke/LambdaForm$Name";
-     private static final String CLS     = "java/lang/Class";
-     private static final String OBJ     = "java/lang/Object";
-     private static final String OBJARY  = "[Ljava/lang/Object;";
- 
-+    private static final String OBJ_SIG = "L" + OBJ + ";";
-     private static final String LF_SIG  = "L" + LF + ";";
-     private static final String LFN_SIG = "L" + LFN + ";";
-     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
-@@ -72,9 +71,11 @@
-     private final String sourceFile;
- 
-     private final LambdaForm lambdaForm;
-+    private final Template   lambdaFormTemplate;
-     private final String     invokerName;
-     private final MethodType invokerType;
-     private final int[] localsMap;
-+    private final Object[] localsModel;
- 
-     /** ASM bytecode generation. */
-     private ClassWriter cw;
-@@ -83,7 +84,7 @@
-     private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
-     private static final Class<?> HOST_CLASS = LambdaForm.class;
- 
--    private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
-+    private InvokerBytecodeGenerator(Template temp, LambdaForm form, int localsMapSize,
-                                      String className, String invokerName, MethodType invokerType) {
-         if (invokerName.contains(".")) {
-             int p = invokerName.indexOf(".");
-@@ -95,14 +96,16 @@
-         }
-         this.className  = superName + "$" + className;
-         this.sourceFile = "LambdaForm$" + className;
--        this.lambdaForm = lambdaForm;
-+        this.lambdaFormTemplate = temp;
-+        this.lambdaForm = form;
-         this.invokerName = invokerName;
-         this.invokerType = invokerType;
-         this.localsMap = new int[localsMapSize];
-+        this.localsModel = new Object[localsMapSize];
-     }
- 
-     private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) {
--        this(null, invokerType.parameterCount(),
-+        this(null, null, invokerType.parameterCount(),
-              className, invokerName, invokerType);
-         // Create an array to map name indexes to locals indexes.
-         for (int i = 0; i < localsMap.length; i++) {
-@@ -110,17 +113,27 @@
-         }
-     }
- 
--    private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
--        this(form, form.names.length,
--             className, form.debugName, invokerType);
-+    private InvokerBytecodeGenerator(String className, Template temp, LambdaForm form) {
-+        this(temp, form, form.names.length + SCRATCH_LOCALS,
-+             className, form.debugName, form.methodType());
-         // Create an array to map name indexes to locals indexes.
-+        assert(temp == null || temp.equals(new Template(form)));
-         Name[] names = form.names;
-         for (int i = 0, index = 0; i < localsMap.length; i++) {
-             localsMap[i] = index;
--            index += Wrapper.forBasicType(names[i].type).stackSlots();
-+            char type = (i < names.length ? names[i].type : 'L');
-+            index += Wrapper.forBasicType(type).stackSlots();
-         }
-+        // final value of index is number of locals, but we don't need it
-     }
- 
-+    private static final int SCRATCH_LOCALS = 2;  // LambdaForm, Name.arguments
-+    private int localThisLF() {
-+        return localsMap.length-2;
-+    }
-+    private int localThisArgList() {
-+        return localsMap.length-1;
-+    }
- 
-     /** instance counters for dumped classes */
-     private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
-@@ -146,6 +159,35 @@
-         }
-     }
- 
-+    static HashSet<LambdaForm.Template> DUMPED_TEMPLATES = null;
-+    void maybeDumpTemplate(final String className) {
-+        if (DUMP_CLASS_FILES) {
-+            if (lambdaForm != null) {
-+                final LambdaForm.Template temp = new LambdaForm.Template(lambdaForm);
-+                if (DUMPED_TEMPLATES == null)
-+                    DUMPED_TEMPLATES = new HashSet<>();
-+                if (!DUMPED_TEMPLATES.add(temp))
-+                    return;
-+                java.security.AccessController.doPrivileged(
-+                new java.security.PrivilegedAction<Void>() {
-+                    public Void run() {
-+                        try {
-+                            String dumpName = className;
-+                            //dumpName = dumpName.replace('/', '-');
-+                            File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".template");
-+                            dumpFile.getParentFile().mkdirs();
-+                            FileOutputStream file = new FileOutputStream(dumpFile);
-+                            file.write(temp.toString().getBytes());
-+                            file.close();
-+                            return null;
-+                        } catch (IOException ex) {
-+                            throw new InternalError(ex);
-+                        }
-+                    }
-+                });
-+            }
-+        }
-+    }
-     static void maybeDump(final String className, final byte[] classFile) {
-         if (DUMP_CLASS_FILES) {
-             System.out.println("dump: " + className);
-@@ -416,8 +458,8 @@
-      *
-      * @param type primitive type class to box.
-      */
--    private void emitBoxing(Class<?> type) {
--        Wrapper wrapper = Wrapper.forPrimitiveType(type);
-+    private void emitBoxing(Wrapper wrapper) {
-+        if (wrapper == Wrapper.OBJECT)  return;
-         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
-         String name  = "valueOf";
-         String desc  = "(" + wrapper.basicTypeChar() + ")L" + owner + ";";
-@@ -429,8 +471,8 @@
-      *
-      * @param type wrapper type class to unbox.
-      */
--    private void emitUnboxing(Class<?> type) {
--        Wrapper wrapper = Wrapper.forWrapperType(type);
-+    private void emitUnboxing(Wrapper wrapper) {
-+        if (wrapper == Wrapper.OBJECT)  return;
-         String owner = "java/lang/" + wrapper.wrapperType().getSimpleName();
-         String name  = wrapper.primitiveSimpleName() + "Value";
-         String desc  = "()" + wrapper.basicTypeChar();
-@@ -439,6 +481,27 @@
-     }
- 
-     /**
-+     * Emit a checkcast, if the reference type is non-trivial.
-+     *
-+     * @param type reference type required.
-+     */
-+    private void emitRefCast(Class<?> type) {
-+        assert(!type.isPrimitive());
-+        if (VerifyType.isNullConversion(Object.class, type))
-+            return;
-+        if (isStaticallyNameable(type)) {
-+            mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(type));
-+        } else {
-+            mv.visitLdcInsn(constantPlaceholder(type));
-+            mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
-+            mv.visitInsn(Opcodes.SWAP);
-+            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
-+            if (type.isArray())
-+                mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
-+        }
-+    }
-+
-+    /**
-      * Emit an implicit conversion.
-      *
-      * @param ptype type of value present on stack
-@@ -447,22 +510,11 @@
-     private void emitImplicitConversion(char ptype, Class<?> pclass) {
-         switch (ptype) {
-         case 'L':
--            if (VerifyType.isNullConversion(Object.class, pclass))
--                return;
--            if (isStaticallyNameable(pclass)) {
--                mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass));
--            } else {
--                mv.visitLdcInsn(constantPlaceholder(pclass));
--                mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
--                mv.visitInsn(Opcodes.SWAP);
--                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG);
--                if (pclass.isArray())
--                    mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY);
--            }
-+            emitRefCast(pclass);
-             return;
-         case 'I':
-             if (!VerifyType.isNullConversion(int.class, pclass))
--                emitPrimCast(ptype, Wrapper.basicTypeChar(pclass));
-+                emitPrimCast(Wrapper.INT, Wrapper.forPrimitiveType(pclass));
-             return;
-         case 'J':
-             assert(pclass == long.class);
-@@ -500,6 +552,17 @@
-         return c.getName().replace('.', '/');
-     }
- 
-+    private static final ConcurrentHashMap<Template,MemberName> COMPILED_TEMPLATES;
-+    static {
-+        int   capacity   = 512;    // expect many distinct signatures over time
-+        float loadFactor = 0.75f;  // normal default
-+        int   writers    = 1;
-+        if (BYTECODE_SHARE_LEVEL != null && (int)BYTECODE_SHARE_LEVEL == 0)
-+            COMPILED_TEMPLATES = null;
-+        else
-+            COMPILED_TEMPLATES = new ConcurrentHashMap<>(capacity, loadFactor, writers);
-+    }
-+
-     /**
-      * Generate customized bytecode for a given LambdaForm.
-      *
-@@ -507,9 +570,26 @@
-      * @param invokerType
-      * @return
-      */
--    static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
--        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
--        return g.loadMethod(g.generateCustomizedCodeBytes());
-+    static MemberName generateCustomizedCode(Template temp, LambdaForm form) {
-+        if (temp != null && COMPILED_TEMPLATES != null) {
-+            MemberName compiled = COMPILED_TEMPLATES.get(temp);
-+            if (compiled != null) {
-+                return compiled;
-+            }
-+            // No hit.  Got to generate new code.
-+            temp = temp.copyFactored();
-+        }
-+        InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", temp, form);
-+        MemberName vmentry = g.loadMethod(g.generateCustomizedCodeBytes());
-+        if (temp != null && COMPILED_TEMPLATES != null) {
-+            MemberName vmentry2 = COMPILED_TEMPLATES.putIfAbsent(temp, vmentry);
-+            if (vmentry2 != null)  vmentry = vmentry2;  // another racer won
-+        }
-+        return vmentry;
-+    }
-+
-+    private boolean shouldBeFactored(Name name, int j) {
-+        return lambdaFormTemplate != null && lambdaFormTemplate.shouldBeFactored(name, j);
-     }
- 
-     /**
-@@ -530,7 +610,9 @@
-             Name name = lambdaForm.names[i];
-             MemberName member = name.function.member();
- 
--            if (isSelectAlternative(member)) {
-+            if (shouldBeFactored(name, -1)) {
-+                emitInvoke(name, true);
-+            } else if (isSelectAlternative(member) && lambdaFormTemplate != null && name.arguments[0] instanceof Name) {
-                 // selectAlternative idiom
-                 // FIXME: make sure this idiom is really present!
-                 emitSelectAlternative(name, lambdaForm.names[i + 1]);
-@@ -538,7 +620,7 @@
-             } else if (isStaticallyInvocable(member)) {
-                 emitStaticInvoke(member, name);
-             } else {
--                emitInvoke(name);
-+                emitInvoke(name, false);
-             }
- 
-             // store the result from evaluating to the target name in a local if required
-@@ -560,28 +642,65 @@
- 
-         final byte[] classFile = cw.toByteArray();
-         maybeDump(className, classFile);
-+        maybeDumpTemplate(className);
-         return classFile;
-     }
- 
-+    void emitLoadThisLambdaForm() {
-+        boolean cache = true;
-+        int index = localThisLF();
-+        if (localsModel[index] == lambdaForm) {
-+            emitAloadInsn(index);
-+        } else {
-+            emitAloadInsn(0);
-+            if (invokerType.parameterType(0) != MethodHandle.class)
-+                mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
-+            mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
-+            if (cache) {
-+                mv.visitInsn(Opcodes.DUP);
-+                emitAstoreInsn(index);
-+                localsModel[index] = lambdaForm;
-+            }
-+        }
-+    }
-+
-+    void emitLoadThisArgList(Name name) {
-+        boolean cache = true;
-+        int index = localThisArgList();
-+        Object[] arglist = name.arguments;
-+        if (localsModel[index] == arglist) {
-+            emitAloadInsn(index);
-+        } else {
-+            emitLoadThisLambdaForm();
-+            mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", "[" + LFN_SIG);
-+            emitIconstInsn(name.index());
-+            mv.visitInsn(Opcodes.AALOAD);
-+            mv.visitFieldInsn(Opcodes.GETFIELD, LFN, "arguments", "[" + OBJ_SIG);
-+            if (cache) {
-+                mv.visitInsn(Opcodes.DUP);
-+                emitAstoreInsn(index);
-+                localsModel[index] = arglist;
-+            }
-+        }
-+    }
-+
-     /**
-      * Emit an invoke for the given name.
-      *
-      * @param name
-      */
--    void emitInvoke(Name name) {
--        if (true) {
-+    void emitInvoke(Name name, boolean shouldBeFactored) {
-+        if (!shouldBeFactored) {
-             // push receiver
-             MethodHandle target = name.function.resolvedHandle;
-             assert(target != null) : name.exprString();
-             mv.visitLdcInsn(constantPlaceholder(target));
-             mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
-         } else {
--            // load receiver
--            emitAloadInsn(0);
--            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
--            mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
--            mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
--            // TODO more to come
-+            // load receiver from the MH.form
-+            emitLoadThisLambdaForm();
-+            emitIconstInsn(name.index());
-+            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "getResolvedHandleAt", "(I)L" + MH + ";");
-         }
- 
-         // push arguments
-@@ -708,8 +827,6 @@
-      * @param invokeBasicName
-      */
-     private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
--        MethodType type = selectAlternativeName.function.methodType();
--
-         Name receiver = (Name) invokeBasicName.arguments[0];
- 
-         Label L_fallback = new Label();
-@@ -723,10 +840,9 @@
-         mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
- 
-         // invoke selectAlternativeName.arguments[1]
--        MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
-         emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
-         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
--        emitInvoke(invokeBasicName);
-+        emitInvoke(invokeBasicName, shouldBeFactored(invokeBasicName, -1));
- 
-         // goto L_done
-         mv.visitJumpInsn(Opcodes.GOTO, L_done);
-@@ -735,10 +851,9 @@
-         mv.visitLabel(L_fallback);
- 
-         // invoke selectAlternativeName.arguments[2]
--        MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
-         emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
-         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
--        emitInvoke(invokeBasicName);
-+        emitInvoke(invokeBasicName, shouldBeFactored(invokeBasicName, -1));
- 
-         // L_done:
-         mv.visitLabel(L_done);
-@@ -753,10 +868,21 @@
-         Object arg = name.arguments[paramIndex];
-         char ptype = name.function.parameterType(paramIndex);
-         MethodType mtype = name.function.methodType();
-+        if (shouldBeFactored(name, -1))
-+            mtype = mtype.basicType();
-+        Class<?> parameterType = mtype.parameterType(paramIndex);
-         if (arg instanceof Name) {
-             Name n = (Name) arg;
-             emitLoadInsn(n.type, n.index());
--            emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
-+            emitImplicitConversion(n.type, parameterType);
-+        } else if (shouldBeFactored(name, paramIndex)) {
-+            emitLoadThisArgList(name);
-+            emitIconstInsn(paramIndex);
-+            mv.visitInsn(Opcodes.AALOAD);
-+            if (parameterType.isPrimitive())
-+                emitUnboxing(Wrapper.forBasicType(ptype));
-+            else
-+                emitImplicitConversion('L', parameterType);
-         } else if ((arg == null || arg instanceof String) && ptype == 'L') {
-             emitConst(arg);
-         } else {
-@@ -764,7 +890,7 @@
-                 emitConst(arg);
-             } else {
-                 mv.visitLdcInsn(constantPlaceholder(arg));
--                emitImplicitConversion('L', mtype.parameterType(paramIndex));
-+                emitImplicitConversion('L', parameterType);
-             }
-         }
-     }
-@@ -774,52 +900,66 @@
-      */
-     private void emitReturn() {
-         // return statement
--        if (lambdaForm.result == -1) {
-+        Class<?> reqType = invokerType.returnType();
-+        char reqbt = Wrapper.basicTypeChar(reqType);
-+        if (reqType == void.class) {
-             // void
--            mv.visitInsn(Opcodes.RETURN);
-+        } else if (lambdaForm.result == -1) {
-+            emitConst(Wrapper.forBasicType(reqbt).zero());
-         } else {
-             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
--            char rtype = Wrapper.basicTypeChar(invokerType.returnType());
- 
-             // put return value on the stack if it is not already there
-             if (lambdaForm.result != lambdaForm.names.length - 1) {
-                 emitLoadInsn(rn.type, lambdaForm.result);
-             }
- 
-+            char tosbt = rn.type();
-+            Class<?> tosType = LambdaForm.typeClass(tosbt);
-+            if (!rn.isParam())
-+                tosType = rn.function.methodType().returnType();
-+
-             // potentially generate cast
-             // rtype is the return type of the invoker - generated code must conform to this
-             // rn.type is the type of the result Name in the LF
--            if (rtype != rn.type) {
-+            if (tosbt != reqbt) {
-                 // need cast
--                if (rtype == 'L') {
-+                Wrapper w;
-+                if (reqbt == 'L') {
-                     // possibly cast the primitive to the correct type for boxing
--                    char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar();
--                    if (boxedType != rn.type) {
--                        emitPrimCast(rn.type, boxedType);
--                    }
-+                    if (reqType == Object.class || !Wrapper.isWrapperType(reqType))
-+                        w = Wrapper.forPrimitiveType(tosType);
-+                    else
-+                        w = Wrapper.forWrapperType(reqType);
-+                    if (tosType != w.primitiveType())
-+                        emitPrimCast(Wrapper.forBasicType(tosbt), w);
-                     // cast primitive to reference ("boxing")
--                    emitBoxing(invokerType.returnType());
--                } else {
--                    // to-primitive cast
--                    if (rn.type != 'L') {
--                        // prim-to-prim cast
--                        emitPrimCast(rn.type, rtype);
--                    } else {
--                        // ref-to-prim cast ("unboxing")
--                        throw new InternalError("no ref-to-prim (unboxing) casts supported right now");
--                    }
-+                    emitBoxing(w);
-+                    tosType = w.wrapperType();
-+                } else if (tosbt == 'L') {
-+                    w = Wrapper.forBasicType(reqbt);
-+                    // ref-to-prim cast ("unboxing")
-+                    emitUnboxing(w);
-+                    tosType = w.primitiveType();
-+                } else if (tosType != reqType) {
-+                    // prim-to-prim cast
-+                    emitPrimCast(Wrapper.forBasicType(tosbt), Wrapper.forBasicType(reqbt));
-+                    tosType = reqType;
-                 }
-             }
--
--            // generate actual return statement
--            emitReturnInsn(invokerType.returnType());
-+            if (!VerifyType.isNullConversion(tosType, reqType)) {
-+                assert(reqbt == 'L');
-+                emitRefCast(reqType);
-+            }
-         }
-+        // generate actual return statement
-+        emitReturnInsn(reqType);
-     }
- 
-     /**
-      * Emit a type conversion bytecode casting from "from" to "to".
-      */
--    private void emitPrimCast(char from, char to) {
-+    private void emitPrimCast(Wrapper wfrom, Wrapper wto) {
-         // Here's how.
-         // -   indicates forbidden
-         // <-> indicates implicit
-@@ -832,12 +972,11 @@
-         //      long        -     l2i,i2b   l2i,i2s  l2i,i2c    l2i      <->      l2f      l2d
-         //      float       -     f2i,i2b   f2i,i2s  f2i,i2c    f2i      f2l      <->      f2d
-         //      double      -     d2i,i2b   d2i,i2s  d2i,i2c    d2i      d2l      d2f      <->
--        if (from == to) {
-+        if (wfrom == wto) {
-             // no cast required, should be dead code anyway
-             return;
-         }
--        Wrapper wfrom = Wrapper.forBasicType(from);
--        Wrapper wto   = Wrapper.forBasicType(to);
-+        char from = wfrom.basicTypeChar(), to = wto.basicTypeChar();
-         if (wfrom.isSubwordOrInt()) {
-             // cast from {byte,short,char,int} to anything
-             emitI2X(to);
-@@ -955,20 +1094,20 @@
-             emitLoadInsn(Wrapper.basicTypeChar(ptype), i);
-             // box if primitive type
-             if (ptype.isPrimitive()) {
--                emitBoxing(ptype);
-+                emitBoxing(Wrapper.forPrimitiveType(ptype));
-             }
-             mv.visitInsn(Opcodes.AASTORE);
-         }
-         // invoke
-         emitAloadInsn(0);
--        mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;");
-+        mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
-         mv.visitInsn(Opcodes.SWAP);  // swap form and array; avoid local variable
-         mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;");
- 
-         // maybe unbox
-         Class<?> rtype = invokerType.returnType();
-         if (rtype.isPrimitive() && rtype != void.class) {
--            emitUnboxing(Wrapper.asWrapperType(rtype));
-+            emitUnboxing(Wrapper.forPrimitiveType(rtype));
-         }
- 
-         // return statement
-@@ -1020,8 +1159,8 @@
-                 Class<?> sptype = dstType.basicType().wrap().parameterType(i);
-                 Wrapper dstWrapper = Wrapper.forBasicType(dptype);
-                 Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper;  // narrow subword from int
--                emitUnboxing(srcWrapper.wrapperType());
--                emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
-+                emitUnboxing(srcWrapper);
-+                emitPrimCast(srcWrapper, dstWrapper);
-             }
-         }
- 
-@@ -1035,8 +1174,8 @@
-             Wrapper srcWrapper = Wrapper.forBasicType(rtype);
-             Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper;  // widen subword to int
-             // boolean casts not allowed
--            emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar());
--            emitBoxing(dstWrapper.primitiveType());
-+            emitPrimCast(srcWrapper, dstWrapper);
-+            emitBoxing(dstWrapper);
-         }
- 
-         // If the return type is void we return a null reference.
-diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java
---- a/src/share/classes/java/lang/invoke/Invokers.java
-+++ b/src/share/classes/java/lang/invoke/Invokers.java
-@@ -336,7 +336,7 @@
-         names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
-         lform = new LambdaForm(debugName, INARG_LIMIT, names);
-         if (isLinker)
--            lform.compileToBytecode();  // JVM needs a real methodOop
-+            lform.compileToBytecode(true);  // JVM needs a real methodOop
-         if (isCached)
-             lform = mtype.form().setCachedLambdaForm(which, lform);
-         return lform;
-@@ -424,7 +424,7 @@
-         outArgs[PREPEND_MH] = names[CALL_MH];
-         names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs);
-         lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names);
--        lform.compileToBytecode();  // JVM needs a real methodOop
-+        lform.compileToBytecode(true);  // JVM needs a real methodOop
-         lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform);
-         return lform;
-     }
-diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
---- a/src/share/classes/java/lang/invoke/LambdaForm.java
-+++ b/src/share/classes/java/lang/invoke/LambdaForm.java
-@@ -27,18 +27,13 @@
- 
- import java.lang.annotation.*;
- import java.lang.reflect.Method;
--import java.util.Map;
- import java.util.List;
- import java.util.Arrays;
--import java.util.ArrayList;
--import java.util.HashMap;
--import java.util.concurrent.ConcurrentHashMap;
- import sun.invoke.util.Wrapper;
- import sun.invoke.Stable;
- import static java.lang.invoke.MethodHandleStatics.*;
- import static java.lang.invoke.MethodHandleNatives.Constants.*;
- import java.lang.reflect.Field;
--import java.util.Objects;
- 
- /**
-  * The symbolic, non-executable form of a method handle's invocation semantics.
-@@ -124,7 +119,7 @@
-     @Stable final Name[] names;
-     final String debugName;
-     MemberName vmentry;   // low-level behavior, or null if not yet prepared
--    private boolean isCompiled;
-+    private byte compileLevel;  // 0: interp, 1: normal, 2: full-custom
- 
-     // Caches for common structural transforms:
-     LambdaForm[] bindCache;
-@@ -191,12 +186,12 @@
-     }
- 
-     private static int fixResult(int result, Name[] names) {
--        if (result >= 0) {
--            if (names[result].type == 'V')
--                return -1;
--        } else if (result == LAST_RESULT) {
--            return names.length - 1;
--        }
-+        // Normalize the result field to either one of non-void names or -1.
-+        if (result == LAST_RESULT)
-+          result = names.length - 1;
-+        assert(result >= VOID_RESULT);
-+        if (result != VOID_RESULT && names[result].type == 'V')
-+            result = VOID_RESULT;
-         return result;
-     }
- 
-@@ -312,9 +307,16 @@
-         return arity;
-     }
- 
-+    /** Report the lead argument type.
-+     *  This is usually MethodHandle but could also be an interface wrapper type.
-+     */
-+    Class<?> receiver() {
-+        return MethodHandle.class;
-+    }
-+
-     /** Return the method type corresponding to my basic type signature. */
-     MethodType methodType() {
--        return signatureType(basicTypeSignature());
-+        return signatureType(basicTypeSignature()).changeParameterType(0, receiver());
-     }
-     /** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
-     final String basicTypeSignature() {
-@@ -433,7 +435,7 @@
-      */
-     public void prepare() {
-         if (COMPILE_THRESHOLD == 0) {
--            compileToBytecode();
-+            compileToBytecode(false);
-         }
-         if (this.vmentry != null) {
-             // already prepared (e.g., a primitive DMH invoker form)
-@@ -445,143 +447,35 @@
-     }
- 
-     /** Generate optimizable bytecode for this form. */
--    MemberName compileToBytecode() {
--        MethodType invokerType = methodType();
--        assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
--        if (vmentry != null && isCompiled) {
-+    MemberName compileToBytecode(boolean fullCustom) {
-+        // FIXME: compileLevel should be an immutable property of template and/or vmentry
-+        byte requiredLevel = (byte)(fullCustom ? 2 : 1);
-+        assert(vmentry == null || vmentry.getMethodType().basicType().equals(methodType().basicType()));
-+        if (vmentry != null && compileLevel >= requiredLevel) {
-             return vmentry;  // already compiled somehow
-         }
-         try {
--            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
-+            Template temp = fullCustom ? null : new Template(this);
-+            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(temp, this);
-             if (TRACE_INTERPRETER)
--                traceInterpreter("compileToBytecode", this);
--            isCompiled = true;
-+                traceInterpreter("compileToBytecode" + (fullCustom ? "/fullCustom" : ""), this);
-+            compileLevel = requiredLevel;
-             return vmentry;
-         } catch (Error | Exception ex) {
-             throw new InternalError(this.toString(), ex);
-         }
-     }
- 
--    private static final ConcurrentHashMap<String,LambdaForm> PREPARED_FORMS;
--    static {
--        int   capacity   = 512;    // expect many distinct signatures over time
--        float loadFactor = 0.75f;  // normal default
--        int   writers    = 1;
--        PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers);
--    }
--
--    private static Map<String,LambdaForm> computeInitialPreparedForms() {
--        // Find all predefined invokers and associate them with canonical empty lambda forms.
--        HashMap<String,LambdaForm> forms = new HashMap<>();
--        for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
--            if (!m.isStatic() || !m.isPackage())  continue;
--            MethodType mt = m.getMethodType();
--            if (mt.parameterCount() > 0 &&
--                mt.parameterType(0) == MethodHandle.class &&
--                m.getName().startsWith("interpret_")) {
--                String sig = basicTypeSignature(mt);
--                assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_'))));
--                LambdaForm form = new LambdaForm(sig);
--                form.vmentry = m;
--                mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form);
--                // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only
--                forms.put(sig, form);
--            }
--        }
--        //System.out.println("computeInitialPreparedForms => "+forms);
--        return forms;
--    }
--
--    // Set this false to disable use of the interpret_L methods defined in this file.
--    private static final boolean USE_PREDEFINED_INTERPRET_METHODS = true;
--
--    // The following are predefined exact invokers.  The system must build
--    // a separate invoker for each distinct signature.
--    static Object interpret_L(MethodHandle mh) throws Throwable {
--        Object[] av = {mh};
--        String sig = null;
--        assert(argumentTypesMatch(sig = "L_L", av));
--        Object res = mh.form.interpretWithArguments(av);
--        assert(returnTypesMatch(sig, av, res));
--        return res;
--    }
--    static Object interpret_L(MethodHandle mh, Object x1) throws Throwable {
--        Object[] av = {mh, x1};
--        String sig = null;
--        assert(argumentTypesMatch(sig = "LL_L", av));
--        Object res = mh.form.interpretWithArguments(av);
--        assert(returnTypesMatch(sig, av, res));
--        return res;
--    }
--    static Object interpret_L(MethodHandle mh, Object x1, Object x2) throws Throwable {
--        Object[] av = {mh, x1, x2};
--        String sig = null;
--        assert(argumentTypesMatch(sig = "LLL_L", av));
--        Object res = mh.form.interpretWithArguments(av);
--        assert(returnTypesMatch(sig, av, res));
--        return res;
--    }
-     private static LambdaForm getPreparedForm(String sig) {
-         MethodType mtype = signatureType(sig);
--        //LambdaForm prep = PREPARED_FORMS.get(sig);
-         LambdaForm prep =  mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET);
-         if (prep != null)  return prep;
-         assert(isValidSignature(sig));
-         prep = new LambdaForm(sig);
-         prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig);
--        //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep);
-         return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep);
-     }
- 
--    // The next few routines are called only from assert expressions
--    // They verify that the built-in invokers process the correct raw data types.
--    private static boolean argumentTypesMatch(String sig, Object[] av) {
--        int arity = signatureArity(sig);
--        assert(av.length == arity) : "av.length == arity: av.length=" + av.length + ", arity=" + arity;
--        assert(av[0] instanceof MethodHandle) : "av[0] not instace of MethodHandle: " + av[0];
--        MethodHandle mh = (MethodHandle) av[0];
--        MethodType mt = mh.type();
--        assert(mt.parameterCount() == arity-1);
--        for (int i = 0; i < av.length; i++) {
--            Class<?> pt = (i == 0 ? MethodHandle.class : mt.parameterType(i-1));
--            assert(valueMatches(sig.charAt(i), pt, av[i]));
--        }
--        return true;
--    }
--    private static boolean valueMatches(char tc, Class<?> type, Object x) {
--        // The following line is needed because (...)void method handles can use non-void invokers
--        if (type == void.class)  tc = 'V';   // can drop any kind of value
--        assert tc == basicType(type) : tc + " == basicType(" + type + ")=" + basicType(type);
--        switch (tc) {
--        case 'I': assert checkInt(type, x)   : "checkInt(" + type + "," + x +")";   break;
--        case 'J': assert x instanceof Long   : "instanceof Long: " + x;             break;
--        case 'F': assert x instanceof Float  : "instanceof Float: " + x;            break;
--        case 'D': assert x instanceof Double : "instanceof Double: " + x;           break;
--        case 'L': assert checkRef(type, x)   : "checkRef(" + type + "," + x + ")";  break;
--        case 'V': break;  // allow anything here; will be dropped
--        default:  assert(false);
--        }
--        return true;
--    }
--    private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
--        MethodHandle mh = (MethodHandle) av[0];
--        return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
--    }
--    private static boolean checkInt(Class<?> type, Object x) {
--        assert(x instanceof Integer);
--        if (type == int.class)  return true;
--        Wrapper w = Wrapper.forBasicType(type);
--        assert(w.isSubwordOrInt());
--        Object x1 = Wrapper.INT.wrap(w.wrap(x));
--        return x.equals(x1);
--    }
--    private static boolean checkRef(Class<?> type, Object x) {
--        assert(!type.isPrimitive());
--        if (x == null)  return true;
--        if (type.isInterface())  return true;
--        return type.isInstance(x);
--    }
--
-     /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */
-     private static final int COMPILE_THRESHOLD;
-     static {
-@@ -627,22 +521,18 @@
-     private void checkInvocationCounter() {
-         if (COMPILE_THRESHOLD != 0 &&
-             invocationCounter < COMPILE_THRESHOLD) {
--            invocationCounter++;  // benign race
--            if (invocationCounter >= COMPILE_THRESHOLD) {
-+            int ctr = ++invocationCounter;  // benign race
-+            if (TRACE_INTERPRETER)
-+                traceInterpreter("| invocationCounter", ctr);
-+            if (ctr >= COMPILE_THRESHOLD) {
-                 // Replace vmentry with a bytecode version of this LF.
--                compileToBytecode();
-+                compileToBytecode(false);
-             }
-         }
-     }
-     Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
-         traceInterpreter("[ interpretWithArguments", this, argumentValues);
--        if (invocationCounter < COMPILE_THRESHOLD) {
--            int ctr = invocationCounter++;  // benign race
--            traceInterpreter("| invocationCounter", ctr);
--            if (invocationCounter >= COMPILE_THRESHOLD) {
--                compileToBytecode();
--            }
--        }
-+        checkInvocationCounter();
-         Object rval;
-         try {
-             assert(arityCheck(argumentValues));
-@@ -707,7 +597,14 @@
-     }
- 
-     public String toString() {
--        StringBuilder buf = new StringBuilder(debugName+"=Lambda(");
-+        return toString((Template)null);
-+    }
-+    String toString(Template temp) {
-+        StringBuilder buf = new StringBuilder();
-+        if (temp != null)
-+            buf.append("Template(");
-+        else
-+            buf.append(debugName).append("=Lambda(");
-         for (int i = 0; i < names.length; i++) {
-             if (i == arity)  buf.append(")=>{");
-             Name n = names[i];
-@@ -717,7 +614,7 @@
-                 if (i+1 < arity)  buf.append(",");
-                 continue;
-             }
--            buf.append("=").append(n.exprString());
-+            buf.append("=").append(n.exprString(temp));
-             buf.append(";");
-         }
-         buf.append(result < 0 ? "void" : names[result]).append("}");
-@@ -968,6 +865,193 @@
-         return true;
-     }
- 
-+    /** Use this for making equivalence classes of LFs. */
-+    static class Template {
-+        final LambdaForm form;
-+        Template(LambdaForm form) {
-+            this.form = form;
-+        }
-+        @Override public int hashCode() {
-+            final int M1 = 31;
-+            int hc = form.arity * M1 + form.result;
-+            for (Name name : form.names) {
-+                hc *= M1;
-+                hc += nameHashCode(name);
-+            }
-+            return hc;
-+        }
-+        boolean sameFormAfterFactoring(LambdaForm form1, LambdaForm form2) {
-+            if (form1.arity  != form2.arity)   return false;
-+            if (form1.result != form2.result)  return false;
-+            Name[] names1 = form1.names;
-+            Name[] names2 = form2.names;
-+            int length = names1.length;
-+            if (length != names2.length)  return false;
-+            for (int i = 0; i < length; i++) {
-+                if (!sameNameAfterFactoring(names1[i], names2[i]))  return false;
-+                assert(nameHashCode(names1[i]) == nameHashCode(names2[i]));
-+            }
-+            return true;
-+        }
-+
-+        int nameHashCode(Name name) {
-+            final int M2 = 127;
-+            int nhc = name.type;
-+            if (name.isParam())
-+                return nhc;
-+            nhc *= M2;
-+            if (shouldBeFactored(name, -1))
-+                nhc += name.function.basicTypeSignature().hashCode();
-+            else
-+                nhc += name.function.hashCode();
-+            int j = -1;
-+            for (Object a : name.arguments) {
-+                ++j;  // index of name
-+                int ahc;
-+                if (a instanceof Name)
-+                    ahc = ((Name)a).indexAndType();
-+                else if (shouldBeFactored(name, j))
-+                    ahc = 0;  // this value will be factored; suppress
-+                else
-+                    ahc = a.hashCode();
-+                nhc = (nhc * M2) + ahc;
-+            }
-+            return nhc;
-+        }
-+
-+        boolean sameNameAfterFactoring(Name n1, Name n2) {
-+            if (n1 == n2)  return true;
-+            if (n1.indexAndType() != n2.indexAndType())  return false;
-+            if (n1.isParam())  return n2.isParam();
-+            if (n2.isParam())  return false;
-+            int argc = n1.arguments.length;
-+            if (argc != n2.arguments.length)  return false;
-+            String sig = n1.function.basicTypeSignature();
-+            if (!sig.equals(n2.function.basicTypeSignature()))  return false;
-+            if (!shouldBeFactored(n1, -1)) {
-+                // check identity of functions
-+                if (!n1.function.equals(n2.function))  return false;
-+            }
-+            assert(shouldBeFactored(n1, -1) == shouldBeFactored(n2, -1));
-+            for (int j = 0; j < argc; j++) {
-+                Object a1 = n1.arguments[j];
-+                Object a2 = n2.arguments[j];
-+                if (a1 == a2)  continue;
-+                if (a1 instanceof Name) {
-+                    if (a2 instanceof Name && ((Name)a1).index == ((Name)a2).index)
-+                        continue;
-+                    return false;
-+                }
-+                if (a2 instanceof Name)  return false;
-+                if (!shouldBeFactored(n1, j)) {
-+                    // recheck identity of arguments
-+                    if (sig.charAt(j) != 'L') {
-+                        if (a1.equals(a2))  a1 = a2;  // rebox
-+                    }
-+                    if (a1 != a2)  return false;
-+                }
-+                assert(shouldBeFactored(n1, j) == shouldBeFactored(n2, j));
-+            }
-+            return true;
-+        }
-+
-+        @Override public boolean equals(Object x) {
-+            return x instanceof Template && equals((Template)x);
-+        }
-+        public boolean equals(Template that) {
-+            if (this == that || this.form == that.form)  return true;
-+            boolean z = sameFormAfterFactoring(this.form, that.form);
-+            assert(!(z && this.hashCode() != that.hashCode()));
-+            return z;
-+        }
-+
-+        boolean shouldBeFactored(Name name, int argIndex) {
-+            int BSL = (BYTECODE_SHARE_LEVEL != null ? (int)BYTECODE_SHARE_LEVEL : 255);
-+            if (BSL == 0) {
-+                return false;
-+            }
-+            if (argIndex < 0) {
-+                // Factor the head of the function application?
-+                if ((BSL & 1) == 0)  return false;
-+                if (name.function.isFactored())  return true;  // already decided
-+                MethodType mt = name.function.methodType();
-+                if (mt.parameterSlotCount() >= 255)  return false;  // edge case: no invokeBasic
-+                MemberName m = name.function.member;
-+                if (m == null)  return true;
-+                // Factor if the declaring class is not on the BCP.
-+                // See also InvokerBytecodeGenerator.isStaticallyInvocable.
-+                return m.getDeclaringClass().getClassLoader() != null;
-+            }
-+            Object arg = name.arguments[argIndex];
-+            if (arg == null)
-+                return false;
-+            if (arg instanceof Name)
-+                return false;
-+            if ((BSL & 2) == 0) {
-+                Class<?> ptype = name.function.methodType().parameterType(argIndex);
-+                if (ptype.isPrimitive() || ptype == String.class) {
-+                    return false;
-+                }
-+            }
-+            return true;
-+        }
-+
-+        /** Generate a fully factored lambda form. */
-+        Template copyFactored() {
-+            Name[] names = form.names.clone();
-+            boolean changed = false;
-+            int startFixing = -1;
-+            for (int i = form.arity; i < names.length; i++) {
-+                Name n0 = names[i];
-+                Name n1 = copyFactored(n0);
-+                if (changed)
-+                    // also edit occurrences of previously changed names
-+                    n1 = n1.replaceNames(form.names, names, startFixing, i);
-+                if (n1 == n0)  continue;
-+                names[i] = n1;
-+                if (!changed)  startFixing = i;
-+                changed = true;
-+            }
-+            if (!changed)  return this;
-+            LambdaForm lfact = new LambdaForm(form.debugName, form.arity, names, form.result);
-+            Template tfact = new Template(lfact);
-+            assert(tfact.equals(this)) : Arrays.asList(tfact, this, form);
-+            return tfact;
-+        }
-+        private Name copyFactored(Name name) {
-+            boolean changed = false;
-+            NamedFunction f = name.function;
-+            if (shouldBeFactored(name, -1)) {
-+                f = f.copyFactored();
-+                changed = true;
-+            }
-+            Object[] args = name.arguments.clone();
-+            String sig = name.function.basicTypeSignature();
-+            for (int j = 0; j < args.length; j++) {
-+                if (!shouldBeFactored(name, j))  continue;
-+                args[j] = copyFactoredArgument(sig.charAt(j), args[j]);
-+                changed = true;
-+            }
-+            if (!changed)  return name;
-+            return new Name(f, args);
-+        }
-+        private Object copyFactoredArgument(char type, Object arg) {
-+            if (type == 'L')  return "**";
-+            return Wrapper.forBasicType(type).zero();
-+        }
-+
-+        @Override public String toString() {
-+            return form.toString(this);
-+        }
-+
-+    }
-+
-+    /** Get the function for the Nth name.  This is used by the bytecode compiler and can be constant folded. */
-+    MethodHandle getResolvedHandleAt(int index) {
-+        return names[index].function.resolvedHandle();
-+    }
-+
-+    /** The head of a function application in a Name. */
-     static class NamedFunction {
-         final MemberName member;
-         @Stable MethodHandle resolvedHandle;
-@@ -1002,24 +1086,39 @@
-             return resolvedHandle;
-         }
- 
-+        NamedFunction copyFactored() {
-+            MemberName fake = new MemberName(Void.class, "***", methodType().basicType(), REF_invokeStatic);
-+            return new NamedFunction(fake);
-+        }
-+        boolean isFactored() {
-+            return member != null && member.getDeclaringClass() == Void.class && member.getName().equals("***");
-+        }
-+
-         void resolve() {
-             resolvedHandle = DirectMethodHandle.make(member);
-         }
- 
-         @Override
--        public boolean equals(Object other) {
--            if (this == other) return true;
--            if (other == null) return false;
--            if (!(other instanceof NamedFunction)) return false;
--            NamedFunction that = (NamedFunction) other;
--            return this.member != null && this.member.equals(that.member);
-+        public boolean equals(Object x) {
-+            return x instanceof NamedFunction && equals((NamedFunction)x);
-+        }
-+        public boolean equals(NamedFunction that) {
-+            if (this == that) return true;
-+            if (that == null) return false;
-+            if (this.member != null)
-+                return this.member.equals(that.member);
-+            assert(this.resolvedHandle != null);
-+            return (that.member == null &&
-+                    this.resolvedHandle != null &&
-+                    this.resolvedHandle == that.resolvedHandle);
-         }
- 
-         @Override
-         public int hashCode() {
-             if (member != null)
-                 return member.hashCode();
--            return super.hashCode();
-+            else
-+                return resolvedHandle.hashCode();
-         }
- 
-         // Put the predefined NamedFunction invokers into the table.
-@@ -1251,15 +1350,9 @@
-         return btypes;
-     }
-     public static String basicTypeSignature(MethodType type) {
--        char[] sig = new char[type.parameterCount() + 2];
--        int sigp = 0;
--        for (Class<?> pt : type.parameterList()) {
--            sig[sigp++] = basicType(pt);
--        }
--        sig[sigp++] = '_';
--        sig[sigp++] = basicType(type.returnType());
--        assert(sigp == sig.length);
--        return String.valueOf(sig);
-+        String sig = type.basicType().form().typeString();
-+        assert(sig.matches("[LIJFD]*_[LIJFDV]"));
-+        return sig;
-     }
- 
-     static final class Name {
-@@ -1392,13 +1485,26 @@
-             return (function == null) ? s : s + "=" + exprString();
-         }
-         public String exprString() {
-+            return exprString((Template)null);
-+        }
-+        public String exprString(Template temp) {
-             if (function == null)  return "null";
--            StringBuilder buf = new StringBuilder(function.toString());
-+            StringBuilder buf = new StringBuilder();
-+            if (temp != null && temp.shouldBeFactored(this, -1))
-+                buf.append("***").append(function.basicTypeSignature());
-+            else
-+                buf.append(function.toString());
-             buf.append("(");
-             String cma = "";
-+            int j = -1;
-             for (Object a : arguments) {
-                 buf.append(cma); cma = ",";
--                if (a instanceof Name || a instanceof Integer)
-+                ++j;
-+                if (a instanceof Name)
-+                    buf.append(a);
-+                else if (temp != null && temp.shouldBeFactored(this, j))
-+                    buf.append("***");
-+                else if (a instanceof Integer)
-                     buf.append(a);
-                 else
-                     buf.append("(").append(a).append(")");
-@@ -1454,7 +1560,7 @@
-         public boolean equals(Name that) {
-             if (this == that)  return true;
-             if (isParam())
--                // each parameter is a unique atom
-+            // each parameter is a unique atom
-                 return false;  // this != that
-             return
-                 //this.index == that.index &&
-@@ -1466,10 +1572,13 @@
-         public boolean equals(Object x) {
-             return x instanceof Name && equals((Name)x);
-         }
-+        int indexAndType() {
-+            return (index + (type << 8));
-+        }
-         @Override
-         public int hashCode() {
-             if (isParam())
--                return index | (type << 8);
-+                return indexAndType();
-             return function.hashCode() ^ Arrays.hashCode(arguments);
-         }
-     }
-@@ -1565,12 +1674,6 @@
-     private static double zeroD() { return 0; }
-     private static Object zeroL() { return null; }
- 
--    // Put this last, so that previous static inits can run before.
--    static {
--        if (USE_PREDEFINED_INTERPRET_METHODS)
--            PREPARED_FORMS.putAll(computeInitialPreparedForms());
--    }
--
-     /**
-      * Internal marker for byte-compiled LambdaForms.
-      */
-diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
---- a/src/share/classes/java/lang/invoke/MemberName.java
-+++ b/src/share/classes/java/lang/invoke/MemberName.java
-@@ -151,7 +151,10 @@
-         MethodType itype = getMethodOrFieldType();
-         if (isConstructor() && getReferenceKind() == REF_newInvokeSpecial)
-             return itype.changeReturnType(clazz);
--        if (!isStatic())
-+        //if (!isStatic())
-+        assert(!isStatic() == MethodHandleNatives.refKindHasReceiver(getReferenceKind())
-+                || !isResolved());
-+        if (MethodHandleNatives.refKindHasReceiver(getReferenceKind()))
-             return itype.insertParameterTypes(0, clazz);
-         return itype;
-     }
-diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
---- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java
-+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
-@@ -424,7 +424,9 @@
-             Class<?> src = lambdaType.parameterType(i);
-             if (i == spreadArgPos) {
-                 // Spread the array.
--                MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
-+                Class<?> basicArrayType = spreadArgType;
-+                if (Object[].class.isAssignableFrom(spreadArgType))  basicArrayType = Object[].class;
-+                MethodHandle aload = ArrayAccessor.getAccessor(basicArrayType, false);
-                 Name array = names[argIndex];
-                 names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
-                 for (int j = 0; j < spreadArgCount; i++, j++) {
-diff --git a/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
---- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java
-+++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java
-@@ -46,8 +46,9 @@
-     static final boolean TRACE_INTERPRETER;
-     static final boolean TRACE_METHOD_LINKAGE;
-     static final Integer COMPILE_THRESHOLD;
-+    static final Integer BYTECODE_SHARE_LEVEL;
-     static {
--        final Object[] values = { false, false, false, false, null };
-+        final Object[] values = { false, false, false, false, null, null };
-         AccessController.doPrivileged(new PrivilegedAction<Void>() {
-                 public Void run() {
-                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
-@@ -55,6 +56,7 @@
-                     values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
-                     values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
-                     values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD");
-+                    values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.BYTECODE_SHARE_LEVEL");
-                     return null;
-                 }
-             });
-@@ -63,6 +65,7 @@
-         TRACE_INTERPRETER         = (Boolean) values[2];
-         TRACE_METHOD_LINKAGE      = (Boolean) values[3];
-         COMPILE_THRESHOLD         = (Integer) values[4];
-+        BYTECODE_SHARE_LEVEL      = (Integer) values[5];
-     }
- 
-     /*non-public*/ static String getNameString(MethodHandle target, MethodType type) {
-diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java
---- a/src/share/classes/java/lang/invoke/MethodTypeForm.java
-+++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java
-@@ -85,6 +85,23 @@
-         return basicType;
-     }
- 
-+    public String typeString() {
-+        if (typeString == null) {
-+            MethodType type = erasedType;
-+            char[] sig = new char[type.parameterCount() + 2];
-+            int sigp = 0;
-+            for (Class<?> pt : type.parameterList()) {
-+                sig[sigp++] = Wrapper.basicTypeChar(pt);
-+            }
-+            sig[sigp++] = '_';
-+            Class<?> rt = type.returnType();
-+            sig[sigp++] = Wrapper.basicTypeChar(rt);
-+            assert(sigp == sig.length);
-+            return typeString = String.valueOf(sig);
-+        }
-+        return typeString;
-+    }
-+
-     public LambdaForm cachedLambdaForm(int which) {
-         return lambdaForms[which];
-     }
--- a/meth.patch	Wed Oct 17 21:02:38 2012 -0700
+++ b/meth.patch	Wed Oct 17 21:25:59 2012 -0700
@@ -1,6 +1,7 @@
 # Unsorted changes to method handles are entrained here.
-0000000: an attempt to use "<init>" as a method name should elicit NoSuchMethodException
-0000000: arity mismatch on a call to spreader method handle should elicit WrongMethodTypeException
+8001108: an attempt to use "<init>" as a method name should elicit NoSuchMethodException
+8001109: arity mismatch on a call to spreader method handle should elicit WrongMethodTypeException
+8001110: method handles should have a collectArguments transform, generalizing asCollector
 add comments where MR1 changes are likely
 update copyright headers
 
@@ -43,18 +44,6 @@
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
-diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java
---- a/src/share/classes/java/lang/invoke/LambdaForm.java
-+++ b/src/share/classes/java/lang/invoke/LambdaForm.java
-@@ -180,7 +180,7 @@
-         assert(isValidSignature(basicTypeSignature));
-         int resultPos = arity + 1;  // skip '_'
-         if (arity < 0 || basicTypeSignature.length() != resultPos+1)
--            throw new IllegalArgumentException("bad arity for "+basicTypeSignature);
-+            throw newIllegalArgumentException("bad arity", basicTypeSignature);
-         int numRes = (basicTypeSignature.charAt(resultPos) == 'V' ? 0 : 1);
-         Name[] names = arguments(numRes, basicTypeSignature.substring(0, arity));
-         for (int i = 0; i < numRes; i++) {
 diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
 --- a/src/share/classes/java/lang/invoke/MemberName.java
 +++ b/src/share/classes/java/lang/invoke/MemberName.java
@@ -65,7 +54,7 @@
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
-@@ -299,10 +299,6 @@
+@@ -302,10 +302,6 @@
          assert(getReferenceKind() == oldKind);
          assert(MethodHandleNatives.refKindIsValid(refKind));
          flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT);
@@ -76,7 +65,7 @@
          return this;
      }
  
-@@ -494,14 +486,14 @@
+@@ -493,14 +489,14 @@
          case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
          case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
          }
@@ -93,7 +82,7 @@
      }
      /** Create a name for the given reflected constructor.  The resulting name will be in a resolved state. */
      @SuppressWarnings("LeakingThisInConstructor")
-@@ -570,7 +562,7 @@
+@@ -569,7 +565,7 @@
       *  This may be in a super-class of the declaring class of this member.
       */
      public MemberName getDefinition() {
@@ -102,7 +91,7 @@
          if (isType())  return this;
          MemberName res = this.clone();
          res.clazz = null;
-@@ -614,15 +606,6 @@
+@@ -613,15 +609,6 @@
          init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind));
          initResolved(false);
      }
@@ -118,7 +107,7 @@
      /** Create a method or constructor name from the given components:  Declaring class, name, type, modifiers.
       *  It will be a constructor if and only if the name is {@code "&lt;init&gt;"}.
       *  The declaring class may be supplied as null if this is to be a bare name and type.
-@@ -635,15 +618,6 @@
+@@ -634,15 +621,6 @@
          init(defClass, name, type, flagsMods(flags, 0, refKind));
          initResolved(false);
      }
@@ -153,7 +142,7 @@
  
  /**
   * A method handle is a typed, directly executable reference to an underlying method,
-@@ -803,6 +801,7 @@
+@@ -804,6 +802,7 @@
       * @throws WrongMethodTypeException if the implied {@code asType} call fails
       * @see #asCollector
       */
@@ -180,7 +169,7 @@
              return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
          }
          static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false;  // FIXME: decide
-@@ -452,7 +452,7 @@
+@@ -460,7 +460,7 @@
      static void checkSpreadArgument(Object av, int n) {
          // FIXME: regression test for bug 7141637 erroneously expects an NPE, and other tests may expect IAE
          // but the actual exception raised by an arity mismatch should be WMTE
@@ -189,7 +178,7 @@
          if (av == null) {
              if (n == 0)  return;
              int len;
-@@ -468,7 +468,7 @@
+@@ -476,7 +476,7 @@
          // fall through to error:
          if (RAISE_RANDOM_EXCEPTIONS)
              throw newIllegalArgumentException("Array is not of length "+n);
@@ -337,7 +326,7 @@
          /**
           * Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>.
           * This function performs stack walk magic: do not refactor it.
-@@ -1337,7 +1348,7 @@
+@@ -1344,7 +1355,7 @@
      static public
      MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
          if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
@@ -346,7 +335,7 @@
          return type.invokers().spreadInvoker(leadingArgCount);
      }
  
-@@ -1876,7 +1887,7 @@
+@@ -1883,7 +1894,7 @@
          return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
      }
  
@@ -358,7 +347,7 @@
 diff --git a/src/share/classes/java/lang/invoke/MethodType.java b/src/share/classes/java/lang/invoke/MethodType.java
 --- a/src/share/classes/java/lang/invoke/MethodType.java
 +++ b/src/share/classes/java/lang/invoke/MethodType.java
-@@ -882,7 +882,7 @@
+@@ -883,7 +883,7 @@
          if (!descriptor.startsWith("(") ||  // also generates NPE if needed
              descriptor.indexOf(')') < 0 ||
              descriptor.indexOf('.') >= 0)
@@ -419,7 +408,7 @@
 diff --git a/test/java/lang/invoke/MethodHandlesTest.java b/test/java/lang/invoke/MethodHandlesTest.java
 --- a/test/java/lang/invoke/MethodHandlesTest.java
 +++ b/test/java/lang/invoke/MethodHandlesTest.java
-@@ -363,6 +363,7 @@
+@@ -387,6 +387,7 @@
          protected Example(String name) { this.name = name; }
          @SuppressWarnings("LeakingThisInConstructor")
          protected Example(int x) { this(); called("protected <init>", this, x); }
@@ -427,7 +416,7 @@
          @Override public String toString() { return name; }
  
          public void            v0()     { called("v0", this); }
-@@ -463,6 +464,9 @@
+@@ -487,6 +488,9 @@
          return lookup.in(defc);
      }
  
@@ -437,7 +426,7 @@
      @Test
      public void testFindStatic() throws Throwable {
          if (CAN_SKIP_WORKING)  return;
-@@ -483,6 +487,8 @@
+@@ -507,6 +511,8 @@
          testFindStatic(Example.class, Object.class, "s7", float.class, double.class);
  
          testFindStatic(false, PRIVATE, Example.class, void.class, "bogus");
@@ -446,7 +435,7 @@
          testFindStatic(false, PRIVATE, Example.class, void.class, "v0");
      }
  
-@@ -505,11 +511,12 @@
+@@ -529,11 +535,12 @@
              target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
          } catch (ReflectiveOperationException ex) {
              noAccess = ex;
@@ -463,7 +452,7 @@
          }
          if (verbosity >= 3)
              System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
-@@ -527,6 +534,13 @@
+@@ -551,6 +558,13 @@
              System.out.print(':');
      }
  
@@ -477,7 +466,7 @@
      static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
  
      // rough check of name string
-@@ -556,6 +570,8 @@
+@@ -580,6 +594,8 @@
          testFindVirtual(PubExample.class, void.class, "Pub/pro_v0");
  
          testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus");
@@ -486,7 +475,7 @@
          testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "s0");
  
          // test dispatch
-@@ -591,11 +607,12 @@
+@@ -625,11 +641,12 @@
              target = maybeMoveIn(lookup, defc).findVirtual(defc, methodName, type);
          } catch (ReflectiveOperationException ex) {
              noAccess = ex;
@@ -503,7 +492,7 @@
          }
          if (verbosity >= 3)
              System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
-@@ -632,11 +649,11 @@
+@@ -679,11 +696,11 @@
          testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
          testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
          // Do some negative testing:
@@ -517,7 +506,7 @@
              testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
          }
      }
-@@ -662,19 +679,25 @@
+@@ -709,19 +726,25 @@
          countTest(positive);
          String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
          MethodType type = MethodType.methodType(ret, params);
@@ -549,7 +538,7 @@
          }
          if (verbosity >= 3)
              System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
-@@ -719,7 +742,7 @@
+@@ -766,7 +789,7 @@
              target = lookup.findConstructor(defc, type);
          } catch (ReflectiveOperationException ex) {
              noAccess = ex;
@@ -558,7 +547,7 @@
          }
          if (verbosity >= 3)
              System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
-@@ -750,6 +773,8 @@
+@@ -797,6 +820,8 @@
          testBind(Example.class, Object.class, "v2", int.class, Object.class);
          testBind(Example.class, Object.class, "v2", int.class, int.class);
          testBind(false, PRIVATE, Example.class, void.class, "bogus");
@@ -567,7 +556,7 @@
          testBind(SubExample.class, void.class, "Sub/v0");
          testBind(SubExample.class, void.class, "Sub/pkg_v0");
          testBind(IntExample.Impl.class, void.class, "Int/v0");
-@@ -773,11 +798,12 @@
+@@ -820,11 +845,12 @@
              target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
          } catch (ReflectiveOperationException ex) {
              noAccess = ex;
@@ -584,7 +573,7 @@
          }
          if (verbosity >= 3)
              System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
-@@ -840,6 +866,10 @@
+@@ -887,6 +913,10 @@
          countTest(positive);
          String methodName = name.substring(1 + name.indexOf('/'));  // foo/bar => foo
          MethodType type = MethodType.methodType(ret, params);
@@ -595,7 +584,7 @@
          Method rmethod = defc.getDeclaredMethod(methodName, params);
          MethodHandle target = null;
          Exception noAccess = null;
-@@ -848,16 +878,15 @@
+@@ -895,16 +925,15 @@
          try {
              if (verbosity >= 4)  System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
              if (isSpecial)
@@ -616,7 +605,7 @@
          }
          if (verbosity >= 3)
              System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
-@@ -1091,11 +1120,12 @@
+@@ -1138,11 +1167,12 @@
          } catch (ReflectiveOperationException ex) {
              mh = null;
              noAccess = ex;
--- a/series	Wed Oct 17 21:02:38 2012 -0700
+++ b/series	Wed Oct 17 21:25:59 2012 -0700
@@ -6,9 +6,10 @@
 # review pending before push to hotspot-comp:
 
 # non-pushed files are under review or development, or merely experimental:
-anno-stable.patch               #-/meth #+1dde94130b0c
-meth-lfi.patch                  #-/meth #+1dde94130b0c
+anno-stable-8001107.patch       #-/meth #+1dde94130b0c
+meth-lfi-8001106.patch          #-/meth #+1dde94130b0c
 meth-info-7087570.patch         #-/meth #+1dde94130b0c
+meth-aclone-8001105.patch       #-/meth #+1dde94130b0c
 meth.patch                      #-/meth #+1dde94130b0c
 meth-7177472.patch              #-/meth #+1dde94130b0c #-buildable
 indy.pack.patch                 #-/meth #+1dde94130b0c #-buildable