changeset 55908:49667f599890 records-and-sealed

moving java.lang.compiler to java.lang.runtime
author vromero
date Wed, 01 May 2019 10:46:37 -0400
parents fbf54d7428f1
children 66dd561b3653 bcfd366e2054
files src/java.base/share/classes/java/lang/compiler/Extractor.java src/java.base/share/classes/java/lang/compiler/ExtractorCarriers.java src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java src/java.base/share/classes/java/lang/compiler/PatternSim.java src/java.base/share/classes/java/lang/compiler/SwitchBootstraps.java src/java.base/share/classes/java/lang/compiler/_pattern.java src/java.base/share/classes/java/lang/runtime/Extractor.java src/java.base/share/classes/java/lang/runtime/ExtractorCarriers.java src/java.base/share/classes/java/lang/runtime/ExtractorImpl.java src/java.base/share/classes/java/lang/runtime/PatternSim.java src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java src/java.base/share/classes/java/lang/runtime/_pattern.java src/java.base/share/classes/module-info.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java test/jdk/java/lang/compiler/ExtractorTest.java test/jdk/java/lang/compiler/RecordTest.java test/jdk/java/lang/compiler/SwitchBootstrapsTest.java test/jdk/java/lang/compiler/boottest/TEST.properties test/jdk/java/lang/compiler/boottest/java.base/java/lang/compiler/CarrierTest.java test/jdk/java/lang/extractor/ExtractorTest.java test/jdk/java/lang/extractor/RecordTest.java test/jdk/java/lang/extractor/SwitchBootstrapsTest.java test/jdk/java/lang/extractor/boottest/TEST.properties test/jdk/java/lang/extractor/boottest/java.base/java/lang/runtime/CarrierTest.java
diffstat 24 files changed, 3275 insertions(+), 3276 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/compiler/Extractor.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,563 +0,0 @@
-/*
- * 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 java.lang.compiler;
-
-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.util.Arrays;
-import java.util.Objects;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
-
-import sun.invoke.util.BytecodeName;
-
-import static java.lang.invoke.MethodHandleInfo.REF_invokeInterface;
-import static java.lang.invoke.MethodHandleInfo.REF_invokeStatic;
-import static java.lang.invoke.MethodHandleInfo.REF_invokeVirtual;
-import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial;
-import static java.util.Objects.requireNonNull;
-
-/**
- * Supporting type for implementation of pattern matching.  An {@linkplain Extractor}
- * is a constant bundle of method handles that describe a particular pattern, and
- * suitable for storing in the constant pool.
- *
- * <p>An {@linkplain Extractor} is describe by a {@code descriptor} in the form
- * of a {@link MethodType}.  The return value of the descriptor is ignored; the
- * argument types of the descriptor indicate the types of the output binding
- * variables.
- *
- * Notes:
- *  - totality is erased;
- *  - compilers expected to optimize away total type patterns;
- *  - adaptation done in nest() and switch combinators
- *
- * @author Brian Goetz
- */
-public interface Extractor {
-    /**
-     * A method handle that attempts to perform a match.  It will have type
-     * {@code (Object)Object}.  It accepts the target to be matched, and returns
-     * an opaque carrier if the match succeeds, or {@code null} if it fails.
-     *
-     * @return the {@code tryMatch} method handle
-     */
-    MethodHandle tryMatch();
-
-    /**
-     * A method handle that extracts a component from the match carrier.  It
-     * will take the match carrier and return the corresponding match binding.
-     *
-     * @param i the index of the component
-     * @return the {@code component} method handle
-     */
-    MethodHandle component(int i);
-
-    /**
-     * Returns the component method handles, as an array
-     * @return the component method handles
-     */
-    MethodHandle[] components();
-
-    /**
-     * The descriptor of the {@linkplain Extractor}.  The parameter types of
-     * the descriptor are the types of the binding variables.  The return type
-     * is ignored.
-     *
-     * @return the descriptor
-     */
-    MethodType descriptor();
-
-    /**
-     * Compose an extractor with a method handle that receives the bindings
-     *
-     * @param target method handle to receive the bindings
-     * @param sentinel value to return when the extractor does not match
-     * @return the composed method handle
-     */
-    default MethodHandle compose(MethodHandle target, Object sentinel) {
-        int count = descriptor().parameterCount();
-        MethodHandle[] components = components();
-        Class<?> carrierType = tryMatch().type().returnType();
-        Class<?> resultType = target.type().returnType();
-
-        MethodHandle mh = MethodHandles.filterArguments(target, 0, components);
-        mh = MethodHandles.permuteArguments(mh, MethodType.methodType(resultType, carrierType), new int[count]);
-        mh = MethodHandles.guardWithTest(ExtractorImpl.MH_OBJECTS_NONNULL.asType(MethodType.methodType(boolean.class, carrierType)),
-                                         mh,
-                                         MethodHandles.dropArguments(MethodHandles.constant(resultType, sentinel), 0, carrierType));
-        mh = MethodHandles.filterArguments(mh, 0, tryMatch());
-        return mh;
-    }
-
-    private static MethodType descriptor(Class<?> targetType, MethodHandle[] components) {
-        Class<?>[] paramTypes = Stream.of(components)
-                                      .map(mh -> mh.type().returnType())
-                                      .toArray(Class[]::new);
-        return MethodType.methodType(targetType, paramTypes);
-    }
-
-    private static MethodHandle carrierTryExtract(MethodType descriptor, MethodHandle[] components) {
-        MethodHandle carrierFactory = ExtractorCarriers.carrierFactory(descriptor);
-        int[] reorder = new int[descriptor.parameterCount()]; // default value is what we want already
-
-        return MethodHandles.permuteArguments(MethodHandles.filterArguments(carrierFactory, 0, components),
-                                              MethodType.methodType(carrierFactory.type().returnType(), descriptor.returnType()),
-                                              reorder);
-    }
-
-    /**
-     * Construct a partial method handle that uses the predicate as guardWithTest,
-     * which applies the target if the test succeeds, and returns null if the
-     * test fails.  The resulting method handle is of the same type as the
-     * {@code target} method handle.
-     * @param target
-     * @param predicate
-     * @return
-     */
-    private static MethodHandle partialize(MethodHandle target, MethodHandle predicate) {
-        Class<?> targetType = target.type().parameterType(0);
-        Class<?> carrierType = target.type().returnType();
-        return MethodHandles.guardWithTest(predicate,
-                                           target,
-                                           MethodHandles.dropArguments(MethodHandles.constant(carrierType, null),
-                                                                       0, targetType));
-    }
-
-    /**
-     * Construct a method handle that delegates to target, unless the nth argument
-     * is null, in which case it returns null
-     */
-    private static MethodHandle bailIfNthNull(MethodHandle target, int n) {
-        MethodHandle test = ExtractorImpl.MH_OBJECTS_ISNULL.asType(ExtractorImpl.MH_OBJECTS_ISNULL.type().changeParameterType(0, target.type().parameterType(n)));
-        test = MethodHandles.permuteArguments(test, target.type().changeReturnType(boolean.class), n);
-        MethodHandle nullh = MethodHandles.dropArguments(MethodHandles.constant(target.type().returnType(), null), 0, target.type().parameterArray());
-        return MethodHandles.guardWithTest(test, nullh, target);
-    }
-
-    /**
-     * Create a total {@linkplain Extractor} with the given descriptor, which
-     * operates by feeding results into a factory method handle and returning
-     * the result.
-     *
-     * @param descriptor the descriptor
-     * @param digester the digester method handle
-     * @return the extractor
-     */
-    public static Extractor of(MethodType descriptor,
-                               MethodHandle digester) {
-        return new ExtractorImpl(descriptor,
-                                 MethodHandles.insertArguments(digester,
-                                                               1, ExtractorCarriers.carrierFactory(descriptor)),
-                                 ExtractorCarriers.carrierComponents(descriptor));
-    }
-
-    /**
-     * Create a total {@linkplain Extractor} for a target of type {@code targetType}
-     * and a given set of component method handles.
-     *
-     * @param targetType The type of the match target
-     * @param components The component method handles
-     * @return the extractor
-     */
-    public static Extractor ofTotal(Class<?> targetType, MethodHandle... components) {
-        MethodType descriptor = descriptor(targetType, components);
-        return new ExtractorImpl(descriptor,
-                                 carrierTryExtract(descriptor, components),
-                                 ExtractorCarriers.carrierComponents(descriptor));
-    }
-
-    /**
-     * Create a total {@linkplain Extractor} for a target of type {@code targetType}
-     * and a given set of component method handles, using itself as a carrier.
-     *
-     * @param targetType The type of the match target
-     * @param components The component method handles
-     * @return the extractor
-     */
-    public static Extractor ofSelfTotal(Class<?> targetType, MethodHandle... components) {
-        return new ExtractorImpl(descriptor(targetType, components),
-                                 MethodHandles.identity(targetType), components);
-    }
-
-    /**
-     * Create a partial {@linkplain Extractor} for a given set of component
-     * method handles.
-     *
-     * @param targetType the target type
-     * @param predicate The match predicate
-     * @param components The component method handles
-     * @return the extractor
-     */
-    public static Extractor ofPartial(Class<?> targetType, MethodHandle predicate, MethodHandle... components) {
-        MethodType descriptor = descriptor(targetType, components);
-        MethodHandle carrierTryExtract = carrierTryExtract(descriptor, components);
-        return new ExtractorImpl(descriptor,
-                                 partialize(carrierTryExtract, predicate),
-                                 ExtractorCarriers.carrierComponents(descriptor));
-    }
-
-    /**
-     * Create a partial {@linkplain Extractor} for a given set of component
-     * method handles, using itself as a carrier.
-     *
-     * @param targetType the target type
-     * @param predicate The match predicate
-     * @param components The component method handles
-     * @return the extractor
-     */
-    public static Extractor ofSelfPartial(Class<?> targetType, MethodHandle predicate, MethodHandle... components) {
-        return new ExtractorImpl(descriptor(targetType, components),
-                                 partialize(MethodHandles.identity(targetType), predicate),
-                                 components);
-    }
-
-    /**
-     * Create an {@linkplain Extractor} for a type pattern, with a single binding
-     * variable, whose target type is {@code Object}
-     *
-     * @param type the type to match against
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor ofType(Class<?> type) {
-        requireNonNull(type);
-        if (type.isPrimitive())
-            throw new IllegalArgumentException("Reference type expected, found: " + type);
-        return new ExtractorImpl(MethodType.methodType(type, type),
-                                 ExtractorImpl.MH_OF_TYPE_HELPER.bindTo(type).asType(MethodType.methodType(type, type)),
-                                 MethodHandles.identity(type));
-    }
-
-    /**
-     * Create an {@linkplain Extractor} for a constant pattern
-     *
-     * @param o the constant
-     * @return the extractor
-     */
-    public static Extractor ofConstant(Object o) {
-        Class<?> type = o == null ? Object.class : o.getClass();
-        MethodHandle match = partialize(MethodHandles.dropArguments(MethodHandles.constant(Object.class, Boolean.TRUE), 0, type),
-                                        MethodHandles.insertArguments(ExtractorImpl.MH_OBJECTS_EQUAL, 0, o)
-                                                     .asType(MethodType.methodType(boolean.class, type)));
-        return new ExtractorImpl(MethodType.methodType(type), match);
-    }
-
-    /**
-     * Create an {@linkplain Extractor} for a nullable type pattern, with a
-     * single binding variable, whose target type is {@code Object}
-     *
-     * @param type the type to match against
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor ofTypeNullable(Class<?> type) {
-        requireNonNull(type);
-        if (type.isPrimitive())
-            throw new IllegalArgumentException("Reference type expected, found: " + type);
-        return new ExtractorImpl(MethodType.methodType(type, type),
-                                 ExtractorImpl.MH_OF_TYPE_NULLABLE_HELPER.bindTo(type).asType(MethodType.methodType(type, type)),
-                                 MethodHandles.identity(type));
-    }
-
-    /**
-     * Create an {@linkplain Extractor} that is identical to another {@linkplain Extractor},
-     * but without the specified binding variables
-     * @param etor the original extractor
-     * @param positions which binding variables to drop
-     * @return the extractor
-     */
-    public static Extractor dropBindings(Extractor etor, int... positions) {
-        MethodHandle[] mhs = etor.components();
-        for (int position : positions)
-            mhs[position] = null;
-        mhs = Stream.of(mhs).filter(Objects::nonNull).toArray(MethodHandle[]::new);
-        return new ExtractorImpl(descriptor(etor.descriptor().returnType(), mhs), etor.tryMatch(), mhs);
-    }
-
-    /**
-     * Adapt an extractor to a new target type
-     *
-     * @param e the extractor
-     * @param newTarget the new target type
-     * @return the new extractor
-     */
-    public static Extractor adapt(Extractor e, Class<?> newTarget) {
-        if (e.descriptor().returnType().isAssignableFrom(newTarget))
-            return e;
-        MethodHandle tryMatch = partialize(e.tryMatch().asType(e.tryMatch().type().changeParameterType(0, newTarget)),
-                                           ExtractorImpl.MH_ADAPT_HELPER.bindTo(e.descriptor().returnType())
-        .asType(MethodType.methodType(boolean.class, newTarget)));
-        return new ExtractorImpl(e.descriptor().changeReturnType(newTarget),
-                                 tryMatch, e.components());
-    }
-
-    /**
-     * Construct a nested extractor, which first matches the target to the
-     * outer extractor, and then matches the resulting bindings to the inner
-     * extractors (if not null).  The resulting extractor is partial if any
-     * of the input extractors are; its target type is the target type of the
-     * outer extractor; and its bindings are the concatenation of the bindings
-     * of the outer extractor followed by the bindings of the non-null inner
-     * extractors.
-     *
-     * @param outer The outer extractor
-     * @param extractors The inner extractors, or null if no nested extraction
-     *                   for this outer binding is desired
-     * @return the nested extractor
-     */
-    public static Extractor ofNested(Extractor outer, Extractor... extractors) {
-        int outerCount = outer.descriptor().parameterCount();
-        Class<?> outerCarrierType = outer.tryMatch().type().returnType();
-
-        // Adapt inners to types of outer bindings
-        for (int i = 0; i < extractors.length; i++) {
-            Extractor extractor = extractors[i];
-            if (extractor.descriptor().returnType() != outer.descriptor().parameterType(i))
-                extractors[i] = adapt(extractor, outer.descriptor().parameterType(i));
-        }
-
-        int[] innerPositions = IntStream.range(0, extractors.length)
-                                        .filter(i -> extractors[i] != null)
-                                        .toArray();
-        MethodHandle[] innerComponents = Stream.of(extractors)
-                                               .filter(Objects::nonNull)
-                                               .map(Extractor::components)
-                                               .flatMap(Stream::of)
-                                               .toArray(MethodHandle[]::new);
-        MethodHandle[] innerTryMatches = Stream.of(extractors)
-                                               .filter(Objects::nonNull)
-                                               .map(e -> e.tryMatch())
-                                               .toArray(MethodHandle[]::new);
-        Class<?>[] innerCarriers = Stream.of(extractors)
-                                         .filter(Objects::nonNull)
-                                         .map(e -> e.tryMatch().type().returnType())
-                                         .toArray(Class[]::new);
-        Class<?>[] innerTypes = Stream.of(innerComponents)
-                                      .map(mh -> mh.type().returnType())
-                                      .toArray(Class[]::new);
-
-        MethodType descriptor = outer.descriptor().appendParameterTypes(innerTypes);
-
-        MethodHandle mh = ExtractorCarriers.carrierFactory(descriptor);
-        mh = MethodHandles.filterArguments(mh, outerCount, innerComponents);
-        int[] spreadInnerCarriers = new int[outerCount + innerComponents.length];
-        for (int i=0; i<outerCount; i++)
-            spreadInnerCarriers[i] = i;
-        int k = outerCount;
-        int j = 0;
-        for (Extractor e : extractors) {
-            if (e == null)
-                continue;
-            for (int i=0; i<e.descriptor().parameterCount(); i++)
-                spreadInnerCarriers[k++] = outerCount + j;
-            j++;
-        }
-        MethodType spreadInnerCarriersMT = outer.descriptor()
-                                                .appendParameterTypes(innerCarriers)
-                                                .changeReturnType(mh.type().returnType());
-        mh = MethodHandles.permuteArguments(mh, spreadInnerCarriersMT, spreadInnerCarriers);
-        for (int position : innerPositions)
-            mh = bailIfNthNull(mh, outerCount + position);
-        mh = MethodHandles.filterArguments(mh, outerCount, innerTryMatches);
-        int[] spreadNestedCarrier = new int[outerCount + innerPositions.length];
-        for (int i=0; i<outerCount; i++)
-            spreadNestedCarrier[i] = i;
-        for (int i=0; i<innerPositions.length; i++)
-            spreadNestedCarrier[outerCount+i] = innerPositions[i];
-        mh = MethodHandles.permuteArguments(mh, outer.descriptor().changeReturnType(mh.type().returnType()),
-                                            spreadNestedCarrier);
-        mh = MethodHandles.filterArguments(mh, 0, outer.components());
-        mh = MethodHandles.permuteArguments(mh, MethodType.methodType(mh.type().returnType(), outerCarrierType),
-                                            new int[outerCount]);
-        mh = bailIfNthNull(mh, 0);
-        mh = MethodHandles.filterArguments(mh, 0, outer.tryMatch());
-
-        MethodHandle tryExtract = mh;
-
-        return new ExtractorImpl(descriptor, tryExtract, ExtractorCarriers.carrierComponents(descriptor));
-    }
-
-
-    /**
-     * Bootstrap for creating a lazy, partial, self-carrier {@linkplain Extractor} from components
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code ()Extractor}
-     * @param descriptor the descriptor method type
-     * @param components the {@code components} method handles
-     * @return a callsite
-     * @throws Throwable doc
-     */
-    public static CallSite makeLazyExtractor(MethodHandles.Lookup lookup, String constantName, MethodType constantType,
-                                             MethodType descriptor, MethodHandle... components) throws Throwable {
-        return new ConstantCallSite(MethodHandles.constant(Extractor.class, ofSelfTotal(descriptor.returnType(), components)));
-    }
-
-    /**
-     * Condy bootstrap for creating lazy extractors
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType ignored
-     * @param descriptor the extractor descriptor
-     * @param components the extractor components
-     * @return the extractor factory
-     * @throws Throwable if something went wrong
-     */
-    public static Extractor ofSelfTotal(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                        MethodType descriptor, MethodHandle... components) throws Throwable {
-        return ofSelfTotal(descriptor.returnType(), components);
-    }
-
-    /**
-     * Condy bootstrap for creating nested extractors
-     *
-     * @param lookup ignored
-     * @param invocationName ignored
-     * @param invocationType must be {@code Class<Extractor>}
-     * @param outer the outer extractor
-     * @param inners the inner extractors, null if no nesting is needed for this binding
-     * @return the nested extractor
-     */
-    public static Extractor ofNested(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
-                                     Extractor outer, Extractor... inners) {
-        return ofNested(outer, inners);
-    }
-
-    /**
-     * Condy bootstrap for creating non-nullable type extractor
-     *
-     * @param lookup ignored
-     * @param invocationName ignored
-     * @param invocationType must be {@code Class<Extractor>}
-     * @param type the type
-     * @return the extractor
-     */
-    public static Extractor ofType(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
-                                   Class<?> type) {
-        return ofType(type);
-    }
-
-    /**
-     * Condy bootstrap for creating nullable type extractor
-     *
-     * @param lookup ignored
-     * @param invocationName ignored
-     * @param invocationType must be {@code Class<Extractor>}
-     * @param type the type
-     * @return the extractor
-     */
-    public static Extractor ofTypeNullable(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
-                                           Class<?> type) {
-        return ofTypeNullable(type);
-    }
-
-    /**
-     * Condy bootstrap for creating constant extractor
-     *
-     * @param lookup ignored
-     * @param invocationName ignored
-     * @param invocationType must be {@code Class<Extractor>}
-     * @param constant the constant
-     * @return the extractor
-     */
-    public static Extractor ofConstant(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
-                                       Object constant) {
-        return ofConstant(constant);
-    }
-
-    /**
-     * Condy bootstrap for finding extractors
-     *
-     * @param lookup the lookup context
-     * @param constantName ignored
-     * @param constantType ignored
-     * @param owner the class containing the extractor
-     * @param descriptor the extractor descriptor
-     * @param name the extractor name
-     * @param refKind the kind of method
-     * @return the extractor
-     * @throws Throwable if something went wrong
-     */
-    public static Extractor findExtractor(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                          Class<?> owner, MethodType descriptor, String name, int refKind) throws Throwable {
-        String dd = descriptor.toMethodDescriptorString();
-        String patternMethodName
-                = BytecodeName.toBytecodeName(String.format("$pattern$%s$%s",
-                                                            (refKind == REF_newInvokeSpecial ? owner.getSimpleName() : name),
-                                                            dd.substring(0, dd.indexOf(')') + 1)));
-        MethodType factoryDesc = MethodType.methodType(Extractor.class);
-        MethodHandle mh;
-        switch (refKind) {
-            case REF_invokeStatic:
-            case REF_newInvokeSpecial:
-                mh = lookup.findStatic(owner, patternMethodName, factoryDesc);
-                break;
-            case REF_invokeVirtual:
-            case REF_invokeInterface:
-                mh = lookup.findVirtual(owner, patternMethodName, factoryDesc);
-                break;
-            default:
-                throw new IllegalAccessException(Integer.toString(refKind));
-        }
-
-        return (Extractor) mh.invoke();
-    }
-
-    /**
-     * Bootstrap for extracting the {@code tryMatch} method handle from a {@linkplain Extractor}
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code MethodHandle.class}
-     * @param extractor the {@linkplain Extractor}
-     * @return the {@code tryMatch} method handle
-     */
-
-
-    public static MethodHandle extractorTryMatch(MethodHandles.Lookup lookup, String constantName, Class<MethodHandle> constantType,
-                                                 Extractor extractor) {
-        return extractor.tryMatch();
-    }
-
-    /**
-     * Bootstrap for extracting a {@code component} method handle from a {@linkplain Extractor}
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code MethodHandle.class}
-     * @param extractor the {@linkplain Extractor}
-     * @param i the component index
-     * @return the {@code component} method handle
-     */
-    public static MethodHandle extractorComponent(MethodHandles.Lookup lookup, String constantName, Class<MethodHandle> constantType,
-                                                  Extractor extractor, int i) {
-        return extractor.component(i);
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/compiler/ExtractorCarriers.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * 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 java.lang.compiler;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.util.Arrays;
-
-/**
- * PatternCarriers
- */
-public class ExtractorCarriers {
-
-    private static final CarrierFactory factory = CarrierFactories.DUMB;
-
-    interface CarrierFactory {
-        MethodHandle constructor(MethodType methodType);
-        MethodHandle component(MethodType methodType, int component);
-    }
-
-    static class DumbCarrier {
-        private final Object[] args;
-
-        DumbCarrier(Object... args) {
-            this.args = args.clone();
-        }
-
-        Object get(int i) {
-            return args[i];
-        }
-    }
-
-    enum CarrierFactories implements CarrierFactory {
-        DUMB {
-            private final MethodHandle CARRIER_CTOR;
-            private final MethodHandle CARRIER_GET;
-
-            {
-                try {
-                    CARRIER_CTOR = MethodHandles.lookup().findConstructor(DumbCarrier.class, MethodType.methodType(void.class, Object[].class));
-                    CARRIER_GET = MethodHandles.lookup().findVirtual(DumbCarrier.class, "get", MethodType.methodType(Object.class, int.class));
-                }
-                catch (ReflectiveOperationException e) {
-                    throw new ExceptionInInitializerError(e);
-                }
-            }
-
-            @Override
-            public MethodHandle constructor(MethodType methodType) {
-                return CARRIER_CTOR.asType(methodType.changeReturnType(Object.class));
-            }
-
-            @Override
-            public MethodHandle component(MethodType methodType, int component) {
-                return MethodHandles.insertArguments(CARRIER_GET, 1, component)
-                                    .asType(MethodType.methodType(methodType.parameterType(component), Object.class));
-            }
-        },
-        DUMB_SINGLE {
-            // An optimization of DUMB, where we use the value itself as carrier when there is only one value
-
-            @Override
-            public MethodHandle constructor(MethodType methodType) {
-                return methodType.parameterCount() == 1 ? MethodHandles.identity(methodType.parameterType(0)) : DUMB.constructor(methodType);
-            }
-
-            @Override
-            public MethodHandle component(MethodType methodType, int component) {
-                return methodType.parameterCount() == 1 ? MethodHandles.identity(methodType.parameterType(0)) : DUMB.component(methodType, component);
-            }
-        }
-    }
-
-    /**
-     * Returns a method handle with the given method type that instantiates
-     * a new carrier object.
-     *
-     * @param methodType the types of the carrier elements
-     * @return the carrier factory
-     */
-    public static MethodHandle carrierFactory(MethodType methodType) {
-        return factory.constructor(methodType);
-    }
-
-    /**
-     * Returns a method handle that accepts a carrier and returns the i'th component
-     *
-     * @param methodType the type of the carrier elements
-     * @param i the index of the component
-     * @return the component method handle
-     */
-    public static MethodHandle carrierComponent(MethodType methodType, int i) {
-        return factory.component(methodType, i);
-    }
-
-    /**
-     * Return all the components method handles for a carrier
-     * @param methodType the type of the carrier elements
-     * @return the component method handles
-     */
-    public static MethodHandle[] carrierComponents(MethodType methodType) {
-        MethodHandle[] components = new MethodHandle[methodType.parameterCount()];
-        Arrays.setAll(components, i -> factory.component(methodType, i));
-        return components;
-    }
-}
--- a/src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/*
- * 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 java.lang.compiler;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Non-public implementation of {@link Extractor}
- */
-class ExtractorImpl implements Extractor {
-    private final MethodType descriptor;
-    private final MethodHandle tryMatch;
-    private final List<MethodHandle> components;
-
-    // These are helpers for Extractors
-    static final MethodHandle MH_OF_TYPE_HELPER;
-    static final MethodHandle MH_OF_TYPE_NULLABLE_HELPER;
-    static final MethodHandle MH_ADAPT_HELPER;
-    static final MethodHandle MH_OBJECTS_ISNULL;
-    static final MethodHandle MH_OBJECTS_NONNULL;
-    static final MethodHandle MH_OBJECTS_EQUAL;
-    static {
-        try {
-            MH_OF_TYPE_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "ofTypeHelper", MethodType.methodType(Object.class, Class.class, Object.class));
-            MH_OF_TYPE_NULLABLE_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "ofTypeNullableHelper", MethodType.methodType(Object.class, Class.class, Object.class));
-            MH_ADAPT_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "adaptHelper", MethodType.methodType(boolean.class, Class.class, Object.class));
-            MH_OBJECTS_ISNULL = MethodHandles.lookup().findStatic(Objects.class, "isNull", MethodType.methodType(boolean.class, Object.class));
-            MH_OBJECTS_NONNULL = MethodHandles.lookup().findStatic(Objects.class, "nonNull", MethodType.methodType(boolean.class, Object.class));
-            MH_OBJECTS_EQUAL = MethodHandles.lookup().findStatic(Objects.class, "equals", MethodType.methodType(boolean.class, Object.class, Object.class));
-        }
-        catch (ReflectiveOperationException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
-    /**
-     * Construct an {@link Extractor} from components
-     * Constraints:
-     *  - output of tryMatch must match input of components
-     *  - input of tryMatch must match descriptor
-     *  - output of components must match descriptor
-     *
-     * @param descriptor The {@code descriptor} method type
-     * @param tryMatch The {@code tryMatch} method handle
-     * @param components The {@code component} method handles
-     */
-    ExtractorImpl(MethodType descriptor, MethodHandle tryMatch, MethodHandle... components) {
-        Class<?> carrierType = tryMatch.type().returnType();
-        if (descriptor.parameterCount() != components.length)
-            throw new IllegalArgumentException(String.format("MethodType %s arity should match component count %d", descriptor, components.length));
-        if (!descriptor.returnType().equals(tryMatch.type().parameterType(0)))
-            throw new IllegalArgumentException(String.format("Descriptor %s should match tryMatch input %s", descriptor, tryMatch.type()));
-        for (int i = 0; i < components.length; i++) {
-            MethodHandle component = components[i];
-            if (component.type().parameterCount() != 1
-                || component.type().returnType().equals(void.class)
-                || !component.type().parameterType(0).equals(carrierType))
-                throw new IllegalArgumentException("Invalid component descriptor " + component.type());
-            if (!component.type().returnType().equals(descriptor.parameterType(i)))
-                throw new IllegalArgumentException(String.format("Descriptor %s should match %d'th component %s", descriptor, i, component));
-        }
-
-        this.descriptor = descriptor;
-        this.tryMatch = tryMatch;
-        this.components = List.of(components);
-    }
-
-    @Override
-    public MethodHandle tryMatch() {
-        return tryMatch;
-    }
-
-    @Override
-    public MethodHandle component(int i) {
-        return components.get(i);
-    }
-
-    @Override
-    public MethodHandle[] components() {
-        return components.toArray(new MethodHandle[0]);
-    }
-
-    @Override
-    public MethodType descriptor() {
-        return descriptor;
-    }
-
-    private static Object ofTypeHelper(Class<?> type, Object o) {
-        return o != null && type.isAssignableFrom(o.getClass()) ? o : null;
-    }
-
-    private static Object ofTypeNullableHelper(Class<?> type, Object o) {
-        return o == null || type.isAssignableFrom(o.getClass()) ? o : null;
-    }
-
-    private static boolean adaptHelper(Class<?> type, Object o) {
-        return o != null && type.isAssignableFrom(o.getClass());
-    }
-}
--- a/src/java.base/share/classes/java/lang/compiler/PatternSim.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,362 +0,0 @@
-/*
- * 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 java.lang.compiler;
-
-import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-/**
- * Temporary scaffolding to allow matching / switching on constrained patterns
- * without language support.
- */
-public class PatternSim {
-    private static String DEFAULT_LABEL = "";
-
-    /**
-     * Simulator for statement switch
-     * @param label the switch label
-     * @param target the switch target
-     * @return the switch simulator
-     */
-    public static StatementSwitch _switch(String label, Object target) {
-        return new SwitchImpl(target, label);
-    }
-
-    /**
-     * Simulator for statement switch
-     * @param target the switch target
-     * @return the switch simulator
-     */
-    public static StatementSwitch _switch(Object target) {
-        return new SwitchImpl(target, DEFAULT_LABEL);
-    }
-
-    /**
-     * Simulator for expression switch
-     * @param label the switch label
-     * @param target the switch target
-     * @param <T> the the return type
-     * @return the switch simulator
-     */
-    public static<T> ExpressionSwitch<T> _expswitch(String label, Object target) {
-        return new ExprSwitchImpl<>(target, label);
-    }
-
-    /**
-     * Simulator for expression switch
-     * @param target the switch target
-     * @param <T> the the return type
-     * @return the switch simulator
-     */
-    public static<T> ExpressionSwitch<T> _expswitch(Object target) {
-        return new ExprSwitchImpl<>(target, DEFAULT_LABEL);
-    }
-
-    /**
-     * Simulator for continuing out of a switch
-     */
-    public static void _continue() {
-        throw new ContinueSignal(DEFAULT_LABEL);
-    }
-
-    /**
-     * Simulator for continuing out of the labeled switch
-     *
-     * @param label the label of the switch to continue at
-     */
-    public static void _continue(String label) {
-        throw new ContinueSignal(label);
-    }
-
-    /**
-     * Simulator type for statement switch
-     */
-    public interface StatementSwitch {
-        /**
-         * Simulate a case of a statement switch
-         * @param pattern the pattern to match against
-         * @param action the success action
-         * @param <B> the type of the binding variable
-         * @return the switch
-         */
-        <B> StatementSwitch _case(Supplier<_pattern<B>> pattern, Consumer<B> action);
-
-        /**
-         * Simulate a case of a statement switch
-         * @param pattern the pattern to match against
-         * @param action the success action
-         * @param <B> the type of the binding variable
-         * @return the switch
-         */
-        <B> StatementSwitch _case(Supplier<_pattern<B>> pattern, Runnable action);
-
-        /**
-         * Simulate the default clause of a statement switch
-         * @param r the action
-         * @return the switch
-         */
-        StatementSwitch _default(Runnable r);
-    }
-
-    /**
-     * Simulator type for expression switch
-     * @param <T> the switch type
-     */
-    public interface ExpressionSwitch<T> {
-        /**
-         * Simulate a case of an expression switch
-         * @param pattern the pattern to match against
-         * @param action the success action
-         * @param <B> the type of the binding variable
-         * @return the switch
-         */
-        <B> ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Function<B, T> action);
-
-        /**
-         * Simulate a case of an expression switch
-         * @param pattern the pattern to match against
-         * @param action the success action
-         * @param <B> the type of the binding variable
-         * @return the switch
-         */
-        <B> ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Supplier<T> action);
-
-        /**
-         * Simulate the default clause of an expression switch
-         * @param r the action
-         * @return the switch
-         */
-        ExpressionSwitch<T> _default(Supplier<T> r);
-
-        /**
-         * Get the result of an expression switch
-         * @return the result
-         */
-        T result();
-    }
-
-    /**
-     * Helper method for nested pattern in a statement switch
-     * @param target the nested target
-     * @param pattern the nested pattern
-     * @param action the success action
-     * @param <B> the type of the nested target
-     */
-    public static<B> void _nest(Object target, Supplier<_pattern<B>> pattern, Consumer<B> action) {
-        Optional<B> match = pattern.get().match(target);
-        if (match.isPresent())
-            action.accept(match.get());
-        else
-            throw new ContinueSignal("<$nested$>");
-    }
-
-    /**
-     * Helper method for nested pattern in a statement switch
-     * @param target the nested target
-     * @param pattern the nested pattern
-     * @param action the success action
-     * @param <B> the type of the nested target
-     */
-    public static<B> void _nest(Object target, Supplier<_pattern<B>> pattern, Runnable action) {
-        Optional<B> match = pattern.get().match(target);
-        if (match.isPresent())
-            action.run();
-        else
-            throw new ContinueSignal("<$nested$>");
-    }
-
-    /**
-     * Helper method for nested pattern in an expression switch
-     * @param target the nested target
-     * @param pattern the nested pattern
-     * @param action the success action
-     * @param <B> the type of the nested target
-     * @param <T> the return type of the success action
-     * @return the return value of the success action
-     */
-    public static<B, T> T _expnest(Object target, Supplier<_pattern<B>> pattern, Function<B, T> action) {
-        Optional<B> match = pattern.get().match(target);
-        if (match.isPresent())
-            return action.apply(match.get());
-        else
-            throw new ContinueSignal("<$nested$>");
-    }
-
-    /**
-     * Helper method for nested pattern in an expression switch
-     * @param target the nested target
-     * @param pattern the nested pattern
-     * @param action the success action
-     * @param <B> the type of the nested target
-     * @param <T> the return type of the success action
-     * @return the return value of the success action
-     */
-    public static<B, T> T _expnest(Object target, Supplier<_pattern<B>> pattern, Supplier<T> action) {
-        Optional<B> match = pattern.get().match(target);
-        if (match.isPresent())
-            return action.get();
-        else
-            throw new ContinueSignal("<$nested$>");
-    }
-
-    @SuppressWarnings("serial")
-    static class ContinueSignal extends RuntimeException {
-        String label;
-
-        ContinueSignal(String label) {
-            super();
-            this.label = label;
-        }
-
-        void maybeRethrow(String label) {
-            if (!this.label.equals(label))
-                throw this;
-        }
-    }
-}
-
-class SwitchImpl implements PatternSim.StatementSwitch {
-    private final Object target;
-    private final String label;
-    private boolean done = false;
-
-    SwitchImpl(Object target,
-               String label) {
-        this.target = target;
-        this.label = label;
-    }
-
-    public <B> PatternSim.StatementSwitch _case(Supplier<_pattern<B>> pattern, Consumer<B> action) {
-        if (!done) {
-            Optional<B> match = pattern.get().match(target);
-            if (match.isPresent()) {
-                try {
-                    action.accept(match.get());
-                    done = true;
-                }
-                catch (PatternSim.ContinueSignal signal) {
-                    signal.maybeRethrow(label);
-                }
-            }
-        }
-        return this;
-    }
-
-    public <B> PatternSim.StatementSwitch _case(Supplier<_pattern<B>> pattern, Runnable action) {
-        if (!done) {
-            Optional<B> match = pattern.get().match(target);
-            if (match.isPresent()) {
-                try {
-                    action.run();
-                    done = true;
-                }
-                catch (PatternSim.ContinueSignal signal) {
-                    signal.maybeRethrow(label);
-                }
-            }
-        }
-        return this;
-    }
-
-    public PatternSim.StatementSwitch _default(Runnable r) {
-        if (!done) {
-            try {
-                r.run();
-                done = true;
-            }
-            catch (PatternSim.ContinueSignal signal) {
-                signal.maybeRethrow(label);
-            }
-        }
-        return this;
-    }
-
-}
-
-class ExprSwitchImpl<T> implements PatternSim.ExpressionSwitch<T> {
-    private final Object target;
-    private final String label;
-    private boolean done = false;
-    private T result = null;
-
-    ExprSwitchImpl(Object target,
-                   String label) {
-        this.target = target;
-        this.label = label;
-    }
-
-    public<B> PatternSim.ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Function<B, T> action) {
-        if (!done) {
-            Optional<B> match = pattern.get().match(target);
-            if (match.isPresent()) {
-                try {
-                    result = action.apply(match.get());
-                    done = true;
-                }
-                catch (PatternSim.ContinueSignal signal) {
-                    signal.maybeRethrow(label);
-                }
-            }
-        }
-        return this;
-    }
-
-    @Override
-    public <B> PatternSim.ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Supplier<T> action) {
-        if (!done) {
-            Optional<B> match = pattern.get().match(target);
-            if (match.isPresent()) {
-                try {
-                    result = action.get();
-                    done = true;
-                }
-                catch (PatternSim.ContinueSignal signal) {
-                    signal.maybeRethrow(label);
-                }
-            }
-        }
-        return this;
-    }
-
-    public PatternSim.ExpressionSwitch<T> _default(Supplier<T> r) {
-        if (!done) {
-            try {
-                result = r.get();
-                done = true;
-            }
-            catch (PatternSim.ContinueSignal signal) {
-                signal.maybeRethrow(label);
-            }
-        }
-        return this;
-    }
-
-    public T result() {
-        return result;
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/compiler/SwitchBootstraps.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,882 +0,0 @@
-/*
- * 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.compiler;
-
-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.util.Arrays;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.stream.IntStream;
-import java.util.stream.LongStream;
-import java.util.stream.Stream;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Bootstrap methods for linking {@code invokedynamic} call sites that implement
- * the selection functionality of the {@code switch} statement.  The bootstraps
- * take additional static arguments corresponding to the {@code case} labels
- * of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}.
- *
- * <p>The bootstrap call site accepts a single parameter of the type of the
- * operand of the {@code switch}, and return an {@code int} that is the index of
- * the matched {@code case} label, {@code -1} if the target is {@code null},
- * or {@code N} if the target is not null but matches no {@code case} label.
- */
-public class SwitchBootstraps {
-
-    // Shared INIT_HOOK for all switch call sites; looks the target method up in a map
-    private static final MethodHandle CONSTANT_INIT_HOOK;
-    private static final MethodHandle PATTERN_INIT_HOOK;
-    private static final MethodHandle TYPE_INIT_HOOK;
-    private static final MethodHandle PATTERN_SWITCH_METHOD;
-    private static final MethodHandle TYPE_SWITCH_METHOD;
-    private static final Map<Class<?>, MethodHandle> switchMethods = new ConcurrentHashMap<>();
-
-    private static final Set<Class<?>> BOOLEAN_TYPES
-            = Set.of(boolean.class, Boolean.class);
-    // Types that can be handled as int switches
-    private static final Set<Class<?>> INT_TYPES
-            = Set.of(int.class, short.class, byte.class, char.class,
-                     Integer.class, Short.class, Byte.class, Character.class);
-    private static final Set<Class<?>> FLOAT_TYPES
-            = Set.of(float.class, Float.class);
-    private static final Set<Class<?>> LONG_TYPES
-            = Set.of(long.class, Long.class);
-    private static final Set<Class<?>> DOUBLE_TYPES
-            = Set.of(double.class, Double.class);
-
-    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
-    private static final Function<Class<?>, MethodHandle> lookupSwitchMethod =
-            new Function<>() {
-                @Override
-                public MethodHandle apply(Class<?> c) {
-                    try {
-                        Class<?> switchClass;
-                        if (c == Enum.class)
-                            switchClass = EnumSwitchCallSite.class;
-                        else if (c == String.class)
-                            switchClass = StringSwitchCallSite.class;
-                        else if (BOOLEAN_TYPES.contains(c) || INT_TYPES.contains(c) ||
-                                 FLOAT_TYPES.contains(c))
-                            switchClass = IntSwitchCallSite.class;
-                        else if (LONG_TYPES.contains(c) || DOUBLE_TYPES.contains(c))
-                            switchClass = LongSwitchCallSite.class;
-                        else if (c == Object.class)
-                            switchClass = TypeSwitchCallSite.class;
-                        else
-                            throw new BootstrapMethodError("Invalid switch type: " + c);
-
-                        return LOOKUP.findVirtual(switchClass, "doSwitch",
-                                                  MethodType.methodType(int.class, c));
-                    }
-                    catch (ReflectiveOperationException e) {
-                        throw new BootstrapMethodError("Invalid switch type: " + c);
-                    }
-                }
-            };
-
-    static {
-        try {
-            CONSTANT_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "constantInitHook",
-                                                   MethodType.methodType(MethodHandle.class, CallSite.class));
-            PATTERN_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "patternInitHook",
-                                                  MethodType.methodType(MethodHandle.class, CallSite.class));
-            TYPE_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "typeInitHook",
-                                                  MethodType.methodType(MethodHandle.class, CallSite.class));
-            PATTERN_SWITCH_METHOD = LOOKUP.findVirtual(PatternSwitchCallSite.class, "doSwitch",
-                                                       MethodType.methodType(PatternSwitchResult.class, Object.class));
-            TYPE_SWITCH_METHOD = LOOKUP.findVirtual(TypeSwitchCallSite.class, "doSwitch",
-                                                    MethodType.methodType(int.class, Object.class));
-        }
-        catch (ReflectiveOperationException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
-    private static<T extends CallSite> MethodHandle constantInitHook(T receiver) {
-        return switchMethods.computeIfAbsent(receiver.type().parameterType(0), lookupSwitchMethod)
-                            .bindTo(receiver);
-    }
-
-    private static<T extends CallSite> MethodHandle typeInitHook(T receiver) {
-        return TYPE_SWITCH_METHOD.bindTo(receiver);
-    }
-
-    private static<T extends CallSite> MethodHandle patternInitHook(T receiver) {
-        return PATTERN_SWITCH_METHOD.bindTo(receiver);
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on a {@code boolean} or {@code Boolean}.
-     * The static arguments are a varargs array of {@code boolean} labels,
-     *
-     * <p>The results are undefined if the labels array contains duplicates.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter which is
-     *                       {@code boolean} or {@code Boolean},and return {@code int}.
-     *                       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 booleanLabels boolean values corresponding to the case labels of the
-     *                  {@code switch} statement.
-     * @return the index into {@code booleanLabels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code booleanLabels.length} if the target value does
-     *         not match any of the labels.
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if the invocation type is not
-     * {@code (boolean)int} or {@code (Boolean)int}
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite booleanSwitch(MethodHandles.Lookup lookup,
-                                         String invocationName,
-                                         MethodType invocationType,
-                                         boolean... booleanLabels) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || (!BOOLEAN_TYPES.contains(invocationType.parameterType(0))))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(booleanLabels);
-
-        int[] intLabels = IntStream.range(0, booleanLabels.length)
-                                   .map(i -> booleanLabels[i] ? 1 : 0)
-                                   .toArray();
-
-        assert IntStream.of(intLabels).distinct().count() == intLabels.length
-                : "switch labels are not distinct: " + Arrays.toString(booleanLabels);
-
-        return new IntSwitchCallSite(invocationType, intLabels);
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on an {@code int}, {@code short}, {@code byte},
-     * {@code char}, or one of their box types.  The static arguments are a
-     * varargs array of {@code int} labels.
-     *
-     * <p>The results are undefined if the labels array contains duplicates.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter which is
-     *                       one of the 32-bit or shorter primitive types, or
-     *                       one of their box types, and return {@code int}.  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 intLabels integral values corresponding to the case labels of the
-     *                  {@code switch} statement.
-     * @return the index into {@code intLabels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code intLabels.length} if the target value does
-     *         not match any of the labels.
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if the invocation type is not
-     * {@code (T)int}, where {@code T} is one of the 32-bit or smaller integral
-     * primitive types, or one of their box types
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite intSwitch(MethodHandles.Lookup lookup,
-                                     String invocationName,
-                                     MethodType invocationType,
-                                     int... intLabels) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || (!INT_TYPES.contains(invocationType.parameterType(0))))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(intLabels);
-
-        assert IntStream.of(intLabels).distinct().count() == intLabels.length
-                : "switch labels are not distinct: " + Arrays.toString(intLabels);
-
-        return new IntSwitchCallSite(invocationType, intLabels);
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on an {@code float} or {@code Float}.
-     * The static arguments are a varargs array of {@code float} labels.
-     *
-     * <p>The results are undefined if the labels array contains duplicates
-     * according to {@link Float#floatToIntBits(float)}.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter which is
-     *                       one of the 32-bit or shorter primitive types, or
-     *                       one of their box types, and return {@code int}.  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 floatLabels float values corresponding to the case labels of the
-     *                    {@code switch} statement.
-     * @return the index into {@code floatLabels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code floatLabels.length} if the target value does
-     *         not match any of the labels.
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if the invocation type is not
-     * {@code (float)int} or {@code (Float)int}
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite floatSwitch(MethodHandles.Lookup lookup,
-                                       String invocationName,
-                                       MethodType invocationType,
-                                       float... floatLabels) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || (!FLOAT_TYPES.contains(invocationType.parameterType(0))))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(floatLabels);
-
-        int[] intLabels = new int[floatLabels.length];
-        for (int i=0; i<floatLabels.length; i++)
-            intLabels[i] = Float.floatToIntBits(floatLabels[i]);
-
-        assert IntStream.of(intLabels).distinct().count() == intLabels.length
-                : "switch labels are not distinct: " + Arrays.toString(floatLabels);
-
-        return new IntSwitchCallSite(invocationType, intLabels);
-    }
-
-    static class IntSwitchCallSite extends ConstantCallSite {
-        private final int[] labels;
-        private final int[] indexes;
-
-        IntSwitchCallSite(MethodType targetType,
-                          int[] intLabels) throws Throwable {
-            super(targetType, CONSTANT_INIT_HOOK);
-
-            // expensive way to index an array
-            indexes = IntStream.range(0, intLabels.length)
-                               .boxed()
-                               .sorted(Comparator.comparingInt(a -> intLabels[a]))
-                               .mapToInt(Integer::intValue)
-                               .toArray();
-            labels = new int[indexes.length];
-            for (int i=0; i<indexes.length; i++)
-                labels[i] = intLabels[indexes[i]];
-        }
-
-        int doSwitch(int target) {
-            int index = Arrays.binarySearch(labels, target);
-            return (index >= 0) ? indexes[index] : indexes.length;
-        }
-
-        int doSwitch(boolean target) {
-            return doSwitch(target ? 1 : 0);
-        }
-
-        int doSwitch(float target) {
-            return doSwitch(Float.floatToIntBits(target));
-        }
-
-        int doSwitch(short target) {
-            return doSwitch((int) target);
-        }
-
-        int doSwitch(byte target) {
-            return doSwitch((int) target);
-        }
-
-        int doSwitch(char target) {
-            return doSwitch((int) target);
-        }
-
-        int doSwitch(Boolean target) {
-            return (target == null) ? -1 : doSwitch((boolean) target);
-        }
-
-        int doSwitch(Integer target) {
-            return (target == null) ? -1 : doSwitch((int) target);
-        }
-
-        int doSwitch(Float target) {
-            return (target == null) ? -1 : doSwitch((float) target);
-        }
-
-        int doSwitch(Short target) {
-            return (target == null) ? -1 : doSwitch((int) target);
-        }
-
-        int doSwitch(Character target) {
-            return (target == null) ? -1 : doSwitch((int) target);
-        }
-
-        int doSwitch(Byte target) {
-            return (target == null) ? -1 : doSwitch((int) target);
-        }
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on a {@code long} or {@code Long}.
-     * The static arguments are a varargs array of {@code long} labels.
-     *
-     * <p>The results are undefined if the labels array contains duplicates.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter which is
-     *                       one of the 32-bit or shorter primitive types, or
-     *                       one of their box types, and return {@code int}.  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 longLabels long values corresponding to the case labels of the
-     *                  {@code switch} statement.
-     * @return the index into {@code longLabels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code longLabels.length} if the target value does
-     *         not match any of the labels.
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if the invocation type is not
-     * {@code (long)int} or {@code (Long)int}
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite longSwitch(MethodHandles.Lookup lookup,
-                                      String invocationName,
-                                      MethodType invocationType,
-                                      long... longLabels) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || (!LONG_TYPES.contains(invocationType.parameterType(0))))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(longLabels);
-
-        assert LongStream.of(longLabels).distinct().count() == longLabels.length
-                : "switch labels are not distinct: " + Arrays.toString(longLabels);
-
-        return new LongSwitchCallSite(invocationType, longLabels);
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on a {@code double} or {@code Double}.
-     * The static arguments are a varargs array of {@code double} labels.
-     *
-     * <p>The results are undefined if the labels array contains duplicates
-     * according to {@link Double#doubleToLongBits(double)}.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter which is
-     *                       one of the 32-bit or shorter primitive types, or
-     *                       one of their box types, and return {@code int}.  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 doubleLabels long values corresponding to the case labels of the
-     *                  {@code switch} statement.
-     * @return the index into {@code doubleLabels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code doubleLabels.length} if the target value does
-     *         not match any of the labels.
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if the invocation type is not
-     * {@code (double)int} or {@code (Double)int}
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite doubleSwitch(MethodHandles.Lookup lookup,
-                                        String invocationName,
-                                        MethodType invocationType,
-                                        double... doubleLabels) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || (!DOUBLE_TYPES.contains(invocationType.parameterType(0))))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(doubleLabels);
-
-        long[] longLabels = new long[doubleLabels.length];
-        for (int i=0; i<doubleLabels.length; i++)
-            longLabels[i] = Double.doubleToLongBits(doubleLabels[i]);
-
-        assert LongStream.of(longLabels).distinct().count() == longLabels.length
-                : "switch labels are not distinct: " + Arrays.toString(doubleLabels);
-
-        return new LongSwitchCallSite(invocationType, longLabels);
-    }
-
-    static class LongSwitchCallSite extends ConstantCallSite {
-        private final long[] labels;
-        private final int[] indexes;
-
-        LongSwitchCallSite(MethodType targetType,
-                           long[] longLabels) throws Throwable {
-            super(targetType, CONSTANT_INIT_HOOK);
-
-            // expensive way to index an array
-            indexes = IntStream.range(0, longLabels.length)
-                               .boxed()
-                               .sorted(Comparator.comparingLong(a -> longLabels[a]))
-                               .mapToInt(Integer::intValue)
-                               .toArray();
-            labels = new long[indexes.length];
-            for (int i=0; i<indexes.length; i++)
-                labels[i] = longLabels[indexes[i]];
-        }
-
-        int doSwitch(long target) {
-            int index = Arrays.binarySearch(labels, target);
-            return (index >= 0) ? indexes[index] : indexes.length;
-        }
-
-        int doSwitch(double target) {
-            return doSwitch(Double.doubleToLongBits(target));
-        }
-
-        int doSwitch(Long target) {
-            return (target == null) ? -1 : doSwitch((long) target);
-        }
-
-        int doSwitch(Double target) {
-            return (target == null) ? -1 : doSwitch((double) target);
-        }
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on a {@code String} target.  The static
-     * arguments are a varargs array of {@code String} labels.
-     *
-     * <p>The results are undefined if the labels array contains duplicates
-     * according to {@link String#equals(Object)}.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter of
-     *                       {@code String}, and return {@code int}.  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 stringLabels non-null string values corresponding to the case
-     *                     labels of the {@code switch} statement.
-     * @return the index into {@code labels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code stringLabels.length} if the target value
-     *         does not match any of the labels.
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if any labels are null, or if the
-     * invocation type is not {@code (String)int}
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite stringSwitch(MethodHandles.Lookup lookup,
-                                        String invocationName,
-                                        MethodType invocationType,
-                                        String... stringLabels) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || (!invocationType.parameterType(0).equals(String.class)))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(stringLabels);
-        if (Stream.of(stringLabels).anyMatch(Objects::isNull))
-            throw new IllegalArgumentException("null label found");
-
-        assert Stream.of(stringLabels).distinct().count() == stringLabels.length
-                : "switch labels are not distinct: " + Arrays.toString(stringLabels);
-
-        return new StringSwitchCallSite(invocationType, stringLabels);
-    }
-
-    static class StringSwitchCallSite extends ConstantCallSite {
-        private static final Comparator<String> STRING_BY_HASH
-                = Comparator.comparingInt(Objects::hashCode);
-
-        private final String[] sortedByHash;
-        private final int[] indexes;
-        private final boolean collisions;
-
-        StringSwitchCallSite(MethodType targetType,
-                             String[] stringLabels) throws Throwable {
-            super(targetType, CONSTANT_INIT_HOOK);
-
-            // expensive way to index an array
-            indexes = IntStream.range(0, stringLabels.length)
-                               .boxed()
-                               .sorted(Comparator.comparingInt(i -> stringLabels[i].hashCode()))
-                               .mapToInt(Integer::intValue)
-                               .toArray();
-            sortedByHash = new String[indexes.length];
-            for (int i=0; i<indexes.length; i++)
-                sortedByHash[i] = stringLabels[indexes[i]];
-
-            collisions = IntStream.range(0, sortedByHash.length-1)
-                                  .anyMatch(i -> sortedByHash[i].hashCode() == sortedByHash[i + 1].hashCode());
-        }
-
-        int doSwitch(String target) {
-            if (target == null)
-                return -1;
-
-            int index = Arrays.binarySearch(sortedByHash, target, STRING_BY_HASH);
-            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 && sortedByHash[index-1].hashCode() == hash)
-                    --index;
-                for (; index < sortedByHash.length && sortedByHash[index].hashCode() == hash; index++)
-                    if (target.equals(sortedByHash[index]))
-                        return indexes[index];
-            }
-
-            return indexes.length;
-        }
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on an {@code Enum} target.  The static
-     * arguments are the enum class, and a varargs arrays of {@code String}
-     * that are the names of the enum constants corresponding to the
-     * {@code case} labels.
-     *
-     * <p>The results are undefined if the names array contains duplicates.
-     *
-     * @implNote
-     *
-     * The implementation only enforces the requirement that the labels array
-     * be duplicate-free if system assertions are enabled.
-     *
-     * @param <E> the enum type
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter of
-     *                       {@code Enum}, and return {@code int}.  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 enumClass the enum class
-     * @param enumNames names of the enum constants against which the target
-     *                  should be matched
-     * @return the index into {@code labels} of the target value, if the target
-     *         matches any of the labels, {@literal -1} if the target value is
-     *         {@code null}, or {@code stringLabels.length} if the target value
-     *         does not match any of the labels.
-     * @throws IllegalArgumentException if the specified class is not an
-     *                                  enum class, or any label name is null,
-     *                                  or if the invocation type is not
-     *                                  {@code (Enum)int}
-     * @throws NullPointerException if any required argument is null
-     * @throws Throwable if there is any error linking the call site
-     */
-    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().equals(int.class))
-            || (!invocationType.parameterType(0).equals(Enum.class)))
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(enumClass);
-        requireNonNull(enumNames);
-        if (!enumClass.isEnum())
-            throw new IllegalArgumentException("not an enum class");
-        if (Stream.of(enumNames).anyMatch(Objects::isNull))
-            throw new IllegalArgumentException("null label found");
-
-        assert Stream.of(enumNames).distinct().count() == enumNames.length
-                : "switch labels are not distinct: " + Arrays.toString(enumNames);
-
-        return new EnumSwitchCallSite<>(invocationType, enumClass, enumNames);
-    }
-
-    static class EnumSwitchCallSite<E extends Enum<E>> extends ConstantCallSite {
-        private final int[] ordinalMap;
-
-        EnumSwitchCallSite(MethodType targetType,
-                           Class<E> enumClass,
-                           String... enumNames) throws Throwable {
-            super(targetType, CONSTANT_INIT_HOOK);
-
-            ordinalMap = new int[enumClass.getEnumConstants().length];
-            Arrays.fill(ordinalMap, enumNames.length);
-
-            for (int i=0; i<enumNames.length; i++) {
-                try {
-                    ordinalMap[E.valueOf(enumClass, enumNames[i]).ordinal()] = i;
-                }
-                catch (Exception e) {
-                    // allow non-existent labels, but never match them
-                    continue;
-                }
-            }
-        }
-
-        @SuppressWarnings("rawtypes")
-        int doSwitch(Enum target) {
-            return (target == null) ? -1 : ordinalMap[target.ordinal()];
-        }
-    }
-
-    /**
-     * Bootstrap method for linking an {@code invokedynamic} call site that
-     * implements a {@code switch} on a reference-typed target.  The static
-     * arguments are a varargs array of {@code Class} labels.
-     *
-     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
-     *                       method type should have a single parameter of
-     *                       a reference type, and return {@code int}.  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 types non-null {@link Class} values
-     * @return the index into {@code labels} of the target value, if the target
-     *         is an instance of any of the types, {@literal -1} if the target
-     *         value is {@code null}, or {@code types.length} if the target value
-     *         is not an instance of any of the types
-     * @throws NullPointerException if any required argument is null
-     * @throws IllegalArgumentException if any labels are null, or if the
-     * invocation type is not {@code (T)int for some reference type {@code T}}
-     * @throws Throwable if there is any error linking the call site
-     */
-    public static CallSite typeSwitch(MethodHandles.Lookup lookup,
-                                      String invocationName,
-                                      MethodType invocationType,
-                                      Class<?>... types) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(int.class))
-            || invocationType.parameterType(0).isPrimitive())
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(types);
-
-        types = types.clone();
-        if (Stream.of(types).anyMatch(Objects::isNull))
-            throw new IllegalArgumentException("null label found");
-
-        assert Stream.of(types).distinct().count() == types.length
-                : "switch labels are not distinct: " + Arrays.toString(types);
-
-        return new TypeSwitchCallSite(invocationType, types);
-    }
-
-    static class TypeSwitchCallSite extends ConstantCallSite {
-        private final Class<?>[] types;
-
-        TypeSwitchCallSite(MethodType targetType,
-                           Class<?>[] types) throws Throwable {
-            super(targetType, TYPE_INIT_HOOK);
-            this.types = types;
-        }
-
-        int doSwitch(Object target) {
-            if (target == null)
-                return -1;
-
-            // Dumbest possible strategy
-            Class<?> targetClass = target.getClass();
-            for (int i = 0; i < types.length; i++) {
-                Class<?> c = types[i];
-                if (c.isAssignableFrom(targetClass))
-                    return i;
-            }
-
-            return types.length;
-        }
-    }
-
-    /**
-     * Result type for pattern switches
-     */
-    public static class PatternSwitchResult {
-        /**
-         * The selected index, -1 if input was null, or length if not matched
-         */
-        public final int index;
-
-        /**
-         * The carrier
-         */
-        public final Object carrier;
-
-        /**
-         * Construct a PatternSwitchResult
-         *
-         * @param index the index
-         * @param carrier the carrier
-         */
-        public PatternSwitchResult(int index, Object carrier) {
-            this.index = index;
-            this.carrier = carrier;
-        }
-    }
-
-    /**
-     * Bootstrap for pattern switches
-     *
-     * @param lookup the lookup (ignored)
-     * @param invocationName the invocation name (ignored)
-     * @param invocationType the invocation type (must return PatternSwitchResult)
-     * @param patterns the patterns
-     * @return the result
-     * @throws Throwable if something went wrong
-     */
-    public static CallSite patternSwitch(MethodHandles.Lookup lookup,
-                                         String invocationName,
-                                         MethodType invocationType,
-                                         Extractor... patterns) throws Throwable {
-        if (invocationType.parameterCount() != 1
-            || (!invocationType.returnType().equals(PatternSwitchResult.class))
-            || invocationType.parameterType(0).isPrimitive())
-            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
-        requireNonNull(patterns);
-
-        patterns = patterns.clone();
-        Class<?> targetType = invocationType.parameterType(0);
-
-        for (int i = 0; i < patterns.length; i++) {
-            Extractor pattern = patterns[i];
-            if (pattern.descriptor().returnType() != targetType)
-                patterns[i] = Extractor.adapt(pattern, targetType);
-        }
-
-        if (Stream.of(patterns).anyMatch(Objects::isNull))
-            throw new IllegalArgumentException("null pattern found");
-
-        return new PatternSwitchCallSite(invocationType, patterns);
-    }
-
-    static class PatternSwitchCallSite extends ConstantCallSite {
-        private final Extractor[] patterns;
-
-        PatternSwitchCallSite(MethodType targetType,
-                              Extractor[] patterns) throws Throwable {
-            super(targetType, PATTERN_INIT_HOOK);
-            this.patterns = patterns;
-        }
-
-        PatternSwitchResult doSwitch(Object target) throws Throwable {
-            if (target == null)
-                return new PatternSwitchResult(-1, null);
-
-            // Dumbest possible strategy
-            for (int i = 0; i < patterns.length; i++) {
-                Extractor e = patterns[i];
-                Object o = e.tryMatch().invoke(target);
-                if (o != null)
-                    return new PatternSwitchResult(i, o);
-            }
-
-            return new PatternSwitchResult(patterns.length, null);
-
-        }
-    }
-}
--- a/src/java.base/share/classes/java/lang/compiler/_pattern.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * 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 java.lang.compiler;
-
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * Temporary scaffolding to allow declaration of constrained patterns
- * without language support.
- */
-public interface _pattern<B> {
-    /**
-     * Attempt to match
-     * @param o the target
-     * @return the result, or an empty optional
-     */
-    Optional<B> match(Object o);
-
-    /**
-     * Construct a PatternDecl for a partial pattern
-     * @param predicate the applicability test
-     * @param extract the extraction logic
-     * @param <T> the type of a successful target
-     * @param <B> the type of the binding
-     * @return the PatternDecl
-     */
-    @SuppressWarnings("unchecked")
-    static<T, B> _pattern<B> of(Predicate<T> predicate, Function<T, B> extract) {
-        return (Object o) ->
-                (predicate.test((T) o))
-                ? Optional.of(extract.apply((T) o))
-                : Optional.empty();
-    }
-
-    /**
-     * Construct a PatternDecl for a total pattern on Object
-     * @param extract the extraction logic
-     * @param <B> the type of the binding
-     * @return the PatternDecl
-     */
-    static<B> _pattern<B> of(Function<?, B> extract) {
-        return of(o -> true, extract);
-    }
-
-    /**
-     * Construct a PatternDecl for a type test pattern
-     * @param clazz The type to test against
-     * @param extract the extraction logic
-     * @param <T> the type of a successful target
-     * @param <B> the type of the binding
-     * @return the PatternDecl
-     */
-    static<T, B> _pattern<B> ofType(Class<T> clazz, Function<T, B> extract) {
-        return of(o -> clazz.isAssignableFrom(o.getClass()),
-                  o -> extract.apply(clazz.cast(o)));
-    }
-
-    /**
-     * Construct a PatternDecl for a type test pattern
-     * @param clazz The type to test against
-     * @param <T> the type of a successful target
-     * @return the PatternDecl
-     */
-    static<T> _pattern<T> ofType(Class<T> clazz) {
-        return of(o -> clazz.isAssignableFrom(o.getClass()), clazz::cast);
-    }
-
-    /**
-     * Construct a PatternDecl for a constant
-     * @param constant the constant
-     * @param <T> the type of the constant
-     * @return the PatternDecl
-     */
-    static<T> _pattern<T> ofConstant(T constant) {
-        return of(constant::equals, o -> constant);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/Extractor.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,562 @@
+/*
+ * 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 java.lang.runtime;
+
+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.util.Objects;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import sun.invoke.util.BytecodeName;
+
+import static java.lang.invoke.MethodHandleInfo.REF_invokeInterface;
+import static java.lang.invoke.MethodHandleInfo.REF_invokeStatic;
+import static java.lang.invoke.MethodHandleInfo.REF_invokeVirtual;
+import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Supporting type for implementation of pattern matching.  An {@linkplain Extractor}
+ * is a constant bundle of method handles that describe a particular pattern, and
+ * suitable for storing in the constant pool.
+ *
+ * <p>An {@linkplain Extractor} is describe by a {@code descriptor} in the form
+ * of a {@link MethodType}.  The return value of the descriptor is ignored; the
+ * argument types of the descriptor indicate the types of the output binding
+ * variables.
+ *
+ * Notes:
+ *  - totality is erased;
+ *  - compilers expected to optimize away total type patterns;
+ *  - adaptation done in nest() and switch combinators
+ *
+ * @author Brian Goetz
+ */
+public interface Extractor {
+    /**
+     * A method handle that attempts to perform a match.  It will have type
+     * {@code (Object)Object}.  It accepts the target to be matched, and returns
+     * an opaque carrier if the match succeeds, or {@code null} if it fails.
+     *
+     * @return the {@code tryMatch} method handle
+     */
+    MethodHandle tryMatch();
+
+    /**
+     * A method handle that extracts a component from the match carrier.  It
+     * will take the match carrier and return the corresponding match binding.
+     *
+     * @param i the index of the component
+     * @return the {@code component} method handle
+     */
+    MethodHandle component(int i);
+
+    /**
+     * Returns the component method handles, as an array
+     * @return the component method handles
+     */
+    MethodHandle[] components();
+
+    /**
+     * The descriptor of the {@linkplain Extractor}.  The parameter types of
+     * the descriptor are the types of the binding variables.  The return type
+     * is ignored.
+     *
+     * @return the descriptor
+     */
+    MethodType descriptor();
+
+    /**
+     * Compose an extractor with a method handle that receives the bindings
+     *
+     * @param target method handle to receive the bindings
+     * @param sentinel value to return when the extractor does not match
+     * @return the composed method handle
+     */
+    default MethodHandle compose(MethodHandle target, Object sentinel) {
+        int count = descriptor().parameterCount();
+        MethodHandle[] components = components();
+        Class<?> carrierType = tryMatch().type().returnType();
+        Class<?> resultType = target.type().returnType();
+
+        MethodHandle mh = MethodHandles.filterArguments(target, 0, components);
+        mh = MethodHandles.permuteArguments(mh, MethodType.methodType(resultType, carrierType), new int[count]);
+        mh = MethodHandles.guardWithTest(ExtractorImpl.MH_OBJECTS_NONNULL.asType(MethodType.methodType(boolean.class, carrierType)),
+                                         mh,
+                                         MethodHandles.dropArguments(MethodHandles.constant(resultType, sentinel), 0, carrierType));
+        mh = MethodHandles.filterArguments(mh, 0, tryMatch());
+        return mh;
+    }
+
+    private static MethodType descriptor(Class<?> targetType, MethodHandle[] components) {
+        Class<?>[] paramTypes = Stream.of(components)
+                                      .map(mh -> mh.type().returnType())
+                                      .toArray(Class[]::new);
+        return MethodType.methodType(targetType, paramTypes);
+    }
+
+    private static MethodHandle carrierTryExtract(MethodType descriptor, MethodHandle[] components) {
+        MethodHandle carrierFactory = ExtractorCarriers.carrierFactory(descriptor);
+        int[] reorder = new int[descriptor.parameterCount()]; // default value is what we want already
+
+        return MethodHandles.permuteArguments(MethodHandles.filterArguments(carrierFactory, 0, components),
+                                              MethodType.methodType(carrierFactory.type().returnType(), descriptor.returnType()),
+                                              reorder);
+    }
+
+    /**
+     * Construct a partial method handle that uses the predicate as guardWithTest,
+     * which applies the target if the test succeeds, and returns null if the
+     * test fails.  The resulting method handle is of the same type as the
+     * {@code target} method handle.
+     * @param target
+     * @param predicate
+     * @return
+     */
+    private static MethodHandle partialize(MethodHandle target, MethodHandle predicate) {
+        Class<?> targetType = target.type().parameterType(0);
+        Class<?> carrierType = target.type().returnType();
+        return MethodHandles.guardWithTest(predicate,
+                                           target,
+                                           MethodHandles.dropArguments(MethodHandles.constant(carrierType, null),
+                                                                       0, targetType));
+    }
+
+    /**
+     * Construct a method handle that delegates to target, unless the nth argument
+     * is null, in which case it returns null
+     */
+    private static MethodHandle bailIfNthNull(MethodHandle target, int n) {
+        MethodHandle test = ExtractorImpl.MH_OBJECTS_ISNULL.asType(ExtractorImpl.MH_OBJECTS_ISNULL.type().changeParameterType(0, target.type().parameterType(n)));
+        test = MethodHandles.permuteArguments(test, target.type().changeReturnType(boolean.class), n);
+        MethodHandle nullh = MethodHandles.dropArguments(MethodHandles.constant(target.type().returnType(), null), 0, target.type().parameterArray());
+        return MethodHandles.guardWithTest(test, nullh, target);
+    }
+
+    /**
+     * Create a total {@linkplain Extractor} with the given descriptor, which
+     * operates by feeding results into a factory method handle and returning
+     * the result.
+     *
+     * @param descriptor the descriptor
+     * @param digester the digester method handle
+     * @return the extractor
+     */
+    public static Extractor of(MethodType descriptor,
+                               MethodHandle digester) {
+        return new ExtractorImpl(descriptor,
+                                 MethodHandles.insertArguments(digester,
+                                                               1, ExtractorCarriers.carrierFactory(descriptor)),
+                                 ExtractorCarriers.carrierComponents(descriptor));
+    }
+
+    /**
+     * Create a total {@linkplain Extractor} for a target of type {@code targetType}
+     * and a given set of component method handles.
+     *
+     * @param targetType The type of the match target
+     * @param components The component method handles
+     * @return the extractor
+     */
+    public static Extractor ofTotal(Class<?> targetType, MethodHandle... components) {
+        MethodType descriptor = descriptor(targetType, components);
+        return new ExtractorImpl(descriptor,
+                                 carrierTryExtract(descriptor, components),
+                                 ExtractorCarriers.carrierComponents(descriptor));
+    }
+
+    /**
+     * Create a total {@linkplain Extractor} for a target of type {@code targetType}
+     * and a given set of component method handles, using itself as a carrier.
+     *
+     * @param targetType The type of the match target
+     * @param components The component method handles
+     * @return the extractor
+     */
+    public static Extractor ofSelfTotal(Class<?> targetType, MethodHandle... components) {
+        return new ExtractorImpl(descriptor(targetType, components),
+                                 MethodHandles.identity(targetType), components);
+    }
+
+    /**
+     * Create a partial {@linkplain Extractor} for a given set of component
+     * method handles.
+     *
+     * @param targetType the target type
+     * @param predicate The match predicate
+     * @param components The component method handles
+     * @return the extractor
+     */
+    public static Extractor ofPartial(Class<?> targetType, MethodHandle predicate, MethodHandle... components) {
+        MethodType descriptor = descriptor(targetType, components);
+        MethodHandle carrierTryExtract = carrierTryExtract(descriptor, components);
+        return new ExtractorImpl(descriptor,
+                                 partialize(carrierTryExtract, predicate),
+                                 ExtractorCarriers.carrierComponents(descriptor));
+    }
+
+    /**
+     * Create a partial {@linkplain Extractor} for a given set of component
+     * method handles, using itself as a carrier.
+     *
+     * @param targetType the target type
+     * @param predicate The match predicate
+     * @param components The component method handles
+     * @return the extractor
+     */
+    public static Extractor ofSelfPartial(Class<?> targetType, MethodHandle predicate, MethodHandle... components) {
+        return new ExtractorImpl(descriptor(targetType, components),
+                                 partialize(MethodHandles.identity(targetType), predicate),
+                                 components);
+    }
+
+    /**
+     * Create an {@linkplain Extractor} for a type pattern, with a single binding
+     * variable, whose target type is {@code Object}
+     *
+     * @param type the type to match against
+     * @return the {@linkplain Extractor}
+     */
+    public static Extractor ofType(Class<?> type) {
+        requireNonNull(type);
+        if (type.isPrimitive())
+            throw new IllegalArgumentException("Reference type expected, found: " + type);
+        return new ExtractorImpl(MethodType.methodType(type, type),
+                                 ExtractorImpl.MH_OF_TYPE_HELPER.bindTo(type).asType(MethodType.methodType(type, type)),
+                                 MethodHandles.identity(type));
+    }
+
+    /**
+     * Create an {@linkplain Extractor} for a constant pattern
+     *
+     * @param o the constant
+     * @return the extractor
+     */
+    public static Extractor ofConstant(Object o) {
+        Class<?> type = o == null ? Object.class : o.getClass();
+        MethodHandle match = partialize(MethodHandles.dropArguments(MethodHandles.constant(Object.class, Boolean.TRUE), 0, type),
+                                        MethodHandles.insertArguments(ExtractorImpl.MH_OBJECTS_EQUAL, 0, o)
+                                                     .asType(MethodType.methodType(boolean.class, type)));
+        return new ExtractorImpl(MethodType.methodType(type), match);
+    }
+
+    /**
+     * Create an {@linkplain Extractor} for a nullable type pattern, with a
+     * single binding variable, whose target type is {@code Object}
+     *
+     * @param type the type to match against
+     * @return the {@linkplain Extractor}
+     */
+    public static Extractor ofTypeNullable(Class<?> type) {
+        requireNonNull(type);
+        if (type.isPrimitive())
+            throw new IllegalArgumentException("Reference type expected, found: " + type);
+        return new ExtractorImpl(MethodType.methodType(type, type),
+                                 ExtractorImpl.MH_OF_TYPE_NULLABLE_HELPER.bindTo(type).asType(MethodType.methodType(type, type)),
+                                 MethodHandles.identity(type));
+    }
+
+    /**
+     * Create an {@linkplain Extractor} that is identical to another {@linkplain Extractor},
+     * but without the specified binding variables
+     * @param etor the original extractor
+     * @param positions which binding variables to drop
+     * @return the extractor
+     */
+    public static Extractor dropBindings(Extractor etor, int... positions) {
+        MethodHandle[] mhs = etor.components();
+        for (int position : positions)
+            mhs[position] = null;
+        mhs = Stream.of(mhs).filter(Objects::nonNull).toArray(MethodHandle[]::new);
+        return new ExtractorImpl(descriptor(etor.descriptor().returnType(), mhs), etor.tryMatch(), mhs);
+    }
+
+    /**
+     * Adapt an extractor to a new target type
+     *
+     * @param e the extractor
+     * @param newTarget the new target type
+     * @return the new extractor
+     */
+    public static Extractor adapt(Extractor e, Class<?> newTarget) {
+        if (e.descriptor().returnType().isAssignableFrom(newTarget))
+            return e;
+        MethodHandle tryMatch = partialize(e.tryMatch().asType(e.tryMatch().type().changeParameterType(0, newTarget)),
+                                           ExtractorImpl.MH_ADAPT_HELPER.bindTo(e.descriptor().returnType())
+        .asType(MethodType.methodType(boolean.class, newTarget)));
+        return new ExtractorImpl(e.descriptor().changeReturnType(newTarget),
+                                 tryMatch, e.components());
+    }
+
+    /**
+     * Construct a nested extractor, which first matches the target to the
+     * outer extractor, and then matches the resulting bindings to the inner
+     * extractors (if not null).  The resulting extractor is partial if any
+     * of the input extractors are; its target type is the target type of the
+     * outer extractor; and its bindings are the concatenation of the bindings
+     * of the outer extractor followed by the bindings of the non-null inner
+     * extractors.
+     *
+     * @param outer The outer extractor
+     * @param extractors The inner extractors, or null if no nested extraction
+     *                   for this outer binding is desired
+     * @return the nested extractor
+     */
+    public static Extractor ofNested(Extractor outer, Extractor... extractors) {
+        int outerCount = outer.descriptor().parameterCount();
+        Class<?> outerCarrierType = outer.tryMatch().type().returnType();
+
+        // Adapt inners to types of outer bindings
+        for (int i = 0; i < extractors.length; i++) {
+            Extractor extractor = extractors[i];
+            if (extractor.descriptor().returnType() != outer.descriptor().parameterType(i))
+                extractors[i] = adapt(extractor, outer.descriptor().parameterType(i));
+        }
+
+        int[] innerPositions = IntStream.range(0, extractors.length)
+                                        .filter(i -> extractors[i] != null)
+                                        .toArray();
+        MethodHandle[] innerComponents = Stream.of(extractors)
+                                               .filter(Objects::nonNull)
+                                               .map(Extractor::components)
+                                               .flatMap(Stream::of)
+                                               .toArray(MethodHandle[]::new);
+        MethodHandle[] innerTryMatches = Stream.of(extractors)
+                                               .filter(Objects::nonNull)
+                                               .map(e -> e.tryMatch())
+                                               .toArray(MethodHandle[]::new);
+        Class<?>[] innerCarriers = Stream.of(extractors)
+                                         .filter(Objects::nonNull)
+                                         .map(e -> e.tryMatch().type().returnType())
+                                         .toArray(Class[]::new);
+        Class<?>[] innerTypes = Stream.of(innerComponents)
+                                      .map(mh -> mh.type().returnType())
+                                      .toArray(Class[]::new);
+
+        MethodType descriptor = outer.descriptor().appendParameterTypes(innerTypes);
+
+        MethodHandle mh = ExtractorCarriers.carrierFactory(descriptor);
+        mh = MethodHandles.filterArguments(mh, outerCount, innerComponents);
+        int[] spreadInnerCarriers = new int[outerCount + innerComponents.length];
+        for (int i=0; i<outerCount; i++)
+            spreadInnerCarriers[i] = i;
+        int k = outerCount;
+        int j = 0;
+        for (Extractor e : extractors) {
+            if (e == null)
+                continue;
+            for (int i=0; i<e.descriptor().parameterCount(); i++)
+                spreadInnerCarriers[k++] = outerCount + j;
+            j++;
+        }
+        MethodType spreadInnerCarriersMT = outer.descriptor()
+                                                .appendParameterTypes(innerCarriers)
+                                                .changeReturnType(mh.type().returnType());
+        mh = MethodHandles.permuteArguments(mh, spreadInnerCarriersMT, spreadInnerCarriers);
+        for (int position : innerPositions)
+            mh = bailIfNthNull(mh, outerCount + position);
+        mh = MethodHandles.filterArguments(mh, outerCount, innerTryMatches);
+        int[] spreadNestedCarrier = new int[outerCount + innerPositions.length];
+        for (int i=0; i<outerCount; i++)
+            spreadNestedCarrier[i] = i;
+        for (int i=0; i<innerPositions.length; i++)
+            spreadNestedCarrier[outerCount+i] = innerPositions[i];
+        mh = MethodHandles.permuteArguments(mh, outer.descriptor().changeReturnType(mh.type().returnType()),
+                                            spreadNestedCarrier);
+        mh = MethodHandles.filterArguments(mh, 0, outer.components());
+        mh = MethodHandles.permuteArguments(mh, MethodType.methodType(mh.type().returnType(), outerCarrierType),
+                                            new int[outerCount]);
+        mh = bailIfNthNull(mh, 0);
+        mh = MethodHandles.filterArguments(mh, 0, outer.tryMatch());
+
+        MethodHandle tryExtract = mh;
+
+        return new ExtractorImpl(descriptor, tryExtract, ExtractorCarriers.carrierComponents(descriptor));
+    }
+
+
+    /**
+     * Bootstrap for creating a lazy, partial, self-carrier {@linkplain Extractor} from components
+     *
+     * @param lookup ignored
+     * @param constantName ignored
+     * @param constantType Must be {@code ()Extractor}
+     * @param descriptor the descriptor method type
+     * @param components the {@code components} method handles
+     * @return a callsite
+     * @throws Throwable doc
+     */
+    public static CallSite makeLazyExtractor(MethodHandles.Lookup lookup, String constantName, MethodType constantType,
+                                             MethodType descriptor, MethodHandle... components) throws Throwable {
+        return new ConstantCallSite(MethodHandles.constant(Extractor.class, ofSelfTotal(descriptor.returnType(), components)));
+    }
+
+    /**
+     * Condy bootstrap for creating lazy extractors
+     *
+     * @param lookup ignored
+     * @param constantName ignored
+     * @param constantType ignored
+     * @param descriptor the extractor descriptor
+     * @param components the extractor components
+     * @return the extractor factory
+     * @throws Throwable if something went wrong
+     */
+    public static Extractor ofSelfTotal(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
+                                        MethodType descriptor, MethodHandle... components) throws Throwable {
+        return ofSelfTotal(descriptor.returnType(), components);
+    }
+
+    /**
+     * Condy bootstrap for creating nested extractors
+     *
+     * @param lookup ignored
+     * @param invocationName ignored
+     * @param invocationType must be {@code Class<Extractor>}
+     * @param outer the outer extractor
+     * @param inners the inner extractors, null if no nesting is needed for this binding
+     * @return the nested extractor
+     */
+    public static Extractor ofNested(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
+                                     Extractor outer, Extractor... inners) {
+        return ofNested(outer, inners);
+    }
+
+    /**
+     * Condy bootstrap for creating non-nullable type extractor
+     *
+     * @param lookup ignored
+     * @param invocationName ignored
+     * @param invocationType must be {@code Class<Extractor>}
+     * @param type the type
+     * @return the extractor
+     */
+    public static Extractor ofType(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
+                                   Class<?> type) {
+        return ofType(type);
+    }
+
+    /**
+     * Condy bootstrap for creating nullable type extractor
+     *
+     * @param lookup ignored
+     * @param invocationName ignored
+     * @param invocationType must be {@code Class<Extractor>}
+     * @param type the type
+     * @return the extractor
+     */
+    public static Extractor ofTypeNullable(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
+                                           Class<?> type) {
+        return ofTypeNullable(type);
+    }
+
+    /**
+     * Condy bootstrap for creating constant extractor
+     *
+     * @param lookup ignored
+     * @param invocationName ignored
+     * @param invocationType must be {@code Class<Extractor>}
+     * @param constant the constant
+     * @return the extractor
+     */
+    public static Extractor ofConstant(MethodHandles.Lookup lookup, String invocationName, Class<Extractor> invocationType,
+                                       Object constant) {
+        return ofConstant(constant);
+    }
+
+    /**
+     * Condy bootstrap for finding extractors
+     *
+     * @param lookup the lookup context
+     * @param constantName ignored
+     * @param constantType ignored
+     * @param owner the class containing the extractor
+     * @param descriptor the extractor descriptor
+     * @param name the extractor name
+     * @param refKind the kind of method
+     * @return the extractor
+     * @throws Throwable if something went wrong
+     */
+    public static Extractor findExtractor(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
+                                          Class<?> owner, MethodType descriptor, String name, int refKind) throws Throwable {
+        String dd = descriptor.toMethodDescriptorString();
+        String patternMethodName
+                = BytecodeName.toBytecodeName(String.format("$pattern$%s$%s",
+                                                            (refKind == REF_newInvokeSpecial ? owner.getSimpleName() : name),
+                                                            dd.substring(0, dd.indexOf(')') + 1)));
+        MethodType factoryDesc = MethodType.methodType(Extractor.class);
+        MethodHandle mh;
+        switch (refKind) {
+            case REF_invokeStatic:
+            case REF_newInvokeSpecial:
+                mh = lookup.findStatic(owner, patternMethodName, factoryDesc);
+                break;
+            case REF_invokeVirtual:
+            case REF_invokeInterface:
+                mh = lookup.findVirtual(owner, patternMethodName, factoryDesc);
+                break;
+            default:
+                throw new IllegalAccessException(Integer.toString(refKind));
+        }
+
+        return (Extractor) mh.invoke();
+    }
+
+    /**
+     * Bootstrap for extracting the {@code tryMatch} method handle from a {@linkplain Extractor}
+     *
+     * @param lookup ignored
+     * @param constantName ignored
+     * @param constantType Must be {@code MethodHandle.class}
+     * @param extractor the {@linkplain Extractor}
+     * @return the {@code tryMatch} method handle
+     */
+
+
+    public static MethodHandle extractorTryMatch(MethodHandles.Lookup lookup, String constantName, Class<MethodHandle> constantType,
+                                                 Extractor extractor) {
+        return extractor.tryMatch();
+    }
+
+    /**
+     * Bootstrap for extracting a {@code component} method handle from a {@linkplain Extractor}
+     *
+     * @param lookup ignored
+     * @param constantName ignored
+     * @param constantType Must be {@code MethodHandle.class}
+     * @param extractor the {@linkplain Extractor}
+     * @param i the component index
+     * @return the {@code component} method handle
+     */
+    public static MethodHandle extractorComponent(MethodHandles.Lookup lookup, String constantName, Class<MethodHandle> constantType,
+                                                  Extractor extractor, int i) {
+        return extractor.component(i);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/ExtractorCarriers.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,129 @@
+/*
+ * 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 java.lang.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+
+/**
+ * PatternCarriers
+ */
+public class ExtractorCarriers {
+
+    private static final CarrierFactory factory = CarrierFactories.DUMB;
+
+    interface CarrierFactory {
+        MethodHandle constructor(MethodType methodType);
+        MethodHandle component(MethodType methodType, int component);
+    }
+
+    static class DumbCarrier {
+        private final Object[] args;
+
+        DumbCarrier(Object... args) {
+            this.args = args.clone();
+        }
+
+        Object get(int i) {
+            return args[i];
+        }
+    }
+
+    enum CarrierFactories implements CarrierFactory {
+        DUMB {
+            private final MethodHandle CARRIER_CTOR;
+            private final MethodHandle CARRIER_GET;
+
+            {
+                try {
+                    CARRIER_CTOR = MethodHandles.lookup().findConstructor(DumbCarrier.class, MethodType.methodType(void.class, Object[].class));
+                    CARRIER_GET = MethodHandles.lookup().findVirtual(DumbCarrier.class, "get", MethodType.methodType(Object.class, int.class));
+                }
+                catch (ReflectiveOperationException e) {
+                    throw new ExceptionInInitializerError(e);
+                }
+            }
+
+            @Override
+            public MethodHandle constructor(MethodType methodType) {
+                return CARRIER_CTOR.asType(methodType.changeReturnType(Object.class));
+            }
+
+            @Override
+            public MethodHandle component(MethodType methodType, int component) {
+                return MethodHandles.insertArguments(CARRIER_GET, 1, component)
+                                    .asType(MethodType.methodType(methodType.parameterType(component), Object.class));
+            }
+        },
+        DUMB_SINGLE {
+            // An optimization of DUMB, where we use the value itself as carrier when there is only one value
+
+            @Override
+            public MethodHandle constructor(MethodType methodType) {
+                return methodType.parameterCount() == 1 ? MethodHandles.identity(methodType.parameterType(0)) : DUMB.constructor(methodType);
+            }
+
+            @Override
+            public MethodHandle component(MethodType methodType, int component) {
+                return methodType.parameterCount() == 1 ? MethodHandles.identity(methodType.parameterType(0)) : DUMB.component(methodType, component);
+            }
+        }
+    }
+
+    /**
+     * Returns a method handle with the given method type that instantiates
+     * a new carrier object.
+     *
+     * @param methodType the types of the carrier elements
+     * @return the carrier factory
+     */
+    public static MethodHandle carrierFactory(MethodType methodType) {
+        return factory.constructor(methodType);
+    }
+
+    /**
+     * Returns a method handle that accepts a carrier and returns the i'th component
+     *
+     * @param methodType the type of the carrier elements
+     * @param i the index of the component
+     * @return the component method handle
+     */
+    public static MethodHandle carrierComponent(MethodType methodType, int i) {
+        return factory.component(methodType, i);
+    }
+
+    /**
+     * Return all the components method handles for a carrier
+     * @param methodType the type of the carrier elements
+     * @return the component method handles
+     */
+    public static MethodHandle[] carrierComponents(MethodType methodType) {
+        MethodHandle[] components = new MethodHandle[methodType.parameterCount()];
+        Arrays.setAll(components, i -> factory.component(methodType, i));
+        return components;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/ExtractorImpl.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,125 @@
+/*
+ * 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 java.lang.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Non-public implementation of {@link Extractor}
+ */
+class ExtractorImpl implements Extractor {
+    private final MethodType descriptor;
+    private final MethodHandle tryMatch;
+    private final List<MethodHandle> components;
+
+    // These are helpers for Extractors
+    static final MethodHandle MH_OF_TYPE_HELPER;
+    static final MethodHandle MH_OF_TYPE_NULLABLE_HELPER;
+    static final MethodHandle MH_ADAPT_HELPER;
+    static final MethodHandle MH_OBJECTS_ISNULL;
+    static final MethodHandle MH_OBJECTS_NONNULL;
+    static final MethodHandle MH_OBJECTS_EQUAL;
+    static {
+        try {
+            MH_OF_TYPE_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "ofTypeHelper", MethodType.methodType(Object.class, Class.class, Object.class));
+            MH_OF_TYPE_NULLABLE_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "ofTypeNullableHelper", MethodType.methodType(Object.class, Class.class, Object.class));
+            MH_ADAPT_HELPER = MethodHandles.lookup().findStatic(ExtractorImpl.class, "adaptHelper", MethodType.methodType(boolean.class, Class.class, Object.class));
+            MH_OBJECTS_ISNULL = MethodHandles.lookup().findStatic(Objects.class, "isNull", MethodType.methodType(boolean.class, Object.class));
+            MH_OBJECTS_NONNULL = MethodHandles.lookup().findStatic(Objects.class, "nonNull", MethodType.methodType(boolean.class, Object.class));
+            MH_OBJECTS_EQUAL = MethodHandles.lookup().findStatic(Objects.class, "equals", MethodType.methodType(boolean.class, Object.class, Object.class));
+        }
+        catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    /**
+     * Construct an {@link Extractor} from components
+     * Constraints:
+     *  - output of tryMatch must match input of components
+     *  - input of tryMatch must match descriptor
+     *  - output of components must match descriptor
+     *
+     * @param descriptor The {@code descriptor} method type
+     * @param tryMatch The {@code tryMatch} method handle
+     * @param components The {@code component} method handles
+     */
+    ExtractorImpl(MethodType descriptor, MethodHandle tryMatch, MethodHandle... components) {
+        Class<?> carrierType = tryMatch.type().returnType();
+        if (descriptor.parameterCount() != components.length)
+            throw new IllegalArgumentException(String.format("MethodType %s arity should match component count %d", descriptor, components.length));
+        if (!descriptor.returnType().equals(tryMatch.type().parameterType(0)))
+            throw new IllegalArgumentException(String.format("Descriptor %s should match tryMatch input %s", descriptor, tryMatch.type()));
+        for (int i = 0; i < components.length; i++) {
+            MethodHandle component = components[i];
+            if (component.type().parameterCount() != 1
+                || component.type().returnType().equals(void.class)
+                || !component.type().parameterType(0).equals(carrierType))
+                throw new IllegalArgumentException("Invalid component descriptor " + component.type());
+            if (!component.type().returnType().equals(descriptor.parameterType(i)))
+                throw new IllegalArgumentException(String.format("Descriptor %s should match %d'th component %s", descriptor, i, component));
+        }
+
+        this.descriptor = descriptor;
+        this.tryMatch = tryMatch;
+        this.components = List.of(components);
+    }
+
+    @Override
+    public MethodHandle tryMatch() {
+        return tryMatch;
+    }
+
+    @Override
+    public MethodHandle component(int i) {
+        return components.get(i);
+    }
+
+    @Override
+    public MethodHandle[] components() {
+        return components.toArray(new MethodHandle[0]);
+    }
+
+    @Override
+    public MethodType descriptor() {
+        return descriptor;
+    }
+
+    private static Object ofTypeHelper(Class<?> type, Object o) {
+        return o != null && type.isAssignableFrom(o.getClass()) ? o : null;
+    }
+
+    private static Object ofTypeNullableHelper(Class<?> type, Object o) {
+        return o == null || type.isAssignableFrom(o.getClass()) ? o : null;
+    }
+
+    private static boolean adaptHelper(Class<?> type, Object o) {
+        return o != null && type.isAssignableFrom(o.getClass());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/PatternSim.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,362 @@
+/*
+ * 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 java.lang.runtime;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Temporary scaffolding to allow matching / switching on constrained patterns
+ * without language support.
+ */
+public class PatternSim {
+    private static String DEFAULT_LABEL = "";
+
+    /**
+     * Simulator for statement switch
+     * @param label the switch label
+     * @param target the switch target
+     * @return the switch simulator
+     */
+    public static StatementSwitch _switch(String label, Object target) {
+        return new SwitchImpl(target, label);
+    }
+
+    /**
+     * Simulator for statement switch
+     * @param target the switch target
+     * @return the switch simulator
+     */
+    public static StatementSwitch _switch(Object target) {
+        return new SwitchImpl(target, DEFAULT_LABEL);
+    }
+
+    /**
+     * Simulator for expression switch
+     * @param label the switch label
+     * @param target the switch target
+     * @param <T> the the return type
+     * @return the switch simulator
+     */
+    public static<T> ExpressionSwitch<T> _expswitch(String label, Object target) {
+        return new ExprSwitchImpl<>(target, label);
+    }
+
+    /**
+     * Simulator for expression switch
+     * @param target the switch target
+     * @param <T> the the return type
+     * @return the switch simulator
+     */
+    public static<T> ExpressionSwitch<T> _expswitch(Object target) {
+        return new ExprSwitchImpl<>(target, DEFAULT_LABEL);
+    }
+
+    /**
+     * Simulator for continuing out of a switch
+     */
+    public static void _continue() {
+        throw new ContinueSignal(DEFAULT_LABEL);
+    }
+
+    /**
+     * Simulator for continuing out of the labeled switch
+     *
+     * @param label the label of the switch to continue at
+     */
+    public static void _continue(String label) {
+        throw new ContinueSignal(label);
+    }
+
+    /**
+     * Simulator type for statement switch
+     */
+    public interface StatementSwitch {
+        /**
+         * Simulate a case of a statement switch
+         * @param pattern the pattern to match against
+         * @param action the success action
+         * @param <B> the type of the binding variable
+         * @return the switch
+         */
+        <B> StatementSwitch _case(Supplier<_pattern<B>> pattern, Consumer<B> action);
+
+        /**
+         * Simulate a case of a statement switch
+         * @param pattern the pattern to match against
+         * @param action the success action
+         * @param <B> the type of the binding variable
+         * @return the switch
+         */
+        <B> StatementSwitch _case(Supplier<_pattern<B>> pattern, Runnable action);
+
+        /**
+         * Simulate the default clause of a statement switch
+         * @param r the action
+         * @return the switch
+         */
+        StatementSwitch _default(Runnable r);
+    }
+
+    /**
+     * Simulator type for expression switch
+     * @param <T> the switch type
+     */
+    public interface ExpressionSwitch<T> {
+        /**
+         * Simulate a case of an expression switch
+         * @param pattern the pattern to match against
+         * @param action the success action
+         * @param <B> the type of the binding variable
+         * @return the switch
+         */
+        <B> ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Function<B, T> action);
+
+        /**
+         * Simulate a case of an expression switch
+         * @param pattern the pattern to match against
+         * @param action the success action
+         * @param <B> the type of the binding variable
+         * @return the switch
+         */
+        <B> ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Supplier<T> action);
+
+        /**
+         * Simulate the default clause of an expression switch
+         * @param r the action
+         * @return the switch
+         */
+        ExpressionSwitch<T> _default(Supplier<T> r);
+
+        /**
+         * Get the result of an expression switch
+         * @return the result
+         */
+        T result();
+    }
+
+    /**
+     * Helper method for nested pattern in a statement switch
+     * @param target the nested target
+     * @param pattern the nested pattern
+     * @param action the success action
+     * @param <B> the type of the nested target
+     */
+    public static<B> void _nest(Object target, Supplier<_pattern<B>> pattern, Consumer<B> action) {
+        Optional<B> match = pattern.get().match(target);
+        if (match.isPresent())
+            action.accept(match.get());
+        else
+            throw new ContinueSignal("<$nested$>");
+    }
+
+    /**
+     * Helper method for nested pattern in a statement switch
+     * @param target the nested target
+     * @param pattern the nested pattern
+     * @param action the success action
+     * @param <B> the type of the nested target
+     */
+    public static<B> void _nest(Object target, Supplier<_pattern<B>> pattern, Runnable action) {
+        Optional<B> match = pattern.get().match(target);
+        if (match.isPresent())
+            action.run();
+        else
+            throw new ContinueSignal("<$nested$>");
+    }
+
+    /**
+     * Helper method for nested pattern in an expression switch
+     * @param target the nested target
+     * @param pattern the nested pattern
+     * @param action the success action
+     * @param <B> the type of the nested target
+     * @param <T> the return type of the success action
+     * @return the return value of the success action
+     */
+    public static<B, T> T _expnest(Object target, Supplier<_pattern<B>> pattern, Function<B, T> action) {
+        Optional<B> match = pattern.get().match(target);
+        if (match.isPresent())
+            return action.apply(match.get());
+        else
+            throw new ContinueSignal("<$nested$>");
+    }
+
+    /**
+     * Helper method for nested pattern in an expression switch
+     * @param target the nested target
+     * @param pattern the nested pattern
+     * @param action the success action
+     * @param <B> the type of the nested target
+     * @param <T> the return type of the success action
+     * @return the return value of the success action
+     */
+    public static<B, T> T _expnest(Object target, Supplier<_pattern<B>> pattern, Supplier<T> action) {
+        Optional<B> match = pattern.get().match(target);
+        if (match.isPresent())
+            return action.get();
+        else
+            throw new ContinueSignal("<$nested$>");
+    }
+
+    @SuppressWarnings("serial")
+    static class ContinueSignal extends RuntimeException {
+        String label;
+
+        ContinueSignal(String label) {
+            super();
+            this.label = label;
+        }
+
+        void maybeRethrow(String label) {
+            if (!this.label.equals(label))
+                throw this;
+        }
+    }
+}
+
+class SwitchImpl implements PatternSim.StatementSwitch {
+    private final Object target;
+    private final String label;
+    private boolean done = false;
+
+    SwitchImpl(Object target,
+               String label) {
+        this.target = target;
+        this.label = label;
+    }
+
+    public <B> PatternSim.StatementSwitch _case(Supplier<_pattern<B>> pattern, Consumer<B> action) {
+        if (!done) {
+            Optional<B> match = pattern.get().match(target);
+            if (match.isPresent()) {
+                try {
+                    action.accept(match.get());
+                    done = true;
+                }
+                catch (PatternSim.ContinueSignal signal) {
+                    signal.maybeRethrow(label);
+                }
+            }
+        }
+        return this;
+    }
+
+    public <B> PatternSim.StatementSwitch _case(Supplier<_pattern<B>> pattern, Runnable action) {
+        if (!done) {
+            Optional<B> match = pattern.get().match(target);
+            if (match.isPresent()) {
+                try {
+                    action.run();
+                    done = true;
+                }
+                catch (PatternSim.ContinueSignal signal) {
+                    signal.maybeRethrow(label);
+                }
+            }
+        }
+        return this;
+    }
+
+    public PatternSim.StatementSwitch _default(Runnable r) {
+        if (!done) {
+            try {
+                r.run();
+                done = true;
+            }
+            catch (PatternSim.ContinueSignal signal) {
+                signal.maybeRethrow(label);
+            }
+        }
+        return this;
+    }
+
+}
+
+class ExprSwitchImpl<T> implements PatternSim.ExpressionSwitch<T> {
+    private final Object target;
+    private final String label;
+    private boolean done = false;
+    private T result = null;
+
+    ExprSwitchImpl(Object target,
+                   String label) {
+        this.target = target;
+        this.label = label;
+    }
+
+    public<B> PatternSim.ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Function<B, T> action) {
+        if (!done) {
+            Optional<B> match = pattern.get().match(target);
+            if (match.isPresent()) {
+                try {
+                    result = action.apply(match.get());
+                    done = true;
+                }
+                catch (PatternSim.ContinueSignal signal) {
+                    signal.maybeRethrow(label);
+                }
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public <B> PatternSim.ExpressionSwitch<T> _case(Supplier<_pattern<B>> pattern, Supplier<T> action) {
+        if (!done) {
+            Optional<B> match = pattern.get().match(target);
+            if (match.isPresent()) {
+                try {
+                    result = action.get();
+                    done = true;
+                }
+                catch (PatternSim.ContinueSignal signal) {
+                    signal.maybeRethrow(label);
+                }
+            }
+        }
+        return this;
+    }
+
+    public PatternSim.ExpressionSwitch<T> _default(Supplier<T> r) {
+        if (!done) {
+            try {
+                result = r.get();
+                done = true;
+            }
+            catch (PatternSim.ContinueSignal signal) {
+                signal.maybeRethrow(label);
+            }
+        }
+        return this;
+    }
+
+    public T result() {
+        return result;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,882 @@
+/*
+ * 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.runtime;
+
+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.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Bootstrap methods for linking {@code invokedynamic} call sites that implement
+ * the selection functionality of the {@code switch} statement.  The bootstraps
+ * take additional static arguments corresponding to the {@code case} labels
+ * of the {@code switch}, implicitly numbered sequentially from {@code [0..N)}.
+ *
+ * <p>The bootstrap call site accepts a single parameter of the type of the
+ * operand of the {@code switch}, and return an {@code int} that is the index of
+ * the matched {@code case} label, {@code -1} if the target is {@code null},
+ * or {@code N} if the target is not null but matches no {@code case} label.
+ */
+public class SwitchBootstraps {
+
+    // Shared INIT_HOOK for all switch call sites; looks the target method up in a map
+    private static final MethodHandle CONSTANT_INIT_HOOK;
+    private static final MethodHandle PATTERN_INIT_HOOK;
+    private static final MethodHandle TYPE_INIT_HOOK;
+    private static final MethodHandle PATTERN_SWITCH_METHOD;
+    private static final MethodHandle TYPE_SWITCH_METHOD;
+    private static final Map<Class<?>, MethodHandle> switchMethods = new ConcurrentHashMap<>();
+
+    private static final Set<Class<?>> BOOLEAN_TYPES
+            = Set.of(boolean.class, Boolean.class);
+    // Types that can be handled as int switches
+    private static final Set<Class<?>> INT_TYPES
+            = Set.of(int.class, short.class, byte.class, char.class,
+                     Integer.class, Short.class, Byte.class, Character.class);
+    private static final Set<Class<?>> FLOAT_TYPES
+            = Set.of(float.class, Float.class);
+    private static final Set<Class<?>> LONG_TYPES
+            = Set.of(long.class, Long.class);
+    private static final Set<Class<?>> DOUBLE_TYPES
+            = Set.of(double.class, Double.class);
+
+    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+    private static final Function<Class<?>, MethodHandle> lookupSwitchMethod =
+            new Function<>() {
+                @Override
+                public MethodHandle apply(Class<?> c) {
+                    try {
+                        Class<?> switchClass;
+                        if (c == Enum.class)
+                            switchClass = EnumSwitchCallSite.class;
+                        else if (c == String.class)
+                            switchClass = StringSwitchCallSite.class;
+                        else if (BOOLEAN_TYPES.contains(c) || INT_TYPES.contains(c) ||
+                                 FLOAT_TYPES.contains(c))
+                            switchClass = IntSwitchCallSite.class;
+                        else if (LONG_TYPES.contains(c) || DOUBLE_TYPES.contains(c))
+                            switchClass = LongSwitchCallSite.class;
+                        else if (c == Object.class)
+                            switchClass = TypeSwitchCallSite.class;
+                        else
+                            throw new BootstrapMethodError("Invalid switch type: " + c);
+
+                        return LOOKUP.findVirtual(switchClass, "doSwitch",
+                                                  MethodType.methodType(int.class, c));
+                    }
+                    catch (ReflectiveOperationException e) {
+                        throw new BootstrapMethodError("Invalid switch type: " + c);
+                    }
+                }
+            };
+
+    static {
+        try {
+            CONSTANT_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "constantInitHook",
+                                                   MethodType.methodType(MethodHandle.class, CallSite.class));
+            PATTERN_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "patternInitHook",
+                                                  MethodType.methodType(MethodHandle.class, CallSite.class));
+            TYPE_INIT_HOOK = LOOKUP.findStatic(SwitchBootstraps.class, "typeInitHook",
+                                                  MethodType.methodType(MethodHandle.class, CallSite.class));
+            PATTERN_SWITCH_METHOD = LOOKUP.findVirtual(PatternSwitchCallSite.class, "doSwitch",
+                                                       MethodType.methodType(PatternSwitchResult.class, Object.class));
+            TYPE_SWITCH_METHOD = LOOKUP.findVirtual(TypeSwitchCallSite.class, "doSwitch",
+                                                    MethodType.methodType(int.class, Object.class));
+        }
+        catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private static<T extends CallSite> MethodHandle constantInitHook(T receiver) {
+        return switchMethods.computeIfAbsent(receiver.type().parameterType(0), lookupSwitchMethod)
+                            .bindTo(receiver);
+    }
+
+    private static<T extends CallSite> MethodHandle typeInitHook(T receiver) {
+        return TYPE_SWITCH_METHOD.bindTo(receiver);
+    }
+
+    private static<T extends CallSite> MethodHandle patternInitHook(T receiver) {
+        return PATTERN_SWITCH_METHOD.bindTo(receiver);
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on a {@code boolean} or {@code Boolean}.
+     * The static arguments are a varargs array of {@code boolean} labels,
+     *
+     * <p>The results are undefined if the labels array contains duplicates.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter which is
+     *                       {@code boolean} or {@code Boolean},and return {@code int}.
+     *                       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 booleanLabels boolean values corresponding to the case labels of the
+     *                  {@code switch} statement.
+     * @return the index into {@code booleanLabels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code booleanLabels.length} if the target value does
+     *         not match any of the labels.
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if the invocation type is not
+     * {@code (boolean)int} or {@code (Boolean)int}
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite booleanSwitch(MethodHandles.Lookup lookup,
+                                         String invocationName,
+                                         MethodType invocationType,
+                                         boolean... booleanLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || (!BOOLEAN_TYPES.contains(invocationType.parameterType(0))))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(booleanLabels);
+
+        int[] intLabels = IntStream.range(0, booleanLabels.length)
+                                   .map(i -> booleanLabels[i] ? 1 : 0)
+                                   .toArray();
+
+        assert IntStream.of(intLabels).distinct().count() == intLabels.length
+                : "switch labels are not distinct: " + Arrays.toString(booleanLabels);
+
+        return new IntSwitchCallSite(invocationType, intLabels);
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on an {@code int}, {@code short}, {@code byte},
+     * {@code char}, or one of their box types.  The static arguments are a
+     * varargs array of {@code int} labels.
+     *
+     * <p>The results are undefined if the labels array contains duplicates.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter which is
+     *                       one of the 32-bit or shorter primitive types, or
+     *                       one of their box types, and return {@code int}.  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 intLabels integral values corresponding to the case labels of the
+     *                  {@code switch} statement.
+     * @return the index into {@code intLabels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code intLabels.length} if the target value does
+     *         not match any of the labels.
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if the invocation type is not
+     * {@code (T)int}, where {@code T} is one of the 32-bit or smaller integral
+     * primitive types, or one of their box types
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite intSwitch(MethodHandles.Lookup lookup,
+                                     String invocationName,
+                                     MethodType invocationType,
+                                     int... intLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || (!INT_TYPES.contains(invocationType.parameterType(0))))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(intLabels);
+
+        assert IntStream.of(intLabels).distinct().count() == intLabels.length
+                : "switch labels are not distinct: " + Arrays.toString(intLabels);
+
+        return new IntSwitchCallSite(invocationType, intLabels);
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on an {@code float} or {@code Float}.
+     * The static arguments are a varargs array of {@code float} labels.
+     *
+     * <p>The results are undefined if the labels array contains duplicates
+     * according to {@link Float#floatToIntBits(float)}.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter which is
+     *                       one of the 32-bit or shorter primitive types, or
+     *                       one of their box types, and return {@code int}.  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 floatLabels float values corresponding to the case labels of the
+     *                    {@code switch} statement.
+     * @return the index into {@code floatLabels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code floatLabels.length} if the target value does
+     *         not match any of the labels.
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if the invocation type is not
+     * {@code (float)int} or {@code (Float)int}
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite floatSwitch(MethodHandles.Lookup lookup,
+                                       String invocationName,
+                                       MethodType invocationType,
+                                       float... floatLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || (!FLOAT_TYPES.contains(invocationType.parameterType(0))))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(floatLabels);
+
+        int[] intLabels = new int[floatLabels.length];
+        for (int i=0; i<floatLabels.length; i++)
+            intLabels[i] = Float.floatToIntBits(floatLabels[i]);
+
+        assert IntStream.of(intLabels).distinct().count() == intLabels.length
+                : "switch labels are not distinct: " + Arrays.toString(floatLabels);
+
+        return new IntSwitchCallSite(invocationType, intLabels);
+    }
+
+    static class IntSwitchCallSite extends ConstantCallSite {
+        private final int[] labels;
+        private final int[] indexes;
+
+        IntSwitchCallSite(MethodType targetType,
+                          int[] intLabels) throws Throwable {
+            super(targetType, CONSTANT_INIT_HOOK);
+
+            // expensive way to index an array
+            indexes = IntStream.range(0, intLabels.length)
+                               .boxed()
+                               .sorted(Comparator.comparingInt(a -> intLabels[a]))
+                               .mapToInt(Integer::intValue)
+                               .toArray();
+            labels = new int[indexes.length];
+            for (int i=0; i<indexes.length; i++)
+                labels[i] = intLabels[indexes[i]];
+        }
+
+        int doSwitch(int target) {
+            int index = Arrays.binarySearch(labels, target);
+            return (index >= 0) ? indexes[index] : indexes.length;
+        }
+
+        int doSwitch(boolean target) {
+            return doSwitch(target ? 1 : 0);
+        }
+
+        int doSwitch(float target) {
+            return doSwitch(Float.floatToIntBits(target));
+        }
+
+        int doSwitch(short target) {
+            return doSwitch((int) target);
+        }
+
+        int doSwitch(byte target) {
+            return doSwitch((int) target);
+        }
+
+        int doSwitch(char target) {
+            return doSwitch((int) target);
+        }
+
+        int doSwitch(Boolean target) {
+            return (target == null) ? -1 : doSwitch((boolean) target);
+        }
+
+        int doSwitch(Integer target) {
+            return (target == null) ? -1 : doSwitch((int) target);
+        }
+
+        int doSwitch(Float target) {
+            return (target == null) ? -1 : doSwitch((float) target);
+        }
+
+        int doSwitch(Short target) {
+            return (target == null) ? -1 : doSwitch((int) target);
+        }
+
+        int doSwitch(Character target) {
+            return (target == null) ? -1 : doSwitch((int) target);
+        }
+
+        int doSwitch(Byte target) {
+            return (target == null) ? -1 : doSwitch((int) target);
+        }
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on a {@code long} or {@code Long}.
+     * The static arguments are a varargs array of {@code long} labels.
+     *
+     * <p>The results are undefined if the labels array contains duplicates.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter which is
+     *                       one of the 32-bit or shorter primitive types, or
+     *                       one of their box types, and return {@code int}.  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 longLabels long values corresponding to the case labels of the
+     *                  {@code switch} statement.
+     * @return the index into {@code longLabels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code longLabels.length} if the target value does
+     *         not match any of the labels.
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if the invocation type is not
+     * {@code (long)int} or {@code (Long)int}
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite longSwitch(MethodHandles.Lookup lookup,
+                                      String invocationName,
+                                      MethodType invocationType,
+                                      long... longLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || (!LONG_TYPES.contains(invocationType.parameterType(0))))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(longLabels);
+
+        assert LongStream.of(longLabels).distinct().count() == longLabels.length
+                : "switch labels are not distinct: " + Arrays.toString(longLabels);
+
+        return new LongSwitchCallSite(invocationType, longLabels);
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on a {@code double} or {@code Double}.
+     * The static arguments are a varargs array of {@code double} labels.
+     *
+     * <p>The results are undefined if the labels array contains duplicates
+     * according to {@link Double#doubleToLongBits(double)}.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter which is
+     *                       one of the 32-bit or shorter primitive types, or
+     *                       one of their box types, and return {@code int}.  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 doubleLabels long values corresponding to the case labels of the
+     *                  {@code switch} statement.
+     * @return the index into {@code doubleLabels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code doubleLabels.length} if the target value does
+     *         not match any of the labels.
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if the invocation type is not
+     * {@code (double)int} or {@code (Double)int}
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite doubleSwitch(MethodHandles.Lookup lookup,
+                                        String invocationName,
+                                        MethodType invocationType,
+                                        double... doubleLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || (!DOUBLE_TYPES.contains(invocationType.parameterType(0))))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(doubleLabels);
+
+        long[] longLabels = new long[doubleLabels.length];
+        for (int i=0; i<doubleLabels.length; i++)
+            longLabels[i] = Double.doubleToLongBits(doubleLabels[i]);
+
+        assert LongStream.of(longLabels).distinct().count() == longLabels.length
+                : "switch labels are not distinct: " + Arrays.toString(doubleLabels);
+
+        return new LongSwitchCallSite(invocationType, longLabels);
+    }
+
+    static class LongSwitchCallSite extends ConstantCallSite {
+        private final long[] labels;
+        private final int[] indexes;
+
+        LongSwitchCallSite(MethodType targetType,
+                           long[] longLabels) throws Throwable {
+            super(targetType, CONSTANT_INIT_HOOK);
+
+            // expensive way to index an array
+            indexes = IntStream.range(0, longLabels.length)
+                               .boxed()
+                               .sorted(Comparator.comparingLong(a -> longLabels[a]))
+                               .mapToInt(Integer::intValue)
+                               .toArray();
+            labels = new long[indexes.length];
+            for (int i=0; i<indexes.length; i++)
+                labels[i] = longLabels[indexes[i]];
+        }
+
+        int doSwitch(long target) {
+            int index = Arrays.binarySearch(labels, target);
+            return (index >= 0) ? indexes[index] : indexes.length;
+        }
+
+        int doSwitch(double target) {
+            return doSwitch(Double.doubleToLongBits(target));
+        }
+
+        int doSwitch(Long target) {
+            return (target == null) ? -1 : doSwitch((long) target);
+        }
+
+        int doSwitch(Double target) {
+            return (target == null) ? -1 : doSwitch((double) target);
+        }
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on a {@code String} target.  The static
+     * arguments are a varargs array of {@code String} labels.
+     *
+     * <p>The results are undefined if the labels array contains duplicates
+     * according to {@link String#equals(Object)}.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter of
+     *                       {@code String}, and return {@code int}.  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 stringLabels non-null string values corresponding to the case
+     *                     labels of the {@code switch} statement.
+     * @return the index into {@code labels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code stringLabels.length} if the target value
+     *         does not match any of the labels.
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if any labels are null, or if the
+     * invocation type is not {@code (String)int}
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite stringSwitch(MethodHandles.Lookup lookup,
+                                        String invocationName,
+                                        MethodType invocationType,
+                                        String... stringLabels) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || (!invocationType.parameterType(0).equals(String.class)))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(stringLabels);
+        if (Stream.of(stringLabels).anyMatch(Objects::isNull))
+            throw new IllegalArgumentException("null label found");
+
+        assert Stream.of(stringLabels).distinct().count() == stringLabels.length
+                : "switch labels are not distinct: " + Arrays.toString(stringLabels);
+
+        return new StringSwitchCallSite(invocationType, stringLabels);
+    }
+
+    static class StringSwitchCallSite extends ConstantCallSite {
+        private static final Comparator<String> STRING_BY_HASH
+                = Comparator.comparingInt(Objects::hashCode);
+
+        private final String[] sortedByHash;
+        private final int[] indexes;
+        private final boolean collisions;
+
+        StringSwitchCallSite(MethodType targetType,
+                             String[] stringLabels) throws Throwable {
+            super(targetType, CONSTANT_INIT_HOOK);
+
+            // expensive way to index an array
+            indexes = IntStream.range(0, stringLabels.length)
+                               .boxed()
+                               .sorted(Comparator.comparingInt(i -> stringLabels[i].hashCode()))
+                               .mapToInt(Integer::intValue)
+                               .toArray();
+            sortedByHash = new String[indexes.length];
+            for (int i=0; i<indexes.length; i++)
+                sortedByHash[i] = stringLabels[indexes[i]];
+
+            collisions = IntStream.range(0, sortedByHash.length-1)
+                                  .anyMatch(i -> sortedByHash[i].hashCode() == sortedByHash[i + 1].hashCode());
+        }
+
+        int doSwitch(String target) {
+            if (target == null)
+                return -1;
+
+            int index = Arrays.binarySearch(sortedByHash, target, STRING_BY_HASH);
+            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 && sortedByHash[index-1].hashCode() == hash)
+                    --index;
+                for (; index < sortedByHash.length && sortedByHash[index].hashCode() == hash; index++)
+                    if (target.equals(sortedByHash[index]))
+                        return indexes[index];
+            }
+
+            return indexes.length;
+        }
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on an {@code Enum} target.  The static
+     * arguments are the enum class, and a varargs arrays of {@code String}
+     * that are the names of the enum constants corresponding to the
+     * {@code case} labels.
+     *
+     * <p>The results are undefined if the names array contains duplicates.
+     *
+     * @implNote
+     *
+     * The implementation only enforces the requirement that the labels array
+     * be duplicate-free if system assertions are enabled.
+     *
+     * @param <E> the enum type
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter of
+     *                       {@code Enum}, and return {@code int}.  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 enumClass the enum class
+     * @param enumNames names of the enum constants against which the target
+     *                  should be matched
+     * @return the index into {@code labels} of the target value, if the target
+     *         matches any of the labels, {@literal -1} if the target value is
+     *         {@code null}, or {@code stringLabels.length} if the target value
+     *         does not match any of the labels.
+     * @throws IllegalArgumentException if the specified class is not an
+     *                                  enum class, or any label name is null,
+     *                                  or if the invocation type is not
+     *                                  {@code (Enum)int}
+     * @throws NullPointerException if any required argument is null
+     * @throws Throwable if there is any error linking the call site
+     */
+    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().equals(int.class))
+            || (!invocationType.parameterType(0).equals(Enum.class)))
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(enumClass);
+        requireNonNull(enumNames);
+        if (!enumClass.isEnum())
+            throw new IllegalArgumentException("not an enum class");
+        if (Stream.of(enumNames).anyMatch(Objects::isNull))
+            throw new IllegalArgumentException("null label found");
+
+        assert Stream.of(enumNames).distinct().count() == enumNames.length
+                : "switch labels are not distinct: " + Arrays.toString(enumNames);
+
+        return new EnumSwitchCallSite<>(invocationType, enumClass, enumNames);
+    }
+
+    static class EnumSwitchCallSite<E extends Enum<E>> extends ConstantCallSite {
+        private final int[] ordinalMap;
+
+        EnumSwitchCallSite(MethodType targetType,
+                           Class<E> enumClass,
+                           String... enumNames) throws Throwable {
+            super(targetType, CONSTANT_INIT_HOOK);
+
+            ordinalMap = new int[enumClass.getEnumConstants().length];
+            Arrays.fill(ordinalMap, enumNames.length);
+
+            for (int i=0; i<enumNames.length; i++) {
+                try {
+                    ordinalMap[E.valueOf(enumClass, enumNames[i]).ordinal()] = i;
+                }
+                catch (Exception e) {
+                    // allow non-existent labels, but never match them
+                    continue;
+                }
+            }
+        }
+
+        @SuppressWarnings("rawtypes")
+        int doSwitch(Enum target) {
+            return (target == null) ? -1 : ordinalMap[target.ordinal()];
+        }
+    }
+
+    /**
+     * Bootstrap method for linking an {@code invokedynamic} call site that
+     * implements a {@code switch} on a reference-typed target.  The static
+     * arguments are a varargs array of {@code Class} labels.
+     *
+     * @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 invocation name, which is ignored.  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 invocation type of the {@code CallSite}.  This
+     *                       method type should have a single parameter of
+     *                       a reference type, and return {@code int}.  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 types non-null {@link Class} values
+     * @return the index into {@code labels} of the target value, if the target
+     *         is an instance of any of the types, {@literal -1} if the target
+     *         value is {@code null}, or {@code types.length} if the target value
+     *         is not an instance of any of the types
+     * @throws NullPointerException if any required argument is null
+     * @throws IllegalArgumentException if any labels are null, or if the
+     * invocation type is not {@code (T)int for some reference type {@code T}}
+     * @throws Throwable if there is any error linking the call site
+     */
+    public static CallSite typeSwitch(MethodHandles.Lookup lookup,
+                                      String invocationName,
+                                      MethodType invocationType,
+                                      Class<?>... types) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(int.class))
+            || invocationType.parameterType(0).isPrimitive())
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(types);
+
+        types = types.clone();
+        if (Stream.of(types).anyMatch(Objects::isNull))
+            throw new IllegalArgumentException("null label found");
+
+        assert Stream.of(types).distinct().count() == types.length
+                : "switch labels are not distinct: " + Arrays.toString(types);
+
+        return new TypeSwitchCallSite(invocationType, types);
+    }
+
+    static class TypeSwitchCallSite extends ConstantCallSite {
+        private final Class<?>[] types;
+
+        TypeSwitchCallSite(MethodType targetType,
+                           Class<?>[] types) throws Throwable {
+            super(targetType, TYPE_INIT_HOOK);
+            this.types = types;
+        }
+
+        int doSwitch(Object target) {
+            if (target == null)
+                return -1;
+
+            // Dumbest possible strategy
+            Class<?> targetClass = target.getClass();
+            for (int i = 0; i < types.length; i++) {
+                Class<?> c = types[i];
+                if (c.isAssignableFrom(targetClass))
+                    return i;
+            }
+
+            return types.length;
+        }
+    }
+
+    /**
+     * Result type for pattern switches
+     */
+    public static class PatternSwitchResult {
+        /**
+         * The selected index, -1 if input was null, or length if not matched
+         */
+        public final int index;
+
+        /**
+         * The carrier
+         */
+        public final Object carrier;
+
+        /**
+         * Construct a PatternSwitchResult
+         *
+         * @param index the index
+         * @param carrier the carrier
+         */
+        public PatternSwitchResult(int index, Object carrier) {
+            this.index = index;
+            this.carrier = carrier;
+        }
+    }
+
+    /**
+     * Bootstrap for pattern switches
+     *
+     * @param lookup the lookup (ignored)
+     * @param invocationName the invocation name (ignored)
+     * @param invocationType the invocation type (must return PatternSwitchResult)
+     * @param patterns the patterns
+     * @return the result
+     * @throws Throwable if something went wrong
+     */
+    public static CallSite patternSwitch(MethodHandles.Lookup lookup,
+                                         String invocationName,
+                                         MethodType invocationType,
+                                         Extractor... patterns) throws Throwable {
+        if (invocationType.parameterCount() != 1
+            || (!invocationType.returnType().equals(PatternSwitchResult.class))
+            || invocationType.parameterType(0).isPrimitive())
+            throw new IllegalArgumentException("Illegal invocation type " + invocationType);
+        requireNonNull(patterns);
+
+        patterns = patterns.clone();
+        Class<?> targetType = invocationType.parameterType(0);
+
+        for (int i = 0; i < patterns.length; i++) {
+            Extractor pattern = patterns[i];
+            if (pattern.descriptor().returnType() != targetType)
+                patterns[i] = Extractor.adapt(pattern, targetType);
+        }
+
+        if (Stream.of(patterns).anyMatch(Objects::isNull))
+            throw new IllegalArgumentException("null pattern found");
+
+        return new PatternSwitchCallSite(invocationType, patterns);
+    }
+
+    static class PatternSwitchCallSite extends ConstantCallSite {
+        private final Extractor[] patterns;
+
+        PatternSwitchCallSite(MethodType targetType,
+                              Extractor[] patterns) throws Throwable {
+            super(targetType, PATTERN_INIT_HOOK);
+            this.patterns = patterns;
+        }
+
+        PatternSwitchResult doSwitch(Object target) throws Throwable {
+            if (target == null)
+                return new PatternSwitchResult(-1, null);
+
+            // Dumbest possible strategy
+            for (int i = 0; i < patterns.length; i++) {
+                Extractor e = patterns[i];
+                Object o = e.tryMatch().invoke(target);
+                if (o != null)
+                    return new PatternSwitchResult(i, o);
+            }
+
+            return new PatternSwitchResult(patterns.length, null);
+
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/_pattern.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,101 @@
+/*
+ * 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 java.lang.runtime;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Temporary scaffolding to allow declaration of constrained patterns
+ * without language support.
+ */
+public interface _pattern<B> {
+    /**
+     * Attempt to match
+     * @param o the target
+     * @return the result, or an empty optional
+     */
+    Optional<B> match(Object o);
+
+    /**
+     * Construct a PatternDecl for a partial pattern
+     * @param predicate the applicability test
+     * @param extract the extraction logic
+     * @param <T> the type of a successful target
+     * @param <B> the type of the binding
+     * @return the PatternDecl
+     */
+    @SuppressWarnings("unchecked")
+    static<T, B> _pattern<B> of(Predicate<T> predicate, Function<T, B> extract) {
+        return (Object o) ->
+                (predicate.test((T) o))
+                ? Optional.of(extract.apply((T) o))
+                : Optional.empty();
+    }
+
+    /**
+     * Construct a PatternDecl for a total pattern on Object
+     * @param extract the extraction logic
+     * @param <B> the type of the binding
+     * @return the PatternDecl
+     */
+    static<B> _pattern<B> of(Function<?, B> extract) {
+        return of(o -> true, extract);
+    }
+
+    /**
+     * Construct a PatternDecl for a type test pattern
+     * @param clazz The type to test against
+     * @param extract the extraction logic
+     * @param <T> the type of a successful target
+     * @param <B> the type of the binding
+     * @return the PatternDecl
+     */
+    static<T, B> _pattern<B> ofType(Class<T> clazz, Function<T, B> extract) {
+        return of(o -> clazz.isAssignableFrom(o.getClass()),
+                  o -> extract.apply(clazz.cast(o)));
+    }
+
+    /**
+     * Construct a PatternDecl for a type test pattern
+     * @param clazz The type to test against
+     * @param <T> the type of a successful target
+     * @return the PatternDecl
+     */
+    static<T> _pattern<T> ofType(Class<T> clazz) {
+        return of(o -> clazz.isAssignableFrom(o.getClass()), clazz::cast);
+    }
+
+    /**
+     * Construct a PatternDecl for a constant
+     * @param constant the constant
+     * @param <T> the type of the constant
+     * @return the PatternDecl
+     */
+    static<T> _pattern<T> ofConstant(T constant) {
+        return of(constant::equals, o -> constant);
+    }
+}
--- a/src/java.base/share/classes/module-info.java	Mon Apr 29 18:10:00 2019 -0400
+++ b/src/java.base/share/classes/module-info.java	Wed May 01 10:46:37 2019 -0400
@@ -79,12 +79,12 @@
     exports java.io;
     exports java.lang;
     exports java.lang.annotation;
-    exports java.lang.compiler;
     exports java.lang.constant;
     exports java.lang.invoke;
     exports java.lang.module;
     exports java.lang.ref;
     exports java.lang.reflect;
+    exports java.lang.runtime;
     exports java.math;
     exports java.net;
     exports java.net.spi;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Mon Apr 29 18:10:00 2019 -0400
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Wed May 01 10:46:37 2019 -0400
@@ -578,7 +578,7 @@
         lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory");
         stringConcatFactory = enterClass("java.lang.invoke.StringConcatFactory");
         functionalInterfaceType = enterClass("java.lang.FunctionalInterface");
-        extractorType = enterClass("java.lang.compiler.Extractor");
+        extractorType = enterClass("java.lang.runtime.Extractor");
         typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
 
         synthesizeEmptyInterfaceIfMissing(autoCloseableType);
--- a/test/jdk/java/lang/compiler/ExtractorTest.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,277 +0,0 @@
-/*
- * Copyright (c) 2018, 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.compiler.Extractor;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import org.testng.annotations.Test;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNotSame;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertSame;
-import static org.testng.Assert.assertTrue;
-
-/**
- * @test
- * @run testng ExtractorTest
- * @summary Smoke tests for java.lang.compiler.Extractor
- */
-@Test
-public class ExtractorTest {
-
-    enum MatchKind { CARRIER, SELF, FAIL, MATCH }
-
-    static void assertMatch(MatchKind kind, Extractor e, Object target, Object... args) throws Throwable {
-        int count = e.descriptor().parameterCount();
-        Object[] bindings = new Object[count];
-        Object carrier = Extractor.adapt(e, Object.class).tryMatch().invoke(target);
-        if (carrier != null) {
-            for (int i = 0; i < count; i++)
-                bindings[i] = e.component(i).invoke(carrier);
-        }
-
-        if (kind == MatchKind.FAIL)
-            assertNull(carrier);
-        else {
-            if (target != null)
-                assertNotNull(carrier);
-            assertEquals(bindings.length, args.length);
-            for (int i = 0; i < args.length; i++)
-                assertEquals(bindings[i], args[i]);
-
-            if (kind == MatchKind.SELF)
-                assertSame(carrier, target);
-            else if (kind == MatchKind.CARRIER)
-                assertNotSame(carrier, target);
-        }
-    }
-
-    private static class TestClass {
-        static MethodHandle MH_S, MH_I, MH_L, MH_B, MH_PRED;
-        static MethodHandle CONSTRUCTOR;
-        static MethodHandle DIGESTER;
-        static MethodHandle DIGESTER_PARTIAL;
-        static MethodType TYPE = MethodType.methodType(TestClass.class, String.class, int.class, long.class, byte.class);
-        static {
-            try {
-                MH_B = MethodHandles.lookup().findGetter(TestClass.class, "b", byte.class);
-                MH_S = MethodHandles.lookup().findGetter(TestClass.class, "s", String.class);
-                MH_I = MethodHandles.lookup().findGetter(TestClass.class, "i", int.class);
-                MH_L = MethodHandles.lookup().findGetter(TestClass.class, "l", long.class);
-                MH_PRED = MethodHandles.lookup().findVirtual(TestClass.class, "matches", MethodType.methodType(boolean.class));
-                CONSTRUCTOR = MethodHandles.lookup().findConstructor(TestClass.class, TYPE.changeReturnType(void.class));
-                DIGESTER = MethodHandles.lookup().findVirtual(TestClass.class, "digest", MethodType.methodType(Object.class, MethodHandle.class));
-                DIGESTER_PARTIAL = MethodHandles.lookup().findVirtual(TestClass.class, "digestPartial", MethodType.methodType(Object.class, MethodHandle.class));
-            }
-            catch (ReflectiveOperationException e) {
-                throw new ExceptionInInitializerError(e);
-            }
-        }
-
-        String s;
-        int i;
-        long l;
-        byte b;
-
-        TestClass(String s, int i, long l, byte b) {
-            this.s = s;
-            this.i = i;
-            this.l = l;
-            this.b = b;
-        }
-
-        TestClass copy() {
-            return new TestClass(s, i, l, b);
-        }
-
-        boolean matches() { return s != null && s.length() == i; }
-
-        Object digest(MethodHandle target) throws Throwable {
-            return target.invoke(s, i, l, b);
-        }
-
-        Object digestPartial(MethodHandle target) throws Throwable {
-            return matches() ? target.invoke(s, i, l, b) : null;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            TestClass aClass = (TestClass) o;
-            return i == aClass.i &&
-                   l == aClass.l &&
-                   b == aClass.b &&
-                   Objects.equals(s, aClass.s);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(s, i, l, b);
-        }
-    }
-
-    private static class TestClass2 {
-        static MethodHandle MH_X;
-        static MethodType TYPE = MethodType.methodType(TestClass2.class, Object.class);
-        static {
-            try {
-                MH_X = MethodHandles.lookup().findGetter(TestClass2.class, "x", Object.class);
-            }
-            catch (ReflectiveOperationException e) {
-                throw new ExceptionInInitializerError(e);
-            }
-        }
-
-        Object x;
-
-        public TestClass2(Object x) {
-            this.x = x;
-        }
-    }
-
-    private static final MethodHandle[] COMPONENTS = {TestClass.MH_S, TestClass.MH_I, TestClass.MH_L, TestClass.MH_B };
-
-    public void testTotal() throws Throwable {
-        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
-        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.CARRIER, e, new TestClass(null, 0, 0L, (byte) 0),
-                    null, 0, 0L, (byte) 0);
-    }
-
-    public void testSelfTotal() throws Throwable {
-        Extractor e = Extractor.ofSelfTotal(TestClass.class, COMPONENTS);
-        assertMatch(MatchKind.SELF, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.SELF, e, new TestClass(null, 0, 0L, (byte) 0),
-                    null, 0, 0L, (byte) 0);
-    }
-
-    public void testPartial() throws Throwable {
-        Extractor e = Extractor.ofPartial(TestClass.class, TestClass.MH_PRED, COMPONENTS);
-        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
-        assertMatch(MatchKind.FAIL, e, new TestClass(null, 0, 0L, (byte) 0));
-    }
-
-    public void testSelfPartial() throws Throwable {
-        Extractor e = Extractor.ofSelfPartial(TestClass.class, TestClass.MH_PRED, COMPONENTS);
-        assertMatch(MatchKind.SELF, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
-        assertMatch(MatchKind.FAIL, e, new TestClass(null, 0, 0L, (byte) 0));
-    }
-
-    public void testDigest() throws Throwable {
-        Extractor e = Extractor.of(TestClass.TYPE, TestClass.DIGESTER);
-        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 2, 4L, (byte) 5),
-                    "foo", 2, 4L, (byte) 5);
-        assertMatch(MatchKind.CARRIER, e, new TestClass(null, 0, 0L, (byte) 0),
-                    null, 0, 0L, (byte) 0);
-    }
-
-    public void testDigestPartial() throws Throwable {
-        Extractor e = Extractor.of(TestClass.TYPE, TestClass.DIGESTER_PARTIAL);
-        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
-    }
-
-    public void testCompose() throws Throwable {
-        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
-        MethodHandle mh = e.compose(TestClass.CONSTRUCTOR, null);
-        TestClass target = new TestClass("foo", 3, 4L, (byte) 5);
-        Object o = mh.invoke(target);
-        assertTrue(o instanceof TestClass);
-        assertNotSame(target, o);
-        assertEquals(target, o);
-    }
-
-    public void testDropBindings() throws Throwable {
-        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
-        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L, (byte) 5);
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 0), new TestClass("foo", 3, 4L, (byte) 5),
-                    3, 4L, (byte) 5);
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 0, 0), new TestClass("foo", 3, 4L, (byte) 5),
-                    3, 4L, (byte) 5);
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 3), new TestClass("foo", 3, 4L, (byte) 5),
-                    "foo", 3, 4L);
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 0, 1, 2, 3), new TestClass("foo", 3, 4L, (byte) 5));
-    }
-
-    public void testAsType() throws Throwable {
-        assertMatch(MatchKind.SELF, Extractor.ofType(String.class), "Foo", "Foo");
-        assertMatch(MatchKind.FAIL, Extractor.ofType(String.class), 3);
-        assertMatch(MatchKind.FAIL, Extractor.ofType(String.class), null);
-
-        assertMatch(MatchKind.SELF, Extractor.ofType(List.class), List.of(3), List.of(3));
-        assertMatch(MatchKind.SELF, Extractor.ofType(List.class), List.of(), List.of());
-        assertMatch(MatchKind.SELF, Extractor.ofType(List.class), new ArrayList<>(), List.of());
-    }
-
-    public void testAsNullableType() throws Throwable {
-        assertMatch(MatchKind.SELF, Extractor.ofTypeNullable(String.class), "Foo", "Foo");
-        assertMatch(MatchKind.FAIL, Extractor.ofTypeNullable(String.class), 3);
-        assertMatch(MatchKind.MATCH, Extractor.ofTypeNullable(String.class), null, (Object) null);
-    }
-
-    public void testConstant() throws Throwable {
-        assertMatch(MatchKind.MATCH, Extractor.ofConstant(null), null);
-        assertMatch(MatchKind.FAIL, Extractor.ofConstant(null), "foo");
-        assertMatch(MatchKind.MATCH, Extractor.ofConstant("foo"), "foo");
-        assertMatch(MatchKind.FAIL, Extractor.ofConstant("foo"), "bar");
-        assertMatch(MatchKind.FAIL, Extractor.ofConstant("foo"), 3);
-        assertMatch(MatchKind.FAIL, Extractor.ofConstant("foo"), null);
-        assertMatch(MatchKind.MATCH, Extractor.ofConstant(3), 3);
-    }
-
-    public void testNested() throws Throwable {
-        Extractor TC2 = Extractor.ofTotal(TestClass2.class, TestClass2.MH_X);
-        Extractor STRING = Extractor.ofType(String.class);
-        Extractor OBJECT  = Extractor.ofType(Object.class);
-
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.ofNested(TC2, STRING), 0), new TestClass2("foo"),
-                    "foo");
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.ofNested(TC2, OBJECT), 0), new TestClass2("foo"),
-                    "foo");
-        assertMatch(MatchKind.FAIL, Extractor.dropBindings(Extractor.ofNested(TC2, STRING), 0), new TestClass2(List.of(3)),
-                    "foo");
-
-        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.ofNested(TC2, Extractor.ofNested(TC2, STRING)), 0, 1), new TestClass2(new TestClass2("foo")),
-                    "foo");
-    }
-}
--- a/test/jdk/java/lang/compiler/RecordTest.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2018, 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.compiler.Extractor;
-import java.lang.compiler.SwitchBootstraps;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
-import org.testng.annotations.Test;
-
-import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial;
-import static org.testng.Assert.assertEquals;
-
-@Test
-/**
- * @test
- * @run testng RecordTest
- * @summary End-to-end test for record patterns
- */
-public class RecordTest {
-    record R(int a, String b, double c);
-    record RR(R r1, R R2);
-
-    private Extractor recordExtractor(Class<?> recordClass,
-                                      Class<?>... paramTypes) throws Throwable {
-        return Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
-                                       recordClass, MethodType.methodType(void.class, paramTypes), recordClass.getName(), REF_newInvokeSpecial);
-    }
-
-    public void testRecord() throws Throwable {
-        R r = new R(1, "two", 3.14d);
-        Extractor rExtract = recordExtractor(R.class, int.class, String.class, double.class);
-
-        MethodHandle tryExtract = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rExtract);
-        MethodHandle a = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 0);
-        MethodHandle b = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 1);
-        MethodHandle c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 2);
-
-        Object o = tryExtract.invoke(r);
-        assertEquals(1, a.invoke(o));
-        assertEquals("two", b.invoke(o));
-        assertEquals(3.14d, c.invoke(o));
-    }
-
-    public void testFakeNested() throws Throwable {
-        R r1 = new R(1, "two", 3.14d);
-        R r2 = new R(2, "four", 6.0d);
-        RR rr = new RR(r1, r2);
-
-        Extractor rExtract = recordExtractor(R.class, int.class, String.class, double.class);
-        Extractor rrExtract = recordExtractor(RR.class, R.class, R.class);
-
-        MethodHandle tryExtractR = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rExtract);
-        MethodHandle ra = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 0);
-        MethodHandle rb = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 1);
-        MethodHandle rc = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 2);
-
-        MethodHandle tryExtractRr = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract);
-        MethodHandle r1c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract, 0);
-        MethodHandle r2c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract, 1);
-
-        Object o = tryExtractRr.invoke(rr);
-        R o1 = (R) r1c.invoke(o);
-        R o2 = (R) r2c.invoke(o);
-
-        assertEquals(1, ra.invoke(o1));
-        assertEquals("two", rb.invoke(o1));
-        assertEquals(3.14d, rc.invoke(o1));
-
-        assertEquals(2, ra.invoke(o2));
-        assertEquals("four", rb.invoke(o2));
-        assertEquals(6.0d, rc.invoke(o2));
-    }
-
-    public void testNested() throws Throwable {
-        Extractor rExtract = recordExtractor(R.class, int.class, String.class, double.class);
-        Extractor rrExtract = recordExtractor(RR.class, R.class, R.class);
-
-        Extractor e = Extractor.ofNested(rrExtract, rExtract);
-
-        R r1 = new R(1, "two", 3.14d);
-        R r2 = new R(2, "four", 6.0d);
-        RR rr = new RR(r1, r2);
-
-        Object o = e.tryMatch().invoke(rr);
-
-        assertEquals(e.component(0).invoke(o), new R(1, "two", 3.14d));
-        assertEquals(e.component(1).invoke(o), new R(2, "four", 6.0d));
-        assertEquals(e.component(2).invoke(o), 1);
-        assertEquals(e.component(3).invoke(o), "two");
-        assertEquals(e.component(4).invoke(o), 3.14d);
-
-        Extractor ee = Extractor.ofNested(rrExtract, rExtract, rExtract);
-        o = ee.tryMatch().invoke(rr);
-
-        assertEquals(ee.component(0).invoke(o), new R(1, "two", 3.14d));
-        assertEquals(ee.component(1).invoke(o), new R(2, "four", 6.0d));
-        assertEquals(ee.component(2).invoke(o), 1);
-        assertEquals(ee.component(3).invoke(o), "two");
-        assertEquals(ee.component(4).invoke(o), 3.14d);
-        assertEquals(ee.component(5).invoke(o), 2);
-        assertEquals(ee.component(6).invoke(o), "four");
-        assertEquals(ee.component(7).invoke(o), 6.0d);
-    }
-
-    record A(int a);
-    record B(int a, int b);
-    record S(String s);
-    record T(String s, String t);
-    record U();
-
-    private Object component(Extractor e, int num, Object carrier) throws Throwable {
-        return Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class,
-                                            e, num).invoke(carrier);
-    }
-
-    public void testRecordSwitch() throws Throwable {
-        Extractor[] extractors = {
-                recordExtractor(A.class, int.class),
-                recordExtractor(B.class, int.class, int.class),
-                recordExtractor(S.class, String.class),
-                recordExtractor(T.class, String.class, String.class),
-                recordExtractor(U.class)
-        };
-
-        Object[] exemplars = {
-                new A(1),
-                new B(2, 3),
-                new S("four"),
-                new T("five", "six"),
-                new U()
-        };
-
-        CallSite cs = SwitchBootstraps.patternSwitch(MethodHandles.lookup(), "_",
-                                                     MethodType.methodType(SwitchBootstraps.PatternSwitchResult.class, Object.class),
-                                                     extractors);
-        MethodHandle mh = cs.dynamicInvoker();
-        for (int i = 0; i < exemplars.length; i++) {
-            Object exemplar = exemplars[i];
-            SwitchBootstraps.PatternSwitchResult result = (SwitchBootstraps.PatternSwitchResult) mh.invoke(exemplar);
-            assertEquals(result.index, i);
-            switch (result.index) {
-                case 0:
-                    assertEquals(component(extractors[i], 0, result.carrier), 1);
-                    break;
-                case 1:
-                    assertEquals(component(extractors[i], 0, result.carrier), 2);
-                    assertEquals(component(extractors[i], 1, result.carrier), 3);
-                    break;
-                case 2:
-                    assertEquals(component(extractors[i], 0, result.carrier), "four");
-                    break;
-                case 3:
-                    assertEquals(component(extractors[i], 0, result.carrier), "five");
-                    assertEquals(component(extractors[i], 1, result.carrier), "six");
-                    break;
-            };
-
-            result = (SwitchBootstraps.PatternSwitchResult) mh.invoke(null);
-            assertEquals(result.index, -1);
-
-            result = (SwitchBootstraps.PatternSwitchResult) mh.invoke("foo");
-            assertEquals(result.index, 5);
-        }
-    }
-
-    record Box(Object o1);
-
-    public void testNestedRecord() throws Throwable {
-        Extractor boxA = Extractor.ofNested(recordExtractor(Box.class, Object.class),
-                                            recordExtractor(A.class, int.class));
-        Extractor boxB = Extractor.ofNested(recordExtractor(Box.class, Object.class),
-                                            recordExtractor(B.class, int.class, int.class));
-
-        CallSite cs = SwitchBootstraps.patternSwitch(MethodHandles.lookup(), "_",
-                                                     MethodType.methodType(SwitchBootstraps.PatternSwitchResult.class, Object.class),
-                                                     boxA, boxB);
-        MethodHandle mh = cs.dynamicInvoker();
-
-        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box(new A(1)))).index, 0);
-        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box(new B(2, 3)))).index, 1);
-        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box("foo"))).index, 2);
-        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box(null))).index, 2);
-        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke("foo")).index, 2);
-        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(null)).index, -1);
-    }
-
-    record RString(String i) { }
-    record RObject(Object i) { }
-    record Rint(int i) { }
-
-
-    public void testNestedWithConstant() throws Throwable {
-        Extractor rb = recordExtractor(Box.class, Object.class);
-        Extractor rs = recordExtractor(RString.class, String.class);
-        Extractor ro = recordExtractor(RObject.class, Object.class);
-        Extractor ri = recordExtractor(Rint.class, int.class);
-        Extractor cs = Extractor.ofConstant("foo");
-        Extractor cn = Extractor.ofConstant(null);
-        Extractor ci = Extractor.ofConstant(3);
-
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH, Extractor.ofNested(rs, cs), new RString("foo"), "foo");
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(rs, cs), new RString("bar"));
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(rs, cs), new RString(null));
-
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH, Extractor.ofNested(ro, cs), new RObject("foo"), "foo");
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ro, cs), new RObject("bar"));
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ro, cs), new RObject(3));
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ro, cs), new RObject(null));
-
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH, Extractor.ofNested(ri, ci), new Rint(3), 3);
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ri, ci), new Rint(2));
-
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH,
-                                  Extractor.ofNested(rb, Extractor.ofNested(rs, cs)),
-                                  new Box(new RString("foo")), new RString("foo"), "foo");
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL,
-                                  Extractor.ofNested(rb, Extractor.ofNested(rs, cs)),
-                                  new Box(new RString("bar")));
-        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL,
-                                  Extractor.ofNested(rb, Extractor.ofNested(rs, cs)),
-                                  new Box("foo"));
-    }
-}
--- a/test/jdk/java/lang/compiler/SwitchBootstrapsTest.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,466 +0,0 @@
-/*
- * 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.io.Serializable;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.compiler.SwitchBootstraps;
-import java.util.ArrayList;
-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 jdk.test.lib.RandomFactory;
-
-import org.testng.annotations.Test;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-
-/**
- * @test
- * @key randomness
- * @library /test/lib
- * @build jdk.test.lib.RandomFactory
- * @run testng SwitchBootstrapsTest
- */
-@Test
-public class SwitchBootstrapsTest {
-    private final static Set<Class<?>> BOOLEAN_TYPES = Set.of(boolean.class, Boolean.class);
-    private final static Set<Class<?>> ALL_INT_TYPES = Set.of(int.class, short.class, byte.class, char.class,
-                                                              Integer.class, Short.class, Byte.class, Character.class);
-    private final static Set<Class<?>> SIGNED_NON_BYTE_TYPES = Set.of(int.class, Integer.class, short.class, Short.class);
-    private final static Set<Class<?>> CHAR_TYPES = Set.of(char.class, Character.class);
-    private final static Set<Class<?>> BYTE_TYPES = Set.of(byte.class, Byte.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_BOOLEAN_SWITCH;
-    public static final MethodHandle BSM_INT_SWITCH;
-    public static final MethodHandle BSM_LONG_SWITCH;
-    public static final MethodHandle BSM_FLOAT_SWITCH;
-    public static final MethodHandle BSM_DOUBLE_SWITCH;
-    public static final MethodHandle BSM_STRING_SWITCH;
-    public static final MethodHandle BSM_ENUM_SWITCH;
-    public static final MethodHandle BSM_TYPE_SWITCH;
-
-    private final static Random random = RandomFactory.getRandom();
-
-    static {
-        try {
-            BSM_BOOLEAN_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "booleanSwitch",
-                                                                   MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, boolean[].class));
-            BSM_INT_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "intSwitch",
-                                                               MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int[].class));
-            BSM_LONG_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "longSwitch",
-                                                                MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, long[].class));
-            BSM_FLOAT_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "floatSwitch",
-                                                                 MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, float[].class));
-            BSM_DOUBLE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "doubleSwitch",
-                                                                  MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, double[].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));
-            BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch",
-                                                                MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Class[].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 testBoolean(boolean... labels) throws Throwable {
-        Map<Class<?>, MethodHandle> mhs
-                = Map.of(boolean.class, ((CallSite) BSM_BOOLEAN_SWITCH.invoke(MethodHandles.lookup(), "", switchType(boolean.class), labels)).dynamicInvoker(),
-                         Boolean.class, ((CallSite) BSM_BOOLEAN_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Boolean.class), labels)).dynamicInvoker());
-
-        List<Boolean> labelList = new ArrayList<>();
-        for (boolean label : labels)
-            labelList.add(label);
-
-        for (int i=0; i<labels.length; i++) {
-            assertEquals(i, (int) mhs.get(boolean.class).invokeExact((boolean) labels[i]));
-            assertEquals(i, (int) mhs.get(Boolean.class).invokeExact((Boolean) labels[i]));
-        }
-
-        boolean[] booleans = { false, true };
-        for (boolean b : booleans) {
-            if (!labelList.contains(b)) {
-                assertEquals(labels.length, mhs.get(boolean.class).invoke((boolean) b));
-                assertEquals(labels.length, mhs.get(Boolean.class).invoke((boolean) b));
-            }
-        }
-
-        assertEquals(-1, (int) mhs.get(Boolean.class).invoke(null));
-    }
-
-    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));
-                if (i >= 0) {
-                    assertEquals(labels.length, mhs.get(char.class).invoke((char)i));
-                    assertEquals(labels.length, mhs.get(Character.class).invoke((char)i));
-                }
-                if (i >= -128 && i <= 127) {
-                    assertEquals(labels.length, mhs.get(byte.class).invoke((byte)i));
-                    assertEquals(labels.length, mhs.get(Byte.class).invoke((byte)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 testFloat(float... labels) throws Throwable {
-        Map<Class<?>, MethodHandle> mhs
-                = Map.of(float.class, ((CallSite) BSM_FLOAT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(float.class), labels)).dynamicInvoker(),
-                         Float.class, ((CallSite) BSM_FLOAT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Float.class), labels)).dynamicInvoker());
-
-        List<Float> labelList = new ArrayList<>();
-        for (float label : labels)
-            labelList.add(label);
-
-        for (int i=0; i<labels.length; i++) {
-            assertEquals(i, (int) mhs.get(float.class).invokeExact((float) labels[i]));
-            assertEquals(i, (int) mhs.get(Float.class).invokeExact((Float) labels[i]));
-        }
-
-        float[] someFloats = { 1.0f, Float.MIN_VALUE, 3.14f };
-        for (float f : someFloats) {
-            if (!labelList.contains(f)) {
-                assertEquals(labels.length, mhs.get(float.class).invoke((float) f));
-                assertEquals(labels.length, mhs.get(Float.class).invoke((float) f));
-            }
-        }
-
-        assertEquals(-1, (int) mhs.get(Float.class).invoke(null));
-    }
-
-    private void testDouble(double... labels) throws Throwable {
-        Map<Class<?>, MethodHandle> mhs
-                = Map.of(double.class, ((CallSite) BSM_DOUBLE_SWITCH.invoke(MethodHandles.lookup(), "", switchType(double.class), labels)).dynamicInvoker(),
-                         Double.class, ((CallSite) BSM_DOUBLE_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Double.class), labels)).dynamicInvoker());
-
-        var labelList = new ArrayList<Double>();
-        for (double label : labels)
-            labelList.add(label);
-
-        for (int i=0; i<labels.length; i++) {
-            assertEquals(i, (int) mhs.get(double.class).invokeExact((double) labels[i]));
-            assertEquals(i, (int) mhs.get(Double.class).invokeExact((Double) labels[i]));
-        }
-
-        double[] someDoubles = { 1.0, Double.MIN_VALUE, 3.14 };
-        for (double f : someDoubles) {
-            if (!labelList.contains(f)) {
-                assertEquals(labels.length, mhs.get(double.class).invoke((double) f));
-                assertEquals(labels.length, mhs.get(Double.class).invoke((double) f));
-            }
-        }
-
-        assertEquals(-1, (int) mhs.get(Double.class).invoke(null));
-    }
-
-    private void testLong(long... labels) throws Throwable {
-        Map<Class<?>, MethodHandle> mhs
-                = Map.of(long.class, ((CallSite) BSM_LONG_SWITCH.invoke(MethodHandles.lookup(), "", switchType(long.class), labels)).dynamicInvoker(),
-                         Long.class, ((CallSite) BSM_LONG_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Long.class), labels)).dynamicInvoker());
-
-        List<Long> labelList = new ArrayList<>();
-        for (long label : labels)
-            labelList.add(label);
-
-        for (int i=0; i<labels.length; i++) {
-            assertEquals(i, (int) mhs.get(long.class).invokeExact((long) labels[i]));
-            assertEquals(i, (int) mhs.get(Long.class).invokeExact((Long) labels[i]));
-        }
-
-        long[] someLongs = { 1L, Long.MIN_VALUE, Long.MAX_VALUE };
-        for (long l : someLongs) {
-            if (!labelList.contains(l)) {
-                assertEquals(labels.length, mhs.get(long.class).invoke((long) l));
-                assertEquals(labels.length, mhs.get(Long.class).invoke((long) l));
-            }
-        }
-
-        assertEquals(-1, (int) mhs.get(Long.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 testBoolean() throws Throwable {
-        testBoolean(new boolean[0]);
-        testBoolean(false);
-        testBoolean(true);
-        testBoolean(false, true);
-    }
-
-    public void testInt() throws Throwable {
-        testInt(ALL_INT_TYPES, 8, 6, 7, 5, 3, 0, 9);
-        testInt(ALL_INT_TYPES, 1, 2, 4, 8, 16);
-        testInt(ALL_INT_TYPES, 5, 4, 3, 2, 1, 0);
-        testInt(SIGNED_TYPES, 5, 4, 3, 2, 1, 0, -1);
-        testInt(SIGNED_TYPES, -1);
-        testInt(ALL_INT_TYPES, new int[] { });
-
-        for (int i=0; i<5; i++) {
-            int len = 50 + random.nextInt(800);
-            int[] arr = IntStream.generate(() -> random.nextInt(10000) - 5000)
-                                 .distinct()
-                                 .limit(len)
-                                 .toArray();
-            testInt(SIGNED_NON_BYTE_TYPES, arr);
-
-            arr = IntStream.generate(() -> random.nextInt(10000))
-                    .distinct()
-                    .limit(len)
-                    .toArray();
-            testInt(CHAR_TYPES, arr);
-
-            arr = IntStream.generate(() -> random.nextInt(127) - 64)
-                           .distinct()
-                           .limit(120)
-                           .toArray();
-            testInt(BYTE_TYPES, arr);
-        }
-    }
-
-    public void testLong() throws Throwable {
-        testLong(1L, Long.MIN_VALUE, Long.MAX_VALUE);
-        testLong(8L, 2L, 5L, 4L, 3L, 9L, 1L);
-        testLong(new long[] { });
-
-        // @@@ Random tests
-        // @@@ More tests for weird values
-    }
-
-    public void testFloat() throws Throwable {
-        testFloat(0.0f, -0.0f, -1.0f, 1.0f, 3.14f, Float.MIN_VALUE, Float.MAX_VALUE, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY);
-        testFloat(new float[] { });
-        testFloat(0.0f, 1.0f, 3.14f, Float.NaN);
-
-        // @@@ Random tests
-        // @@@ More tests for weird values
-    }
-
-    public void testDouble() throws Throwable {
-        testDouble(0.0, -0.0, -1.0, 1.0, 3.14, Double.MIN_VALUE, Double.MAX_VALUE,
-                   Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
-        testDouble(new double[] { });
-        testDouble(0.0f, 1.0f, 3.14f, Double.NaN);
-
-        // @@@ Random tests
-        // @@@ More tests for weird values
-    }
-
-    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");
-
-        // Tests with hash collisions; Ba/CB, Ca/DB
-        testString("Ba", "CB");
-        testString("Ba", "CB", "Ca", "DB");
-
-        // Test with null
-        try {
-            testString("a", null, "c");
-            fail("expected failure");
-        }
-        catch (IllegalArgumentException t) {
-            // success
-        }
-    }
-
-    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");
-
-        // Bad enum class
-        try {
-            testEnum((Class) String.class, "A");
-            fail("expected failure");
-        }
-        catch (IllegalArgumentException t) {
-            // success
-        }
-
-        // Bad enum constants
-        try {
-            testEnum(E1.class, "B", "A", "FILE_NOT_FOUND");
-            fail("expected failure");
-        }
-        catch (IllegalArgumentException t) {
-            // success
-        }
-
-        // Null enum constant
-        try {
-            testEnum(E1.class, "A", null, "B");
-            fail("expected failure");
-        }
-        catch (IllegalArgumentException t) {
-            // success
-        }
-    }
-
-    private void testType(Object target, int result, Class... labels) throws Throwable {
-        MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Object.class), labels)).dynamicInvoker();
-        assertEquals((int) indy.invoke(target), result);
-        assertEquals(-1, (int) indy.invoke(null));
-    }
-
-    public void testTypes() throws Throwable {
-        testType("", 0, String.class, Object.class);
-        testType("", 0, Object.class);
-        testType("", 1, Integer.class);
-        testType("", 1, Integer.class, Serializable.class);
-        testType(E1.A, 0, E1.class, Object.class);
-        testType(E2.C, 1, E1.class, Object.class);
-        testType(new Serializable() { }, 1, Comparable.class, Serializable.class);
-
-        // test failures: duplicates, nulls, dominance inversion
-    }
-}
--- a/test/jdk/java/lang/compiler/boottest/TEST.properties	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-# This file identifies root(s) of the test-ng hierarchy.
-
-TestNG.dirs = .
--- a/test/jdk/java/lang/compiler/boottest/java.base/java/lang/compiler/CarrierTest.java	Mon Apr 29 18:10:00 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/*
- * 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 java.lang.compiler;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-
-import org.testng.annotations.Test;
-
-import static org.testng.Assert.assertEquals;
-
-/**
- * @test
- * @key randomness
- * @run testng CarrierTest
- * @summary unit tests for java.lang.compiler.ExtractorCarriers
- */
-@Test
-public class CarrierTest {
-    static final int N_ITER = 1000;
-    static final Class<?>[] TYPES = { byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class, Object.class };
-
-    static Object[] byteVals = { Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) -1, (byte) 0, (byte) 1, (byte) 42 };
-    static Object[] shortVals = { Short.MIN_VALUE, Short.MAX_VALUE, (short) -1, (short) 0, (short) 1, (short) 42 };
-    static Object[] charVals = { Character.MIN_VALUE, Character.MAX_VALUE, (char) 0, 'a', 'Z' };
-    static Object[] intVals = { Integer.MIN_VALUE, Integer.MAX_VALUE, -1, 0, 1, 42 };
-    static Object[] longVals = { Long.MIN_VALUE, Long.MAX_VALUE, -1L, 0L, 1L, 42L };
-    static Object[] floatVals = { Float.MIN_VALUE, Float.MAX_VALUE, -1.0f, 0.0f, 1.0f, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN };
-    static Object[] doubleVals = { Double.MIN_VALUE, Double.MAX_VALUE, -1.0d, 0.0d, 1.0d, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN };
-    static Object[] booleanVals = { true, false };
-    static Object[] objectVals = {null, "", "Foo", "foo", List.of(), Collections.EMPTY_SET };
-
-    // @@@ Should use RandomFactory, but can't get that to link
-    private static final Random random = new Random(System.nanoTime());
-
-    static Map<Class<?>, Object[]> primVals = Map.of(byte.class, byteVals,
-                                                     short.class, shortVals,
-                                                     char.class, charVals,
-                                                     int.class, intVals,
-                                                     long.class, longVals,
-                                                     float.class, floatVals,
-                                                     double.class, doubleVals,
-                                                     boolean.class, booleanVals);
-
-    void testCarrier(MethodType type, Object[] values) throws Throwable {
-        for (ExtractorCarriers.CarrierFactory cf : ExtractorCarriers.CarrierFactories.values()) {
-            assertEquals(type.parameterCount(), values.length);
-            Object carrier = cf.constructor(type).invokeWithArguments(values);
-            for (int i = 0; i < values.length; i++)
-                assertEquals(values[i], cf.component(type, i).invoke(carrier));
-        }
-    }
-
-    void testCarrier(MethodType type) throws Throwable {
-        // generate data, in a loop
-        for (int i=0; i<N_ITER; i++) {
-            Object[] values = new Object[type.parameterCount()];
-            for (int j=0; j<type.parameterCount(); j++) {
-                Class<?> c = type.parameterType(j);
-                Object[] vals = c.isPrimitive() ? primVals.get(c) : objectVals;
-                values[j] = vals[random.nextInt(vals.length)];
-            }
-            testCarrier(type, values);
-        }
-    }
-
-    public void testCarrier() throws Throwable {
-        Class[] lotsOfInts = new Class[252];
-        Arrays.fill(lotsOfInts, int.class);
-
-        // known types
-        for (MethodType mt : List.of(
-                MethodType.methodType(Object.class),
-                MethodType.methodType(Object.class, int.class),
-                MethodType.methodType(Object.class, int.class, int.class),
-                MethodType.methodType(Object.class, Object.class),
-                MethodType.methodType(Object.class, Object.class, Object.class),
-                MethodType.methodType(Object.class, byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class),
-                MethodType.methodType(Object.class, lotsOfInts))) {
-            testCarrier(mt);
-        }
-
-        // random types
-        for (int i=0; i<N_ITER; i++) {
-            int nTypes = random.nextInt(10);
-            Class[] paramTypes = new Class[nTypes];
-            Arrays.setAll(paramTypes, ix -> TYPES[random.nextInt(TYPES.length)]);
-            testCarrier(MethodType.methodType(Object.class, paramTypes));
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/extractor/ExtractorTest.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2018, 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.runtime.Extractor;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @test
+ * @run testng ExtractorTest
+ * @summary Smoke tests for java.lang.runtime.Extractor
+ */
+@Test
+public class ExtractorTest {
+
+    enum MatchKind { CARRIER, SELF, FAIL, MATCH }
+
+    static void assertMatch(MatchKind kind, Extractor e, Object target, Object... args) throws Throwable {
+        int count = e.descriptor().parameterCount();
+        Object[] bindings = new Object[count];
+        Object carrier = Extractor.adapt(e, Object.class).tryMatch().invoke(target);
+        if (carrier != null) {
+            for (int i = 0; i < count; i++)
+                bindings[i] = e.component(i).invoke(carrier);
+        }
+
+        if (kind == MatchKind.FAIL)
+            assertNull(carrier);
+        else {
+            if (target != null)
+                assertNotNull(carrier);
+            assertEquals(bindings.length, args.length);
+            for (int i = 0; i < args.length; i++)
+                assertEquals(bindings[i], args[i]);
+
+            if (kind == MatchKind.SELF)
+                assertSame(carrier, target);
+            else if (kind == MatchKind.CARRIER)
+                assertNotSame(carrier, target);
+        }
+    }
+
+    private static class TestClass {
+        static MethodHandle MH_S, MH_I, MH_L, MH_B, MH_PRED;
+        static MethodHandle CONSTRUCTOR;
+        static MethodHandle DIGESTER;
+        static MethodHandle DIGESTER_PARTIAL;
+        static MethodType TYPE = MethodType.methodType(TestClass.class, String.class, int.class, long.class, byte.class);
+        static {
+            try {
+                MH_B = MethodHandles.lookup().findGetter(TestClass.class, "b", byte.class);
+                MH_S = MethodHandles.lookup().findGetter(TestClass.class, "s", String.class);
+                MH_I = MethodHandles.lookup().findGetter(TestClass.class, "i", int.class);
+                MH_L = MethodHandles.lookup().findGetter(TestClass.class, "l", long.class);
+                MH_PRED = MethodHandles.lookup().findVirtual(TestClass.class, "matches", MethodType.methodType(boolean.class));
+                CONSTRUCTOR = MethodHandles.lookup().findConstructor(TestClass.class, TYPE.changeReturnType(void.class));
+                DIGESTER = MethodHandles.lookup().findVirtual(TestClass.class, "digest", MethodType.methodType(Object.class, MethodHandle.class));
+                DIGESTER_PARTIAL = MethodHandles.lookup().findVirtual(TestClass.class, "digestPartial", MethodType.methodType(Object.class, MethodHandle.class));
+            }
+            catch (ReflectiveOperationException e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        String s;
+        int i;
+        long l;
+        byte b;
+
+        TestClass(String s, int i, long l, byte b) {
+            this.s = s;
+            this.i = i;
+            this.l = l;
+            this.b = b;
+        }
+
+        TestClass copy() {
+            return new TestClass(s, i, l, b);
+        }
+
+        boolean matches() { return s != null && s.length() == i; }
+
+        Object digest(MethodHandle target) throws Throwable {
+            return target.invoke(s, i, l, b);
+        }
+
+        Object digestPartial(MethodHandle target) throws Throwable {
+            return matches() ? target.invoke(s, i, l, b) : null;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            TestClass aClass = (TestClass) o;
+            return i == aClass.i &&
+                   l == aClass.l &&
+                   b == aClass.b &&
+                   Objects.equals(s, aClass.s);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(s, i, l, b);
+        }
+    }
+
+    private static class TestClass2 {
+        static MethodHandle MH_X;
+        static MethodType TYPE = MethodType.methodType(TestClass2.class, Object.class);
+        static {
+            try {
+                MH_X = MethodHandles.lookup().findGetter(TestClass2.class, "x", Object.class);
+            }
+            catch (ReflectiveOperationException e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        Object x;
+
+        public TestClass2(Object x) {
+            this.x = x;
+        }
+    }
+
+    private static final MethodHandle[] COMPONENTS = {TestClass.MH_S, TestClass.MH_I, TestClass.MH_L, TestClass.MH_B };
+
+    public void testTotal() throws Throwable {
+        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
+        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.CARRIER, e, new TestClass(null, 0, 0L, (byte) 0),
+                    null, 0, 0L, (byte) 0);
+    }
+
+    public void testSelfTotal() throws Throwable {
+        Extractor e = Extractor.ofSelfTotal(TestClass.class, COMPONENTS);
+        assertMatch(MatchKind.SELF, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.SELF, e, new TestClass(null, 0, 0L, (byte) 0),
+                    null, 0, 0L, (byte) 0);
+    }
+
+    public void testPartial() throws Throwable {
+        Extractor e = Extractor.ofPartial(TestClass.class, TestClass.MH_PRED, COMPONENTS);
+        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
+        assertMatch(MatchKind.FAIL, e, new TestClass(null, 0, 0L, (byte) 0));
+    }
+
+    public void testSelfPartial() throws Throwable {
+        Extractor e = Extractor.ofSelfPartial(TestClass.class, TestClass.MH_PRED, COMPONENTS);
+        assertMatch(MatchKind.SELF, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
+        assertMatch(MatchKind.FAIL, e, new TestClass(null, 0, 0L, (byte) 0));
+    }
+
+    public void testDigest() throws Throwable {
+        Extractor e = Extractor.of(TestClass.TYPE, TestClass.DIGESTER);
+        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 2, 4L, (byte) 5),
+                    "foo", 2, 4L, (byte) 5);
+        assertMatch(MatchKind.CARRIER, e, new TestClass(null, 0, 0L, (byte) 0),
+                    null, 0, 0L, (byte) 0);
+    }
+
+    public void testDigestPartial() throws Throwable {
+        Extractor e = Extractor.of(TestClass.TYPE, TestClass.DIGESTER_PARTIAL);
+        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
+    }
+
+    public void testCompose() throws Throwable {
+        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
+        MethodHandle mh = e.compose(TestClass.CONSTRUCTOR, null);
+        TestClass target = new TestClass("foo", 3, 4L, (byte) 5);
+        Object o = mh.invoke(target);
+        assertTrue(o instanceof TestClass);
+        assertNotSame(target, o);
+        assertEquals(target, o);
+    }
+
+    public void testDropBindings() throws Throwable {
+        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
+        assertMatch(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L, (byte) 5);
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 0), new TestClass("foo", 3, 4L, (byte) 5),
+                    3, 4L, (byte) 5);
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 0, 0), new TestClass("foo", 3, 4L, (byte) 5),
+                    3, 4L, (byte) 5);
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 3), new TestClass("foo", 3, 4L, (byte) 5),
+                    "foo", 3, 4L);
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(e, 0, 1, 2, 3), new TestClass("foo", 3, 4L, (byte) 5));
+    }
+
+    public void testAsType() throws Throwable {
+        assertMatch(MatchKind.SELF, Extractor.ofType(String.class), "Foo", "Foo");
+        assertMatch(MatchKind.FAIL, Extractor.ofType(String.class), 3);
+        assertMatch(MatchKind.FAIL, Extractor.ofType(String.class), null);
+
+        assertMatch(MatchKind.SELF, Extractor.ofType(List.class), List.of(3), List.of(3));
+        assertMatch(MatchKind.SELF, Extractor.ofType(List.class), List.of(), List.of());
+        assertMatch(MatchKind.SELF, Extractor.ofType(List.class), new ArrayList<>(), List.of());
+    }
+
+    public void testAsNullableType() throws Throwable {
+        assertMatch(MatchKind.SELF, Extractor.ofTypeNullable(String.class), "Foo", "Foo");
+        assertMatch(MatchKind.FAIL, Extractor.ofTypeNullable(String.class), 3);
+        assertMatch(MatchKind.MATCH, Extractor.ofTypeNullable(String.class), null, (Object) null);
+    }
+
+    public void testConstant() throws Throwable {
+        assertMatch(MatchKind.MATCH, Extractor.ofConstant(null), null);
+        assertMatch(MatchKind.FAIL, Extractor.ofConstant(null), "foo");
+        assertMatch(MatchKind.MATCH, Extractor.ofConstant("foo"), "foo");
+        assertMatch(MatchKind.FAIL, Extractor.ofConstant("foo"), "bar");
+        assertMatch(MatchKind.FAIL, Extractor.ofConstant("foo"), 3);
+        assertMatch(MatchKind.FAIL, Extractor.ofConstant("foo"), null);
+        assertMatch(MatchKind.MATCH, Extractor.ofConstant(3), 3);
+    }
+
+    public void testNested() throws Throwable {
+        Extractor TC2 = Extractor.ofTotal(TestClass2.class, TestClass2.MH_X);
+        Extractor STRING = Extractor.ofType(String.class);
+        Extractor OBJECT  = Extractor.ofType(Object.class);
+
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.ofNested(TC2, STRING), 0), new TestClass2("foo"),
+                    "foo");
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.ofNested(TC2, OBJECT), 0), new TestClass2("foo"),
+                    "foo");
+        assertMatch(MatchKind.FAIL, Extractor.dropBindings(Extractor.ofNested(TC2, STRING), 0), new TestClass2(List.of(3)),
+                    "foo");
+
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.ofNested(TC2, Extractor.ofNested(TC2, STRING)), 0, 1), new TestClass2(new TestClass2("foo")),
+                    "foo");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/extractor/RecordTest.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2018, 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.runtime.Extractor;
+import java.lang.runtime.SwitchBootstraps;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import org.testng.annotations.Test;
+
+import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial;
+import static org.testng.Assert.assertEquals;
+
+@Test
+/**
+ * @test
+ * @run testng RecordTest
+ * @summary End-to-end test for record patterns
+ */
+public class RecordTest {
+    record R(int a, String b, double c);
+    record RR(R r1, R R2);
+
+    private Extractor recordExtractor(Class<?> recordClass,
+                                      Class<?>... paramTypes) throws Throwable {
+        return Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
+                                       recordClass, MethodType.methodType(void.class, paramTypes), recordClass.getName(), REF_newInvokeSpecial);
+    }
+
+    public void testRecord() throws Throwable {
+        R r = new R(1, "two", 3.14d);
+        Extractor rExtract = recordExtractor(R.class, int.class, String.class, double.class);
+
+        MethodHandle tryExtract = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rExtract);
+        MethodHandle a = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 0);
+        MethodHandle b = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 1);
+        MethodHandle c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 2);
+
+        Object o = tryExtract.invoke(r);
+        assertEquals(1, a.invoke(o));
+        assertEquals("two", b.invoke(o));
+        assertEquals(3.14d, c.invoke(o));
+    }
+
+    public void testFakeNested() throws Throwable {
+        R r1 = new R(1, "two", 3.14d);
+        R r2 = new R(2, "four", 6.0d);
+        RR rr = new RR(r1, r2);
+
+        Extractor rExtract = recordExtractor(R.class, int.class, String.class, double.class);
+        Extractor rrExtract = recordExtractor(RR.class, R.class, R.class);
+
+        MethodHandle tryExtractR = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rExtract);
+        MethodHandle ra = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 0);
+        MethodHandle rb = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 1);
+        MethodHandle rc = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 2);
+
+        MethodHandle tryExtractRr = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract);
+        MethodHandle r1c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract, 0);
+        MethodHandle r2c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract, 1);
+
+        Object o = tryExtractRr.invoke(rr);
+        R o1 = (R) r1c.invoke(o);
+        R o2 = (R) r2c.invoke(o);
+
+        assertEquals(1, ra.invoke(o1));
+        assertEquals("two", rb.invoke(o1));
+        assertEquals(3.14d, rc.invoke(o1));
+
+        assertEquals(2, ra.invoke(o2));
+        assertEquals("four", rb.invoke(o2));
+        assertEquals(6.0d, rc.invoke(o2));
+    }
+
+    public void testNested() throws Throwable {
+        Extractor rExtract = recordExtractor(R.class, int.class, String.class, double.class);
+        Extractor rrExtract = recordExtractor(RR.class, R.class, R.class);
+
+        Extractor e = Extractor.ofNested(rrExtract, rExtract);
+
+        R r1 = new R(1, "two", 3.14d);
+        R r2 = new R(2, "four", 6.0d);
+        RR rr = new RR(r1, r2);
+
+        Object o = e.tryMatch().invoke(rr);
+
+        assertEquals(e.component(0).invoke(o), new R(1, "two", 3.14d));
+        assertEquals(e.component(1).invoke(o), new R(2, "four", 6.0d));
+        assertEquals(e.component(2).invoke(o), 1);
+        assertEquals(e.component(3).invoke(o), "two");
+        assertEquals(e.component(4).invoke(o), 3.14d);
+
+        Extractor ee = Extractor.ofNested(rrExtract, rExtract, rExtract);
+        o = ee.tryMatch().invoke(rr);
+
+        assertEquals(ee.component(0).invoke(o), new R(1, "two", 3.14d));
+        assertEquals(ee.component(1).invoke(o), new R(2, "four", 6.0d));
+        assertEquals(ee.component(2).invoke(o), 1);
+        assertEquals(ee.component(3).invoke(o), "two");
+        assertEquals(ee.component(4).invoke(o), 3.14d);
+        assertEquals(ee.component(5).invoke(o), 2);
+        assertEquals(ee.component(6).invoke(o), "four");
+        assertEquals(ee.component(7).invoke(o), 6.0d);
+    }
+
+    record A(int a);
+    record B(int a, int b);
+    record S(String s);
+    record T(String s, String t);
+    record U();
+
+    private Object component(Extractor e, int num, Object carrier) throws Throwable {
+        return Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class,
+                                            e, num).invoke(carrier);
+    }
+
+    public void testRecordSwitch() throws Throwable {
+        Extractor[] extractors = {
+                recordExtractor(A.class, int.class),
+                recordExtractor(B.class, int.class, int.class),
+                recordExtractor(S.class, String.class),
+                recordExtractor(T.class, String.class, String.class),
+                recordExtractor(U.class)
+        };
+
+        Object[] exemplars = {
+                new A(1),
+                new B(2, 3),
+                new S("four"),
+                new T("five", "six"),
+                new U()
+        };
+
+        CallSite cs = SwitchBootstraps.patternSwitch(MethodHandles.lookup(), "_",
+                                                     MethodType.methodType(SwitchBootstraps.PatternSwitchResult.class, Object.class),
+                                                     extractors);
+        MethodHandle mh = cs.dynamicInvoker();
+        for (int i = 0; i < exemplars.length; i++) {
+            Object exemplar = exemplars[i];
+            SwitchBootstraps.PatternSwitchResult result = (SwitchBootstraps.PatternSwitchResult) mh.invoke(exemplar);
+            assertEquals(result.index, i);
+            switch (result.index) {
+                case 0:
+                    assertEquals(component(extractors[i], 0, result.carrier), 1);
+                    break;
+                case 1:
+                    assertEquals(component(extractors[i], 0, result.carrier), 2);
+                    assertEquals(component(extractors[i], 1, result.carrier), 3);
+                    break;
+                case 2:
+                    assertEquals(component(extractors[i], 0, result.carrier), "four");
+                    break;
+                case 3:
+                    assertEquals(component(extractors[i], 0, result.carrier), "five");
+                    assertEquals(component(extractors[i], 1, result.carrier), "six");
+                    break;
+            };
+
+            result = (SwitchBootstraps.PatternSwitchResult) mh.invoke(null);
+            assertEquals(result.index, -1);
+
+            result = (SwitchBootstraps.PatternSwitchResult) mh.invoke("foo");
+            assertEquals(result.index, 5);
+        }
+    }
+
+    record Box(Object o1);
+
+    public void testNestedRecord() throws Throwable {
+        Extractor boxA = Extractor.ofNested(recordExtractor(Box.class, Object.class),
+                                            recordExtractor(A.class, int.class));
+        Extractor boxB = Extractor.ofNested(recordExtractor(Box.class, Object.class),
+                                            recordExtractor(B.class, int.class, int.class));
+
+        CallSite cs = SwitchBootstraps.patternSwitch(MethodHandles.lookup(), "_",
+                                                     MethodType.methodType(SwitchBootstraps.PatternSwitchResult.class, Object.class),
+                                                     boxA, boxB);
+        MethodHandle mh = cs.dynamicInvoker();
+
+        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box(new A(1)))).index, 0);
+        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box(new B(2, 3)))).index, 1);
+        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box("foo"))).index, 2);
+        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(new Box(null))).index, 2);
+        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke("foo")).index, 2);
+        assertEquals(((SwitchBootstraps.PatternSwitchResult) mh.invoke(null)).index, -1);
+    }
+
+    record RString(String i) { }
+    record RObject(Object i) { }
+    record Rint(int i) { }
+
+
+    public void testNestedWithConstant() throws Throwable {
+        Extractor rb = recordExtractor(Box.class, Object.class);
+        Extractor rs = recordExtractor(RString.class, String.class);
+        Extractor ro = recordExtractor(RObject.class, Object.class);
+        Extractor ri = recordExtractor(Rint.class, int.class);
+        Extractor cs = Extractor.ofConstant("foo");
+        Extractor cn = Extractor.ofConstant(null);
+        Extractor ci = Extractor.ofConstant(3);
+
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH, Extractor.ofNested(rs, cs), new RString("foo"), "foo");
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(rs, cs), new RString("bar"));
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(rs, cs), new RString(null));
+
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH, Extractor.ofNested(ro, cs), new RObject("foo"), "foo");
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ro, cs), new RObject("bar"));
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ro, cs), new RObject(3));
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ro, cs), new RObject(null));
+
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH, Extractor.ofNested(ri, ci), new Rint(3), 3);
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL, Extractor.ofNested(ri, ci), new Rint(2));
+
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.MATCH,
+                                  Extractor.ofNested(rb, Extractor.ofNested(rs, cs)),
+                                  new Box(new RString("foo")), new RString("foo"), "foo");
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL,
+                                  Extractor.ofNested(rb, Extractor.ofNested(rs, cs)),
+                                  new Box(new RString("bar")));
+        ExtractorTest.assertMatch(ExtractorTest.MatchKind.FAIL,
+                                  Extractor.ofNested(rb, Extractor.ofNested(rs, cs)),
+                                  new Box("foo"));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/extractor/SwitchBootstrapsTest.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,466 @@
+/*
+ * 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.io.Serializable;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.runtime.SwitchBootstraps;
+import java.util.ArrayList;
+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 jdk.test.lib.RandomFactory;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+/**
+ * @test
+ * @key randomness
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run testng SwitchBootstrapsTest
+ */
+@Test
+public class SwitchBootstrapsTest {
+    private final static Set<Class<?>> BOOLEAN_TYPES = Set.of(boolean.class, Boolean.class);
+    private final static Set<Class<?>> ALL_INT_TYPES = Set.of(int.class, short.class, byte.class, char.class,
+                                                              Integer.class, Short.class, Byte.class, Character.class);
+    private final static Set<Class<?>> SIGNED_NON_BYTE_TYPES = Set.of(int.class, Integer.class, short.class, Short.class);
+    private final static Set<Class<?>> CHAR_TYPES = Set.of(char.class, Character.class);
+    private final static Set<Class<?>> BYTE_TYPES = Set.of(byte.class, Byte.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_BOOLEAN_SWITCH;
+    public static final MethodHandle BSM_INT_SWITCH;
+    public static final MethodHandle BSM_LONG_SWITCH;
+    public static final MethodHandle BSM_FLOAT_SWITCH;
+    public static final MethodHandle BSM_DOUBLE_SWITCH;
+    public static final MethodHandle BSM_STRING_SWITCH;
+    public static final MethodHandle BSM_ENUM_SWITCH;
+    public static final MethodHandle BSM_TYPE_SWITCH;
+
+    private final static Random random = RandomFactory.getRandom();
+
+    static {
+        try {
+            BSM_BOOLEAN_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "booleanSwitch",
+                                                                   MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, boolean[].class));
+            BSM_INT_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "intSwitch",
+                                                               MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int[].class));
+            BSM_LONG_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "longSwitch",
+                                                                MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, long[].class));
+            BSM_FLOAT_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "floatSwitch",
+                                                                 MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, float[].class));
+            BSM_DOUBLE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "doubleSwitch",
+                                                                  MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, double[].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));
+            BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch",
+                                                                MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Class[].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 testBoolean(boolean... labels) throws Throwable {
+        Map<Class<?>, MethodHandle> mhs
+                = Map.of(boolean.class, ((CallSite) BSM_BOOLEAN_SWITCH.invoke(MethodHandles.lookup(), "", switchType(boolean.class), labels)).dynamicInvoker(),
+                         Boolean.class, ((CallSite) BSM_BOOLEAN_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Boolean.class), labels)).dynamicInvoker());
+
+        List<Boolean> labelList = new ArrayList<>();
+        for (boolean label : labels)
+            labelList.add(label);
+
+        for (int i=0; i<labels.length; i++) {
+            assertEquals(i, (int) mhs.get(boolean.class).invokeExact((boolean) labels[i]));
+            assertEquals(i, (int) mhs.get(Boolean.class).invokeExact((Boolean) labels[i]));
+        }
+
+        boolean[] booleans = { false, true };
+        for (boolean b : booleans) {
+            if (!labelList.contains(b)) {
+                assertEquals(labels.length, mhs.get(boolean.class).invoke((boolean) b));
+                assertEquals(labels.length, mhs.get(Boolean.class).invoke((boolean) b));
+            }
+        }
+
+        assertEquals(-1, (int) mhs.get(Boolean.class).invoke(null));
+    }
+
+    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));
+                if (i >= 0) {
+                    assertEquals(labels.length, mhs.get(char.class).invoke((char)i));
+                    assertEquals(labels.length, mhs.get(Character.class).invoke((char)i));
+                }
+                if (i >= -128 && i <= 127) {
+                    assertEquals(labels.length, mhs.get(byte.class).invoke((byte)i));
+                    assertEquals(labels.length, mhs.get(Byte.class).invoke((byte)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 testFloat(float... labels) throws Throwable {
+        Map<Class<?>, MethodHandle> mhs
+                = Map.of(float.class, ((CallSite) BSM_FLOAT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(float.class), labels)).dynamicInvoker(),
+                         Float.class, ((CallSite) BSM_FLOAT_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Float.class), labels)).dynamicInvoker());
+
+        List<Float> labelList = new ArrayList<>();
+        for (float label : labels)
+            labelList.add(label);
+
+        for (int i=0; i<labels.length; i++) {
+            assertEquals(i, (int) mhs.get(float.class).invokeExact((float) labels[i]));
+            assertEquals(i, (int) mhs.get(Float.class).invokeExact((Float) labels[i]));
+        }
+
+        float[] someFloats = { 1.0f, Float.MIN_VALUE, 3.14f };
+        for (float f : someFloats) {
+            if (!labelList.contains(f)) {
+                assertEquals(labels.length, mhs.get(float.class).invoke((float) f));
+                assertEquals(labels.length, mhs.get(Float.class).invoke((float) f));
+            }
+        }
+
+        assertEquals(-1, (int) mhs.get(Float.class).invoke(null));
+    }
+
+    private void testDouble(double... labels) throws Throwable {
+        Map<Class<?>, MethodHandle> mhs
+                = Map.of(double.class, ((CallSite) BSM_DOUBLE_SWITCH.invoke(MethodHandles.lookup(), "", switchType(double.class), labels)).dynamicInvoker(),
+                         Double.class, ((CallSite) BSM_DOUBLE_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Double.class), labels)).dynamicInvoker());
+
+        var labelList = new ArrayList<Double>();
+        for (double label : labels)
+            labelList.add(label);
+
+        for (int i=0; i<labels.length; i++) {
+            assertEquals(i, (int) mhs.get(double.class).invokeExact((double) labels[i]));
+            assertEquals(i, (int) mhs.get(Double.class).invokeExact((Double) labels[i]));
+        }
+
+        double[] someDoubles = { 1.0, Double.MIN_VALUE, 3.14 };
+        for (double f : someDoubles) {
+            if (!labelList.contains(f)) {
+                assertEquals(labels.length, mhs.get(double.class).invoke((double) f));
+                assertEquals(labels.length, mhs.get(Double.class).invoke((double) f));
+            }
+        }
+
+        assertEquals(-1, (int) mhs.get(Double.class).invoke(null));
+    }
+
+    private void testLong(long... labels) throws Throwable {
+        Map<Class<?>, MethodHandle> mhs
+                = Map.of(long.class, ((CallSite) BSM_LONG_SWITCH.invoke(MethodHandles.lookup(), "", switchType(long.class), labels)).dynamicInvoker(),
+                         Long.class, ((CallSite) BSM_LONG_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Long.class), labels)).dynamicInvoker());
+
+        List<Long> labelList = new ArrayList<>();
+        for (long label : labels)
+            labelList.add(label);
+
+        for (int i=0; i<labels.length; i++) {
+            assertEquals(i, (int) mhs.get(long.class).invokeExact((long) labels[i]));
+            assertEquals(i, (int) mhs.get(Long.class).invokeExact((Long) labels[i]));
+        }
+
+        long[] someLongs = { 1L, Long.MIN_VALUE, Long.MAX_VALUE };
+        for (long l : someLongs) {
+            if (!labelList.contains(l)) {
+                assertEquals(labels.length, mhs.get(long.class).invoke((long) l));
+                assertEquals(labels.length, mhs.get(Long.class).invoke((long) l));
+            }
+        }
+
+        assertEquals(-1, (int) mhs.get(Long.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 testBoolean() throws Throwable {
+        testBoolean(new boolean[0]);
+        testBoolean(false);
+        testBoolean(true);
+        testBoolean(false, true);
+    }
+
+    public void testInt() throws Throwable {
+        testInt(ALL_INT_TYPES, 8, 6, 7, 5, 3, 0, 9);
+        testInt(ALL_INT_TYPES, 1, 2, 4, 8, 16);
+        testInt(ALL_INT_TYPES, 5, 4, 3, 2, 1, 0);
+        testInt(SIGNED_TYPES, 5, 4, 3, 2, 1, 0, -1);
+        testInt(SIGNED_TYPES, -1);
+        testInt(ALL_INT_TYPES, new int[] { });
+
+        for (int i=0; i<5; i++) {
+            int len = 50 + random.nextInt(800);
+            int[] arr = IntStream.generate(() -> random.nextInt(10000) - 5000)
+                                 .distinct()
+                                 .limit(len)
+                                 .toArray();
+            testInt(SIGNED_NON_BYTE_TYPES, arr);
+
+            arr = IntStream.generate(() -> random.nextInt(10000))
+                    .distinct()
+                    .limit(len)
+                    .toArray();
+            testInt(CHAR_TYPES, arr);
+
+            arr = IntStream.generate(() -> random.nextInt(127) - 64)
+                           .distinct()
+                           .limit(120)
+                           .toArray();
+            testInt(BYTE_TYPES, arr);
+        }
+    }
+
+    public void testLong() throws Throwable {
+        testLong(1L, Long.MIN_VALUE, Long.MAX_VALUE);
+        testLong(8L, 2L, 5L, 4L, 3L, 9L, 1L);
+        testLong(new long[] { });
+
+        // @@@ Random tests
+        // @@@ More tests for weird values
+    }
+
+    public void testFloat() throws Throwable {
+        testFloat(0.0f, -0.0f, -1.0f, 1.0f, 3.14f, Float.MIN_VALUE, Float.MAX_VALUE, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY);
+        testFloat(new float[] { });
+        testFloat(0.0f, 1.0f, 3.14f, Float.NaN);
+
+        // @@@ Random tests
+        // @@@ More tests for weird values
+    }
+
+    public void testDouble() throws Throwable {
+        testDouble(0.0, -0.0, -1.0, 1.0, 3.14, Double.MIN_VALUE, Double.MAX_VALUE,
+                   Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
+        testDouble(new double[] { });
+        testDouble(0.0f, 1.0f, 3.14f, Double.NaN);
+
+        // @@@ Random tests
+        // @@@ More tests for weird values
+    }
+
+    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");
+
+        // Tests with hash collisions; Ba/CB, Ca/DB
+        testString("Ba", "CB");
+        testString("Ba", "CB", "Ca", "DB");
+
+        // Test with null
+        try {
+            testString("a", null, "c");
+            fail("expected failure");
+        }
+        catch (IllegalArgumentException t) {
+            // success
+        }
+    }
+
+    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");
+
+        // Bad enum class
+        try {
+            testEnum((Class) String.class, "A");
+            fail("expected failure");
+        }
+        catch (IllegalArgumentException t) {
+            // success
+        }
+
+        // Bad enum constants
+        try {
+            testEnum(E1.class, "B", "A", "FILE_NOT_FOUND");
+            fail("expected failure");
+        }
+        catch (IllegalArgumentException t) {
+            // success
+        }
+
+        // Null enum constant
+        try {
+            testEnum(E1.class, "A", null, "B");
+            fail("expected failure");
+        }
+        catch (IllegalArgumentException t) {
+            // success
+        }
+    }
+
+    private void testType(Object target, int result, Class... labels) throws Throwable {
+        MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType(Object.class), labels)).dynamicInvoker();
+        assertEquals((int) indy.invoke(target), result);
+        assertEquals(-1, (int) indy.invoke(null));
+    }
+
+    public void testTypes() throws Throwable {
+        testType("", 0, String.class, Object.class);
+        testType("", 0, Object.class);
+        testType("", 1, Integer.class);
+        testType("", 1, Integer.class, Serializable.class);
+        testType(E1.A, 0, E1.class, Object.class);
+        testType(E2.C, 1, E1.class, Object.class);
+        testType(new Serializable() { }, 1, Comparable.class, Serializable.class);
+
+        // test failures: duplicates, nulls, dominance inversion
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/extractor/boottest/TEST.properties	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,3 @@
+# This file identifies root(s) of the test-ng hierarchy.
+
+TestNG.dirs = .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/extractor/boottest/java.base/java/lang/runtime/CarrierTest.java	Wed May 01 10:46:37 2019 -0400
@@ -0,0 +1,118 @@
+/*
+ * 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 java.lang.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @test
+ * @key randomness
+ * @run testng CarrierTest
+ * @summary unit tests for java.lang.runtime.ExtractorCarriers
+ */
+@Test
+public class CarrierTest {
+    static final int N_ITER = 1000;
+    static final Class<?>[] TYPES = { byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class, Object.class };
+
+    static Object[] byteVals = { Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) -1, (byte) 0, (byte) 1, (byte) 42 };
+    static Object[] shortVals = { Short.MIN_VALUE, Short.MAX_VALUE, (short) -1, (short) 0, (short) 1, (short) 42 };
+    static Object[] charVals = { Character.MIN_VALUE, Character.MAX_VALUE, (char) 0, 'a', 'Z' };
+    static Object[] intVals = { Integer.MIN_VALUE, Integer.MAX_VALUE, -1, 0, 1, 42 };
+    static Object[] longVals = { Long.MIN_VALUE, Long.MAX_VALUE, -1L, 0L, 1L, 42L };
+    static Object[] floatVals = { Float.MIN_VALUE, Float.MAX_VALUE, -1.0f, 0.0f, 1.0f, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN };
+    static Object[] doubleVals = { Double.MIN_VALUE, Double.MAX_VALUE, -1.0d, 0.0d, 1.0d, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN };
+    static Object[] booleanVals = { true, false };
+    static Object[] objectVals = {null, "", "Foo", "foo", List.of(), Collections.EMPTY_SET };
+
+    // @@@ Should use RandomFactory, but can't get that to link
+    private static final Random random = new Random(System.nanoTime());
+
+    static Map<Class<?>, Object[]> primVals = Map.of(byte.class, byteVals,
+                                                     short.class, shortVals,
+                                                     char.class, charVals,
+                                                     int.class, intVals,
+                                                     long.class, longVals,
+                                                     float.class, floatVals,
+                                                     double.class, doubleVals,
+                                                     boolean.class, booleanVals);
+
+    void testCarrier(MethodType type, Object[] values) throws Throwable {
+        for (ExtractorCarriers.CarrierFactory cf : ExtractorCarriers.CarrierFactories.values()) {
+            assertEquals(type.parameterCount(), values.length);
+            Object carrier = cf.constructor(type).invokeWithArguments(values);
+            for (int i = 0; i < values.length; i++)
+                assertEquals(values[i], cf.component(type, i).invoke(carrier));
+        }
+    }
+
+    void testCarrier(MethodType type) throws Throwable {
+        // generate data, in a loop
+        for (int i=0; i<N_ITER; i++) {
+            Object[] values = new Object[type.parameterCount()];
+            for (int j=0; j<type.parameterCount(); j++) {
+                Class<?> c = type.parameterType(j);
+                Object[] vals = c.isPrimitive() ? primVals.get(c) : objectVals;
+                values[j] = vals[random.nextInt(vals.length)];
+            }
+            testCarrier(type, values);
+        }
+    }
+
+    public void testCarrier() throws Throwable {
+        Class[] lotsOfInts = new Class[252];
+        Arrays.fill(lotsOfInts, int.class);
+
+        // known types
+        for (MethodType mt : List.of(
+                MethodType.methodType(Object.class),
+                MethodType.methodType(Object.class, int.class),
+                MethodType.methodType(Object.class, int.class, int.class),
+                MethodType.methodType(Object.class, Object.class),
+                MethodType.methodType(Object.class, Object.class, Object.class),
+                MethodType.methodType(Object.class, byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class),
+                MethodType.methodType(Object.class, lotsOfInts))) {
+            testCarrier(mt);
+        }
+
+        // random types
+        for (int i=0; i<N_ITER; i++) {
+            int nTypes = random.nextInt(10);
+            Class[] paramTypes = new Class[nTypes];
+            Arrays.setAll(paramTypes, ix -> TYPES[random.nextInt(TYPES.length)]);
+            testCarrier(MethodType.methodType(Object.class, paramTypes));
+        }
+    }
+}