changeset 48441:bb054c3c7d3a switch

8192963: Starting with switch desugaring to indy. Contributed-by: brian.goetz@oracle.com, jan.lahoda@oracle.com
author jlahoda
date Tue, 12 Dec 2017 19:41:58 +0100
parents 1242e71d4e38
children b8a11c234353
files src/java.base/share/classes/java/lang/invoke/SwitchBootstraps.java src/java.base/share/classes/jdk/internal/misc/SwitchBootstrapsImpl.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java test/jdk/java/lang/invoke/TestSwitchBootstrap.java test/langtools/tools/javac/switchnull/SwitchNull.java
diffstat 9 files changed, 779 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/SwitchBootstraps.java	Tue Dec 12 19:41:58 2017 +0100
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import jdk.internal.misc.SwitchBootstrapsImpl;
+
+/**
+ * SwitchBootstraps
+ *
+ * @author Brian Goetz
+ */
+public class SwitchBootstraps {
+    /**Prepare a CallSite implementing switch translation. The value passed to the CallSite will
+     * be matched to all case expressions, and the index of the first matching branch will be returned.
+     *
+     * @param lookup Represents a lookup context with the accessibility
+     *               privileges of the caller.  When used with {@code invokedynamic},
+     *               this is stacked automatically by the VM.
+     * @param invocationName The name of the method to implement.  When used with
+     *                    {@code invokedynamic}, this is provided by the
+     *                    {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     * @param invocationType The expected signature of the {@code CallSite}.  The
+     *                    parameter types represent the types of capture variables;
+     *                    the return type is the interface to implement.   When
+     *                    used with {@code invokedynamic}, this is provided by
+     *                    the {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     *                    In the event that the implementation method is an
+     *                    instance method and this signature has any parameters,
+     *                    the first parameter in the invocation signature must
+     *                    correspond to the receiver.
+     * @param intLabels values that should be matched to the value passed to the CallSite.
+     * @return an index of a value from branches matching the value passed to the CallSite, {@literal -1}
+     *         if the value is null, {@code branches.length} if none is matching.
+     * @throws Throwable if something goes wrong
+     */
+    public static CallSite intSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType,
+                                     int... intLabels) throws Throwable {
+        return SwitchBootstrapsImpl.intSwitch(lookup, invocationName, invocationType, intLabels);
+    }
+
+    /**Prepare a CallSite implementing switch translation. The value passed to the CallSite will
+     * be matched to all case expressions, and the index of the first matching branch will be returned.
+     *
+     * @param lookup Represents a lookup context with the accessibility
+     *               privileges of the caller.  When used with {@code invokedynamic},
+     *               this is stacked automatically by the VM.
+     * @param invocationName The name of the method to implement.  When used with
+     *                    {@code invokedynamic}, this is provided by the
+     *                    {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     * @param invocationType The expected signature of the {@code CallSite}.  The
+     *                    parameter types represent the types of capture variables;
+     *                    the return type is the interface to implement.   When
+     *                    used with {@code invokedynamic}, this is provided by
+     *                    the {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     *                    In the event that the implementation method is an
+     *                    instance method and this signature has any parameters,
+     *                    the first parameter in the invocation signature must
+     *                    correspond to the receiver.
+     * @param stringLabels values that should be matched to the value passed to the CallSite.
+     * @return an index of a value from branches matching the value passed to the CallSite, {@literal -1}
+     *         if the value is null, {@code branches.length} if none is matching.
+     * @throws Throwable if something goes wrong
+     */
+    public static CallSite stringSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType,
+                                        String... stringLabels) throws Throwable {
+        return SwitchBootstrapsImpl.stringSwitch(lookup, invocationName, invocationType, stringLabels);
+    }
+
+    /**Prepare a CallSite implementing switch translation. The value passed to the CallSite will
+     * be matched to all case expressions, and the index of the first matching branch will be returned.
+     *
+     * @param lookup Represents a lookup context with the accessibility
+     *               privileges of the caller.  When used with {@code invokedynamic},
+     *               this is stacked automatically by the VM.
+     * @param invocationName The name of the method to implement.  When used with
+     *                    {@code invokedynamic}, this is provided by the
+     *                    {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     * @param invocationType The expected signature of the {@code CallSite}.  The
+     *                    parameter types represent the types of capture variables;
+     *                    the return type is the interface to implement.   When
+     *                    used with {@code invokedynamic}, this is provided by
+     *                    the {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     *                    In the event that the implementation method is an
+     *                    instance method and this signature has any parameters,
+     *                    the first parameter in the invocation signature must
+     *                    correspond to the receiver.
+     * @param <E> the enum type
+     * @param enumClass the enum class
+     * @param caseLabels enum names that should be matched to the value passed to the CallSite.
+     * @return an index of a value from branches matching the value passed to the CallSite, {@literal -1}
+     *         if the value is null, {@code branches.length} if none is matching.
+     * @throws Throwable if something goes wrong
+     */
+    public static<E extends Enum<E>> CallSite enumSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType,
+                                                         Class<E> enumClass, String... caseLabels) throws Throwable {
+        return SwitchBootstrapsImpl.enumSwitch(lookup, invocationName, invocationType, enumClass, caseLabels);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/SwitchBootstrapsImpl.java	Tue Dec 12 19:41:58 2017 +0100
@@ -0,0 +1,275 @@
+/*
+ * 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 jdk.internal.misc;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+/**
+ * SwitchBootstraps
+ *
+ * @author Brian Goetz
+ */
+public class SwitchBootstrapsImpl {
+    private final static Set<Class<?>> INTEGRAL_TYPES = Set.of(int.class, short.class, byte.class, char.class,
+                                                               Integer.class, Short.class, Byte.class, Character.class);
+    private static final Comparator<String> STRING_COMPARATOR = Comparator.comparingInt(Objects::hashCode);
+
+    public static CallSite intSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType,
+                                     int... intLabels) throws Throwable {
+        int[] labels = intLabels.clone();
+        if (invocationType.parameterCount() != 1
+            || invocationType.returnType() != int.class
+            || (!INTEGRAL_TYPES.contains(invocationType.parameterType(0))))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+
+        int[] indexes = IntStream.range(0, labels.length)
+                                 .boxed()
+                                 .sorted(Comparator.comparingInt(a -> labels[a]))
+                                 .mapToInt(Integer::intValue)
+                                 .toArray();
+        Arrays.sort(labels);
+
+        return new IntSwitchCallSite(invocationType, labels, indexes);
+    }
+
+    private static class IntSwitchCallSite extends ConstantCallSite {
+        private static final MethodHandle HOOK;
+
+        private final int[] labels;
+        private final int[] indexes;
+
+        static {
+            try {
+                HOOK = MethodHandles.lookup().findVirtual(IntSwitchCallSite.class, "initHook", MethodType.methodType(MethodHandle.class));
+            }
+            catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        IntSwitchCallSite(MethodType targetType,
+                                 int[] labels,
+                                 int[] indexes) throws Throwable {
+            super(targetType, HOOK);
+            this.labels = labels;
+            this.indexes = indexes;
+        }
+
+        private MethodHandle initHook() throws ReflectiveOperationException {
+            return MethodHandles.lookup()
+                                .findVirtual(IntSwitchCallSite.class, "intSwitch",
+                                             MethodType.methodType(int.class, type().parameterType(0)))
+                                .bindTo(this);
+        }
+
+        private int intSwitch(short target) {
+            return intSwitch((int) target);
+        }
+
+        private int intSwitch(byte target) {
+            return intSwitch((int) target);
+        }
+
+        private int intSwitch(char target) {
+            return intSwitch((int) target);
+        }
+
+        private int intSwitch(int target) {
+            int index = Arrays.binarySearch(labels, target);
+            return (index >= 0) ? indexes[index] : indexes.length;
+        }
+
+        private int intSwitch(Integer target) {
+            return (target == null)
+                   ? -1
+                   : intSwitch((int) target);
+        }
+
+        private int intSwitch(Short target) {
+            return (target == null)
+                   ? -1
+                   : intSwitch((int) target);
+        }
+
+        private int intSwitch(Character target) {
+            return (target == null)
+                   ? -1
+                   : intSwitch((int) target);
+        }
+
+        private int intSwitch(Byte target) {
+            return (target == null)
+                   ? -1
+                   : intSwitch((int) target);
+        }
+    }
+
+    public static CallSite stringSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType,
+                                        String... stringLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || invocationType.returnType() != int.class
+            || (!invocationType.parameterType(0).equals(String.class)))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+
+        String[] sortedByHash = stringLabels.clone();
+        Arrays.sort(sortedByHash, STRING_COMPARATOR);
+
+        int[] indexes = new int[stringLabels.length];
+        for (int i = 0; i < stringLabels.length; i++) {
+            for (int j = 0; j < stringLabels.length; j++) {
+                if (Objects.equals(sortedByHash[j], stringLabels[i])) {
+                    indexes[j] = i;
+                    break;
+                }
+            }
+        }
+
+        boolean collisions = IntStream.range(0, sortedByHash.length-1)
+                .anyMatch(i -> Objects.hashCode(sortedByHash[i]) == Objects.hashCode(sortedByHash[i + 1]));
+
+        return new StringSwitchCallSite(invocationType, sortedByHash, indexes, collisions);
+    }
+
+    private static class StringSwitchCallSite extends ConstantCallSite {
+        private static final MethodHandle HOOK;
+
+        private final String[] sortedByHash;
+        private final int[] indexes;
+        private final boolean collisions;
+
+        static {
+            try {
+                HOOK = MethodHandles.lookup().findVirtual(StringSwitchCallSite.class, "initHook", MethodType.methodType(MethodHandle.class));
+            }
+            catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        StringSwitchCallSite(MethodType targetType,
+                             String[] sortedByHash,
+                             int[] indexes,
+                             boolean collisions) throws Throwable {
+            super(targetType, HOOK);
+            this.sortedByHash = sortedByHash;
+            this.indexes = indexes;
+            this.collisions = collisions;
+        }
+
+        private MethodHandle initHook() throws ReflectiveOperationException {
+            return MethodHandles.lookup()
+                                .findVirtual(StringSwitchCallSite.class, "stringSwitch",
+                                             MethodType.methodType(int.class, String.class))
+                                .bindTo(this);
+        }
+
+        private int stringSwitch(String target) {
+            if (target == null)
+                return -1;
+
+            int index = Arrays.binarySearch(sortedByHash, target, SwitchBootstrapsImpl.STRING_COMPARATOR);
+            if (index < 0)
+                return indexes.length;
+            else if (target.equals(sortedByHash[index])) {
+                return indexes[index];
+            }
+            else if (collisions) {
+                int hash = target.hashCode();
+                while (index > 0 && Objects.hashCode(sortedByHash[index-1]) == hash)
+                    --index;
+                for (; index < sortedByHash.length && Objects.hashCode(sortedByHash[index]) == hash; index++)
+                    if (target.equals(sortedByHash[index]))
+                        return indexes[index];
+            }
+
+            return indexes.length;
+        }
+    }
+
+    public static<E extends Enum<E>> CallSite enumSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType,
+                                                         Class<E> enumClass, String... enumNames) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || invocationType.returnType() != int.class
+            || (!invocationType.parameterType(0).equals(Enum.class)))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+
+        MethodHandle valuesMH = lookup.findStatic(enumClass, "values", MethodType.methodType(Array.newInstance(enumClass, 0).getClass()));
+        int[] ordinalMap = new int[((Object[]) valuesMH.invoke()).length];
+        Arrays.fill(ordinalMap, enumNames.length);
+
+        for (int i=0; i<enumNames.length; i++) {
+            // @@@ Do we want to recover gracefully if labels are not present?
+            if (enumNames[i] != null)
+                ordinalMap[E.valueOf(enumClass, enumNames[i]).ordinal()] = i;
+        }
+
+        return new EnumSwitchCallSite(invocationType, ordinalMap);
+    }
+
+    private static class EnumSwitchCallSite extends ConstantCallSite {
+        private static final MethodHandle HOOK;
+
+        private final int[] ordinalMap;
+
+        static {
+            try {
+                HOOK = MethodHandles.lookup().findVirtual(EnumSwitchCallSite.class, "initHook", MethodType.methodType(MethodHandle.class));
+            }
+            catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        EnumSwitchCallSite(MethodType targetType,
+                           int[] ordinalMap) throws Throwable {
+            super(targetType, HOOK);
+            this.ordinalMap = ordinalMap;
+        }
+
+        private MethodHandle initHook() throws ReflectiveOperationException {
+            return MethodHandles.lookup()
+                                .findVirtual(EnumSwitchCallSite.class, "enumSwitch",
+                                             MethodType.methodType(int.class, Enum.class))
+                                .bindTo(this);
+        }
+
+        @SuppressWarnings("rawtypes")
+        private int enumSwitch(Enum target) {
+            return (target == null)
+                   ? -1
+                   : ordinalMap[target.ordinal()];
+        }
+    }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Fri Dec 08 17:21:56 2017 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Dec 12 19:41:58 2017 +0100
@@ -185,6 +185,7 @@
     public final Type assertionErrorType;
     public final Type incompatibleClassChangeErrorType;
     public final Type cloneNotSupportedExceptionType;
+    public final Type nullPointerExceptionType;
     public final Type annotationType;
     public final TypeSymbol enumSym;
     public final Type listType;
@@ -208,6 +209,7 @@
     public final Type trustMeType;
     public final Type lambdaMetafactory;
     public final Type stringConcatFactory;
+    public final Type switchBootstraps;
     public final Type repeatableType;
     public final Type documentedType;
     public final Type elementTypeType;
@@ -508,6 +510,7 @@
         assertionErrorType = enterClass("java.lang.AssertionError");
         incompatibleClassChangeErrorType = enterClass("java.lang.IncompatibleClassChangeError");
         cloneNotSupportedExceptionType = enterClass("java.lang.CloneNotSupportedException");
+        nullPointerExceptionType = enterClass("java.lang.NullPointerException");
         annotationType = enterClass("java.lang.annotation.Annotation");
         classLoaderType = enterClass("java.lang.ClassLoader");
         enumSym = enterClass(java_base, names.java_lang_Enum);
@@ -545,6 +548,7 @@
         nativeHeaderType = enterClass("java.lang.annotation.Native");
         lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory");
         stringConcatFactory = enterClass("java.lang.invoke.StringConcatFactory");
+        switchBootstraps = enterClass("java.lang.invoke.SwitchBootstraps");
         functionalInterfaceType = enterClass("java.lang.FunctionalInterface");
 
         synthesizeEmptyInterfaceIfMissing(autoCloseableType);
@@ -553,6 +557,7 @@
         synthesizeEmptyInterfaceIfMissing(lambdaMetafactory);
         synthesizeEmptyInterfaceIfMissing(serializedLambdaType);
         synthesizeEmptyInterfaceIfMissing(stringConcatFactory);
+        synthesizeEmptyInterfaceIfMissing(switchBootstraps);
         synthesizeBoxTypeIfMissing(doubleType);
         synthesizeBoxTypeIfMissing(floatType);
         synthesizeBoxTypeIfMissing(voidType);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Dec 08 17:21:56 2017 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Dec 12 19:41:58 2017 +0100
@@ -27,6 +27,8 @@
 
 import java.util.*;
 import java.util.Map.Entry;
+import java.util.function.Function;
+import java.util.stream.Stream;
 
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Kinds.KindSelector;
@@ -95,6 +97,7 @@
     private final Types types;
     private final boolean debugLower;
     private final PkgInfo pkginfoOpt;
+    private final boolean generateNewSwitch;
 
     protected Lower(Context context) {
         context.put(lowerKey, this);
@@ -122,6 +125,8 @@
         Options options = Options.instance(context);
         debugLower = options.isSet("debuglower");
         pkginfoOpt = PkgInfo.get(options);
+        
+        generateNewSwitch = options.isSet("enableIndySwitch") && target.hasSwichBootstraps();
     }
 
     /** The currently enclosing class.
@@ -3404,23 +3409,127 @@
             (tree.selector.type.tsym.flags() & ENUM) != 0;
         boolean stringSwitch = selsuper != null &&
             types.isSameType(tree.selector.type, syms.stringType);
+        boolean boxSwitch = selsuper != null &&
+            types.unboxedType(tree.selector.type).isPrimitive();
         Type target = enumSwitch ? tree.selector.type :
             (stringSwitch? syms.stringType : syms.intType);
-        if (!enumSwitch && !stringSwitch && hasNullCase(tree)) {
-            result = translate(visitBoxedIntSwitchWithNull(tree));
-            return;
-        }
-
-        tree.selector = translate(tree.selector, target);
-        tree.cases = translateCases(tree.cases);
-        if (enumSwitch) {
-            result = visitEnumSwitch(tree);
-        } else if (stringSwitch) {
-            result = visitStringSwitch(tree);
+
+        if (generateNewSwitch && ((boxSwitch && hasNullCase(tree)) || stringSwitch || enumSwitch)) {
+            tree.selector = translate(tree.selector);
+            tree.cases = translateCases(tree.cases);
+
+            JCExpression qualifier;
+            
+            if (enumSwitch) {
+                qualifier = prepareSwitchIndySelector(tree,
+                                                      names.enumSwitch,
+                                                      syms.stringType,
+                                                      tree.selector.type,
+                                                      syms.enumSym.type,
+                                                      true,
+                                                      pat -> TreeInfo.name(pat).toString());
+            } else if (stringSwitch) {
+                qualifier = prepareSwitchIndySelector(tree,
+                                                      names.stringSwitch,
+                                                      syms.stringType,
+                                                      syms.stringType,
+                                                      syms.stringType,
+                                                      false,
+                                                      pat -> pat.type.constValue());
+            } else {
+                qualifier = prepareSwitchIndySelector(tree,
+                                                      names.intSwitch,
+                                                      syms.intType,
+                                                      tree.selector.type,
+                                                      tree.selector.type,
+                                                      false,
+                                                      pat -> pat.type.constValue());
+            }
+
+            tree.selector = make.Apply(List.nil(), qualifier, List.of(tree.selector));
+            tree.selector.type = qualifier.type;
+            
+            if (!hasNullCase(tree)) {
+                JCThrow npe = make.Throw(makeNewClass(syms.nullPointerExceptionType, List.nil()));
+                tree.cases = tree.cases.prepend(make.Case(makeNull(), List.of(npe)));
+            }
+
+            tree.cases.head.pat = make.Literal(-1);
+
+            int caseIdx = 0;
+            
+            for (JCCase c : tree.cases.tail) {
+                if (c.pat != null) {
+                    c.pat = make.Literal(caseIdx++);
+                }
+            }
+
+            result = tree;
         } else {
-            result = tree;
+            if (!enumSwitch && !stringSwitch && hasNullCase(tree)) {
+                result = translate(visitBoxedIntSwitchWithNull(tree));
+                return;
+            }
+            tree.selector = translate(tree.selector, target);
+            tree.cases = translateCases(tree.cases);
+            if (enumSwitch) {
+                result = visitEnumSwitch(tree);
+            } else if (stringSwitch) {
+                result = visitStringSwitch(tree);
+            } else {
+                result = tree;
+            }
         }
     }
+        //where:
+        private JCExpression prepareSwitchIndySelector(JCSwitch tree,
+                                                       Name bootstrapMethodName,
+                                                       Type labelsType,
+                                                       Type switchType,
+                                                       Type invocationType,
+                                                       boolean prependSwitchType,
+                                                       Function<JCExpression, Object> pattern2Label) {
+            Type.MethodType indyType = new Type.MethodType(List.of(invocationType),
+                    syms.intType,
+                    List.nil(),
+                    syms.methodClass);
+
+            List<Type> bsm_staticArgs = List.of(types.makeArrayType(labelsType));
+            
+            if (prependSwitchType)
+                bsm_staticArgs = bsm_staticArgs.prepend(new ClassType(syms.classType.getEnclosingType(),
+                                                                      List.of(switchType),
+                                                                      syms.classType.tsym));
+            
+            bsm_staticArgs = bsm_staticArgs.prepend(syms.methodTypeType)
+                                           .prepend(syms.stringType)
+                                           .prepend(syms.methodHandleLookupType);
+
+            Symbol intSwitch = rs.resolveInternalMethod(tree.pos(), attrEnv, syms.switchBootstraps,
+                    bootstrapMethodName, bsm_staticArgs, List.nil());
+            
+            Stream<Object> firstParam = prependSwitchType
+                    ? Stream.of(switchType.tsym)
+                    : Stream.empty();
+            Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(bootstrapMethodName,
+                    syms.noSymbol,
+                    ClassFile.REF_invokeStatic,
+                    (Symbol.MethodSymbol)intSwitch,
+                    indyType,
+                    Stream.concat(firstParam,
+                                  tree.cases.stream()
+                                            .filter(c -> c.pat != null && !TreeInfo.isNull(c.pat))
+                                            .map(c -> pattern2Label.apply(c.pat))
+                                 )
+                          .toArray());
+
+            JCFieldAccess qualifier = make.Select(make.QualIdent(intSwitch.owner),
+                                                  bootstrapMethodName);
+            qualifier.sym = dynSym;
+            qualifier.type = indyType.getReturnType();
+            
+            return qualifier;
+        }
 
     public JCTree visitEnumSwitch(JCSwitch tree) {
         TypeSymbol enumSym = tree.selector.type.tsym;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Fri Dec 08 17:21:56 2017 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Tue Dec 12 19:41:58 2017 +0100
@@ -483,7 +483,7 @@
                 poolbuf.appendByte(CONSTANT_Package);
                 poolbuf.appendChar(pool.put(names.fromUtf(externalize(m.fullname))));
             } else {
-                Assert.error("writePool " + value);
+                Assert.error("writePool " + value + "/" + value.getClass());
             }
             i++;
         }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java	Fri Dec 08 17:21:56 2017 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java	Tue Dec 12 19:41:58 2017 +0100
@@ -145,6 +145,12 @@
         return compareTo(JDK1_9) >= 0;
     }
 
+    /** Does the target JDK contain SwitchBootstraps class?
+     */
+    public boolean hasSwichBootstraps() {
+        return compareTo(JDK1_10) >= 0;
+    }
+
     /** Value of platform release used to access multi-release jar files
      */
     public String multiReleaseValue() {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Fri Dec 08 17:21:56 2017 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Tue Dec 12 19:41:58 2017 +0100
@@ -203,6 +203,11 @@
     public final Name makeConcat;
     public final Name makeConcatWithConstants;
 
+    // switch
+    public final Name intSwitch;
+    public final Name stringSwitch;
+    public final Name enumSwitch;
+
     public final Name.Table table;
 
     public Names(Context context) {
@@ -364,6 +369,11 @@
         // string concat
         makeConcat = fromString("makeConcat");
         makeConcatWithConstants = fromString("makeConcatWithConstants");
+
+        //switch desugaring:
+        intSwitch = fromString("intSwitch");
+        stringSwitch = fromString("stringSwitch");
+        enumSwitch = fromString("enumSwitch");
     }
 
     protected Name.Table createTable(Options options) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/TestSwitchBootstrap.java	Tue Dec 12 19:41:58 2017 +0100
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * TestSwitchBootstrap
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class TestSwitchBootstrap {
+    private final static Set<Class<?>> ALL_TYPES = Set.of(int.class, short.class, byte.class, char.class,
+                                                          Integer.class, Short.class, Byte.class, Character.class);
+    private final static Set<Class<?>> NON_BYTE_TYPES = Set.of(int.class, Integer.class, short.class, Short.class);
+    private final static Set<Class<?>> SIGNED_TYPES
+            = Set.of(int.class, short.class, byte.class,
+                     Integer.class, Short.class, Byte.class);
+
+    public static final MethodHandle BSM_INT_SWITCH;
+    public static final MethodHandle BSM_STRING_SWITCH;
+    public static final MethodHandle BSM_ENUM_SWITCH;
+
+    static {
+        try {
+            BSM_INT_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "intSwitch",
+                                                               MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int[].class));
+            BSM_STRING_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "stringSwitch",
+                                                                  MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String[].class));
+            BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch",
+                                                                MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Class.class, String[].class));
+        }
+        catch (NoSuchMethodException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private MethodType switchType(Class<?> target) {
+        return MethodType.methodType(int.class, target);
+    }
+
+    private Object box(Class<?> clazz, int i) {
+        if (clazz == Integer.class)
+            return i;
+        else if (clazz == Short.class)
+            return (short) i;
+        else if (clazz == Character.class)
+            return (char) i;
+        else if (clazz == Byte.class)
+            return (byte) i;
+        else
+            throw new IllegalArgumentException(clazz.toString());
+    }
+
+    private void testInt(Set<Class<?>> targetTypes, int... labels) throws Throwable {
+        Map<Class<?>, MethodHandle> mhs
+                = Map.of(char.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(char.class), labels)).dynamicInvoker(),
+                         byte.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(byte.class), labels)).dynamicInvoker(),
+                         short.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(short.class), labels)).dynamicInvoker(),
+                         int.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(int.class), labels)).dynamicInvoker(),
+                         Character.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Character.class), labels)).dynamicInvoker(),
+                         Byte.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Byte.class), labels)).dynamicInvoker(),
+                         Short.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Short.class), labels)).dynamicInvoker(),
+                         Integer.class, ((CallSite) BSM_INT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Integer.class), labels)).dynamicInvoker());
+
+        List<Integer> labelList = IntStream.of(labels)
+                                           .boxed()
+                                           .collect(Collectors.toList());
+
+        for (int i=0; i<labels.length; i++) {
+            // test with invokeExact
+            if (targetTypes.contains(char.class))
+                assertEquals(i, (int) mhs.get(char.class).invokeExact((char) labels[i]));
+            if (targetTypes.contains(byte.class))
+                assertEquals(i, (int) mhs.get(byte.class).invokeExact((byte) labels[i]));
+            if (targetTypes.contains(short.class))
+                assertEquals(i, (int) mhs.get(short.class).invokeExact((short) labels[i]));
+            if (targetTypes.contains(int.class))
+                assertEquals(i, (int) mhs.get(int.class).invokeExact(labels[i]));
+            if (targetTypes.contains(Integer.class))
+                assertEquals(i, (int) mhs.get(Integer.class).invokeExact((Integer) labels[i]));
+            if (targetTypes.contains(Short.class))
+                assertEquals(i, (int) mhs.get(Short.class).invokeExact((Short) (short) labels[i]));
+            if (targetTypes.contains(Byte.class))
+                assertEquals(i, (int) mhs.get(Byte.class).invokeExact((Byte) (byte) labels[i]));
+            if (targetTypes.contains(Character.class))
+                assertEquals(i, (int) mhs.get(Character.class).invokeExact((Character) (char) labels[i]));
+
+            // and with invoke
+            assertEquals(i, (int) mhs.get(int.class).invoke(labels[i]));
+            assertEquals(i, (int) mhs.get(Integer.class).invoke(labels[i]));
+        }
+
+        for (int i=-1000; i<1000; i++) {
+            if (!labelList.contains(i)) {
+                assertEquals(labels.length, mhs.get(short.class).invoke((short) i));
+                assertEquals(labels.length, mhs.get(Short.class).invoke((short) i));
+                assertEquals(labels.length, mhs.get(int.class).invoke(i));
+                assertEquals(labels.length, mhs.get(Integer.class).invoke(i));
+            }
+        }
+
+        assertEquals(-1, (int) mhs.get(Integer.class).invoke(null));
+        assertEquals(-1, (int) mhs.get(Short.class).invoke(null));
+        assertEquals(-1, (int) mhs.get(Byte.class).invoke(null));
+        assertEquals(-1, (int) mhs.get(Character.class).invoke(null));
+    }
+
+    private void testString(String... targets) throws Throwable {
+        MethodHandle indy = ((CallSite) BSM_STRING_SWITCH.invoke(MethodHandles.lookup(), "", switchType(String.class), targets)).dynamicInvoker();
+        List<String> targetList = Stream.of(targets)
+                                        .collect(Collectors.toList());
+
+        for (int i=0; i<targets.length; i++) {
+            String s = targets[i];
+            int result = (int) indy.invoke(s);
+            assertEquals((s == null) ? -1 : i, result);
+        }
+
+        for (String s : List.of("", "A", "AA", "AAA", "AAAA")) {
+            if (!targetList.contains(s)) {
+                assertEquals(targets.length, indy.invoke(s));
+            }
+        }
+        assertEquals(-1, (int) indy.invoke(null));
+    }
+
+    private<E extends Enum<E>> void testEnum(Class<E> enumClass, String... targets) throws Throwable {
+        MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Enum.class), enumClass, targets)).dynamicInvoker();
+        List<E> targetList = Stream.of(targets)
+                                   .map(s -> Enum.valueOf(enumClass, s))
+                                   .collect(Collectors.toList());
+
+        for (int i=0; i<targets.length; i++) {
+            String s = targets[i];
+            E e = Enum.valueOf(enumClass, s);
+            int result = (int) indy.invoke(e);
+            assertEquals((s == null) ? -1 : i, result);
+        }
+
+        for (E e : enumClass.getEnumConstants()) {
+            int index = (int) indy.invoke(e);
+            if (targetList.contains(e))
+                assertEquals(e.name(), targets[index]);
+            else
+                assertEquals(targets.length, index);
+        }
+
+        assertEquals(-1, (int) indy.invoke(null));
+    }
+
+    public void testInt() throws Throwable {
+        testInt(ALL_TYPES, 3, 1, 2);
+        testInt(ALL_TYPES, 1, 2, 3, 4);
+        testInt(SIGNED_TYPES, -1);
+        testInt(ALL_TYPES, new int[] { });
+
+        Random r = new Random();
+        int len = r.nextInt(1000);
+        int[] arr = IntStream.generate(() -> r.nextInt(10000) - 5000)
+                .distinct()
+                .limit(len)
+                .toArray();
+        testInt(NON_BYTE_TYPES, arr);
+    }
+
+    public void testString() throws Throwable {
+        testString("a", "b", "c");
+        testString("c", "b", "a");
+        testString("cow", "pig", "horse", "orangutan", "elephant", "dog", "frog", "ant");
+        testString("a", "b", "c", "A", "B", "C");
+        testString("C", "B", "A", "c", "b", "a");
+        testString("a", null, "c");
+
+        // Tests with hash collisions; Ba/CB, Ca/DB
+        testString("Ba", "CB");
+        testString("Ba", "CB", "Ca", "DB");
+        testString("Ba", "pig", null, "CB", "cow", "Ca", "horse", "DB");
+    }
+
+    enum E1 { A, B }
+    enum E2 { C, D, E, F, G, H }
+
+    public void testEnum() throws Throwable {
+        testEnum(E1.class);
+        testEnum(E1.class, "A");
+        testEnum(E1.class, "A", "B");
+        testEnum(E1.class, "B", "A");
+        testEnum(E2.class, "C");
+        testEnum(E2.class, "C", "D", "E", "F", "H");
+        testEnum(E2.class, "H", "C", "G", "D", "F", "E");
+    }
+}
--- a/test/langtools/tools/javac/switchnull/SwitchNull.java	Fri Dec 08 17:21:56 2017 +0100
+++ b/test/langtools/tools/javac/switchnull/SwitchNull.java	Tue Dec 12 19:41:58 2017 +0100
@@ -1,6 +1,8 @@
 /**
  * @test
- * @compile -doe SwitchNull.java
+ * @compile SwitchNull.java
+ * @run main SwitchNull
+ * @compile -XDenableIndySwitch SwitchNull.java
  * @run main SwitchNull
  */