changeset 56896:6f4f37689df0 amber-demo-II

manual merge with records branch
author vromero
date Mon, 24 Jun 2019 11:00:53 -0400
parents 8a18152ffe49 76ea6cf6e40a
children 93144cfc2d71
files deps.txt 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/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties 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 29 files changed, 2368 insertions(+), 2438 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/runtime/Extractor.java	Fri Jun 21 01:43:06 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,562 +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.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);
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/runtime/ExtractorCarriers.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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;
-    }
-}
--- a/src/java.base/share/classes/java/lang/runtime/ExtractorImpl.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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/PatternCarriers.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2019, 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;
+import java.util.List;
+
+/**
+ * PatternCarriers
+ */
+class PatternCarriers {
+
+    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 List<MethodHandle> carrierComponents(MethodType methodType) {
+        MethodHandle[] components = new MethodHandle[methodType.parameterCount()];
+        Arrays.setAll(components, i -> factory.component(methodType, i));
+        return List.of(components);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/PatternHandle.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, 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.List;
+
+/**
+ * Runtime object for low-level implementation of <em>pattern matching</em>. A
+ * {@linkplain PatternHandle} exposes functionality for determining if a target
+ * matches the pattern, and if so, for conditionally extracting the resulting
+ * bindings.
+ *
+ * <p>A {@linkplain PatternHandle} is parameterized by a <em>target type</em>
+ * and zero or more <em>binding variable types</em>. The target type (denoted
+ * {@code T}) is the type against which the pattern can be applied (often a
+ * broad type such as {@link Object}, but need not be), and the binding variable
+ * types (denoted {@code B*}) are the types of the binding variables that are
+ * produced by a successful match.  These types are combined into a type
+ * <em>descriptor</em>, accessed via {@link #descriptor()}, where the return
+ * type of the descriptor is the target type, and the parameter types of the
+ * descriptor are the binding variable types.
+ *
+ * <p>The behavior of a {@linkplain PatternHandle} is exposed via method
+ * handles.  The method handle returned by {@link #tryMatch()} is applied to the
+ * target to be tested, and returns an opaque result of type {@code Object}.  If
+ * the result is {@code null}, the match has failed; if is non-null, it has
+ * succeeded, and the result can be used as input to the method handles returned
+ * by {@link #components()} or {@link #component(int)} to retrieve specific
+ * binding variables.
+ *
+ * <p>The class {@link PatternHandles} contains numerous factories and
+ * combinators for {@linkplain PatternHandle}s, including {@link
+ * PatternHandles#adaptTarget(PatternHandle, Class)} which can be used to adapt
+ * a pattern handle from one target type to another (such as widening the set of
+ * types against which it can be applied.)
+ *
+ * <p>{@linkplain PatternHandle} implementations must be <a
+ * href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
+ * classes.
+ */
+public interface PatternHandle {
+
+    /**
+     * Returns a method handle that attempts to perform the pattern match
+     * described by this pattern handle.  It will have type {@code (T)Object},
+     * where {@code T} is the target type of the extractor. It accepts the
+     * target to be matched, and returns a non-null opaque carrier of type
+     * {@link Object} if the match succeeds, or {@code null} if it fails.
+     *
+     * @return the {@code tryMatch} method handle
+     */
+    MethodHandle tryMatch();
+
+    /**
+     * Returns a method handle that extracts a component from a successful
+     * match.  It will have type {@code (Object)Bi}, where {@code Bi} is the
+     * type of the corresponding binding variable, and will take the match
+     * carrier and return the corresponding binding variable.
+     *
+     * @param i the index of the component
+     * @return the component method handle
+     * @throws IndexOutOfBoundsException if {@code i} does not correspond to the
+     *                                   index of a binding variable of this
+     *                                   pattern
+     */
+    MethodHandle component(int i);
+
+    /**
+     * Returns all the component method handles for this pattern as a {@link
+     * List}.
+     *
+     * @return the component method handles
+     */
+    List<MethodHandle> components();
+
+    /**
+     * Returns the descriptor for this pattern.  The parameter types of the
+     * descriptor are the types of the binding variables, and the return type is
+     * the target type.
+     *
+     * @return the pattern type descriptor
+     */
+    MethodType descriptor();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/runtime/PatternHandles.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,740 @@
+/*
+ * Copyright (c) 2019, 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;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import sun.invoke.util.BytecodeName;
+import sun.invoke.util.Wrapper;
+
+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;
+
+/**
+ * Factories and combinators for {@link PatternHandle}s.
+ */
+public final class PatternHandles {
+    private static final MethodHandle[] EMPTY_MH_ARRAY = new MethodHandle[0];
+    private static final Object NULL_SENTINEL = new Object();
+
+    private PatternHandles() {
+    }
+
+    // Factories
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>type pattern</em>, which
+     * matches all non-null instances of the match type, with a single binding
+     * variable which is the target cast to the match type.  The target type of
+     * the resulting pattern is the match type; if a broader target type is
+     * desired, use {@link #ofType(Class, Class)} or adapt the resulting pattern
+     * handle with {@link #adaptTarget(PatternHandle, Class)}.
+     *
+     * @param matchType the type to match against
+     * @return a pattern handle for a type pattern
+     */
+    public static PatternHandle ofType(Class<?> matchType) {
+        requireNonNull(matchType);
+        MethodType descriptor = MethodType.methodType(matchType, matchType);
+        MethodHandle component = MethodHandles.identity(matchType);
+        MethodHandle tryMatch
+                = matchType.isPrimitive()
+                  ? MethodHandles.identity(matchType)
+                  : MH_OF_TYPE_TRY_MATCH.bindTo(matchType).asType(descriptor);
+
+        return new PatternHandleImpl(descriptor, tryMatch, List.of(component));
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>type pattern</em>, which
+     * matches all non-null instances of the match type, with a single binding
+     * variable which is the target cast to the match type.  The target type of
+     * the resulting pattern is the {@code targetType}.
+     *
+     * @param matchType  the type to match against
+     * @param targetType the desired target type for the resulting pattern
+     *                   handle
+     * @return a pattern handle for a type pattern
+     * @throws IllegalArgumentException if the provided match type and target
+     *                                  type are not compatible
+     */
+    public static PatternHandle ofType(Class<?> matchType, Class<?> targetType) {
+        return adaptTarget(ofType(matchType), targetType);
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>nullable type
+     * pattern</em>, which matches all instances of the match type, plus {@code
+     * null}, with a single binding variable which is the target cast to the
+     * match type.  The target type of the resulting pattern is the match type;
+     * if a broader target type is desired, use {@link #ofType(Class, Class)} or
+     * adapt the resulting pattern handle with {@link #adaptTarget(PatternHandle,
+     * Class)}.
+     *
+     * @param matchType the type to match against
+     * @return a pattern handle for a nullable type pattern
+     */
+    public static PatternHandle ofTypeNullable(Class<?> matchType) {
+        requireNonNull(matchType);
+        MethodType descriptor = MethodType.methodType(matchType, matchType);
+        MethodHandle component = MH_OF_TYPE_NULLABLE_COMPONENT
+                .asType(MethodType.methodType(matchType, Object.class));
+        MethodHandle tryMatch
+                = matchType.isPrimitive()
+                  ? MethodHandles.identity(matchType)
+                  : MH_OF_TYPE_NULLABLE_TRY_MATCH.bindTo(matchType)
+                                                 .asType(MethodType.methodType(Object.class, matchType));
+
+        return new PatternHandleImpl(descriptor, tryMatch, List.of(component));
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>nullable type
+     * pattern</em>, which matches all instances of the match type, plus {@code
+     * null}, with a single binding variable which is the target cast to the
+     * match type.  The target type of the resulting pattern is the {@code
+     * targetType}.
+     *
+     * @param matchType  the type to match against
+     * @param targetType the desired target type for the resulting pattern
+     *                   handle
+     * @return a pattern handle for a nullable type pattern
+     * @throws IllegalArgumentException if the provided match type and target
+     *                                  type are not compatible
+     */
+    public static PatternHandle ofTypeNullable(Class<?> matchType, Class<?> targetType) {
+        return adaptTarget(ofTypeNullable(matchType), targetType);
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>constant pattern</em>,
+     * which matches all instances that are {@link Object#equals(Object)} to
+     * the specified constant.  The resulting pattern has no binding variables.
+     * If the constant is {@code null}, the target type of the pattern is
+     * {@link Object}, otherwise it is the result of {@code Object::getClass}
+     * on the constant.
+     *
+     * <p>TODO: restrict type of constant to String, boxes, and enums?
+     *
+     * @param o the constant
+     * @return a pattern handle for a constant pattern
+     */
+    public static PatternHandle 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(MH_OBJECTS_EQUAL, 0, o)
+                                                     .asType(MethodType.methodType(boolean.class, type)));
+        return new PatternHandleImpl(MethodType.methodType(type), match, List.of());
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>constant pattern</em>,
+     * which matches all instances that are {@link Object#equals(Object)} to
+     * the specified constant.  The resulting pattern has no binding variables.
+     * The target type of the pattern is {@code targetType}.
+     *
+     * @param o the constant
+     * @param targetType the target type for the pattern
+     * @return a pattern handle for a constant pattern
+     * @throws IllegalArgumentException if the type of the constant and the
+     * target type are not compatible
+     */
+    public static PatternHandle ofConstant(Object o, Class<?> targetType) {
+        return adaptTarget(ofConstant(0), targetType);
+    }
+
+    // @@@ Primitive constant patterns
+
+    /**
+     * Returns a {@linkplain PatternHandle} for decomposing a target into its
+     * components.  It matches all non-null instances of the specified target
+     * type, and extracts one binding variable for each component specified in
+     * the {@code components} argument.  The method handles in {@code components}
+     * must be of type {@code (T)Bi} where T is the target type of the pattern
+     * and Bi is the i'th binding variable.  The components are extracted
+     * <em>lazily</em> -- when the component method handle is invoked by the
+     * client -- rather than when the {@code tryMatch} method handle is invoked.
+     *
+     * @param targetType The type of the match target
+     * @param components The component method handles
+     * @return a pattern handle for a decomposition pattern
+     */
+    public static PatternHandle ofLazyProjection(Class<?> targetType,
+                                                 MethodHandle... components) {
+        requireNonNull(targetType);
+        requireNonNull(components);
+        return new PatternHandleImpl(descriptor(targetType, components),
+                                     MethodHandles.identity(targetType),
+                                     List.of(components));
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for decomposing a target into its
+     * components.  It matches all non-null instances of the specified target
+     * type, and extracts one binding variable for each component specified in
+     * the {@code components} argument.  The method handles in {@code components}
+     * must be of type {@code (T)Bi} where T is the target type of the pattern
+     * and Bi is the i'th binding variable.  The components are extracted
+     * <em>eagerly</em> -- at the time the {@code tryMatch} method handle is
+     * invoked.
+     *
+     * @param targetType The type of the match target
+     * @param components The component method handles
+     * @return a pattern handle for a decomposition pattern
+     */
+    public static PatternHandle ofEagerProjection(Class<?> targetType,
+                                                  MethodHandle... components) {
+        requireNonNull(targetType);
+        requireNonNull(components);
+        MethodType descriptor = descriptor(targetType, components);
+        return new PatternHandleImpl(descriptor,
+                                     carrierTryExtract(descriptor, components),
+                                     PatternCarriers.carrierComponents(descriptor));
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} that delegates matching and
+     * extraction to another method handle.  The target type of the pattern is
+     * the return type of the {@code descriptor}, and the binding variable types
+     * are the parameter types of the {@code descriptor}.  The {@code tryMatch}
+     * method handle will invoke the specified {@code digester} method handle
+     * with the target, as well as a method handle whose parameter types are
+     * the binding variable types and whose return type is some type {@code C}.
+     * For a successful match, the digester method should invoke this method
+     * handle with the extracted bindings, and return the result; for an
+     * unsuccessful match, it should return {@code null}.
+     *
+     * @param descriptor the type descriptor of the pattern
+     * @param digester   the digester method handle
+     * @return a pattern handle implementing the pattern
+     */
+    public static PatternHandle ofImperative(MethodType descriptor,
+                                             MethodHandle digester) {
+        Class<?> targetType = descriptor.returnType();
+        return new PatternHandleImpl(descriptor,
+                                     partialize(MethodHandles.insertArguments(digester,
+                                                                              1, PatternCarriers.carrierFactory(descriptor)),
+                                                MH_OBJECTS_NONNULL.asType(MH_OBJECTS_NONNULL.type().changeParameterType(0, targetType))),
+                                     PatternCarriers.carrierComponents(descriptor));
+    }
+
+    /**
+     * Compose a pattern handle with a method handle that receives the bindings. The
+     * argument types of the target method must match those of the binding
+     * types.  The resulting method handle accepts an argument which is the
+     * target type of the pattern, and which returns either {@code null}
+     * if the match fails or the result of the target method handle
+     * if the match succeeds.
+     *
+     * @param patternHandle the pattern handle
+     * @param target        a method handle that receives the bindings and
+     *                      produces a result
+     * @return the composed method handle
+     */
+    public static MethodHandle compose(PatternHandle patternHandle, MethodHandle target) {
+        int count = patternHandle.descriptor().parameterCount();
+        MethodHandle[] components = patternHandle.components().toArray(EMPTY_MH_ARRAY);
+        Class<?> carrierType = patternHandle.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(MH_OBJECTS_NONNULL.asType(MethodType.methodType(boolean.class, carrierType)),
+                                         mh,
+                                         MethodHandles.dropArguments(MethodHandles.constant(resultType, null), 0, carrierType));
+        mh = MethodHandles.filterArguments(mh, 0, patternHandle.tryMatch());
+        return mh;
+    }
+
+    // Combinators
+
+    /**
+     * Adapts a {@linkplain PatternHandle} to a new target type.  If the
+     * pattern is of primitive type, it may be adapted to a supertype of its
+     * corresponding box type; if it is of reference type, it may be widened
+     * or narrowed to another reference type.
+     *
+     * @param pattern the pattern
+     * @param newTarget the new target type
+     * @return the adapted pattern
+     * @throws IllegalArgumentException if the new target type is not compatible
+     * with the target type of the pattern
+     */
+    public static PatternHandle adaptTarget(PatternHandle pattern, Class<?> newTarget) {
+        Class<?> oldTarget = pattern.descriptor().returnType();
+        if (oldTarget == newTarget)
+            return pattern;
+
+        Class<?> oldWrapperType = oldTarget.isPrimitive() ? Wrapper.forPrimitiveType(oldTarget).wrapperType() : null;
+        MethodType guardType = MethodType.methodType(boolean.class, newTarget);
+        MethodHandle guard;
+        if (oldWrapperType != null && newTarget.isAssignableFrom(oldWrapperType)) {
+            // Primitive boxing (with optional widening)
+            guard = MH_PRIMITIVE_ADAPT_HELPER.bindTo(oldWrapperType).asType(guardType);
+        }
+        else if (newTarget.isAssignableFrom(oldTarget) || oldTarget.isAssignableFrom(newTarget)) {
+            // reference narrowing or widening
+            guard = MH_REFERENCE_ADAPT_HELPER.bindTo(oldTarget).asType(guardType);
+        }
+        else {
+            throw new IllegalArgumentException(String.format("New target type %s not compatible with old target type %s",
+                                                             newTarget, oldTarget));
+        }
+
+        MethodType tryMatchType = pattern.tryMatch().type().changeParameterType(0, newTarget);
+        return new PatternHandleImpl(pattern.descriptor().changeReturnType(newTarget),
+                                     partialize(pattern.tryMatch().asType(tryMatchType),
+                                                guard),
+                                     pattern.components());
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} that implements the same pattern
+     * as another {@linkplain PatternHandle}, but potentially with fewer binding
+     * variables.
+     *
+     * @param pattern the original pattern
+     * @param positions the indexes of the binding variables to drop
+     * @return the new pattern
+     * @throws IndexOutOfBoundsException if any of the indexes are out of range
+     * for the bindings of the original pattern
+     */
+    public static PatternHandle dropBindings(PatternHandle pattern, int... positions) {
+        MethodHandle[] mhs = pattern.components().toArray(EMPTY_MH_ARRAY);
+        for (int position : positions)
+            mhs[position] = null;
+        mhs = Stream.of(mhs).filter(Objects::nonNull).toArray(MethodHandle[]::new);
+        return new PatternHandleImpl(descriptor(pattern.descriptor().returnType(), mhs), pattern.tryMatch(), List.of(mhs));
+    }
+
+    /**
+     * Returns a {@linkplain PatternHandle} for a <em>nested</em> pattern.  A
+     * nested pattern first matches the target to the outer pattern, and if
+     * it matches successfully, then matches the resulting bindings to the inner
+     * patterns.  The resulting pattern matches if the outer pattern matches
+     * the target, and the bindings match the appropriate inner patterns.  The
+     * target type of the nested pattern is the same as the target type of
+     * the outer pattern.  The bindings are the bindings for the outer pattern,
+     * followed by the concatenation of the bindings for the inner patterns.
+     *
+     * @param outer  The outer pattern
+     * @param inners The inner patterns, which can be null if no nested pattern
+     *               for the corresponding binding is desired
+     * @return the nested pattern
+     */
+    public static PatternHandle nested(PatternHandle outer, PatternHandle... inners) {
+        PatternHandle[] patternHandles = inners.clone();
+        int outerCount = outer.descriptor().parameterCount();
+        Class<?> outerCarrierType = outer.tryMatch().type().returnType();
+
+        // Adapt inners to types of outer bindings
+        for (int i = 0; i < patternHandles.length; i++) {
+            PatternHandle patternHandle = patternHandles[i];
+            if (patternHandle.descriptor().returnType() != outer.descriptor().parameterType(i))
+                patternHandles[i] = adaptTarget(patternHandle, outer.descriptor().parameterType(i));
+        }
+
+        int[] innerPositions = IntStream.range(0, patternHandles.length)
+                                        .filter(i -> patternHandles[i] != null)
+                                        .toArray();
+        MethodHandle[] innerComponents = Stream.of(patternHandles)
+                                               .filter(Objects::nonNull)
+                                               .map(PatternHandle::components)
+                                               .flatMap(List::stream)
+                                               .toArray(MethodHandle[]::new);
+        MethodHandle[] innerTryMatches = Stream.of(patternHandles)
+                                               .filter(Objects::nonNull)
+                                               .map(PatternHandle::tryMatch)
+                                               .toArray(MethodHandle[]::new);
+        Class<?>[] innerCarriers = Stream.of(patternHandles)
+                                         .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 = PatternCarriers.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 (PatternHandle e : patternHandles) {
+            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().toArray(EMPTY_MH_ARRAY));
+        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 PatternHandleImpl(descriptor, tryExtract, PatternCarriers.carrierComponents(descriptor));
+    }
+
+    // @@@ AND combinator
+    // @@@ GUARDED combinator
+
+    // Bootstraps
+
+    /**
+     * Bootstrap method for creating a lazy projection pattern, as per
+     * {@link #ofLazyProjection(Class, MethodHandle...)},
+     * suitable for use as a {@code constantdynamic} bootstrap.  Suitable for use
+     * by compilers which are generating implementations of patterns whose bindings
+     * are independently derived from the target.
+     *
+     * @apiNote When the "bootstrap consolidation" project completes, this method
+     * can go away and {@link #ofLazyProjection(Class, MethodHandle...)}
+     * can be used directly as a condy bootstrap.
+     *
+     * @param lookup       ignored
+     * @param constantName ignored
+     * @param constantType Must be {@code PatternHandle.class}
+     * @param targetType   the target type of the pattern
+     * @param components   the pattern components
+     * @return a pattern handle
+     * @throws Throwable doc
+     */
+    public static PatternHandle ofLazyProjection(MethodHandles.Lookup lookup,
+                                                 String constantName,
+                                                 Class<?> constantType,
+                                                 Class<?> targetType,
+                                                 MethodHandle... components)
+            throws Throwable {
+        return ofLazyProjection(targetType, components);
+    }
+
+    /**
+     * Bootstrap method for finding named {@link PatternHandle}s that have been
+     * compiled according to the scheme outlined in JLS ?.?.
+     *
+     * @param lookup       the lookup context
+     * @param constantName ignored
+     * @param constantType must be {@code PatternHandle.class}
+     * @param owner        the class containing the pattern
+     * @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 PatternHandle ofNamed(MethodHandles.Lookup lookup,
+                                        String constantName,
+                                        Class<PatternHandle> constantType,
+                                        Class<?> owner,
+                                        MethodType descriptor,
+                                        String name,
+                                        int refKind) throws Throwable {
+        String dd = descriptor.toMethodDescriptorString();
+        String memberName = String.format("$pattern$%s$%s",
+                                      (refKind == REF_newInvokeSpecial ? owner.getSimpleName() : name),
+                                      dd.substring(0, dd.indexOf(')') + 1));
+        String patternMethodName = BytecodeName.toBytecodeName(memberName);
+        MethodType factoryDesc = MethodType.methodType(PatternHandle.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 (PatternHandle) mh.invoke();
+    }
+
+    /**
+     * Bootstrap method for extracting the {@code tryMatch} method handle from a
+     * {@linkplain PatternHandle}.
+     *
+     * @apiNote When the "bootstrap consolidation" project completes, this method
+     * can go away and {@link PatternHandle#tryMatch()} can be used directly as
+     * a condy bootstrap.
+     *
+     * @param lookup        ignored
+     * @param constantName  ignored
+     * @param constantType  Must be {@code MethodHandle.class}
+     * @param patternHandle the pattern handle
+     * @return the {@code tryMatch} method handle
+     */
+    public static MethodHandle tryMatch(MethodHandles.Lookup lookup, String constantName, Class<MethodHandle> constantType,
+                                        PatternHandle patternHandle) {
+        return patternHandle.tryMatch();
+    }
+
+    /**
+     * Bootstrap method for extracting a {@code component} method handle from a
+     * {@linkplain PatternHandle}.
+     *
+     * @apiNote When the "bootstrap consolidation" project completes, this method
+     * can go away and {@link PatternHandle#component(int)} ()} can be used directly as
+     * a condy bootstrap.
+     *
+     * @param lookup        ignored
+     * @param constantName  ignored
+     * @param constantType  Must be {@code MethodHandle.class}
+     * @param patternHandle the pattern
+     * @param i the index of the desired component
+     * @return the component method handle
+     */
+    public static MethodHandle component(MethodHandles.Lookup lookup,
+                                         String constantName,
+                                         Class<MethodHandle> constantType,
+                                         PatternHandle patternHandle, int i) {
+        return patternHandle.component(i);
+    }
+
+    // Helpers
+
+    /**
+     * 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 = MH_OBJECTS_ISNULL
+                .asType(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);
+    }
+
+    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 = PatternCarriers.carrierFactory(descriptor);
+        int[] reorder = new int[descriptor.parameterCount()]; // default value is what we want already
+
+        Class<?> targetType = descriptor.returnType();
+        return partialize(MethodHandles.permuteArguments(MethodHandles.filterArguments(carrierFactory, 0, components),
+                                                         MethodType.methodType(carrierFactory.type().returnType(), targetType),
+                                                         reorder),
+                          MH_OBJECTS_NONNULL.asType(MH_OBJECTS_NONNULL.type().changeParameterType(0, targetType)));
+    }
+
+    private static MethodHandle lookupStatic(Class<?> clazz,
+                                             String name,
+                                             Class<?> returnType,
+                                             Class<?>... paramTypes)
+            throws ExceptionInInitializerError {
+        try {
+            return MethodHandles.lookup().findStatic(clazz, name, MethodType.methodType(returnType, paramTypes));
+        }
+        catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private static final MethodHandle MH_OF_TYPE_TRY_MATCH
+            = lookupStatic(PatternHandles.class, "ofTypeTryMatch",
+                           Object.class, Class.class, Object.class);
+    private static final MethodHandle MH_OF_TYPE_NULLABLE_TRY_MATCH
+            = lookupStatic(PatternHandles.class, "ofTypeNullableTryMatch",
+                           Object.class, Class.class, Object.class);
+    private static final MethodHandle MH_OF_TYPE_NULLABLE_COMPONENT
+            = lookupStatic(PatternHandles.class, "ofTypeNullableComponent",
+                           Object.class, Object.class);
+    private static final MethodHandle MH_PRIMITIVE_ADAPT_HELPER
+            = lookupStatic(PatternHandles.class, "primitiveAdaptHelper",
+                           boolean.class, Class.class, Object.class);
+    private static final MethodHandle MH_REFERENCE_ADAPT_HELPER
+            = lookupStatic(PatternHandles.class, "referenceAdaptHelper",
+                           boolean.class, Class.class, Object.class);
+    private static final MethodHandle MH_OBJECTS_ISNULL
+            = lookupStatic(Objects.class, "isNull",
+                           boolean.class, Object.class);
+    private static final MethodHandle MH_OBJECTS_NONNULL
+            = lookupStatic(Objects.class, "nonNull",
+                           boolean.class, Object.class);
+    private static final MethodHandle MH_OBJECTS_EQUAL
+            = lookupStatic(Objects.class, "equals",
+                           boolean.class, Object.class, Object.class);
+
+    private static Object ofTypeTryMatch(Class<?> type, Object o) {
+        return o != null && type.isAssignableFrom(o.getClass())
+               ? o
+               : null;
+    }
+
+    private static Object ofTypeNullableTryMatch(Class<?> type, Object o) {
+        if (o == null)
+            return NULL_SENTINEL;
+        else if (type.isAssignableFrom(o.getClass()))
+            return o;
+        else
+            return null;
+    }
+
+    private static Object ofTypeNullableComponent(Object o) {
+        return o == NULL_SENTINEL ? null : o;
+    }
+
+    private static boolean primitiveAdaptHelper(Class<?> type, Object o) {
+        return o != null && type.isAssignableFrom(o.getClass());
+    }
+
+    private static boolean referenceAdaptHelper(Class<?> type, Object o) {
+        return o == null || type.isAssignableFrom(o.getClass());
+    }
+
+    /**
+     * Non-public implementation of {@link PatternHandle}
+     */
+    private static class PatternHandleImpl implements PatternHandle {
+
+        private final MethodType descriptor;
+        private final MethodHandle tryMatch;
+        private final List<MethodHandle> components;
+
+
+        /**
+         * Construct an {@link PatternHandle} 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
+         */
+        PatternHandleImpl(MethodType descriptor, MethodHandle tryMatch,
+                          List<MethodHandle> components) {
+            MethodHandle[] componentsArray = components.toArray(new MethodHandle[0]);
+            Class<?> carrierType = tryMatch.type().returnType();
+            if (descriptor.parameterCount() != componentsArray.length)
+                throw new IllegalArgumentException(String.format("MethodType %s arity should match component count %d",
+                                                                 descriptor, componentsArray.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 < componentsArray.length; i++) {
+                MethodType componentType = componentsArray[i].type();
+                if (componentType.parameterCount() != 1
+                    || componentType.returnType().equals(void.class)
+                    || !componentType.parameterType(0).equals(carrierType))
+                    throw new IllegalArgumentException("Invalid component descriptor " + componentType);
+                if (!componentType.returnType().equals(descriptor.parameterType(i)))
+                    throw new IllegalArgumentException(String.format("Descriptor %s should match %d'th component %s",
+                                                                     descriptor, i, componentsArray[i]));
+            }
+
+            if (!carrierType.equals(Object.class)) {
+                tryMatch = tryMatch.asType(tryMatch.type().changeReturnType(Object.class));
+                for (int i = 0; i < componentsArray.length; i++) {
+                    MethodHandle component = componentsArray[i];
+                    componentsArray[i] = component.asType(component.type().changeParameterType(0, Object.class));
+                }
+            }
+
+            this.descriptor = descriptor;
+            this.tryMatch = tryMatch;
+            this.components = List.of(componentsArray);
+        }
+
+        @Override
+        public MethodHandle tryMatch() {
+            return tryMatch;
+        }
+
+        @Override
+        public MethodHandle component(int i) {
+            return components.get(i);
+        }
+
+        @Override
+        public List<MethodHandle> components() {
+            return components;
+        }
+
+        @Override
+        public MethodType descriptor() {
+            return descriptor;
+        }
+
+    }
+}
--- a/src/java.base/share/classes/java/lang/runtime/PatternSim.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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;
-    }
-
-}
--- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java	Mon Jun 24 11:00:53 2019 -0400
@@ -832,7 +832,7 @@
     public static CallSite patternSwitch(MethodHandles.Lookup lookup,
                                          String invocationName,
                                          MethodType invocationType,
-                                         Extractor... patterns) throws Throwable {
+                                         PatternHandle... patterns) throws Throwable {
         if (invocationType.parameterCount() != 1
             || (!invocationType.returnType().equals(PatternSwitchResult.class))
             || invocationType.parameterType(0).isPrimitive())
@@ -843,9 +843,9 @@
         Class<?> targetType = invocationType.parameterType(0);
 
         for (int i = 0; i < patterns.length; i++) {
-            Extractor pattern = patterns[i];
+            PatternHandle pattern = patterns[i];
             if (pattern.descriptor().returnType() != targetType)
-                patterns[i] = Extractor.adapt(pattern, targetType);
+                patterns[i] = PatternHandles.adaptTarget(pattern, targetType);
         }
 
         if (Stream.of(patterns).anyMatch(Objects::isNull))
@@ -855,10 +855,10 @@
     }
 
     static class PatternSwitchCallSite extends ConstantCallSite {
-        private final Extractor[] patterns;
+        private final PatternHandle[] patterns;
 
         PatternSwitchCallSite(MethodType targetType,
-                              Extractor[] patterns) throws Throwable {
+                              PatternHandle[] patterns) throws Throwable {
             super(targetType, PATTERN_INIT_HOOK);
             this.patterns = patterns;
         }
@@ -869,7 +869,7 @@
 
             // Dumbest possible strategy
             for (int i = 0; i < patterns.length; i++) {
-                Extractor e = patterns[i];
+                PatternHandle e = patterns[i];
                 Object o = e.tryMatch().invoke(target);
                 if (o != null)
                     return new PatternSwitchResult(i, o);
--- a/src/java.base/share/classes/java/lang/runtime/_pattern.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java	Mon Jun 24 11:00:53 2019 -0400
@@ -277,6 +277,7 @@
 
         case ANNOTATION_TYPE:
         case CLASS:
+        case RECORD:
             return KindName.CLASS;
 
         case INTERFACE:
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Mon Jun 24 11:00:53 2019 -0400
@@ -215,7 +215,8 @@
     public final Type documentedType;
     public final Type elementTypeType;
     public final Type functionalInterfaceType;
-    public final Type extractorType;
+    public final Type patternHandlesType;
+    public final Type patternHandleType;
     public final Type typeDescriptorType;
 
     /** The symbol representing the length field of an array.
@@ -574,7 +575,8 @@
         lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory");
         stringConcatFactory = enterClass("java.lang.invoke.StringConcatFactory");
         functionalInterfaceType = enterClass("java.lang.FunctionalInterface");
-        extractorType = enterClass("java.lang.runtime.Extractor");
+        patternHandlesType = enterClass("java.lang.runtime.PatternHandles");
+        patternHandleType = enterClass("java.lang.runtime.PatternHandle");
         typeDescriptorType = enterClass("java.lang.invoke.TypeDescriptor");
 
         synthesizeEmptyInterfaceIfMissing(autoCloseableType);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Mon Jun 24 11:00:53 2019 -0400
@@ -28,12 +28,8 @@
 import sun.invoke.util.BytecodeName;
 
 import java.util.*;
-import java.util.Map.Entry;
-import java.util.function.Function;
-import java.util.stream.Stream;
 import java.util.stream.Collectors;
 
-import com.sun.source.tree.CaseTree.CaseKind;
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Kinds.KindSelector;
 import com.sun.tools.javac.code.Scope.WriteableScope;
@@ -61,7 +57,6 @@
 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
-import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.DEREF;
 import static com.sun.tools.javac.jvm.ByteCodes.*;
 import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
 import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
@@ -2545,41 +2540,47 @@
 
     JCTree recordExtractor(JCClassDecl tree, MethodHandleSymbol[] getterMethHandles) {
         make_at(tree.pos());
+
+        // let's generate the name of the extractor method
         List<Type> fieldTypes = TreeInfo.types(TreeInfo.recordFields(tree));
         String argsTypeSig = '(' + argsTypeSig(fieldTypes) + ')';
         String extractorStr = BytecodeName.toBytecodeName("$pattern$" + tree.sym.name + "$" + argsTypeSig);
         Name extractorName = names.fromString(extractorStr);
-        // public Extractor extractorName () { return ???; }
-        MethodType extractorMT = new MethodType(List.nil(), syms.extractorType, List.nil(), syms.methodClass);
+
+        // let's create the condy now
+        Name bsmName = names.ofLazyProjection;
+        List<Type> staticArgTypes = List.of(syms.classType,
+                new ArrayType(syms.methodHandleType, syms.arrayClass));
+        List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
+                syms.stringType,
+                syms.classType).appendList(staticArgTypes);
+
+        Symbol bsm = rs.resolveInternalMethod(tree, attrEnv, syms.patternHandlesType,
+                bsmName, bsm_staticArgs, List.nil());
+
+        LoadableConstant[] staticArgs = new LoadableConstant[1 + getterMethHandles.length];
+        staticArgs[0] = (ClassType)tree.sym.type;
+        int index = 1;
+        for (MethodHandleSymbol mho : getterMethHandles) {
+            staticArgs[index] = mho;
+            index++;
+        }
+
+        Symbol.DynamicVarSymbol dynSym = new Symbol.DynamicVarSymbol(extractorName,
+                syms.noSymbol,
+                ((MethodSymbol)bsm).asHandle(),
+                syms.patternHandleType,
+                staticArgs);
+        JCIdent ident = make.Ident(dynSym);
+        ident.type = syms.patternHandleType;
+
+        // public PatternHandle extractorName () { return ???; }
+        MethodType extractorMT = new MethodType(List.nil(), syms.patternHandleType, List.nil(), syms.methodClass);
         MethodSymbol extractorSym = new MethodSymbol(
                 Flags.PUBLIC | Flags.RECORD | Flags.STATIC,
                 extractorName, extractorMT, tree.sym);
         tree.sym.members().enter(extractorSym);
-
-        Name bootstrapName = names.makeLazyExtractor;
-        LoadableConstant[] staticArgsValues = new LoadableConstant[1 + getterMethHandles.length];
-        /** this method descriptor should have the same arguments as the record constructor and its
-         *  return type should be the same as the type of the record
-         */
-        MethodType mt = new MethodType(fieldTypes, tree.type, List.nil(), syms.methodClass);
-        staticArgsValues[0] = mt;
-        int index = 1;
-        for (MethodHandleSymbol mho : getterMethHandles) {
-            staticArgsValues[index] = mho;
-            index++;
-        }
-
-        List<Type> staticArgTypes = List.of(syms.methodTypeType,
-                new ArrayType(syms.methodHandleType, syms.arrayClass));
-        JCFieldAccess qualifier = makeIndyQualifier(syms.extractorType, tree, extractorSym,
-                List.of(syms.methodHandleLookupType,
-                        syms.stringType,
-                        syms.methodTypeType).appendList(staticArgTypes),
-                staticArgsValues, bootstrapName, bootstrapName, true);
-
-        JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, List.nil());
-        proxyCall.type = qualifier.type;
-        return make.MethodDef(extractorSym, make.Block(0, List.of(make.Return(proxyCall))));
+        return make.MethodDef(extractorSym, make.Block(0, List.of(make.Return(ident))));
     }
 
     JCTree recordReadResolve(JCClassDecl tree) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Mon Jun 24 11:00:53 2019 -0400
@@ -1072,7 +1072,7 @@
         private void addAccessors(JCVariableDecl tree, Env<AttrContext> env) {
             for (Pair<Accessors.Kind, Name> accessor : tree.accessors) {
                 Type accessorType = accessor.fst.accessorType(syms, tree.sym.type);
-                Symbol implSym = lookupMethod(env.enclClass.sym, accessor.snd, accessorType.getParameterTypes());
+                MethodSymbol implSym = lookupMethod(env.enclClass.sym, accessor.snd, accessorType.getParameterTypes());
                 if (implSym == null || (implSym.flags_field & MANDATED) != 0) {
                     JCMethodDecl getter = make.at(tree.pos).MethodDef(make.Modifiers(Flags.PUBLIC | Flags.MANDATED),
                               accessor.snd,
@@ -1086,8 +1086,13 @@
                               null);
                     memberEnter.memberEnter(getter, env);
                     tree.sym.accessors = tree.sym.accessors.prepend(new Pair<>(accessor.fst, getter.sym));
-                } else if (implSym != null && (implSym.flags() & Flags.PUBLIC) == 0) {
-                    log.error(TreeInfo.declarationFor(implSym, env.enclClass), Errors.MethodMustBePublic(implSym.name));
+                } else if (implSym != null) {
+                    if ((implSym.flags() & Flags.PUBLIC) == 0) {
+                        log.error(TreeInfo.declarationFor(implSym, env.enclClass), Errors.MethodMustBePublic(implSym.name));
+                    }
+                    if (!types.isSameType(implSym.type.getReturnType(), tree.sym.type)) {
+                        log.error(TreeInfo.declarationFor(implSym, env.enclClass), Errors.AccessorReturnTypeDoesntMatch);
+                    }
                 }
             }
         }
@@ -1231,10 +1236,10 @@
         }
     }
 
-    private Symbol lookupMethod(TypeSymbol tsym, Name name, List<Type> argtypes) {
+    private MethodSymbol lookupMethod(TypeSymbol tsym, Name name, List<Type> argtypes) {
         for (Symbol s : tsym.members().getSymbolsByName(name, s -> s.kind == MTH)) {
             if (types.isSameTypes(s.type.getParameterTypes(), argtypes)) {
-                return s;
+                return (MethodSymbol) s;
             }
         }
         return null;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Jun 24 11:00:53 2019 -0400
@@ -3470,6 +3470,9 @@
 compiler.err.constructor.with.same.erasure.as.canonical=\
     constructor with same erasure as canonical constructor
 
+compiler.err.accessor.return.type.doesnt.match=\
+    type returned by the accessor is not the same as the type of the corresponding record component
+
 ############################################
 # messages previouly at javac.properties
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Fri Jun 21 01:43:06 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Mon Jun 24 11:00:53 2019 -0400
@@ -205,7 +205,7 @@
     public final Name record;
     public final Name where;
     public final Name non;
-    public final Name makeLazyExtractor;
+    public final Name ofLazyProjection;
 
     // sealed types
     public final Name permits;
@@ -372,7 +372,7 @@
         record = fromString("record");
         where = fromString("where");
         non = fromString("non");
-        makeLazyExtractor = fromString("makeLazyExtractor");
+        ofLazyProjection = fromString("ofLazyProjection");
 
         // sealed types
         permits = fromString("permits");
--- a/test/jdk/java/lang/extractor/ExtractorTest.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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");
-    }
-}
--- a/test/jdk/java/lang/extractor/RecordTest.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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"));
-    }
-}
--- a/test/jdk/java/lang/extractor/SwitchBootstrapsTest.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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
-    }
-}
--- a/test/jdk/java/lang/extractor/boottest/TEST.properties	Fri Jun 21 01:43:06 2019 +0200
+++ /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/extractor/boottest/java.base/java/lang/runtime/CarrierTest.java	Fri Jun 21 01:43:06 2019 +0200
+++ /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.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));
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/lang-runtime/PatternHandleTest.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,457 @@
+/*
+ * 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.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.runtime.PatternHandle;
+import java.lang.runtime.PatternHandles;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.testng.annotations.Test;
+
+import static java.util.Map.entry;
+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;
+import static org.testng.Assert.fail;
+
+/**
+ * @test
+ * @run testng PatternHandleTest
+ * @summary Smoke tests for java.lang.runtime.Extractor
+ */
+@Test
+public class PatternHandleTest {
+
+    enum MatchKind {
+        /** Match succeeds, with a carrier object different form the target */
+        MATCH_CARRIER,
+        /** Match succeeds, with self-carrier */
+        MATCH_SELF,
+        /** Match succeeds, carrier provenance unknown */
+        MATCH,
+        /** Match fails */
+        NO_MATCH,
+        /** Match fails with a runtime exception */
+        ERROR;
+    }
+
+    // We have to resort to the subterfuge of laundering the tryMatch invocation
+    // through an ancillary object, because the only way to control the static
+    // signature is through the static types.  So we have a bunch of invokers,
+    // each of which embeds different assumptions about the target type.  This
+    // way we can test mismatches between the expected and actual types.
+
+    interface TryMatchInvoker<T> {
+        Object tryMatch(MethodHandle mh, T target) throws Throwable;
+    }
+
+    static final TryMatchInvoker<Object> objectInvoker = (MethodHandle mh, Object x) -> mh.invokeExact(x);
+    static final TryMatchInvoker<Number> numberInvoker = (MethodHandle mh, Number x) -> mh.invokeExact(x);
+    static final TryMatchInvoker<Integer> integerInvoker = (MethodHandle mh, Integer x) -> mh.invokeExact(x);
+    static final TryMatchInvoker<Integer> intInvoker = (MethodHandle mh, Integer x) -> mh.invokeExact((int) x);
+    static final TryMatchInvoker<String> stringInvoker = (MethodHandle mh, String x) -> mh.invokeExact(x);
+    static final TryMatchInvoker<List> listInvoker = (MethodHandle mh, List x) -> mh.invokeExact(x);
+    static final TryMatchInvoker<TestClass> testClassInvoker = (MethodHandle mh, TestClass x) -> mh.invokeExact(x);
+    static final TryMatchInvoker<TestClass2> testClass2Invoker = (MethodHandle mh, TestClass2 x) -> mh.invokeExact(x);
+
+    static final Map<Class<?>, TryMatchInvoker<?>> invokers
+            = Map.ofEntries(entry(Object.class, objectInvoker),
+                            entry(Number.class, numberInvoker),
+                            entry(Integer.class, integerInvoker),
+                            entry(int.class, intInvoker),
+                            entry(String.class, stringInvoker),
+                            entry(List.class, listInvoker),
+                            entry(TestClass.class, testClassInvoker),
+                            entry(TestClass2.class, testClass2Invoker));
+
+    interface Throwing {
+        public void run() throws Throwable;
+    }
+
+    static void assertThrows(Class<? extends Throwable> exception, Throwing r) {
+        try {
+            r.run();
+            fail("Expected exception: " + exception);
+        }
+        catch (Throwable t) {
+            if (!exception.isAssignableFrom(t.getClass()))
+                fail(String.format("Expected exception %s, got %s", exception, t.getClass()), t);
+        }
+    }
+
+    static void assertMatch(MatchKind expected,
+                            PatternHandle e,
+                            Object target,
+                            Object... expectedBindings) throws Throwable {
+        int count = e.descriptor().parameterCount();
+        Object[] bindings = new Object[count];
+        Object carrier;
+        try {
+            TryMatchInvoker inv = invokers.get(e.descriptor().returnType());
+            // @@@ temporary hack until we break out the assert-match machinery
+            if (inv == null)
+                inv = (MethodHandle mh, Object x) -> mh.invoke(x);
+            // @@@ end temporary hack
+            carrier = inv.tryMatch(e.tryMatch(), target);
+        }
+        catch (Throwable t) {
+            carrier = null;
+            if (expected == MatchKind.ERROR)
+                return;
+            else
+                fail("Unexpected exception in tryMatch", t);
+        }
+
+        if (carrier != null) {
+            for (int i = 0; i < count; i++)
+                bindings[i] = e.component(i).invoke(carrier);
+        }
+
+        if (expected == MatchKind.NO_MATCH)
+            assertNull(carrier);
+        else {
+            assertNotNull(carrier);
+            assertEquals(bindings.length, expectedBindings.length);
+            for (int i = 0; i < expectedBindings.length; i++)
+                assertEquals(bindings[i], expectedBindings[i]);
+
+            if (expected == MatchKind.MATCH_SELF)
+                assertSame(carrier, target);
+            else if (expected == MatchKind.MATCH_CARRIER)
+                assertNotSame(carrier, target);
+        }
+    }
+
+    private static class TestClass {
+        static TestClass INSTANCE_A = new TestClass("foo", 3, 4L, (byte) 5);
+        static TestClass INSTANCE_B = new TestClass(null, 0, 0L, (byte) 0);
+        static TestClass INSTANCE_C = new TestClass("foo", 2, 4L, (byte) 5);
+        static Object[] COMPONENTS_A = new Object[] { "foo", 3, 4L, (byte) 5 };
+        static Object[] COMPONENTS_B = new Object[] { null, 0, 0L, (byte) 0 };
+        static Object[] COMPONENTS_C = new Object[] { "foo", 2, 4L, (byte) 5 };
+
+        static Map<TestClass, Object[]> INSTANCES = Map.of(INSTANCE_A, COMPONENTS_A,
+                                                           INSTANCE_B, COMPONENTS_B,
+                                                           INSTANCE_C, COMPONENTS_C);
+
+        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);
+            }
+        }
+        static MethodHandle[] COMPONENT_MHS = {TestClass.MH_S, TestClass.MH_I, TestClass.MH_L, TestClass.MH_B };
+
+        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;
+        }
+    }
+
+    PatternHandle TYPE_STRING = PatternHandles.ofType(String.class);
+    PatternHandle TYPE_LIST = PatternHandles.ofType(List.class);
+    PatternHandle TYPE_INTEGER = PatternHandles.ofType(Integer.class);
+    PatternHandle TYPE_NUMBER = PatternHandles.ofType(Number.class);
+    PatternHandle TYPE_OBJECT = PatternHandles.ofType(Object.class);
+    PatternHandle TYPE_INT = PatternHandles.ofType(int.class);
+    PatternHandle TYPE_STRING_NULLABLE = PatternHandles.ofTypeNullable(String.class);
+
+    public void testType() throws Throwable {
+        assertMatch(MatchKind.MATCH_SELF, TYPE_STRING, "Foo", "Foo");
+        assertMatch(MatchKind.NO_MATCH, TYPE_STRING, null);
+        assertMatch(MatchKind.ERROR, TYPE_STRING, List.of());
+        assertMatch(MatchKind.ERROR, TYPE_STRING, 3);
+
+        assertMatch(MatchKind.MATCH_SELF, TYPE_LIST, List.of(3), List.of(3));
+        assertMatch(MatchKind.MATCH_SELF, TYPE_LIST, List.of(), List.of());
+        assertMatch(MatchKind.MATCH_SELF, TYPE_LIST, new ArrayList<>(), List.of());
+        assertMatch(MatchKind.NO_MATCH, TYPE_LIST, null);
+
+        assertMatch(MatchKind.MATCH_SELF, TYPE_INTEGER, 3, 3);
+        assertMatch(MatchKind.MATCH_SELF, TYPE_NUMBER, 3, 3);
+        assertMatch(MatchKind.MATCH_SELF, TYPE_OBJECT, 3, 3);
+        assertMatch(MatchKind.NO_MATCH, TYPE_OBJECT, null);
+
+        assertMatch(MatchKind.ERROR, TYPE_INTEGER, 3.14f);
+        assertMatch(MatchKind.ERROR, TYPE_INTEGER, "foo");
+    }
+
+    public void testPrimitiveType() throws Throwable {
+        assertMatch(MatchKind.MATCH_SELF, TYPE_INT, 3, 3);
+        assertMatch(MatchKind.ERROR, TYPE_INT, 3.14f);
+
+        PatternHandle asObject = PatternHandles.adaptTarget(TYPE_INT, Object.class);
+        assertMatch(MatchKind.MATCH_SELF, asObject, 3, 3);
+        assertMatch(MatchKind.NO_MATCH, asObject, 3.14f);
+        assertMatch(MatchKind.NO_MATCH, asObject, null);
+
+        PatternHandle asInteger = PatternHandles.adaptTarget(TYPE_INT, Integer.class);
+        assertMatch(MatchKind.MATCH_SELF, asInteger, 3, 3);
+        assertMatch(MatchKind.NO_MATCH, asInteger, null);
+        assertMatch(MatchKind.ERROR, asInteger, 3.14f);
+    }
+
+    public void testNullableType() throws Throwable {
+        assertMatch(MatchKind.MATCH_SELF, TYPE_STRING_NULLABLE, "Foo", "Foo");
+        assertMatch(MatchKind.MATCH, TYPE_STRING_NULLABLE, null, (Object) null);
+        assertMatch(MatchKind.ERROR, TYPE_STRING_NULLABLE, 3);
+
+        PatternHandle asObjectNullable = PatternHandles.adaptTarget(TYPE_STRING_NULLABLE, Object.class);
+        assertMatch(MatchKind.MATCH_SELF, asObjectNullable, "Foo", "Foo");
+        assertMatch(MatchKind.MATCH, asObjectNullable, null, (Object) null);
+        assertMatch(MatchKind.NO_MATCH, asObjectNullable, 3);
+    }
+
+    public void testAdapt() throws Throwable {
+        PatternHandle e = PatternHandles.ofTypeNullable(Number.class);
+        PatternHandle n = PatternHandles.adaptTarget(e, Integer.class);
+        PatternHandle w = PatternHandles.adaptTarget(e, Object.class);
+
+        assertEquals(e.descriptor().returnType(), Number.class);
+        assertEquals(n.descriptor().returnType(), Integer.class);
+        assertEquals(w.descriptor().returnType(), Object.class);
+
+        assertMatch(MatchKind.MATCH_SELF, e, 1, 1);
+        assertMatch(MatchKind.MATCH_SELF, n, 1, 1);
+        assertMatch(MatchKind.MATCH_SELF, w, 1, 1);
+
+        assertMatch(MatchKind.MATCH_SELF, e, 3.14f, 3.14f);
+        assertMatch(MatchKind.ERROR, n, 3.14f);
+        assertMatch(MatchKind.MATCH_SELF, w, 3.14f, 3.14f);
+
+        assertMatch(MatchKind.MATCH, e, null, (Object) null);
+        assertMatch(MatchKind.MATCH, n, null, (Object) null);
+        assertMatch(MatchKind.MATCH, w, null, (Object) null);
+
+        e = PatternHandles.ofType(Number.class);
+        n = PatternHandles.adaptTarget(e, Integer.class);
+        w = PatternHandles.adaptTarget(e, Object.class);
+
+        assertMatch(MatchKind.MATCH_SELF, e, 1, 1);
+        assertMatch(MatchKind.MATCH_SELF, n, 1, 1);
+        assertMatch(MatchKind.MATCH_SELF, w, 1, 1);
+        assertMatch(MatchKind.NO_MATCH, e, null);
+        assertMatch(MatchKind.NO_MATCH, n, null);
+        assertMatch(MatchKind.NO_MATCH, w, null);
+
+        PatternHandle widenNarrow = PatternHandles.adaptTarget(PatternHandles.adaptTarget(TYPE_STRING, Object.class), String.class);
+        assertMatch(MatchKind.MATCH_SELF, widenNarrow, "Foo", "Foo");
+        assertMatch(MatchKind.NO_MATCH, widenNarrow, null);
+        assertMatch(MatchKind.ERROR, widenNarrow, List.of());
+        assertMatch(MatchKind.ERROR, widenNarrow, 3);
+
+        PatternHandle widenNarrowNullable = PatternHandles.adaptTarget(PatternHandles.adaptTarget(TYPE_STRING_NULLABLE, Object.class), String.class);
+        assertMatch(MatchKind.MATCH_SELF, widenNarrowNullable, "Foo", "Foo");
+        assertMatch(MatchKind.MATCH, widenNarrowNullable, null, (Object) null);
+        assertMatch(MatchKind.ERROR, widenNarrowNullable, List.of());
+        assertMatch(MatchKind.ERROR, widenNarrowNullable, 3);
+    }
+
+    public void testConstant() throws Throwable {
+        PatternHandle constantFoo = PatternHandles.ofConstant("foo");
+        assertMatch(MatchKind.MATCH, constantFoo, "foo");
+        assertMatch(MatchKind.NO_MATCH, constantFoo, "bar");
+        assertMatch(MatchKind.ERROR, constantFoo, 3);
+        assertMatch(MatchKind.NO_MATCH, constantFoo, null);
+
+        PatternHandle constantThree = PatternHandles.ofConstant(3);
+        assertMatch(MatchKind.MATCH, constantThree, 3);
+        assertMatch(MatchKind.NO_MATCH, constantThree, 4);
+        assertMatch(MatchKind.NO_MATCH, constantThree, null);
+    }
+
+    public void testNullConstant() throws Throwable {
+        PatternHandle constantNull = PatternHandles.ofConstant(null);
+        assertMatch(MatchKind.MATCH, constantNull, null);
+        assertMatch(MatchKind.NO_MATCH, constantNull, "foo");
+        assertMatch(MatchKind.NO_MATCH, constantNull, 3);
+    }
+
+    public void testProjections() throws Throwable {
+        Map<PatternHandle, MatchKind> m
+                = Map.of(PatternHandles.ofLazyProjection(TestClass.class, TestClass.COMPONENT_MHS), MatchKind.MATCH_SELF,
+                         PatternHandles.ofEagerProjection(TestClass.class, TestClass.COMPONENT_MHS), MatchKind.MATCH_CARRIER);
+        for (var ps : m.entrySet()) {
+            for (var entry : TestClass.INSTANCES.entrySet()) {
+                assertMatch(ps.getValue(), ps.getKey(), entry.getKey(), entry.getValue());
+            }
+            assertMatch(MatchKind.NO_MATCH, ps.getKey(), null);
+
+            PatternHandle asObject = PatternHandles.adaptTarget(ps.getKey(), Object.class);
+            for (var entry : TestClass.INSTANCES.entrySet())
+                assertMatch(ps.getValue(), asObject, entry.getKey(), entry.getValue());
+            assertMatch(MatchKind.NO_MATCH, asObject, null);
+
+            PatternHandle asTestClassAgain = PatternHandles.adaptTarget(asObject, TestClass.class);
+            for (var entry : TestClass.INSTANCES.entrySet())
+                assertMatch(ps.getValue(), asTestClassAgain, entry.getKey(), entry.getValue());
+            assertMatch(MatchKind.NO_MATCH, asTestClassAgain, null);
+        }
+    }
+
+    public void testDigest() throws Throwable {
+        PatternHandle e = PatternHandles.ofImperative(TestClass.TYPE, TestClass.DIGESTER);
+        for (var entry : TestClass.INSTANCES.entrySet())
+            assertMatch(MatchKind.MATCH_CARRIER, e, entry.getKey(), entry.getValue());
+        assertMatch(MatchKind.NO_MATCH, e, null);
+    }
+
+    public void testDigestPartial() throws Throwable {
+        PatternHandle e = PatternHandles.ofImperative(TestClass.TYPE, TestClass.DIGESTER_PARTIAL);
+        for (var entry : TestClass.INSTANCES.entrySet()) {
+            if (entry.getKey().matches())
+                assertMatch(MatchKind.MATCH_CARRIER, e, entry.getKey(), entry.getValue());
+            else
+                assertMatch(MatchKind.NO_MATCH, e, entry.getKey());
+        }
+        assertMatch(MatchKind.NO_MATCH, e, null);
+    }
+
+    public void testCompose() throws Throwable {
+        PatternHandle e = PatternHandles.ofLazyProjection(TestClass.class, TestClass.COMPONENT_MHS);
+        MethodHandle mh = PatternHandles.compose(e, TestClass.CONSTRUCTOR);
+        TestClass target = TestClass.INSTANCE_A;
+        Object o = mh.invoke(target);
+        assertTrue(o instanceof TestClass);
+        assertNotSame(target, o);
+        assertEquals(target, o);
+
+        assertNull(mh.invoke((Object) null));
+    }
+
+    public void testDropBindings() throws Throwable {
+        PatternHandle e = PatternHandles.ofEagerProjection(TestClass.class, TestClass.COMPONENT_MHS);
+        assertMatch(MatchKind.MATCH_CARRIER, e, TestClass.INSTANCE_A,
+                    TestClass.COMPONENTS_A);
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(e, 0), TestClass.INSTANCE_A,
+                    3, 4L, (byte) 5);
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(e, 0, 0), TestClass.INSTANCE_A,
+                    3, 4L, (byte) 5);
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(e, 3), TestClass.INSTANCE_A,
+                    "foo", 3, 4L);
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(e, 0, 1, 2, 3), TestClass.INSTANCE_A);
+
+        assertThrows(IndexOutOfBoundsException.class,
+                     () -> assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(e, -1), TestClass.INSTANCE_A,
+                                       3, 4L, (byte) 5));
+        assertThrows(IndexOutOfBoundsException.class,
+                     () -> assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(e, 4), TestClass.INSTANCE_A,
+                                       3, 4L, (byte) 5));
+    }
+
+    public void testNested() throws Throwable {
+        PatternHandle TC2 = PatternHandles.ofLazyProjection(TestClass2.class, TestClass2.MH_X);
+        PatternHandle TC2_STRING = PatternHandles.nested(TC2, TYPE_STRING);
+        PatternHandle TC2_OBJECT = PatternHandles.nested(TC2, TYPE_OBJECT);
+
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(TC2_STRING, 0), new TestClass2("foo"),
+                    "foo");
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(TC2_OBJECT, 0), new TestClass2("foo"),
+                    "foo");
+        assertMatch(MatchKind.NO_MATCH, PatternHandles.dropBindings(TC2_STRING, 0), new TestClass2(List.of(3)),
+                    "foo");
+
+        assertMatch(MatchKind.MATCH_CARRIER, PatternHandles.dropBindings(PatternHandles.nested(TC2, 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/lang-runtime/RecordTest.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,250 @@
+/*
+ * 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.PatternHandle;
+import java.lang.runtime.PatternHandles;
+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
+ * @run testng RecordTest
+ * @summary End-to-end test for record patterns
+ */
+@Test
+public class RecordTest {
+    record R(int a, String b, double c);
+    record RR(R r1, R R2);
+
+    private PatternHandle recordExtractor(Class<?> recordClass,
+                                          Class<?>... paramTypes) throws Throwable {
+        return PatternHandles.ofNamed(MethodHandles.lookup(), "_", PatternHandle.class,
+                recordClass, MethodType.methodType(void.class, paramTypes), recordClass.getName(), REF_newInvokeSpecial);
+    }
+
+    public void testRecord() throws Throwable {
+        R r = new R(1, "two", 3.14d);
+        PatternHandle rExtract = recordExtractor(R.class, int.class, String.class, double.class);
+
+        MethodHandle tryExtract = PatternHandles.tryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rExtract);
+        MethodHandle a = PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 0);
+        MethodHandle b = PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 1);
+        MethodHandle c = PatternHandles.component(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);
+
+        PatternHandle rExtract = recordExtractor(R.class, int.class, String.class, double.class);
+        PatternHandle rrExtract = recordExtractor(RR.class, R.class, R.class);
+
+        MethodHandle tryExtractR = PatternHandles.tryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rExtract);
+        MethodHandle ra = PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 0);
+        MethodHandle rb = PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 1);
+        MethodHandle rc = PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class, rExtract, 2);
+
+        MethodHandle tryExtractRr = PatternHandles.tryMatch(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract);
+        MethodHandle r1c = PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class, rrExtract, 0);
+        MethodHandle r2c = PatternHandles.component(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 {
+        PatternHandle rExtract = recordExtractor(R.class, int.class, String.class, double.class);
+        PatternHandle rrExtract = recordExtractor(RR.class, R.class, R.class);
+
+        PatternHandle e = PatternHandles.nested(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);
+
+        PatternHandle ee = PatternHandles.nested(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(PatternHandle e, int num, Object carrier) throws Throwable {
+        return PatternHandles.component(MethodHandles.lookup(), "_", MethodHandle.class,
+                e, num).invoke(carrier);
+    }
+
+    public void testRecordSwitch() throws Throwable {
+        PatternHandle[] 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 {
+        PatternHandle boxA = PatternHandles.nested(recordExtractor(Box.class, Object.class),
+                recordExtractor(A.class, int.class));
+        PatternHandle boxB = PatternHandles.nested(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 {
+        PatternHandle rb = recordExtractor(Box.class, Object.class);
+        PatternHandle rs = recordExtractor(RString.class, String.class);
+        PatternHandle ro = recordExtractor(RObject.class, Object.class);
+        PatternHandle ri = recordExtractor(Rint.class, int.class);
+        PatternHandle cs = PatternHandles.ofConstant("foo");
+        PatternHandle cn = PatternHandles.ofConstant(null);
+        PatternHandle ci = PatternHandles.ofConstant(3);
+
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.MATCH, PatternHandles.nested(rs, cs), new RString("foo"), "foo");
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH, PatternHandles.nested(rs, cs), new RString("bar"));
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH, PatternHandles.nested(rs, cs), new RString(null));
+
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.MATCH, PatternHandles.nested(ro, cs), new RObject("foo"), "foo");
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH, PatternHandles.nested(ro, cs), new RObject("bar"));
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH, PatternHandles.nested(ro, cs), new RObject(3));
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH, PatternHandles.nested(ro, cs), new RObject(null));
+
+        // @@@ These are waiting for support for either unboxing in adapt() or primitive constant patterns
+//        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.MATCH, PatternHandles.nested(ri, ci), new Rint(3), 3);
+//        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH, PatternHandles.nested(ri, ci), new Rint(2));
+
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.MATCH,
+                PatternHandles.nested(rb, PatternHandles.nested(rs, cs)),
+                new Box(new RString("foo")), new RString("foo"), "foo");
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH,
+                PatternHandles.nested(rb, PatternHandles.nested(rs, cs)),
+                new Box(new RString("bar")));
+        PatternHandleTest.assertMatch(PatternHandleTest.MatchKind.NO_MATCH,
+                PatternHandles.nested(rb, PatternHandles.nested(rs, cs)),
+                new Box("foo"));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/lang-runtime/SwitchBootstrapsTest.java	Mon Jun 24 11:00:53 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/lang-runtime/boottest/TEST.properties	Mon Jun 24 11:00:53 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/lang-runtime/boottest/java.base/java/lang/runtime/CarrierTest.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,117 @@
+/*
+ * 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.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 (PatternCarriers.CarrierFactory cf : PatternCarriers.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/langtools/tools/javac/records/mandated_members/accessors/BadAccessorsTest.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary check that the compiler doesn't accept incorrect accessors
+ * @compile/fail/ref=BadAccessorsTest.out -XDrawDiagnostics BadAccessorsTest.java
+ */
+
+import java.util.List;
+
+public class BadAccessorsTest {
+    record R1(int i, int j, int k, List<String> ls) {
+        // accessors has to be public
+        int i() { return i; }
+        private int j() { return j; }
+        protected int k() { return k; }
+        // must match type exactly
+        public List ls() { return ls; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/records/mandated_members/accessors/BadAccessorsTest.out	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,5 @@
+BadAccessorsTest.java:12:13: compiler.err.method.must.be.public: i
+BadAccessorsTest.java:13:21: compiler.err.method.must.be.public: j
+BadAccessorsTest.java:14:23: compiler.err.method.must.be.public: k
+BadAccessorsTest.java:16:21: compiler.err.accessor.return.type.doesnt.match
+4 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/records/miscellaneous/UnexpectedKindRecordTest.java	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,13 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary check that record is not used as a type name
+ * @compile/fail/ref=UnexpectedKindRecordTest.out -XDrawDiagnostics UnexpectedKindRecordTest.java
+ */
+
+class UnexpectedKindRecordTest {
+
+    record R(int i) {
+        public int i() { return i; }
+        public int i() { return i; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/records/miscellaneous/UnexpectedKindRecordTest.out	Mon Jun 24 11:00:53 2019 -0400
@@ -0,0 +1,2 @@
+UnexpectedKindRecordTest.java:11:20: compiler.err.already.defined: kindname.method, i(), kindname.class, UnexpectedKindRecordTest.R
+1 error