changeset 52911:fcd04bbc2cd2 datum

Refactoring Extractor factories, more tests
author briangoetz
date Wed, 24 Oct 2018 14:43:07 -0400
parents 9fca582660cc
children 334332456aa1 2b7b9c2711b3
files src/java.base/share/classes/java/lang/compiler/Extractor.java src/java.base/share/classes/java/lang/compiler/ExtractorCarriers.java src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java test/jdk/java/lang/compiler/ExtractorTest.java test/jdk/java/lang/compiler/boottest/TEST.properties test/jdk/java/lang/compiler/boottest/java.base/java/lang/compiler/CarrierTest.java
diffstat 6 files changed, 397 insertions(+), 341 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/compiler/Extractor.java	Tue Oct 23 23:21:26 2018 -0400
+++ b/src/java.base/share/classes/java/lang/compiler/Extractor.java	Wed Oct 24 14:43:07 2018 -0400
@@ -29,6 +29,7 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.util.stream.Stream;
 
 import sun.invoke.util.BytecodeName;
 
@@ -69,6 +70,12 @@
     MethodHandle component(int i);
 
     /**
+     * Whether this extractor might fail.
+     * @return if this extractor might fail
+     */
+    boolean isPartial();
+
+    /**
      * The descriptor of the {@linkplain Extractor}.  The parameter types of
      * the descriptor are the types of the binding variables.  The return type
      * is ignored.
@@ -100,122 +107,119 @@
         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);
+    }
 
-    /**
-     * Create an {@linkplain Extractor} from its components
-     *
-     * @param descriptor the descriptor method type
-     * @param tryMatch the {@code tryMatch} method handle
-     * @param components the {@code components} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor of(MethodType descriptor,
-                               MethodHandle tryMatch,
-                               MethodHandle... components) {
-        return new ExtractorImpl(descriptor, tryMatch, components);
+    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);
     }
 
     /**
-     * Create a lazy, self-carrier {@linkplain Extractor}
+     * 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 method type
-     * @param components the {@code component} method handles
-     * @return the {@linkplain Extractor}
+     * @param descriptor the descriptor
+     * @param digester the digester method handle
+     * @return the extractor
      */
-    public static Extractor ofLazy(MethodType descriptor,
-                                   MethodHandle... components) {
-        return of(descriptor, MethodHandles.identity(descriptor.returnType()), components);
+    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 lazy, partial, self-carrier {@linkplain Extractor}
+     * 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 method type
-     * @param components the {@code component} method handles
-     * @param predicate a {@link MethodHandle} that accepts the target and returns
-     *                  boolean, indicating whether the pattern matched
-     * @return the {@linkplain Extractor}
+     * @param descriptor the descriptor
+     * @param digester the digester method handle
+     * @return the extractor
      */
-    public static Extractor ofLazyPartial(MethodType descriptor,
-                                          MethodHandle predicate,
-                                          MethodHandle... components) {
-        Class<?> targetType = descriptor.returnType();
-        MethodHandle tryMatch = MethodHandles.guardWithTest(predicate,
-                                                            MethodHandles.identity(targetType),
-                                                            MethodHandles.dropArguments(MethodHandles.constant(targetType, null), 0, targetType));
-        return of(descriptor, tryMatch, components);
+    public static Extractor ofPartial(MethodType descriptor,
+                                      MethodHandle digester) {
+        return new ExtractorImpl(descriptor, true,
+                                 MethodHandles.insertArguments(digester,
+                                                               1, ExtractorCarriers.carrierFactory(descriptor)),
+                                 ExtractorCarriers.carrierComponents(descriptor));
     }
 
     /**
-     * Create a partial, self-carrier {@linkplain Extractor}
-     * @param descriptor the descriptor method type
-     * @param copier a {@link MethodHandle} that clones the target
-     * @param predicate a {@link MethodHandle} that accepts the target and returns
-     *                  boolean, indicating whether the pattern matched
-     * @param components the {@code component} method handles
-     * @return the {@linkplain Extractor}
+     * 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 ofPartial(MethodType descriptor,
-                                      MethodHandle copier,
-                                      MethodHandle predicate,
-                                      MethodHandle... components) {
-
-        Class<?> targetType = descriptor.returnType();
-        MethodHandle guarded = MethodHandles.guardWithTest(predicate,
-                                                           MethodHandles.identity(targetType),
-                                                           MethodHandles.dropArguments(MethodHandles.constant(targetType, null), 0, targetType));
-        MethodHandle tryMatch = MethodHandles.filterArguments(guarded, 0, copier);
-        return of(descriptor, tryMatch, components);
-    }
-
-    // target digester = (R, MH[CDESC]->Obj) -> Obj, where MH[...] is carrier ctor
-
-    /**
-     * Create a {@linkplain Extractor} using a carrier specified by a descriptor.
-     *
-     * <p>
-     *
-     * @param descriptor the extractor descriptor
-     * @param carrierFactory a method handle to create the carrier from the target
-     * @param digester a {@link MethodHandle} that accepts a target and a carrier
-     *                 factory method handle, and which calls the factory with the
-     *                 values extracted from the target
-     * @param components the {@code component} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor ofCarrier(MethodType descriptor,
-                                      MethodHandle carrierFactory,
-                                      MethodHandle digester,
-                                      MethodHandle... components) {
-        MethodHandle tryMatch = MethodHandles.insertArguments(digester, 1, carrierFactory);
-        return of(descriptor, tryMatch, components);
+    public static Extractor ofTotal(Class<?> targetType, MethodHandle... components) {
+        MethodType descriptor = descriptor(targetType, components);
+        return new ExtractorImpl(descriptor, false,
+                                 carrierTryExtract(descriptor, components),
+                                 ExtractorCarriers.carrierComponents(descriptor));
     }
 
     /**
-     * Create a {@linkplain Extractor} using a carrier specified by a 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.
      *
-     * <p>
+     * @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), false,
+                                 MethodHandles.identity(targetType), components);
+    }
+
+    /**
+     * Create a partial {@linkplain Extractor} for a given set of component
+     * method handles.
      *
-     * @param descriptor the extractor descriptor
-     * @param carrierFactory a method handle to create the carrier from the target
-     * @param digester a {@link MethodHandle} that accepts a target and a carrier
-     *                 factory method handle, and which calls the factory with the
-     *                 values extracted from the target
-     * @param predicate Predicate, applied to the carrier values, determining
-     *                  whether there was a match
-     * @param components the {@code component} method handles
-     * @return the {@linkplain Extractor}
+     * @param predicate The match predicate
+     * @param components The component method handles
+     * @return the extractor
      */
-    public static Extractor ofCarrierPartial(MethodType descriptor,
-                                             MethodHandle carrierFactory,
-                                             MethodHandle digester,
-                                             MethodHandle predicate,
-                                             MethodHandle... components) {
-        MethodHandle nuller = MethodHandles.constant(Object.class, null);
-        nuller = MethodHandles.dropArguments(nuller, 0, carrierFactory.type().parameterList());
-        MethodHandle guarded = MethodHandles.guardWithTest(predicate, carrierFactory, nuller);
-        MethodHandle tryMatch = MethodHandles.insertArguments(digester, 1, guarded);
-        return of(descriptor, tryMatch, components);
+    public static Extractor ofPartial(MethodHandle predicate, MethodHandle... components) {
+        Class<?> targetType = predicate.type().parameterType(0);
+        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));
+    }
+
+    /**
+     * Create a partial {@linkplain Extractor} for a given set of component
+     * method handles, using itself as a carrier.
+     *
+     * @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);
     }
 
     /**
@@ -232,42 +236,11 @@
     }
 
     /**
-     * Bootstrap for creating an {@linkplain Extractor} from components
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code Extractor.class}
-     * @param descriptor the descriptor method type
-     * @param tryMatch the {@code tryMatch} method handle
-     * @param components the {@code components} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor of(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                               MethodType descriptor, MethodHandle tryMatch, MethodHandle... components) {
-        return Extractor.of(descriptor, tryMatch, components);
-    }
-
-    /**
      * Bootstrap for creating a lazy, partial, self-carrier {@linkplain Extractor} from components
      *
      * @param lookup ignored
      * @param constantName ignored
-     * @param constantType Must be {@code Extractor.class}
-     * @param descriptor the descriptor method type
-     * @param components the {@code components} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor ofLazy(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                   MethodType descriptor, MethodHandle... components) {
-        return Extractor.ofLazy(descriptor, components);
-    }
-
-    /**
-     * Bootstrap for creating a lazy, partial, self-carrier {@linkplain Extractor} from components
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType doc
+     * @param constantType Must be {@code ()Extractor}
      * @param descriptor the descriptor method type
      * @param components the {@code components} method handles
      * @return a callsite
@@ -275,92 +248,7 @@
      */
     public static CallSite makeLazyExtractor(MethodHandles.Lookup lookup, String constantName, MethodType constantType,
                                              MethodType descriptor, MethodHandle... components) throws Throwable {
-        return new ConstantCallSite(MethodHandles.constant(Extractor.class, ofLazy(descriptor, components)));
-    }
-
-    /**
-     * Bootstrap for creating a lazy, partial, self-carrier {@linkplain Extractor} from components
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code Extractor.class}
-     * @param descriptor the descriptor method type
-     * @param predicate predicate method handle, applied to target
-     * @param components the {@code components} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor ofLazyPartial(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                          MethodType descriptor, MethodHandle predicate, MethodHandle... components) {
-        return ofLazyPartial(descriptor, predicate, components);
-    }
-
-    /**
-     * Bootstrap for creating a lazy, partial, self-carrier {@linkplain Extractor} from components
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code Extractor.class}
-     * @param descriptor the descriptor method type
-     * @param copier a {@link MethodHandle} that clones the target
-     * @param predicate predicate method handle, applied to target
-     * @param components the {@code components} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor ofPartial(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                      MethodType descriptor, MethodHandle copier, MethodHandle predicate, MethodHandle... components) {
-        return ofPartial(descriptor, copier, predicate, components);
-    }
-
-
-    /**
-     * Create a {@linkplain Extractor} using a carrier specified by a descriptor.
-     *
-     * <p>
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code Extractor.class}
-     * @param descriptor the extractor descriptor
-     * @param carrierFactory a method handle to create the carrier from the target
-     * @param digester a {@link MethodHandle} that accepts a target and a carrier
-     *                 factory method handle, and which calls the factory with the
-     *                 values extracted from the target
-     * @param components the {@code component} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor makeCarrierExtractor(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                                 MethodType descriptor,
-                                                 MethodHandle carrierFactory,
-                                                 MethodHandle digester,
-                                                 MethodHandle... components) {
-        return ofCarrier(descriptor, carrierFactory, digester, components);
-    }
-
-    /**
-     * Create a {@linkplain Extractor} using a carrier specified by a descriptor.
-     *
-     * <p>
-     *
-     * @param lookup ignored
-     * @param constantName ignored
-     * @param constantType Must be {@code Extractor.class}
-     * @param descriptor the extractor descriptor
-     * @param carrierFactory a method handle to create the carrier from the target
-     * @param digester a {@link MethodHandle} that accepts a target and a carrier
-     *                 factory method handle, and which calls the factory with the
-     *                 values extracted from the target
-     * @param predicate Predicate, applied to the carrier values, determining
-     *                  whether there was a match
-     * @param components the {@code component} method handles
-     * @return the {@linkplain Extractor}
-     */
-    public static Extractor makeCarrierPartialExtractor(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                                        MethodType descriptor,
-                                                        MethodHandle carrierFactory,
-                                                        MethodHandle digester,
-                                                        MethodHandle predicate,
-                                                        MethodHandle... components) {
-        return ofCarrierPartial(descriptor, carrierFactory, digester, predicate, components);
+        return new ConstantCallSite(MethodHandles.constant(Extractor.class, ofSelfTotal(descriptor.returnType(), components)));
     }
 
     /**
@@ -374,10 +262,9 @@
      * @return the extractor factory
      * @throws Throwable if something went wrong
      */
-
-    public static Extractor makeLazyExtractor(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
-                                              MethodType descriptor, MethodHandle... components) throws Throwable {
-        return ofLazy(descriptor, components);
+    public static Extractor ofSelfTotal(MethodHandles.Lookup lookup, String constantName, Class<Extractor> constantType,
+                                        MethodType descriptor, MethodHandle... components) throws Throwable {
+        return ofSelfTotal(descriptor.returnType(), components);
     }
 
 
--- a/src/java.base/share/classes/java/lang/compiler/ExtractorCarriers.java	Tue Oct 23 23:21:26 2018 -0400
+++ b/src/java.base/share/classes/java/lang/compiler/ExtractorCarriers.java	Wed Oct 24 14:43:07 2018 -0400
@@ -27,20 +27,70 @@
 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 MethodHandle CARRIER_CTOR;
-    private static final MethodHandle CARRIER_GET;
-    static {
-        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));
+
+    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();
         }
-        catch (ReflectiveOperationException e) {
-            throw new ExceptionInInitializerError(e);
+
+        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);
+            }
         }
     }
 
@@ -52,7 +102,7 @@
      * @return the carrier factory
      */
     public static MethodHandle carrierFactory(MethodType methodType) {
-        return CARRIER_CTOR.asType(methodType.changeReturnType(Object.class));
+        return factory.constructor(methodType);
     }
 
     /**
@@ -63,19 +113,17 @@
      * @return the component method handle
      */
     public static MethodHandle carrierComponent(MethodType methodType, int i) {
-        return MethodHandles.insertArguments(CARRIER_GET, 1, i)
-                .asType(MethodType.methodType(methodType.parameterType(i), Object.class));
+        return factory.component(methodType, i);
     }
 
-    static class DumbCarrier {
-        Object[] args;
-
-        DumbCarrier(Object... args) {
-            this.args = args.clone();
-        }
-
-        Object get(int i) {
-            return args[i];
-        }
+    /**
+     * Return all the components method handles for a carrier
+     * @param methodType the type of the carrier elements
+     * @return the component method handles
+     */
+    public static MethodHandle[] carrierComponents(MethodType methodType) {
+        MethodHandle[] components = new MethodHandle[methodType.parameterCount()];
+        Arrays.setAll(components, i -> factory.component(methodType, i));
+        return components;
     }
 }
--- a/src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java	Tue Oct 23 23:21:26 2018 -0400
+++ b/src/java.base/share/classes/java/lang/compiler/ExtractorImpl.java	Wed Oct 24 14:43:07 2018 -0400
@@ -32,9 +32,10 @@
  * 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;
-    private final MethodType descriptor;
 
     /**
      * Construct an {@link Extractor} from components
@@ -47,7 +48,7 @@
      * @param tryMatch The {@code tryMatch} method handle
      * @param components The {@code component} method handles
      */
-    ExtractorImpl(MethodType descriptor, MethodHandle tryMatch, MethodHandle[] components) {
+    ExtractorImpl(MethodType descriptor, boolean partial, 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));
@@ -64,6 +65,7 @@
         }
 
         this.descriptor = descriptor;
+        this.partial = partial;
         this.tryMatch = tryMatch;
         this.components = List.of(components);
     }
@@ -82,4 +84,9 @@
     public MethodType descriptor() {
         return descriptor;
     }
+
+    @Override
+    public boolean isPartial() {
+        return partial;
+    }
 }
--- a/test/jdk/java/lang/compiler/ExtractorTest.java	Tue Oct 23 23:21:26 2018 -0400
+++ b/test/jdk/java/lang/compiler/ExtractorTest.java	Wed Oct 24 14:43:07 2018 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -22,11 +22,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 import java.lang.compiler.Extractor;
-import java.lang.compiler.PatternCarriers;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.util.Objects;
 
 import org.testng.annotations.Test;
 
@@ -40,59 +41,42 @@
 /**
  * @test
  * @run testng ExtractorTest
- * @summary unit tests for java.lang.compiler.Extractor
+ * @summary Smoke tests for java.lang.compiler.Extractor
  */
 @Test
 public class ExtractorTest {
 
-    private Object[] extract(Extractor extractor, Object target) throws Throwable {
-        int count = extractor.descriptor().parameterCount();
-        Object[] result = new Object[count + 1];
-        Object carrier = extractor.tryMatch().invoke(target);
-        if (carrier == null)
-            return null;
-        for (int i=0; i<count; i++)
-            result[i] = extractor.component(i).invoke(carrier);
-        result[count] = carrier;
-        return result;
-    }
+    private enum MatchKind { CARRIER, SELF, FAIL, MATCH }
 
-    private void assertExtracted(Object[] result, Object... args) {
-        assertNotNull(result);
-        assertEquals(result.length - 1, args.length);
-        for (int i = 0; i < args.length; i++) {
-            assertEquals(result[i], args[i]);
+    private void assertMatches(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);
+        if (carrier != null) {
+            for (int i = 0; i < count; i++)
+                bindings[i] = e.component(i).invoke(carrier);
+        }
+
+        if (kind == MatchKind.FAIL)
+            assertNull(carrier);
+        else {
+            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 void testExtractLazy(Extractor e, Object target, Object... args) throws Throwable {
-        Object[] result = extract(e, target);
-        assertExtracted(result, args);
-        assertSame(carrier(result), target);
-    }
-
-    private void testExtractEager(Extractor e, Object target, Object... args) throws Throwable {
-        Object[] result = extract(e, target);
-        assertExtracted(result, args);
-        assertNotSame(carrier(result), target);
-    }
-
-    private void assertExtractFail(Extractor e, Object target) throws Throwable {
-        Object[] result = extract(e, target);
-        assertNull(result);
-    }
-
-    private Object carrier(Object[] result) {
-        return result[result.length-1];
-    }
-
     private static class TestClass {
-        static MethodHandle MH_S, MH_I, MH_L, MH_B;
+        static MethodHandle MH_S, MH_I, MH_L, MH_B, MH_PRED;
         static MethodHandle CONSTRUCTOR;
-        static MethodHandle COPIER;
         static MethodHandle DIGESTER;
-        static MethodHandle NON_NULL;
-        static MethodHandle NON_NULL_EXPLODED;
+        static MethodHandle DIGESTER_PARTIAL;
         static MethodType TYPE = MethodType.methodType(TestClass.class, String.class, int.class, long.class, byte.class);
         static {
             try {
@@ -100,11 +84,10 @@
                 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));
-                COPIER = MethodHandles.lookup().findVirtual(TestClass.class, "copy", MethodType.methodType(TestClass.class));
-                NON_NULL = MethodHandles.lookup().findVirtual(TestClass.class, "test", MethodType.methodType(boolean.class));
-                NON_NULL_EXPLODED = MethodHandles.lookup().findStatic(TestClass.class, "testExploded", MethodType.methodType(boolean.class, String.class, int.class, long.class, byte.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);
@@ -116,7 +99,7 @@
         long l;
         byte b;
 
-        public TestClass(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;
@@ -127,81 +110,91 @@
             return new TestClass(s, i, l, b);
         }
 
-        boolean test() {
-            return s != null;
-        }
-
-        static boolean testExploded(String s, int i, long l, byte b) {
-            return s != null;
-        }
+        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);
+        }
     }
 
-    static final MethodHandle[] COMPONENTS = { TestClass.MH_S, TestClass.MH_I, TestClass.MH_L, TestClass.MH_B };
-    static final MethodHandle CARRIER_FACTORY = PatternCarriers.carrierFactory(TestClass.TYPE);
-    static final MethodHandle[] CARRIER_COMPONENTS = { PatternCarriers.carrierComponent(TestClass.TYPE, 0),
-                                                       PatternCarriers.carrierComponent(TestClass.TYPE, 1),
-                                                       PatternCarriers.carrierComponent(TestClass.TYPE, 2),
-                                                       PatternCarriers.carrierComponent(TestClass.TYPE, 3) };
+    private static final MethodHandle[] COMPONENTS = {TestClass.MH_S, TestClass.MH_I, TestClass.MH_L, TestClass.MH_B };
 
-    public void testLazySelfTotal() throws Throwable {
-        Extractor e = Extractor.ofLazy(TestClass.TYPE, COMPONENTS);
-        testExtractLazy(e, new TestClass("foo", 3, 4L, (byte) 5),
-                        "foo", 3, 4L, (byte) 5);
-        testExtractLazy(e, new TestClass(null, 0, 0L, (byte) 0),
-                        null, 0, 0L, (byte) 0);
+    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);
     }
 
-    public void testEagerSelfTotal() throws Throwable {
-        Extractor e = Extractor.of(TestClass.TYPE, TestClass.COPIER, COMPONENTS);
-        testExtractEager(e, new TestClass("foo", 3, 4L, (byte) 5),
-                        "foo", 3, 4L, (byte) 5);
-        testExtractEager(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);
     }
 
-    public void testLazySelfPartial() throws Throwable {
-        Extractor e = Extractor.ofLazyPartial(TestClass.TYPE, TestClass.NON_NULL, COMPONENTS);
-        testExtractLazy(e, new TestClass("foo", 3, 4L, (byte) 5),
-                        "foo", 3, 4L, (byte) 5);
-        assertExtractFail(e, new TestClass(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));
     }
 
-    public void testEagerSelfPartial() throws Throwable {
-        Extractor e = Extractor.ofPartial(TestClass.TYPE, TestClass.COPIER, TestClass.NON_NULL, COMPONENTS);
-        testExtractEager(e, new TestClass("foo", 3, 4L, (byte) 5),
-                        "foo", 3, 4L, (byte) 5);
-        assertExtractFail(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));
     }
 
-    public void testCarrierTotal() throws Throwable {
-        Extractor e = Extractor.ofCarrier(TestClass.TYPE, CARRIER_FACTORY, TestClass.DIGESTER, CARRIER_COMPONENTS);
-        testExtractEager(e, new TestClass("foo", 3, 4L, (byte) 5),
-                        "foo", 3, 4L, (byte) 5);
-        testExtractEager(e, new TestClass(null, 0, 0L, (byte) 0),
-                        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);
     }
 
-    public void testCarrierPartial() throws Throwable {
-        Extractor e = Extractor.ofCarrierPartial(TestClass.TYPE, CARRIER_FACTORY, TestClass.DIGESTER, TestClass.NON_NULL_EXPLODED, CARRIER_COMPONENTS);
-        testExtractEager(e, new TestClass("foo", 3, 4L, (byte) 5),
-                        "foo", 3, 4L, (byte) 5);
-        assertExtractFail(e, new TestClass(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));
     }
 
-    public void testClamshell() throws Throwable {
-        Extractor e = Extractor.ofCarrier(TestClass.TYPE, CARRIER_FACTORY, TestClass.DIGESTER, CARRIER_COMPONENTS);
+    public void testCompose() throws Throwable {
+        Extractor e = Extractor.ofTotal(TestClass.class, COMPONENTS);
         MethodHandle mh = e.compose(TestClass.CONSTRUCTOR);
         TestClass target = new TestClass("foo", 3, 4L, (byte) 5);
         Object o = mh.invoke(target);
         assertTrue(o instanceof TestClass);
-        TestClass copy = (TestClass) o;
-        assertEquals(target.s, copy.s);
-        assertEquals(target.i, copy.i);
-        assertEquals(target.l, copy.l);
-        assertEquals(target.b, copy.b);
+        assertNotSame(target, o);
+        assertEquals(target, o);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/compiler/boottest/TEST.properties	Wed Oct 24 14:43:07 2018 -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/compiler/boottest/java.base/java/lang/compiler/CarrierTest.java	Wed Oct 24 14:43:07 2018 -0400
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang.compiler;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @test
+ * @key randomness
+ * @run testng CarrierTest
+ * @summary unit tests for java.lang.compiler.ExtractorCarriers
+ */
+@Test
+public class CarrierTest {
+    static final int N_ITER = 1000;
+    static final Class<?>[] TYPES = { byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class, Object.class };
+
+    static Object[] byteVals = { Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) -1, (byte) 0, (byte) 1, (byte) 42 };
+    static Object[] shortVals = { Short.MIN_VALUE, Short.MAX_VALUE, (short) -1, (short) 0, (short) 1, (short) 42 };
+    static Object[] charVals = { Character.MIN_VALUE, Character.MAX_VALUE, (char) 0, 'a', 'Z' };
+    static Object[] intVals = { Integer.MIN_VALUE, Integer.MAX_VALUE, -1, 0, 1, 42 };
+    static Object[] longVals = { Long.MIN_VALUE, Long.MAX_VALUE, -1L, 0L, 1L, 42L };
+    static Object[] floatVals = { Float.MIN_VALUE, Float.MAX_VALUE, -1.0f, 0.0f, 1.0f, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN };
+    static Object[] doubleVals = { Double.MIN_VALUE, Double.MAX_VALUE, -1.0d, 0.0d, 1.0d, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN };
+    static Object[] booleanVals = { true, false };
+    static Object[] objectVals = {null, "", "Foo", "foo", List.of(), Collections.EMPTY_SET };
+
+    // @@@ Should use RandomFactory, but can't get that to link
+    private static final Random random = new Random(System.nanoTime());
+
+    static Map<Class<?>, Object[]> primVals = Map.of(byte.class, byteVals,
+                                                     short.class, shortVals,
+                                                     char.class, charVals,
+                                                     int.class, intVals,
+                                                     long.class, longVals,
+                                                     float.class, floatVals,
+                                                     double.class, doubleVals,
+                                                     boolean.class, booleanVals);
+
+    void testCarrier(MethodType type, Object[] values) throws Throwable {
+        for (ExtractorCarriers.CarrierFactory cf : ExtractorCarriers.CarrierFactories.values()) {
+            assertEquals(type.parameterCount(), values.length);
+            Object carrier = cf.constructor(type).invokeWithArguments(values);
+            for (int i = 0; i < values.length; i++)
+                assertEquals(values[i], cf.component(type, i).invoke(carrier));
+        }
+    }
+
+    void testCarrier(MethodType type) throws Throwable {
+        // generate data, in a loop
+        for (int i=0; i<N_ITER; i++) {
+            Object[] values = new Object[type.parameterCount()];
+            for (int j=0; j<type.parameterCount(); j++) {
+                Class<?> c = type.parameterType(j);
+                Object[] vals = c.isPrimitive() ? primVals.get(c) : objectVals;
+                values[j] = vals[random.nextInt(vals.length)];
+            }
+            testCarrier(type, values);
+        }
+    }
+
+    public void testCarrier() throws Throwable {
+        Class[] lotsOfInts = new Class[252];
+        Arrays.fill(lotsOfInts, int.class);
+
+        // known types
+        for (MethodType mt : List.of(
+                MethodType.methodType(Object.class),
+                MethodType.methodType(Object.class, int.class),
+                MethodType.methodType(Object.class, int.class, int.class),
+                MethodType.methodType(Object.class, Object.class),
+                MethodType.methodType(Object.class, Object.class, Object.class),
+                MethodType.methodType(Object.class, byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class),
+                MethodType.methodType(Object.class, lotsOfInts))) {
+            testCarrier(mt);
+        }
+
+        // random types
+        for (int i=0; i<N_ITER; i++) {
+            int nTypes = random.nextInt(10);
+            Class[] paramTypes = new Class[nTypes];
+            Arrays.setAll(paramTypes, ix -> TYPES[random.nextInt(TYPES.length)]);
+            testCarrier(MethodType.methodType(Object.class, paramTypes));
+        }
+    }
+}