8163370: Reduce number of classes loaded by common usage of java.lang.invoke
authorredestad
Wed, 10 Aug 2016 13:54:38 +0200
changeset 40256 c5e03eaf7ba2
parent 40255 0222e4232e7c
child 40257 87b7b4c3306f
8163370: Reduce number of classes loaded by common usage of java.lang.invoke Reviewed-by: igerasim, psandoz
jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java
jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java
jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java
jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java
jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Aug 10 13:54:38 2016 +0200
@@ -515,7 +515,7 @@
     // Enumerate the different field kinds using Wrapper,
     // with an extra case added for checked references.
     private static final int
-            FT_LAST_WRAPPER    = Wrapper.values().length-1,
+            FT_LAST_WRAPPER    = Wrapper.COUNT-1,
             FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
             FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
             FT_LIMIT           = FT_LAST_WRAPPER+2;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Wed Aug 10 13:54:38 2016 +0200
@@ -60,7 +60,7 @@
     }
 
     /** A description of a cached transform, possibly associated with the result of the transform.
-     *  The logical content is a sequence of byte values, starting with a Kind.ordinal value.
+     *  The logical content is a sequence of byte values, starting with a kind value.
      *  The sequence is unterminated, ending with an indefinite number of zero bytes.
      *  Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
      */
@@ -68,17 +68,22 @@
         final long packedBytes;
         final byte[] fullBytes;
 
-        private enum Kind {
-            NO_KIND,  // necessary because ordinal must be greater than zero
-            BIND_ARG, ADD_ARG, DUP_ARG,
-            SPREAD_ARGS,
-            FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
-            COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
-            FOLD_ARGS, FOLD_ARGS_TO_VOID,
-            PERMUTE_ARGS,
-            LOCAL_TYPES
-            //maybe add more for guard with test, catch exception, pointwise type conversions
-        }
+        // maybe add more for guard with test, catch exception, pointwise type conversions
+        private static final byte
+                BIND_ARG = 1,
+                ADD_ARG = 2,
+                DUP_ARG = 3,
+                SPREAD_ARGS = 4,
+                FILTER_ARG = 5,
+                FILTER_RETURN = 6,
+                FILTER_RETURN_TO_ZERO = 7,
+                COLLECT_ARGS = 8,
+                COLLECT_ARGS_TO_VOID = 9,
+                COLLECT_ARGS_TO_ARRAY = 10,
+                FOLD_ARGS = 11,
+                FOLD_ARGS_TO_VOID = 12,
+                PERMUTE_ARGS = 13,
+                LOCAL_TYPES = 14;
 
         private static final boolean STRESS_TEST = false; // turn on to disable most packing
         private static final int
@@ -131,20 +136,6 @@
             return bytes;
         }
 
-        private byte byteAt(int i) {
-            long pb = packedBytes;
-            if (pb == 0) {
-                if (i >= fullBytes.length)  return 0;
-                return fullBytes[i];
-            }
-            assert(fullBytes == null);
-            if (i > PACKED_BYTE_MAX_LENGTH)  return 0;
-            int pos = (i * PACKED_BYTE_SIZE);
-            return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
-        }
-
-        Kind kind() { return Kind.values()[byteAt(0)]; }
-
         private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
             super(result);
             this.packedBytes = packedBytes;
@@ -162,44 +153,39 @@
             assert((b & 0xFF) == b);  // incoming value must fit in *unsigned* byte
             return (byte)b;
         }
-        private static byte bval(Kind k) {
-            return bval(k.ordinal());
-        }
-        static Transform of(Kind k, int b1) {
+        static Transform of(byte k, int b1) {
             byte b0 = bval(k);
             if (inRange(b0 | b1))
                 return new Transform(packedBytes(b0, b1));
             else
                 return new Transform(fullBytes(b0, b1));
         }
-        static Transform of(Kind k, int b1, int b2) {
-            byte b0 = (byte) k.ordinal();
+        static Transform of(byte b0, int b1, int b2) {
             if (inRange(b0 | b1 | b2))
                 return new Transform(packedBytes(b0, b1, b2));
             else
                 return new Transform(fullBytes(b0, b1, b2));
         }
-        static Transform of(Kind k, int b1, int b2, int b3) {
-            byte b0 = (byte) k.ordinal();
+        static Transform of(byte b0, int b1, int b2, int b3) {
             if (inRange(b0 | b1 | b2 | b3))
                 return new Transform(packedBytes(b0, b1, b2, b3));
             else
                 return new Transform(fullBytes(b0, b1, b2, b3));
         }
         private static final byte[] NO_BYTES = {};
-        static Transform of(Kind k, int... b123) {
-            return ofBothArrays(k, b123, NO_BYTES);
+        static Transform of(byte kind, int... b123) {
+            return ofBothArrays(kind, b123, NO_BYTES);
         }
-        static Transform of(Kind k, int b1, byte[] b234) {
-            return ofBothArrays(k, new int[]{ b1 }, b234);
+        static Transform of(byte kind, int b1, byte[] b234) {
+            return ofBothArrays(kind, new int[]{ b1 }, b234);
         }
-        static Transform of(Kind k, int b1, int b2, byte[] b345) {
-            return ofBothArrays(k, new int[]{ b1, b2 }, b345);
+        static Transform of(byte kind, int b1, int b2, byte[] b345) {
+            return ofBothArrays(kind, new int[]{ b1, b2 }, b345);
         }
-        private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) {
+        private static Transform ofBothArrays(byte kind, int[] b123, byte[] b456) {
             byte[] fullBytes = new byte[1 + b123.length + b456.length];
             int i = 0;
-            fullBytes[i++] = bval(k);
+            fullBytes[i++] = bval(kind);
             for (int bv : b123) {
                 fullBytes[i++] = bval(bv);
             }
@@ -449,7 +435,7 @@
     // Each editing method can (potentially) cache the edited LF so that it can be reused later.
 
     LambdaForm bindArgumentForm(int pos) {
-        Transform key = Transform.of(Transform.Kind.BIND_ARG, pos);
+        Transform key = Transform.of(Transform.BIND_ARG, pos);
         LambdaForm form = getInCache(key);
         if (form != null) {
             assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
@@ -484,7 +470,7 @@
     }
 
     LambdaForm addArgumentForm(int pos, BasicType type) {
-        Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal());
+        Transform key = Transform.of(Transform.ADD_ARG, pos, type.ordinal());
         LambdaForm form = getInCache(key);
         if (form != null) {
             assert(form.arity == lambdaForm.arity+1);
@@ -501,7 +487,7 @@
     }
 
     LambdaForm dupArgumentForm(int srcPos, int dstPos) {
-        Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos);
+        Transform key = Transform.of(Transform.DUP_ARG, srcPos, dstPos);
         LambdaForm form = getInCache(key);
         if (form != null) {
             assert(form.arity == lambdaForm.arity-1);
@@ -530,7 +516,7 @@
                 elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
             }
         }
-        Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
+        Transform key = Transform.of(Transform.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
         LambdaForm form = getInCache(key);
         if (form != null) {
             assert(form.arity == lambdaForm.arity - arrayLength + 1);
@@ -569,9 +555,9 @@
             return filterArgumentForm(pos, basicType(collectorType.parameterType(0)));
         }
         byte[] newTypes = BasicType.basicTypesOrd(collectorType.parameterArray());
-        Transform.Kind kind = (dropResult
-                ? Transform.Kind.COLLECT_ARGS_TO_VOID
-                : Transform.Kind.COLLECT_ARGS);
+        byte kind = (dropResult
+                ? Transform.COLLECT_ARGS_TO_VOID
+                : Transform.COLLECT_ARGS);
         if (dropResult && collectorArity == 0)  pos = 1;  // pure side effect
         Transform key = Transform.of(kind, pos, collectorArity, newTypes);
         LambdaForm form = getInCache(key);
@@ -598,7 +584,7 @@
             argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
         }
         assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
-        Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
+        byte kind = Transform.COLLECT_ARGS_TO_ARRAY;
         Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
         LambdaForm form = getInCache(key);
         if (form != null) {
@@ -634,7 +620,7 @@
     }
 
     LambdaForm filterArgumentForm(int pos, BasicType newType) {
-        Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal());
+        Transform key = Transform.of(Transform.FILTER_ARG, pos, newType.ordinal());
         LambdaForm form = getInCache(key);
         if (form != null) {
             assert(form.arity == lambdaForm.arity);
@@ -710,7 +696,7 @@
     }
 
     LambdaForm filterReturnForm(BasicType newType, boolean constantZero) {
-        Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN);
+        byte kind = (constantZero ? Transform.FILTER_RETURN_TO_ZERO : Transform.FILTER_RETURN);
         Transform key = Transform.of(kind, newType.ordinal());
         LambdaForm form = getInCache(key);
         if (form != null) {
@@ -762,11 +748,11 @@
 
     LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
         int combinerArity = combinerType.parameterCount();
-        Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS);
+        byte kind = (dropResult ? Transform.FOLD_ARGS_TO_VOID : Transform.FOLD_ARGS);
         Transform key = Transform.of(kind, foldPos, combinerArity);
         LambdaForm form = getInCache(key);
         if (form != null) {
-            assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0));
+            assert(form.arity == lambdaForm.arity - (kind == Transform.FOLD_ARGS ? 1 : 0));
             return form;
         }
         form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
@@ -786,7 +772,7 @@
         }
         assert(skip + reorder.length == lambdaForm.arity);
         if (nullPerm)  return lambdaForm;  // do not bother to cache
-        Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder);
+        Transform key = Transform.of(Transform.PERMUTE_ARGS, reorder);
         LambdaForm form = getInCache(key);
         if (form != null) {
             assert(form.arity == skip+inTypes) : form;
@@ -855,7 +841,7 @@
         int[] desc = BasicType.basicTypeOrds(localTypes);
         desc = Arrays.copyOf(desc, desc.length + 1);
         desc[desc.length - 1] = pos;
-        Transform key = Transform.of(Transform.Kind.LOCAL_TYPES, desc);
+        Transform key = Transform.of(Transform.LOCAL_TYPES, desc);
         LambdaForm form = getInCache(key);
         if (form != null) {
             return form;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java	Wed Aug 10 13:54:38 2016 +0200
@@ -1002,7 +1002,9 @@
                     Collections.addAll(result, buf0);
                 }
             }
-            result.addAll(Arrays.asList(buf).subList(0, bufCount));
+            for (int i = 0; i < bufCount; i++) {
+                result.add(buf[i]);
+            }
             // Signature matching is not the same as type matching, since
             // one signature might correspond to several types.
             // So if matchType is a Class or MethodType, refilter the results.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Aug 10 13:54:38 2016 +0200
@@ -3115,7 +3115,7 @@
         return dropArguments(zero(type.returnType()), 0, type.parameterList());
     }
 
-    private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length];
+    private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.COUNT];
     private static MethodHandle makeIdentity(Class<?> ptype) {
         MethodType mtype = methodType(ptype, ptype);
         LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype));
@@ -3133,7 +3133,7 @@
         assert(btw == Wrapper.OBJECT);
         return makeZero(rtype);
     }
-    private static final MethodHandle[] ZERO_MHS = new MethodHandle[Wrapper.values().length];
+    private static final MethodHandle[] ZERO_MHS = new MethodHandle[Wrapper.COUNT];
     private static MethodHandle makeZero(Class<?> rtype) {
         MethodType mtype = methodType(rtype);
         LambdaForm lform = LambdaForm.zeroForm(BasicType.basicType(rtype));
--- a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Wed Aug 10 13:54:38 2016 +0200
@@ -281,8 +281,7 @@
                     if (c == TAG_CONST) {
                         Object cnst = constants[constC++];
                         el.add(new RecipeElement(cnst));
-                    }
-                    if (c == TAG_ARG) {
+                    } else if (c == TAG_ARG) {
                         el.add(new RecipeElement(argC++));
                     }
                 } else {
@@ -322,32 +321,30 @@
     private static final class RecipeElement {
         private final Object value;
         private final int argPos;
-        private final Tag tag;
 
         public RecipeElement(Object cnst) {
             this.value = Objects.requireNonNull(cnst);
             this.argPos = -1;
-            this.tag = Tag.CONST;
         }
 
         public RecipeElement(int arg) {
             this.value = null;
+            assert (arg >= 0);
             this.argPos = arg;
-            this.tag = Tag.ARG;
         }
 
         public Object getValue() {
-            assert (tag == Tag.CONST);
+            assert (isConst());
             return value;
         }
 
         public int getArgPos() {
-            assert (tag == Tag.ARG);
+            assert (!isConst());
             return argPos;
         }
 
-        public Tag getTag() {
-            return tag;
+        public boolean isConst() {
+            return argPos == -1;
         }
 
         @Override
@@ -357,22 +354,19 @@
 
             RecipeElement that = (RecipeElement) o;
 
-            if (tag != that.tag) return false;
-            if (tag == Tag.CONST && (!value.equals(that.value))) return false;
-            if (tag == Tag.ARG && (argPos != that.argPos)) return false;
+            boolean isConst = isConst();
+            if (isConst != that.isConst()) return false;
+            if (isConst && (!value.equals(that.value))) return false;
+            if (!isConst && (argPos != that.argPos)) return false;
             return true;
         }
 
         @Override
         public int hashCode() {
-            return tag.hashCode();
+            return argPos;
         }
     }
 
-    private enum Tag {
-        CONST, ARG
-    }
-
     /**
      * Facilitates the creation of optimized String concatenation methods, that
      * can be used to efficiently concatenate a known number of arguments of
@@ -880,31 +874,24 @@
 
                 int off = 0;
                 for (RecipeElement el : recipe.getElements()) {
-                    switch (el.getTag()) {
-                        case CONST: {
-                            // Guaranteed non-null, no null check required.
-                            break;
+                    if (el.isConst()) {
+                        // Guaranteed non-null, no null check required.
+                    } else {
+                        // Null-checks are needed only for String arguments, and when a previous stage
+                        // did not do implicit null-checks. If a String is null, we eagerly replace it
+                        // with "null" constant. Note, we omit Objects here, because we don't call
+                        // .length() on them down below.
+                        int ac = el.getArgPos();
+                        Class<?> cl = arr[ac];
+                        if (cl == String.class && !guaranteedNonNull[ac]) {
+                            Label l0 = new Label();
+                            mv.visitIntInsn(ALOAD, off);
+                            mv.visitJumpInsn(IFNONNULL, l0);
+                            mv.visitLdcInsn("null");
+                            mv.visitIntInsn(ASTORE, off);
+                            mv.visitLabel(l0);
                         }
-                        case ARG: {
-                            // Null-checks are needed only for String arguments, and when a previous stage
-                            // did not do implicit null-checks. If a String is null, we eagerly replace it
-                            // with "null" constant. Note, we omit Objects here, because we don't call
-                            // .length() on them down below.
-                            int ac = el.getArgPos();
-                            Class<?> cl = arr[ac];
-                            if (cl == String.class && !guaranteedNonNull[ac]) {
-                                Label l0 = new Label();
-                                mv.visitIntInsn(ALOAD, off);
-                                mv.visitJumpInsn(IFNONNULL, l0);
-                                mv.visitLdcInsn("null");
-                                mv.visitIntInsn(ASTORE, off);
-                                mv.visitLabel(l0);
-                            }
-                            off += getParameterSize(cl);
-                            break;
-                        }
-                        default:
-                            throw new StringConcatException("Unhandled tag: " + el.getTag());
+                        off += getParameterSize(cl);
                     }
                 }
             }
@@ -925,37 +912,30 @@
                 mv.visitInsn(ICONST_0);
 
                 for (RecipeElement el : recipe.getElements()) {
-                    switch (el.getTag()) {
-                        case CONST: {
-                            Object cnst = el.getValue();
-                            len += cnst.toString().length();
-                            break;
+                    if (el.isConst()) {
+                        Object cnst = el.getValue();
+                        len += cnst.toString().length();
+                    } else {
+                        /*
+                            If an argument is String, then we can call .length() on it. Sized/Exact modes have
+                            converted arguments for us. If an argument is primitive, we can provide a guess
+                            for its String representation size.
+                        */
+                        Class<?> cl = arr[el.getArgPos()];
+                        if (cl == String.class) {
+                            mv.visitIntInsn(ALOAD, off);
+                            mv.visitMethodInsn(
+                                    INVOKEVIRTUAL,
+                                    "java/lang/String",
+                                    "length",
+                                    "()I",
+                                    false
+                            );
+                            mv.visitInsn(IADD);
+                        } else if (cl.isPrimitive()) {
+                            len += estimateSize(cl);
                         }
-                        case ARG: {
-                            /*
-                                If an argument is String, then we can call .length() on it. Sized/Exact modes have
-                                converted arguments for us. If an argument is primitive, we can provide a guess
-                                for its String representation size.
-                            */
-                            Class<?> cl = arr[el.getArgPos()];
-                            if (cl == String.class) {
-                                mv.visitIntInsn(ALOAD, off);
-                                mv.visitMethodInsn(
-                                        INVOKEVIRTUAL,
-                                        "java/lang/String",
-                                        "length",
-                                        "()I",
-                                        false
-                                );
-                                mv.visitInsn(IADD);
-                            } else if (cl.isPrimitive()) {
-                                len += estimateSize(cl);
-                            }
-                            off += getParameterSize(cl);
-                            break;
-                        }
-                        default:
-                            throw new StringConcatException("Unhandled tag: " + el.getTag());
+                        off += getParameterSize(cl);
                     }
                 }
 
@@ -987,23 +967,17 @@
                 int off = 0;
                 for (RecipeElement el : recipe.getElements()) {
                     String desc;
-                    switch (el.getTag()) {
-                        case CONST: {
-                            Object cnst = el.getValue();
-                            mv.visitLdcInsn(cnst);
-                            desc = getSBAppendDesc(cnst.getClass());
-                            break;
-                        }
-                        case ARG: {
-                            Class<?> cl = arr[el.getArgPos()];
-                            mv.visitVarInsn(getLoadOpcode(cl), off);
-                            off += getParameterSize(cl);
-                            desc = getSBAppendDesc(cl);
-                            break;
-                        }
-                        default:
-                            throw new StringConcatException("Unhandled tag: " + el.getTag());
+                    if (el.isConst()) {
+                        Object cnst = el.getValue();
+                        mv.visitLdcInsn(cnst);
+                        desc = getSBAppendDesc(cnst.getClass());
+                    } else {
+                        Class<?> cl = arr[el.getArgPos()];
+                        mv.visitVarInsn(getLoadOpcode(cl), off);
+                        off += getParameterSize(cl);
+                        desc = getSBAppendDesc(cl);
                     }
+
                     mv.visitMethodInsn(
                             INVOKEVIRTUAL,
                             "java/lang/StringBuilder",
@@ -1279,26 +1253,19 @@
             // call the usual String.length(). Primitive values string sizes can be estimated.
             int initial = 0;
             for (RecipeElement el : recipe.getElements()) {
-                switch (el.getTag()) {
-                    case CONST: {
-                        Object cnst = el.getValue();
-                        initial += cnst.toString().length();
-                        break;
+                if (el.isConst()) {
+                    Object cnst = el.getValue();
+                    initial += cnst.toString().length();
+                } else {
+                    final int i = el.getArgPos();
+                    Class<?> type = ptypesList.get(i);
+                    if (type.isPrimitive()) {
+                        MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
+                        est = MethodHandles.dropArguments(est, 0, type);
+                        lengthers[i] = est;
+                    } else {
+                        lengthers[i] = STRING_LENGTH;
                     }
-                    case ARG: {
-                        final int i = el.getArgPos();
-                        Class<?> type = ptypesList.get(i);
-                        if (type.isPrimitive()) {
-                            MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
-                            est = MethodHandles.dropArguments(est, 0, type);
-                            lengthers[i] = est;
-                        } else {
-                            lengthers[i] = STRING_LENGTH;
-                        }
-                        break;
-                    }
-                    default:
-                        throw new StringConcatException("Unhandled tag: " + el.getTag());
                 }
             }
 
@@ -1311,26 +1278,19 @@
             for (int i = elements.size() - 1; i >= 0; i--) {
                 RecipeElement el = elements.get(i);
                 MethodHandle appender;
-                switch (el.getTag()) {
-                    case CONST: {
-                        Object constant = el.getValue();
-                        MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
-                        appender = MethodHandles.insertArguments(mh, 1, constant);
-                        break;
+                if (el.isConst()) {
+                    Object constant = el.getValue();
+                    MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
+                    appender = MethodHandles.insertArguments(mh, 1, constant);
+                } else {
+                    int ac = el.getArgPos();
+                    appender = appender(ptypesList.get(ac));
+
+                    // Insert dummy arguments to match the prefix in the signature.
+                    // The actual appender argument will be the ac-ith argument.
+                    if (ac != 0) {
+                        appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
                     }
-                    case ARG: {
-                        int ac = el.getArgPos();
-                        appender = appender(ptypesList.get(ac));
-
-                        // Insert dummy arguments to match the prefix in the signature.
-                        // The actual appender argument will be the ac-ith argument.
-                        if (ac != 0) {
-                            appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
-                        }
-                        break;
-                    }
-                    default:
-                        throw new StringConcatException("Unhandled tag: " + el.getTag());
                 }
                 builder = MethodHandles.foldArguments(builder, appender);
             }
@@ -1521,19 +1481,12 @@
             // *ending* index.
             for (RecipeElement el : recipe.getElements()) {
                 MethodHandle prepender;
-                switch (el.getTag()) {
-                    case CONST: {
-                        Object cnst = el.getValue();
-                        prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
-                        break;
-                    }
-                    case ARG: {
-                        int pos = el.getArgPos();
-                        prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
-                        break;
-                    }
-                    default:
-                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                if (el.isConst()) {
+                    Object cnst = el.getValue();
+                    prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
+                } else {
+                    int pos = el.getArgPos();
+                    prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
                 }
 
                 // Remove "old" index from arguments
@@ -1573,43 +1526,36 @@
             byte initialCoder = INITIAL_CODER;
             int initialLen = 0;    // initial length, in characters
             for (RecipeElement el : recipe.getElements()) {
-                switch (el.getTag()) {
-                    case CONST: {
-                        Object constant = el.getValue();
-                        String s = constant.toString();
-                        initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
-                        initialLen += s.length();
-                        break;
-                    }
-                    case ARG: {
-                        int ac = el.getArgPos();
+                if (el.isConst()) {
+                    Object constant = el.getValue();
+                    String s = constant.toString();
+                    initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
+                    initialLen += s.length();
+                } else {
+                    int ac = el.getArgPos();
 
-                        Class<?> argClass = ptypesList.get(ac);
-                        MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
-                        lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
-                        lm = MethodHandles.dropArguments(lm, 2, byte.class);
-
-                        MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypesList, ac);
-                        cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
+                    Class<?> argClass = ptypesList.get(ac);
+                    MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
+                    lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
+                    lm = MethodHandles.dropArguments(lm, 2, byte.class);
 
-                        // Read this bottom up:
+                    MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypesList, ac);
+                    cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
 
-                        // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
-                        mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
+                    // Read this bottom up:
 
-                        // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
-                        //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
-                        mh = MethodHandles.foldArguments(mh, lm);
+                    // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
+                    mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
 
-                        // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
-                        //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
-                        mh = MethodHandles.foldArguments(mh, cm);
+                    // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
+                    //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
+                    mh = MethodHandles.foldArguments(mh, lm);
 
-                        // 1. The mh shape here is ("old-index", "old-coder", <args>)
-                        break;
-                    }
-                    default:
-                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                    // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
+                    //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
+                    mh = MethodHandles.foldArguments(mh, cm);
+
+                    // 1. The mh shape here is ("old-index", "old-coder", <args>)
                 }
             }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Wed Aug 10 13:54:38 2016 +0200
@@ -38,7 +38,7 @@
         super(Opcodes.ASM5, mv);
     }
 
-    private static final int NUM_WRAPPERS = Wrapper.values().length;
+    private static final int NUM_WRAPPERS = Wrapper.COUNT;
 
     private static final String NAME_OBJECT = "java/lang/Object";
     private static final String WRAPPER_PREFIX = "Ljava/lang/";
--- a/jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/VarHandle.java	Wed Aug 10 13:54:38 2016 +0200
@@ -1057,57 +1057,11 @@
     Object addAndGet(Object... args);
 
     enum AccessType {
-        GET(Object.class) {
-            @Override
-            MethodType accessModeType(Class<?> receiver, Class<?> value,
-                                      Class<?>... intermediate) {
-                Class<?>[] ps =  allocateParameters(0, receiver, intermediate);
-                fillParameters(ps, receiver, intermediate);
-                return MethodType.methodType(value, ps);
-            }
-        },
-        SET(void.class) {
-            @Override
-            MethodType accessModeType(Class<?> receiver, Class<?> value,
-                                      Class<?>... intermediate) {
-                Class<?>[] ps =  allocateParameters(1, receiver, intermediate);
-                int i = fillParameters(ps, receiver, intermediate);
-                ps[i] = value;
-                return MethodType.methodType(void.class, ps);
-            }
-        },
-        COMPARE_AND_SWAP(boolean.class) {
-            @Override
-            MethodType accessModeType(Class<?> receiver, Class<?> value,
-                                      Class<?>... intermediate) {
-                Class<?>[] ps =  allocateParameters(2, receiver, intermediate);
-                int i = fillParameters(ps, receiver, intermediate);
-                ps[i++] = value;
-                ps[i] = value;
-                return MethodType.methodType(boolean.class, ps);
-            }
-        },
-        COMPARE_AND_EXCHANGE(Object.class) {
-            @Override
-            MethodType accessModeType(Class<?> receiver, Class<?> value,
-                                      Class<?>... intermediate) {
-                Class<?>[] ps =  allocateParameters(2, receiver, intermediate);
-                int i = fillParameters(ps, receiver, intermediate);
-                ps[i++] = value;
-                ps[i] = value;
-                return MethodType.methodType(value, ps);
-            }
-        },
-        GET_AND_UPDATE(Object.class) {
-            @Override
-            MethodType accessModeType(Class<?> receiver, Class<?> value,
-                                      Class<?>... intermediate) {
-                Class<?>[] ps =  allocateParameters(1, receiver, intermediate);
-                int i = fillParameters(ps, receiver, intermediate);
-                ps[i] = value;
-                return MethodType.methodType(value, ps);
-            }
-        };
+        GET(Object.class),
+        SET(void.class),
+        COMPARE_AND_SWAP(boolean.class),
+        COMPARE_AND_EXCHANGE(Object.class),
+        GET_AND_UPDATE(Object.class);
 
         final Class<?> returnType;
         final boolean isMonomorphicInReturnType;
@@ -1117,8 +1071,41 @@
             isMonomorphicInReturnType = returnType != Object.class;
         }
 
-        abstract MethodType accessModeType(Class<?> receiver, Class<?> value,
-                                           Class<?>... intermediate);
+        MethodType accessModeType(Class<?> receiver, Class<?> value,
+                                  Class<?>... intermediate) {
+            Class<?>[] ps;
+            int i;
+            switch (this) {
+                case GET:
+                    ps = allocateParameters(0, receiver, intermediate);
+                    fillParameters(ps, receiver, intermediate);
+                    return MethodType.methodType(value, ps);
+                case SET:
+                    ps = allocateParameters(1, receiver, intermediate);
+                    i = fillParameters(ps, receiver, intermediate);
+                    ps[i] = value;
+                    return MethodType.methodType(void.class, ps);
+                case COMPARE_AND_SWAP:
+                    ps = allocateParameters(2, receiver, intermediate);
+                    i = fillParameters(ps, receiver, intermediate);
+                    ps[i++] = value;
+                    ps[i] = value;
+                    return MethodType.methodType(boolean.class, ps);
+                case COMPARE_AND_EXCHANGE:
+                    ps = allocateParameters(2, receiver, intermediate);
+                    i = fillParameters(ps, receiver, intermediate);
+                    ps[i++] = value;
+                    ps[i] = value;
+                    return MethodType.methodType(value, ps);
+                case GET_AND_UPDATE:
+                    ps = allocateParameters(1, receiver, intermediate);
+                    i = fillParameters(ps, receiver, intermediate);
+                    ps[i] = value;
+                    return MethodType.methodType(value, ps);
+                default:
+                    throw new InternalError("Unknown AccessType");
+            }
+        }
 
         private static Class<?>[] allocateParameters(int values,
                                                      Class<?> receiver, Class<?>... intermediate) {
--- a/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java	Wed Aug 10 13:54:38 2016 +0200
@@ -29,27 +29,32 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
-import java.util.EnumMap;
+import jdk.internal.vm.annotation.Stable;
 
 public class ValueConversions {
     private static final Class<?> THIS_CLASS = ValueConversions.class;
     private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
 
-    /** Thread-safe canonicalized mapping from Wrapper to MethodHandle
+    /**
+     * Thread-safe canonicalized mapping from Wrapper to MethodHandle
      * with unsynchronized reads and synchronized writes.
-     * It's safe to publish MethodHandles by data race because they are immutable. */
+     * It's safe to publish MethodHandles by data race because they are immutable.
+     */
     private static class WrapperCache {
-        /** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */
-        private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class);
+        @Stable
+        private final MethodHandle[] map = new MethodHandle[Wrapper.COUNT];
 
         public MethodHandle get(Wrapper w) {
-            return map.get(w);
+            return map[w.ordinal()];
         }
         public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
-            // Simulate CAS to avoid racy duplication
-            MethodHandle prev = map.putIfAbsent(w, mh);
-            if (prev != null)  return prev;
-            return mh;
+            MethodHandle prev = map[w.ordinal()];
+            if (prev != null) {
+                return prev;
+            } else {
+                map[w.ordinal()] = mh;
+                return mh;
+            }
         }
     }
 
@@ -623,7 +628,7 @@
         return (x ? (byte)1 : (byte)0);
     }
 
-    private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
+    private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.COUNT);
 
     public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
         WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
--- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Wed Aug 10 11:54:12 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Wed Aug 10 13:54:38 2016 +0200
@@ -42,6 +42,8 @@
     VOID   (     Void.class,    void.class, 'V',           null, Format.other(    0)),
     ;
 
+    public static final int COUNT = 10;
+
     private final Class<?> wrapperType;
     private final Class<?> primitiveType;
     private final char     basicTypeChar;
@@ -160,7 +162,10 @@
         return true;
     }
 
-    static { assert(checkConvertibleFrom()); }
+    static {
+        assert(checkConvertibleFrom());
+        assert(COUNT == Wrapper.values().length);
+    }
     private static boolean checkConvertibleFrom() {
         // Check the matrix for correct classification of widening conversions.
         for (Wrapper w : values()) {