changeset 52924:cb7760bcb5b5 datum

Add nesting combinator for Extractor
author briangoetz
date Mon, 29 Oct 2018 15:12:20 -0400
parents 716341307709
children 89969d71c998
files .hgignore src/java.base/share/classes/java/lang/compiler/Extractor.java src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java test/jdk/java/lang/compiler/ExtractorTest.java test/jdk/java/lang/compiler/RecordTest.java
diffstat 5 files changed, 471 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Oct 26 12:58:45 2018 -0400
+++ b/.hgignore	Mon Oct 29 15:12:20 2018 -0400
@@ -11,6 +11,6 @@
 test/nashorn/script/external
 test/nashorn/lib
 NashornProfile.txt
-.*/JTreport/.*
-.*/JTwork/.*
+JTreport/
+JTwork/
 .*/.git/.*
--- a/src/java.base/share/classes/java/lang/compiler/Extractor.java	Fri Oct 26 12:58:45 2018 -0400
+++ b/src/java.base/share/classes/java/lang/compiler/Extractor.java	Mon Oct 29 15:12:20 2018 -0400
@@ -29,6 +29,8 @@
 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;
@@ -37,6 +39,7 @@
 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}
@@ -70,10 +73,10 @@
     MethodHandle component(int i);
 
     /**
-     * Whether this extractor might fail.
-     * @return if this extractor might fail
+     * Returns the component method handles, as an array
+     * @return the component method handles
      */
-    boolean isPartial();
+    MethodHandle[] components();
 
     /**
      * The descriptor of the {@linkplain Extractor}.  The parameter types of
@@ -88,22 +91,21 @@
      * 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) {
+    default MethodHandle compose(MethodHandle target, Object sentinel) {
         int count = descriptor().parameterCount();
-        MethodHandle[] components = new MethodHandle[count];
-        int[] reorder = new int[count];
-        for (int i=0; i<count; i++) {
-            components[i] = component(i);
-            reorder[i] = 0;
-        }
+        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(target.type().returnType(), tryMatch().type().returnType()),
-                                            reorder);
+        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());
-        // @@@ What if pattern doesn't match?
         return mh;
     }
 
@@ -124,6 +126,35 @@
     }
 
     /**
+     * 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.
@@ -134,24 +165,7 @@
      */
     public static Extractor of(MethodType descriptor,
                                MethodHandle digester) {
-        return new ExtractorImpl(descriptor, false,
-                                 MethodHandles.insertArguments(digester,
-                                                               1, ExtractorCarriers.carrierFactory(descriptor)),
-                                 ExtractorCarriers.carrierComponents(descriptor));
-    }
-
-    /**
-     * Create a partial {@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 ofPartial(MethodType descriptor,
-                                      MethodHandle digester) {
-        return new ExtractorImpl(descriptor, true,
+        return new ExtractorImpl(descriptor,
                                  MethodHandles.insertArguments(digester,
                                                                1, ExtractorCarriers.carrierFactory(descriptor)),
                                  ExtractorCarriers.carrierComponents(descriptor));
@@ -167,7 +181,7 @@
      */
     public static Extractor ofTotal(Class<?> targetType, MethodHandle... components) {
         MethodType descriptor = descriptor(targetType, components);
-        return new ExtractorImpl(descriptor, false,
+        return new ExtractorImpl(descriptor,
                                  carrierTryExtract(descriptor, components),
                                  ExtractorCarriers.carrierComponents(descriptor));
     }
@@ -181,7 +195,7 @@
      * @return the extractor
      */
     public static Extractor ofSelfTotal(Class<?> targetType, MethodHandle... components) {
-        return new ExtractorImpl(descriptor(targetType, components), false,
+        return new ExtractorImpl(descriptor(targetType, components),
                                  MethodHandles.identity(targetType), components);
     }
 
@@ -189,53 +203,186 @@
      * 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(MethodHandle predicate, MethodHandle... components) {
-        Class<?> targetType = predicate.type().parameterType(0);
+    public static Extractor ofPartial(Class<?> targetType, MethodHandle predicate, MethodHandle... components) {
         MethodType descriptor = descriptor(targetType, components);
         MethodHandle carrierTryExtract = carrierTryExtract(descriptor, components);
-        MethodHandle tryExtract = MethodHandles.guardWithTest(predicate,
-                                                              carrierTryExtract,
-                                                              MethodHandles.dropArguments(MethodHandles.constant(carrierTryExtract.type().returnType(), null),
-                                                                                          0, targetType));
-        return new ExtractorImpl(descriptor, true,
-                                 tryExtract, ExtractorCarriers.carrierComponents(descriptor));
+        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(MethodHandle predicate, MethodHandle... components) {
-        Class<?> targetType = predicate.type().parameterType(0);
-        MethodHandle tryExtract = MethodHandles.guardWithTest(predicate,
-                                                              MethodHandles.identity(targetType),
-                                                              MethodHandles.dropArguments(MethodHandles.constant(targetType, null),
-                                                                                          0, targetType));
-        return new ExtractorImpl(descriptor(targetType, components), true, tryExtract, components);
+    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
+     * variable, whose target type is {@code Object}
      *
      * @param type the type to match against
      * @return the {@linkplain Extractor}
      */
     public static Extractor ofType(Class<?> type) {
-        // tryMatch = (t instanceof type) ? t : null
-        // component = (type) o
-        return null;
+        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 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 nested(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
@@ -267,7 +414,6 @@
         return ofSelfTotal(descriptor.returnType(), components);
     }
 
-
     /**
      * Condy bootstrap for finding extractors
      *
@@ -284,11 +430,10 @@
     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();
-        dd = dd.substring(0, dd.indexOf(')') + 1);
         String patternMethodName
                 = BytecodeName.toBytecodeName(String.format("$pattern$%s$%s",
                                                             (refKind == REF_newInvokeSpecial ? owner.getSimpleName() : name),
-                                                            dd));
+                                                            dd.substring(0, dd.indexOf(')') + 1)));
         MethodType factoryDesc = MethodType.methodType(Extractor.class);
         MethodHandle mh;
         switch (refKind) {
--- a/src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java	Fri Oct 26 12:58:45 2018 -0400
+++ b/src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java	Mon Oct 29 15:12:20 2018 -0400
@@ -25,18 +25,38 @@
 package java.lang.compiler;
 
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Non-public implementation of {@link Extractor}
  */
 class ExtractorImpl implements Extractor {
     private final MethodType descriptor;
-    private final boolean partial;
     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 {
+        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));
+        }
+        catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
     /**
      * Construct an {@link Extractor} from components
      * Constraints:
@@ -48,7 +68,7 @@
      * @param tryMatch The {@code tryMatch} method handle
      * @param components The {@code component} method handles
      */
-    ExtractorImpl(MethodType descriptor, boolean partial, MethodHandle tryMatch, MethodHandle[] components) {
+    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));
@@ -65,7 +85,6 @@
         }
 
         this.descriptor = descriptor;
-        this.partial = partial;
         this.tryMatch = tryMatch;
         this.components = List.of(components);
     }
@@ -81,12 +100,24 @@
     }
 
     @Override
+    public MethodHandle[] components() {
+        return components.toArray(new MethodHandle[0]);
+    }
+
+    @Override
     public MethodType descriptor() {
         return descriptor;
     }
 
-    @Override
-    public boolean isPartial() {
-        return partial;
+    private static Object ofTypeHelper(Class<?> type, Object o) {
+        return o != null && type.isAssignableFrom(o.getClass()) ? o : null;
+    }
+
+    private static Object ofTypeNullableHelper(Class<?> type, Object o) {
+        return o == null || type.isAssignableFrom(o.getClass()) ? o : null;
+    }
+
+    private static boolean adaptHelper(Class<?> type, Object o) {
+        return o != null && type.isAssignableFrom(o.getClass());
     }
 }
--- a/test/jdk/java/lang/compiler/ExtractorTest.java	Fri Oct 26 12:58:45 2018 -0400
+++ b/test/jdk/java/lang/compiler/ExtractorTest.java	Mon Oct 29 15:12:20 2018 -0400
@@ -27,6 +27,8 @@
 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;
@@ -48,10 +50,10 @@
 
     private enum MatchKind { CARRIER, SELF, FAIL, MATCH }
 
-    private void assertMatches(MatchKind kind, Extractor e, Object target, Object... args) throws Throwable {
+    private void assertMatch(MatchKind kind, Extractor e, Object target, Object... args) throws Throwable {
         int count = e.descriptor().parameterCount();
         Object[] bindings = new Object[count];
-        Object carrier = e.tryMatch().invoke(target);
+        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);
@@ -60,7 +62,8 @@
         if (kind == MatchKind.FAIL)
             assertNull(carrier);
         else {
-            assertNotNull(carrier);
+            if (target != null)
+                assertNotNull(carrier);
             assertEquals(bindings.length, args.length);
             for (int i = 0; i < args.length; i++)
                 assertEquals(bindings[i], args[i]);
@@ -137,64 +140,129 @@
         }
     }
 
+    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);
-        assertMatches(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                      "foo", 3, 4L, (byte) 5);
-        assertMatches(MatchKind.CARRIER, e, new TestClass(null, 0, 0L, (byte) 0),
-                      null, 0, 0L, (byte) 0);
+        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);
-        assertMatches(MatchKind.SELF, e, new TestClass("foo", 3, 4L, (byte) 5),
-                      "foo", 3, 4L, (byte) 5);
-        assertMatches(MatchKind.SELF, e, new TestClass(null, 0, 0L, (byte) 0),
-                      null, 0, 0L, (byte) 0);
+        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.MH_PRED, COMPONENTS);
-        assertMatches(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                      "foo", 3, 4L, (byte) 5);
-        assertMatches(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
-        assertMatches(MatchKind.FAIL, e, new TestClass(null, 0, 0L, (byte) 0));
+        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.MH_PRED, COMPONENTS);
-        assertMatches(MatchKind.SELF, e, new TestClass("foo", 3, 4L, (byte) 5),
-                      "foo", 3, 4L, (byte) 5);
-        assertMatches(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
-        assertMatches(MatchKind.FAIL, e, new TestClass(null, 0, 0L, (byte) 0));
+        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);
-        assertMatches(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                      "foo", 3, 4L, (byte) 5);
-        assertMatches(MatchKind.CARRIER, e, new TestClass("foo", 2, 4L, (byte) 5),
-                      "foo", 2, 4L, (byte) 5);
-        assertMatches(MatchKind.CARRIER, e, new TestClass(null, 0, 0L, (byte) 0),
-                      null, 0, 0L, (byte) 0);
+        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.ofPartial(TestClass.TYPE, TestClass.DIGESTER_PARTIAL);
-        assertMatches(MatchKind.CARRIER, e, new TestClass("foo", 3, 4L, (byte) 5),
-                      "foo", 3, 4L, (byte) 5);
-        assertMatches(MatchKind.FAIL, e, new TestClass("foo", 2, 4L, (byte) 5));
+        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);
+        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 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.nested(TC2, STRING), 0), new TestClass2("foo"),
+                    "foo");
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.nested(TC2, OBJECT), 0), new TestClass2("foo"),
+                    "foo");
+        assertMatch(MatchKind.FAIL, Extractor.dropBindings(Extractor.nested(TC2, STRING), 0), new TestClass2(List.of(3)),
+                    "foo");
+
+        assertMatch(MatchKind.CARRIER, Extractor.dropBindings(Extractor.nested(TC2, Extractor.nested(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/compiler/RecordTest.java	Mon Oct 29 15:12:20 2018 -0400
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.compiler.Extractor;
+import java.lang.compiler.ExtractorCarriers;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.testng.annotations.Test;
+
+import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial;
+import static java.util.Collections.nCopies;
+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);
+
+    public void testRecord() throws Throwable {
+        R r = new R(1, "two", 3.14d);
+        Extractor ex = Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
+                                               R.class, MethodType.methodType(void.class, int.class, String.class, double.class), "Foo", REF_newInvokeSpecial);
+
+        MethodHandle tryExtract = Extractor.extractorTryMatch(MethodHandles.lookup(), "_", MethodHandle.class, ex);
+        MethodHandle a = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, ex, 0);
+        MethodHandle b = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, ex, 1);
+        MethodHandle c = Extractor.extractorComponent(MethodHandles.lookup(), "_", MethodHandle.class, ex, 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 = Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
+                                                     R.class, MethodType.methodType(void.class, int.class, String.class, double.class), "Foo", REF_newInvokeSpecial);
+        Extractor rrExtract = Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
+                                                      RR.class, MethodType.methodType(void.class, R.class, R.class), "Foo", REF_newInvokeSpecial);
+
+        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 = Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
+                                                     R.class, MethodType.methodType(void.class, int.class, String.class, double.class), "Foo", REF_newInvokeSpecial);
+        Extractor rrExtract = Extractor.findExtractor(MethodHandles.lookup(), "_", Extractor.class,
+                                                      RR.class, MethodType.methodType(void.class, R.class, R.class), "Foo", REF_newInvokeSpecial);
+
+        Extractor e = Extractor.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);
+
+        Extractor ee = Extractor.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);
+    }
+}