Add MethodHandle.asSpreader(int, Class<?>, int) from OpenJDK 11.0.13

Bug: 191446452
Test: atest CtsLibcoreOjTestCases:java.lang.invoke.SpreadCollectTest
Change-Id: I75aea926d58e453081dd4e54930f7d1e769e8eee
diff --git a/api/current.txt b/api/current.txt
index 167e5bc..835ae00 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -4293,6 +4293,7 @@
     method public java.lang.invoke.MethodHandle asCollector(int, Class<?>, int);
     method public java.lang.invoke.MethodHandle asFixedArity();
     method public java.lang.invoke.MethodHandle asSpreader(Class<?>, int);
+    method public java.lang.invoke.MethodHandle asSpreader(int, Class<?>, int);
     method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
     method public java.lang.invoke.MethodHandle asVarargsCollector(Class<?>);
     method public java.lang.invoke.MethodHandle bindTo(Object);
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandle.java b/ojluni/src/main/java/java/lang/invoke/MethodHandle.java
index 10282b8..cc82f54 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodHandle.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandle.java
@@ -896,9 +896,13 @@
      * to the target method handle.
      * (The array may also be null when zero elements are required.)
      * <p>
-     * If, when the adapter is called, the supplied array argument does
-     * not have the correct number of elements, the adapter will throw
-     * an {@link IllegalArgumentException} instead of invoking the target.
+     * When the adapter is called, the length of the supplied {@code array}
+     * argument is queried as if by {@code array.length} or {@code arraylength}
+     * bytecode. If the adapter accepts a zero-length trailing array argument,
+     * the supplied {@code array} argument can either be a zero-length array or
+     * {@code null}; otherwise, the adapter will throw a {@code NullPointerException}
+     * if the array is {@code null} and throw an {@link IllegalArgumentException}
+     * if the array does not have the correct number of elements.
      * <p>
      * Here are some simple examples of array-spreading method handles:
      * <blockquote><pre>{@code
@@ -912,7 +916,7 @@
 assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
 // try to spread from anything but a 2-array:
 for (int n = 0; n <= 10; n++) {
-  Object[] badArityArgs = (n == 2 ? null : new Object[n]);
+  Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]);
   try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
   catch (IllegalArgumentException ex) { } // OK
 }
@@ -959,23 +963,63 @@
      * @see #asCollector
      */
     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
-        MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
+        return asSpreader(type().parameterCount() - arrayLength, arrayType, arrayLength);
+    }
+
+    /**
+     * Makes an <em>array-spreading</em> method handle, which accepts an array argument at a given position and spreads
+     * its elements as positional arguments in place of the array. The new method handle adapts, as its <i>target</i>,
+     * the current method handle. The type of the adapter will be the same as the type of the target, except that the
+     * {@code arrayLength} parameters of the target's type, starting at the zero-based position {@code spreadArgPos},
+     * are replaced by a single array parameter of type {@code arrayType}.
+     * <p>
+     * This method behaves very much like {@link #asSpreader(Class, int)}, but accepts an additional {@code spreadArgPos}
+     * argument to indicate at which position in the parameter list the spreading should take place.
+     *
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+    MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+    MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+    Object[] ints = new Object[]{3, 9, 7, 7};
+    Comparator<Integer> cmp = (a, b) -> a - b;
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+     * }</pre></blockquote>
+     * @param spreadArgPos the position (zero-based index) in the argument list at which spreading should start.
+     * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments
+     * @param arrayLength the number of arguments to spread from an incoming array argument
+     * @return a new method handle which spreads an array argument at a given position,
+     *         before calling the original method handle
+     * @throws NullPointerException if {@code arrayType} is a null reference
+     * @throws IllegalArgumentException if {@code arrayType} is not an array type,
+     *         or if target does not have at least
+     *         {@code arrayLength} parameter types,
+     *         or if {@code arrayLength} is negative,
+     *         or if {@code spreadArgPos} has an illegal value (negative, or together with arrayLength exceeding the
+     *         number of arguments),
+     *         or if the resulting method handle's type would have
+     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
+     * @throws WrongMethodTypeException if the implied {@code asType} call fails
+     *
+     * @see #asSpreader(Class, int)
+     * @since 9
+     */
+    public MethodHandle asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength) {
+        MethodType postSpreadType = asSpreaderChecks(arrayType, spreadArgPos, arrayLength);
         // BEGIN Android-changed: Android specific implementation.
         /*
-        int arity = type().parameterCount();
-        int spreadArgPos = arity - arrayLength;
         MethodHandle afterSpread = this.asType(postSpreadType);
         BoundMethodHandle mh = afterSpread.rebind();
         LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength);
-        MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType);
+        MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, spreadArgPos + arrayLength, arrayType);
         return mh.copyWith(preSpreadType, lform);
         */
-        final int targetParamCount = postSpreadType.parameterCount();
-        MethodType dropArrayArgs = postSpreadType.dropParameterTypes(
-                (targetParamCount - arrayLength), targetParamCount);
-        MethodType adapterType = dropArrayArgs.appendParameterTypes(arrayType);
-
-        return new Transformers.Spreader(this, adapterType, arrayLength);
+        final int spreadEnd = spreadArgPos + arrayLength;
+        final MethodType adapterType =
+                postSpreadType.replaceParameterTypes(spreadArgPos, spreadEnd, arrayType);
+        return new Transformers.Spreader(
+                asType(postSpreadType), adapterType, spreadArgPos, arrayLength);
         // END Android-changed: Android specific implementation.
     }
 
@@ -983,15 +1027,18 @@
      * See if {@code asSpreader} can be validly called with the given arguments.
      * Return the type of the method handle call after spreading but before conversions.
      */
-    private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+    private MethodType asSpreaderChecks(Class<?> arrayType, int pos, int arrayLength) {
         spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
         if (nargs < arrayLength || arrayLength < 0)
             throw newIllegalArgumentException("bad spread array length");
+        if (pos < 0 || pos + arrayLength > nargs) {
+            throw newIllegalArgumentException("bad spread position");
+        }
         Class<?> arrayElement = arrayType.getComponentType();
         MethodType mtype = type();
         boolean match = true, fail = false;
-        for (int i = nargs - arrayLength; i < nargs; i++) {
+        for (int i = pos; i < pos + arrayLength; i++) {
             Class<?> ptype = mtype.parameterType(i);
             if (ptype != arrayElement) {
                 match = false;
@@ -1002,11 +1049,11 @@
             }
         }
         if (match)  return mtype;
-        MethodType needType = mtype.asSpreaderType(arrayType, arrayLength);
+        MethodType needType = mtype.asSpreaderType(arrayType, pos, arrayLength);
         if (!fail)  return needType;
         // elicit an error:
         this.asType(needType);
-        throw newInternalError("should not return", null);
+        throw newInternalError("should not return");
     }
 
     private void spreadArrayChecks(Class<?> arrayType, int arrayLength) {
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodType.java b/ojluni/src/main/java/java/lang/invoke/MethodType.java
index 0a35f45..cf92c7d 100644
--- a/ojluni/src/main/java/java/lang/invoke/MethodType.java
+++ b/ojluni/src/main/java/java/lang/invoke/MethodType.java
@@ -473,12 +473,13 @@
 
     /** Replace the last arrayLength parameter types with the component type of arrayType.
      * @param arrayType any array type
+     * @param pos position at which to spread
      * @param arrayLength the number of parameter types to change
      * @return the resulting type
      */
-    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int pos, int arrayLength) {
         assert(parameterCount() >= arrayLength);
-        int spreadPos = ptypes.length - arrayLength;
+        int spreadPos = pos;
         if (arrayLength == 0)  return this;  // nothing to change
         if (arrayType == Object[].class) {
             if (isGeneric())  return this;  // nothing to change
@@ -493,10 +494,10 @@
         }
         Class<?> elemType = arrayType.getComponentType();
         assert(elemType != null);
-        for (int i = spreadPos; i < ptypes.length; i++) {
+        for (int i = spreadPos; i < spreadPos + arrayLength; i++) {
             if (ptypes[i] != elemType) {
                 Class<?>[] fixedPtypes = ptypes.clone();
-                Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
+                Arrays.fill(fixedPtypes, i, spreadPos + arrayLength, elemType);
                 return methodType(rtype, fixedPtypes);
             }
         }
diff --git a/ojluni/src/main/java/java/lang/invoke/Transformers.java b/ojluni/src/main/java/java/lang/invoke/Transformers.java
index 2e3451d..f73afbe1 100644
--- a/ojluni/src/main/java/java/lang/invoke/Transformers.java
+++ b/ojluni/src/main/java/java/lang/invoke/Transformers.java
@@ -1315,8 +1315,8 @@
          */
         private final int arrayOffset;
 
-        /** The type char of the component type of the array. */
-        private final char arrayTypeChar;
+        /** The component type of the array. */
+        private final Class<?> componentType;
 
         /**
          * The number of input arguments that will be present in the array. In other words, this is
@@ -1328,335 +1328,133 @@
          * Range of arguments to copy verbatim from the input frame, This will cover all arguments
          * that aren't a part of the trailing array.
          */
-        private final Range copyRange;
+        private final Range leadingRange;
+        private final Range trailingRange;
 
-        Spreader(MethodHandle target, MethodType spreaderType, int numArrayArgs) {
+        Spreader(MethodHandle target, MethodType spreaderType, int spreadArgPos, int numArrayArgs) {
             super(spreaderType);
             this.target = target;
-            // Copy all arguments except the last argument (which is the trailing array argument
-            // that needs to be spread).
-            arrayOffset = spreaderType.parameterCount() - 1;
-
-            // Get and cache the component type of the input array.
-            final Class<?> componentType = spreaderType.ptypes()[arrayOffset].getComponentType();
+            arrayOffset = spreadArgPos;
+            componentType = spreaderType.ptypes()[arrayOffset].getComponentType();
             if (componentType == null) {
-                throw new AssertionError("Trailing argument must be an array.");
+                throw new AssertionError("Argument " + spreadArgPos + " must be an array.");
             }
-            arrayTypeChar = Wrapper.basicTypeChar(componentType);
-
             this.numArrayArgs = numArrayArgs;
-            // Copy all args except for the last argument.
-            this.copyRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset);
+            // Copy all args except the spreader array.
+            leadingRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset);
+            trailingRange = EmulatedStackFrame.Range.from(spreaderType, arrayOffset + 1);
         }
 
         @Override
         public void transform(EmulatedStackFrame callerFrame) throws Throwable {
-            // Create a new stack frame for the callee.
-            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
-
-            // Copy all arguments except for the trailing array argument.
-            callerFrame.copyRangeTo(targetFrame, copyRange, 0, 0);
-
-            // Attach the writer, prepare to spread the trailing array arguments into
-            // the callee frame.
-            StackFrameWriter writer = new StackFrameWriter();
-            writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
-
             // Get the array reference and check that its length is as expected.
-            Object arrayObj =
-                    callerFrame.getReference(
-                            copyRange.numReferences, this.type().ptypes()[arrayOffset]);
+            final Class<?> arrayType = type().parameterType(arrayOffset);
+            final Object arrayObj = callerFrame.getReference(arrayOffset, arrayType);
             final int arrayLength = Array.getLength(arrayObj);
             if (arrayLength != numArrayArgs) {
                 throw new IllegalArgumentException(
                         "Invalid array length " + arrayLength + " expected " + numArrayArgs);
             }
 
-            final MethodType type = target.type();
-            switch (arrayTypeChar) {
+            // Create a new stack frame for the callee.
+            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
+
+            // Copy ranges not affected by the spreading.
+            callerFrame.copyRangeTo(targetFrame, leadingRange, 0, 0);
+            if (componentType.isPrimitive()) {
+                final int elementBytes = EmulatedStackFrame.getSize(componentType);
+                final int spreadBytes = elementBytes * arrayLength;
+                callerFrame.copyRangeTo(targetFrame, trailingRange,
+                    leadingRange.numReferences, leadingRange.numBytes + spreadBytes);
+            } else {
+                callerFrame.copyRangeTo(targetFrame, trailingRange,
+                    leadingRange.numReferences + numArrayArgs, leadingRange.numBytes);
+            }
+
+            // Attach the writer, prepare to spread the trailing array arguments into
+            // the callee frame.
+            StackFrameWriter writer = new StackFrameWriter();
+            writer.attach(targetFrame, arrayOffset, leadingRange.numReferences, leadingRange.numBytes);
+
+            final Class<?> componentType = arrayType.getComponentType();
+            switch (Wrapper.basicTypeChar(componentType)) {
                 case 'L':
-                    spreadArray((Object[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final Object[] array = (Object[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextReference(array[i], componentType);
+                    }
                     break;
+                }
                 case 'I':
-                    spreadArray((int[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final int[] array = (int[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextInt(array[i]);
+                    }
                     break;
+                }
                 case 'J':
-                    spreadArray((long[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final long[] array = (long[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextLong(array[i]);
+                    }
                     break;
+                }
                 case 'B':
-                    spreadArray((byte[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final byte[] array = (byte[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextByte(array[i]);
+                    }
                     break;
+                }
                 case 'S':
-                    spreadArray((short[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final short[] array = (short[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextShort(array[i]);
+                    }
                     break;
+                }
                 case 'C':
-                    spreadArray((char[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final char[] array = (char[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextChar(array[i]);
+                    }
                     break;
+                }
                 case 'Z':
-                    spreadArray((boolean[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final boolean[] array = (boolean[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextBoolean(array[i]);
+                    }
                     break;
+                }
                 case 'F':
-                    spreadArray((float[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final float[] array = (float[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextFloat(array[i]);
+                    }
                     break;
+                }
                 case 'D':
-                    spreadArray((double[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
+                {
+                    final double[] array = (double[]) arrayObj;
+                    for (int i = 0; i < array.length; ++i) {
+                        writer.putNextDouble(array[i]);
+                    }
                     break;
+                }
             }
 
-            invokeFromTransform(target, targetFrame);
+            invokeExactFromTransform(target, targetFrame);
             targetFrame.copyReturnValueTo(callerFrame);
         }
-
-        public static void spreadArray(
-                Object[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                Object o = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(o, argumentType);
-                        break;
-                    case 'I':
-                        writer.putNextInt((int) o);
-                        break;
-                    case 'J':
-                        writer.putNextLong((long) o);
-                        break;
-                    case 'B':
-                        writer.putNextByte((byte) o);
-                        break;
-                    case 'S':
-                        writer.putNextShort((short) o);
-                        break;
-                    case 'C':
-                        writer.putNextChar((char) o);
-                        break;
-                    case 'Z':
-                        writer.putNextBoolean((boolean) o);
-                        break;
-                    case 'F':
-                        writer.putNextFloat((float) o);
-                        break;
-                    case 'D':
-                        writer.putNextDouble((double) o);
-                        break;
-                }
-            }
-        }
-
-        public static void spreadArray(
-                int[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                int j = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(j, argumentType);
-                        break;
-                    case 'I':
-                        writer.putNextInt(j);
-                        break;
-                    case 'J':
-                        writer.putNextLong(j);
-                        break;
-                    case 'F':
-                        writer.putNextFloat(j);
-                        break;
-                    case 'D':
-                        writer.putNextDouble(j);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                long[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                long l = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(l, argumentType);
-                        break;
-                    case 'J':
-                        writer.putNextLong(l);
-                        break;
-                    case 'F':
-                        writer.putNextFloat((float) l);
-                        break;
-                    case 'D':
-                        writer.putNextDouble((double) l);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                byte[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                byte b = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(b, argumentType);
-                        break;
-                    case 'I':
-                        writer.putNextInt(b);
-                        break;
-                    case 'J':
-                        writer.putNextLong(b);
-                        break;
-                    case 'B':
-                        writer.putNextByte(b);
-                        break;
-                    case 'S':
-                        writer.putNextShort(b);
-                        break;
-                    case 'F':
-                        writer.putNextFloat(b);
-                        break;
-                    case 'D':
-                        writer.putNextDouble(b);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                short[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                short s = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(s, argumentType);
-                        break;
-                    case 'I':
-                        writer.putNextInt(s);
-                        break;
-                    case 'J':
-                        writer.putNextLong(s);
-                        break;
-                    case 'S':
-                        writer.putNextShort(s);
-                        break;
-                    case 'F':
-                        writer.putNextFloat(s);
-                        break;
-                    case 'D':
-                        writer.putNextDouble(s);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                char[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                char c = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(c, argumentType);
-                        break;
-                    case 'I':
-                        writer.putNextInt(c);
-                        break;
-                    case 'J':
-                        writer.putNextLong(c);
-                        break;
-                    case 'C':
-                        writer.putNextChar(c);
-                        break;
-                    case 'F':
-                        writer.putNextFloat(c);
-                        break;
-                    case 'D':
-                        writer.putNextDouble(c);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                boolean[] array,
-                StackFrameWriter writer,
-                MethodType type,
-                int numArgs,
-                int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                boolean z = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(z, argumentType);
-                        break;
-                    case 'Z':
-                        writer.putNextBoolean(z);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                double[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                double d = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(d, argumentType);
-                        break;
-                    case 'D':
-                        writer.putNextDouble(d);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public static void spreadArray(
-                float[] array, StackFrameWriter writer, MethodType type, int numArgs, int offset) {
-            final Class<?>[] ptypes = type.ptypes();
-            for (int i = 0; i < numArgs; ++i) {
-                Class<?> argumentType = ptypes[i + offset];
-                float f = array[i];
-                switch (Wrapper.basicTypeChar(argumentType)) {
-                    case 'L':
-                        writer.putNextReference(f, argumentType);
-                        break;
-                    case 'D':
-                        writer.putNextDouble((double) f);
-                        break;
-                    case 'F':
-                        writer.putNextFloat(f);
-                        break;
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
     }
 
     /** Implements MethodHandle.asCollector. */
diff --git a/ojluni/src/test/java/lang/invoke/SpreadCollectTest.java b/ojluni/src/test/java/lang/invoke/SpreadCollectTest.java
new file mode 100644
index 0000000..78332d6
--- /dev/null
+++ b/ojluni/src/test/java/lang/invoke/SpreadCollectTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ */
+
+/* @test
+ * @bug 8139885
+ * @bug 8143798
+ * @run testng/othervm -ea -esa test.java.lang.invoke.SpreadCollectTest
+ */
+
+package test.java.lang.invoke;
+
+import java.io.StringWriter;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.util.*;
+
+import static java.lang.invoke.MethodType.methodType;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+/**
+ * Tests for the new asSpreader/asCollector API added in JEP 274.
+ */
+public class SpreadCollectTest {
+
+    static final Lookup LOOKUP = MethodHandles.lookup();
+
+    @Test
+    public static void testAsSpreader() throws Throwable {
+        MethodHandle spreader = SpreadCollect.MH_forSpreading.asSpreader(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_spreader, spreader.type());
+        assertEquals("A456B", (String) spreader.invoke("A", new int[]{4, 5, 6}, "B"));
+    }
+
+    @Test
+    public static void testAsSpreaderExample() throws Throwable {
+        // test the JavaDoc asSpreader-with-pos example
+        MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+        MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+        Object[] ints = new Object[]{3, 9, 7, 7};
+        Comparator<Integer> cmp = (a, b) -> a - b;
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+    }
+
+    @DataProvider
+    static Object[][] asSpreaderIllegalPositions() {
+        return new Object[][]{{-7}, {3}, {19}};
+    }
+
+    @Test(dataProvider = "asSpreaderIllegalPositions")
+    public static void testAsSpreaderIllegalPos(int p) throws Throwable {
+        boolean caught = false;
+        try {
+            SpreadCollect.MH_forSpreading.asSpreader(p, Object[].class, 3);
+        } catch (IllegalArgumentException iae) {
+            assertEquals("bad spread position", iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    @Test(expectedExceptions = {WrongMethodTypeException.class})
+    public static void testAsSpreaderIllegalMethodType() {
+        MethodHandle h = MethodHandles.dropArguments(MethodHandles.constant(String.class, ""), 0, int.class, int.class);
+        MethodHandle s = h.asSpreader(String[].class, 1);
+    }
+
+    @Test(expectedExceptions = {NullPointerException.class})
+    public static void testAsSpreaderNullArrayType() {
+        SpreadCollect.MH_forSpreading.asSpreader(null, 0);
+    }
+
+    @Test(expectedExceptions = {NullPointerException.class})
+    public static void testAsSpreaderNullArrayNonZeroLength() {
+        SpreadCollect.MH_forSpreading.asSpreader(null, 1);
+    }
+
+    @Test(expectedExceptions = {IllegalArgumentException.class})
+    public static void testAsSpreaderTooManyParams() throws Throwable {
+        SpreadCollect.MH_forSpreading.asSpreader(1, int[].class, 6);
+    }
+
+    @Test
+    public static void testAsCollector() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collector1, collector.type());
+        assertEquals("A4B", (String) collector.invoke("A", 4, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collector2, collector.type());
+        assertEquals("A45B", (String) collector.invoke("A", 4, 5, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collector3, collector.type());
+        assertEquals("A456B", (String) collector.invoke("A", 4, 5, 6, "B"));
+    }
+
+    @Test
+    public static void testAsCollectorInvokeWithArguments() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collector1, collector.type());
+        assertEquals("A4B", (String) collector.invokeWithArguments("A", 4, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collector2, collector.type());
+        assertEquals("A45B", (String) collector.invokeWithArguments("A", 4, 5, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collector3, collector.type());
+        assertEquals("A456B", (String) collector.invokeWithArguments("A", 4, 5, 6, "B"));
+    }
+
+    @Test
+    public static void testAsCollectorLeading() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collectorLeading1, collector.type());
+        assertEquals("7Q", (String) collector.invoke(7, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collectorLeading2, collector.type());
+        assertEquals("78Q", (String) collector.invoke(7, 8, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collectorLeading3, collector.type());
+        assertEquals("789Q", (String) collector.invoke(7, 8, 9, "Q"));
+    }
+
+    @Test
+    public static void testAsCollectorLeadingInvokeWithArguments() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collectorLeading1, collector.type());
+        assertEquals("7Q", (String) collector.invokeWithArguments(7, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collectorLeading2, collector.type());
+        assertEquals("78Q", (String) collector.invokeWithArguments(7, 8, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collectorLeading3, collector.type());
+        assertEquals("789Q", (String) collector.invokeWithArguments(7, 8, 9, "Q"));
+    }
+
+    @Test
+    public static void testAsCollectorNone() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 0);
+        assertEquals(SpreadCollect.MT_collector0, collector.type());
+        assertEquals("AB", (String) collector.invoke("A", "B"));
+    }
+
+    @DataProvider
+    static Object[][] asCollectorIllegalPositions() {
+        return new Object[][]{{-1}, {17}};
+    }
+
+    @Test(dataProvider = "asCollectorIllegalPositions")
+    public static void testAsCollectorIllegalPos(int p) {
+        boolean caught = false;
+        try {
+            SpreadCollect.MH_forCollecting.asCollector(p, int[].class, 0);
+        } catch (IllegalArgumentException iae) {
+            assertEquals("bad collect position", iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    @Test
+    public static void testAsCollectorExample() throws Throwable {
+        // test the JavaDoc asCollector-with-pos example
+        StringWriter swr = new StringWriter();
+        MethodHandle swWrite = LOOKUP.
+                findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).
+                bindTo(swr);
+        MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+        swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+        assertEquals("BC", swr.toString());
+        swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+        assertEquals("BCPQRS", swr.toString());
+        swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+        assertEquals("BCPQRSZ", swr.toString());
+    }
+
+    static class SpreadCollect {
+
+        static String forSpreading(String s1, int i1, int i2, int i3, String s2) {
+            return s1 + i1 + i2 + i3 + s2;
+        }
+
+        static String forCollecting(String s1, int[] is, String s2) {
+            StringBuilder sb = new StringBuilder(s1);
+            for (int i : is) {
+                sb.append(i);
+            }
+            return sb.append(s2).toString();
+        }
+
+        static String forCollectingLeading(int[] is, String s) {
+            return forCollecting("", is, s);
+        }
+
+        static final Class<SpreadCollect> SPREAD_COLLECT = SpreadCollect.class;
+
+        static final MethodType MT_forSpreading = methodType(String.class, String.class, int.class, int.class, int.class, String.class);
+        static final MethodType MT_forCollecting = methodType(String.class, String.class, int[].class, String.class);
+        static final MethodType MT_forCollectingLeading = methodType(String.class, int[].class, String.class);
+
+        static final MethodHandle MH_forSpreading;
+        static final MethodHandle MH_forCollecting;
+        static final MethodHandle MH_forCollectingLeading;
+
+        static final MethodType MT_spreader = methodType(String.class, String.class, int[].class, String.class);
+        static final MethodType MT_collector0 = methodType(String.class, String.class, String.class);
+        static final MethodType MT_collector1 = methodType(String.class, String.class, int.class, String.class);
+        static final MethodType MT_collector2 = methodType(String.class, String.class, int.class, int.class, String.class);
+        static final MethodType MT_collector3 = methodType(String.class, String.class, int.class, int.class, int.class, String.class);
+        static final MethodType MT_collectorLeading1 = methodType(String.class, int.class, String.class);
+        static final MethodType MT_collectorLeading2 = methodType(String.class, int.class, int.class, String.class);
+        static final MethodType MT_collectorLeading3 = methodType(String.class, int.class, int.class, int.class, String.class);
+
+        static final String NONE_ERROR = "zero array length in MethodHandle.asCollector";
+
+        static {
+            try {
+                MH_forSpreading = LOOKUP.findStatic(SPREAD_COLLECT, "forSpreading", MT_forSpreading);
+                MH_forCollecting = LOOKUP.findStatic(SPREAD_COLLECT, "forCollecting", MT_forCollecting);
+                MH_forCollectingLeading = LOOKUP.findStatic(SPREAD_COLLECT, "forCollectingLeading", MT_forCollectingLeading);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+}