[Git][java-team/invokebinder][upstream] New upstream version 1.12

Jérôme Charaoui (@lavamind) gitlab at salsa.debian.org
Thu Oct 27 00:53:06 BST 2022



Jérôme Charaoui pushed to branch upstream at Debian Java Maintainers / invokebinder


Commits:
11486fa1 by Jérôme Charaoui at 2022-10-25T19:37:49-04:00
New upstream version 1.12
- - - - -


24 changed files:

- .travis.yml
- pom.xml
- src/main/java/com/headius/invokebinder/Binder.java
- src/main/java/com/headius/invokebinder/Signature.java
- src/main/java/com/headius/invokebinder/SmartBinder.java
- + src/main/java/com/headius/invokebinder/Util.java
- src/main/java/com/headius/invokebinder/transform/Cast.java
- src/main/java/com/headius/invokebinder/transform/Catch.java
- src/main/java/com/headius/invokebinder/transform/Collect.java
- src/main/java/com/headius/invokebinder/transform/Convert.java
- src/main/java/com/headius/invokebinder/transform/Drop.java
- src/main/java/com/headius/invokebinder/transform/Filter.java
- src/main/java/com/headius/invokebinder/transform/FilterReturn.java
- src/main/java/com/headius/invokebinder/transform/Fold.java
- src/main/java/com/headius/invokebinder/transform/Insert.java
- src/main/java/com/headius/invokebinder/transform/Permute.java
- src/main/java/com/headius/invokebinder/transform/Spread.java
- src/main/java/com/headius/invokebinder/transform/Transform.java
- src/main/java/com/headius/invokebinder/transform/TryFinally.java
- src/main/java/com/headius/invokebinder/transform/Varargs.java
- + src/main/java/module-info.java
- src/test/java/com/headius/invokebinder/BinderTest.java
- src/test/java/com/headius/invokebinder/Subjects.java
- + src/test/java/com/headius/invokebinder/ToJavaTest.java


Changes:

=====================================
.travis.yml
=====================================
@@ -2,12 +2,8 @@ language: java
 matrix:
   include:
     - os: linux
-      jdk: oraclejdk8
+      arch: amd64
+      jdk: openjdk11
     - os: linux
-      services:
-        - docker
-      before_install:
-        - docker pull ibmcom/ibmjava:8-sdk
-      script:
-        - docker run -v `pwd`:/work ibmcom/ibmjava:8-sdk /bin/sh -c "cd work; java -version; ./mvnw clean test"
-
+      arch: ppc64le
+      jdk: openjdk11


=====================================
pom.xml
=====================================
@@ -3,7 +3,7 @@
     <groupId>com.headius</groupId>
     <artifactId>invokebinder</artifactId>
     <packaging>bundle</packaging>
-    <version>1.8-SNAPSHOT</version>
+    <version>1.12</version>
     <name>invokebinder</name>
     <url>http://maven.apache.org</url>
 
@@ -51,15 +51,44 @@
                         <Export-Package>com.headius.invokebinder.*</Export-Package>
                     </instructions>
                 </configuration>
+                <!-- https://issues.apache.org/jira/browse/FELIX-5698 -->
+                <dependencies>
+                    <dependency>
+                        <groupId>biz.aQute.bnd</groupId>
+                        <artifactId>biz.aQute.bndlib</artifactId>
+                        <version>5.2.0</version>
+                    </dependency>
+                </dependencies>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>2.0.2</version>
+                <version>3.7.0</version>
+                <executions>
+                    <execution>
+                        <id>default-compile</id>
+                        <configuration>
+                            <includes>
+                                <include>module-info.java</include>
+                            </includes>
+                            <release>9</release>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>base-compile</id>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <excludes>
+                                <exclude>module-info.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </execution>
+                </executions>
                 <configuration>
                     <source>1.7</source>
                     <target>1.7</target>
-                    <encoding>${project.build.sourceEncoding}</encoding>
                 </configuration>
             </plugin>
             <plugin>
@@ -70,6 +99,13 @@
                     <encoding>${project.build.sourceEncoding}</encoding>
                 </configuration>
             </plugin>
+            <plugin>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.2.0</version>
+                <configuration>
+                    <source>8</source>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 
@@ -77,7 +113,7 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>4.9</version>
+            <version>4.13.1</version>
             <scope>test</scope>
         </dependency>
     </dependencies>


=====================================
src/main/java/com/headius/invokebinder/Binder.java
=====================================
@@ -34,9 +34,10 @@ import java.io.PrintStream;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.lang.invoke.VarHandle;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.logging.Logger;
@@ -68,8 +69,8 @@ import java.util.logging.Logger;
 public class Binder {
 
     private final Logger logger = Logger.getLogger("Invoke Binder");
-    private final List<Transform> transforms = new ArrayList<>();
-    private final List<MethodType> types = new ArrayList<>();
+    private final List<Transform> transforms = new LinkedList<>();
+    private final List<MethodType> types = new LinkedList<>();
     private final MethodType start;
     private final MethodHandles.Lookup lookup;
 
@@ -879,6 +880,18 @@ public class Binder {
         return new Binder(this, new Collect(type(), index, type));
     }
 
+    /**
+     * Box all incoming arguments from the given position onward into the given array type.
+     *
+     * @param index the index from which to start boxing args
+     * @param type  the array type into which the args will be boxed
+     * @param collector a function to use for collecting the arguments
+     * @return a new Binder
+     */
+    public Binder collect(int index, Class<?> type, MethodHandle collector) {
+        return new Binder(this, new Collect(type(), index, type, collector));
+    }
+
     /**
      * Box a range of incoming arguments into the given array type.
      *
@@ -891,6 +904,22 @@ public class Binder {
         return new Binder(this, new Collect(type(), index, count, type));
     }
 
+    /**
+     * Box a range of incoming arguments into the given array type using the given constructor to construct the array.
+     *
+     * The collector signature should match (T1, ..., Tn) where T is the type of the arguments being collected and n
+     * is the number of arguments being collected.
+     *
+     * @param index the index from which to start boxing args
+     * @param count the count of arguments to box
+     * @param type  the array type into which the args will be boxed
+     * @param collector a function to use for collecting the arguments
+     * @return a new Binder
+     */
+    public Binder collect(int index, int count, Class<?> type, MethodHandle collector) {
+        return new Binder(this, new Collect(type(), index, count, type, collector));
+    }
+
     /**
      * Box all incoming arguments from the given position onward into the given array type.
      * This version accepts a variable number of incoming arguments.
@@ -927,6 +956,14 @@ public class Binder {
         return new Binder(this, new Fold(function));
     }
 
+    /**
+     * Process the incoming arguments using the given handle, leaving the argument list
+     * unmodified.
+     *
+     * @param function the function that will process the incoming arguments. Its
+     *                 signature must match the current signature's arguments exactly.
+     * @return a new Binder
+     */
     public Binder foldVoid(MethodHandle function) {
         if (type().returnType() == void.class) {
             return fold(function);
@@ -984,8 +1021,12 @@ public class Binder {
     }
 
     /**
-     * Filter incoming arguments, starting at the given index, replacing each with the
-     * result of calling the associated function in the given list.
+     * Filter incoming arguments, from the given index, replacing each with the
+     * result of calling the associated function in the given list. Note that
+     * the order in which the filters are applied is undefined; OpenJDK produces
+     * handles that execute them in reverse order.
+     *
+     * @see #filterForward(int, MethodHandle...)
      *
      * @param index     the index of the first argument to filter
      * @param functions the array of functions to transform the arguments
@@ -995,6 +1036,26 @@ public class Binder {
         return new Binder(this, new Filter(index, functions));
     }
 
+    /**
+     * Filter incoming arguments, from the given index, replacing each with the
+     * result of calling the associated function in the given list. This version
+     * guarantees left-to-right evaluation of filter functions, potentially at
+     * the cost of a more complex handle tree.
+     *
+     * @param index     the index of the first argument to filter
+     * @param functions the array of functions to transform the arguments
+     * @return a new Binder
+     */
+    public Binder filterForward(int index, MethodHandle... functions) {
+        Binder filtered = this;
+
+        for (int i = 0; i < functions.length; i++) {
+            filtered = filtered.filter(index + i, functions[i]);
+        }
+
+        return filtered;
+    }
+
     /**
      * Filter return value, using a function that produces the current return type
      * from another type. The new endpoint will have the return value that the
@@ -1524,6 +1585,36 @@ public class Binder {
         return invoke(MethodHandles.arrayElementSetter(type().parameterType(0)));
     }
 
+    /**
+     * Apply the chain of transforms and bind them to a volatile array element set.
+     *
+     * @see Binder#arraySet()
+     * @see VarHandle#setVolatile(Object...)
+     */
+    public MethodHandle arraySetVolatile() {
+        return arrayAccess(VarHandle.AccessMode.SET_VOLATILE);
+    }
+
+    /**
+     * Apply the chain of transforms and bind them to a release-fenced array element set.
+     *
+     * @see Binder#arraySet()
+     * @see VarHandle#setRelease(Object...)
+     */
+    public MethodHandle arraySetAcquire() {
+        return arrayAccess(VarHandle.AccessMode.SET_RELEASE);
+    }
+
+    /**
+     * Apply the chain of transforms and bind them to an opaque (no ordering guarantee) array element set.
+     *
+     * @see Binder#arraySet()
+     * @see VarHandle#setVolatile(Object...)
+     */
+    public MethodHandle arraySetOpaque() {
+        return arrayAccess(VarHandle.AccessMode.SET_OPAQUE);
+    }
+
 
     /**
      * Apply the chain of transforms and bind them to an array element get. The signature
@@ -1536,6 +1627,44 @@ public class Binder {
         return invoke(MethodHandles.arrayElementGetter(type().parameterType(0)));
     }
 
+    /**
+     * Apply the chain of transforms and bind them to a volatile array element get.
+     *
+     * @see Binder#arrayGet()
+     * @see VarHandle#getVolatile(Object...)
+     */
+    public MethodHandle arrayGetVolatile() {
+        return arrayAccess(VarHandle.AccessMode.GET_VOLATILE);
+    }
+
+    /**
+     * Apply the chain of transforms and bind them to an acquire-fenced array element get.
+     *
+     * @see Binder#arrayGet()
+     * @see VarHandle#getAcquire(Object...)
+     */
+    public MethodHandle arrayGetAcquire() {
+        return arrayAccess(VarHandle.AccessMode.GET_ACQUIRE);
+    }
+
+    /**
+     * Apply the chain of transforms and bind them to an opaque (no ordering guarantee) array element get.
+     *
+     * @see Binder#arrayGet()
+     * @see VarHandle#getVolatile(Object...)
+     */
+    public MethodHandle arrayGetOpaque() {
+        return arrayAccess(VarHandle.AccessMode.GET_OPAQUE);
+    }
+
+    /**
+     * Apply the chain of transforms and bind them to an array varhandle operation. The
+     * signature at the endpoint must match the VarHandle access type passed in.
+     */
+    public MethodHandle arrayAccess(VarHandle.AccessMode mode) {
+        return invoke(MethodHandles.arrayElementVarHandle(type().parameterType(0)).toMethodHandle(mode));
+    }
+
     /**
      * Apply the chain of transforms and bind them to a boolean branch as from
      * java.lang.invoke.MethodHandles.guardWithTest. As with GWT, the current endpoint
@@ -1560,4 +1689,20 @@ public class Binder {
         return invoke(MethodHandles.invoker(start));
     }
 
+    /**
+     * Produce Java code that would perform equivalent operations to this binder.
+     *
+     * @return Java code for the handle adaptations this Binder would produce.
+     */
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder();
+        boolean second = false;
+        for (Transform transform : transforms) {
+            if (second) builder.append('\n');
+            second = true;
+            builder.append(transform.toJava(incoming));
+        }
+        return builder.toString();
+    }
+
 }


=====================================
src/main/java/com/headius/invokebinder/Signature.java
=====================================
@@ -788,4 +788,22 @@ public class Signature {
         return offsets;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Signature signature = (Signature) o;
+
+        if (!methodType.equals(signature.methodType)) return false;
+
+        return Arrays.equals(argNames, signature.argNames);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = methodType.hashCode();
+        result = 31 * result + Arrays.hashCode(argNames);
+        return result;
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/SmartBinder.java
=====================================
@@ -853,6 +853,30 @@ public class SmartBinder {
         return new SmartBinder(this, newSignature, binder.collect(index, signature().argCount() - (newSignature.argCount() - 1), Array.newInstance(signature().argType(index), 0).getClass()));
     }
 
+    /**
+     * Collect arguments matching namePattern into an trailing array argument
+     * named outName, using collector to construct the array object.
+     *
+     * The collector signature should match (T1, ..., Tn) where T is the type of the arguments being collected and n
+     * is the number of arguments being collected.
+     *
+     * The namePattern is a standard regular expression.
+     *
+     * @param outName     the name of the new array argument
+     * @param namePattern a pattern with which to match arguments for collecting
+     * @param collector a function to use for collecting the arguments
+     * @return a new SmartBinder with the collect applied
+     */
+    public SmartBinder collect(String outName, String namePattern, MethodHandle collector) {
+        int index = signature().argOffsets(namePattern);
+
+        assert index >= 0 : "no arguments matching " + namePattern + " found in signature " + signature();
+
+        Signature newSignature = signature().collect(outName, namePattern);
+
+        return new SmartBinder(this, newSignature, binder.collect(index, signature().argCount() - (newSignature.argCount() - 1), Array.newInstance(signature().argType(index), 0).getClass(), collector));
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // CASTS, based on MethodHandles.explicitCastArguments and MethodHandle.asType.
     ///////////////////////////////////////////////////////////////////////////
@@ -1165,4 +1189,11 @@ public class SmartBinder {
 
         return new SmartBinder(newSig, newBinder);
     }
+
+    /**
+     * @see Binder#tryFinally(MethodHandle)
+     */
+    public SmartBinder tryFinally(MethodHandle post) {
+        return new SmartBinder(this, signature(), binder.tryFinally(post));
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/Util.java
=====================================
@@ -0,0 +1,23 @@
+package com.headius.invokebinder;
+
+/**
+ * Utilities used by InvokeBinder classes.
+ */
+public class Util {
+    public static boolean IS_JAVA9;
+
+    static {
+        boolean isJava9;
+        try {
+            Class.forName("java.lang.Module");
+            isJava9 = true;
+        } catch (Exception e) {
+            isJava9 = false;
+        }
+        IS_JAVA9 = isJava9;
+    }
+
+    public static boolean isJava9() {
+        return IS_JAVA9;
+    }
+}


=====================================
src/main/java/com/headius/invokebinder/transform/Cast.java
=====================================
@@ -54,4 +54,12 @@ public class Cast extends Transform {
     public String toString() {
         return "cast args to " + type;
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = MethodHandles.explicitCastArguments(handle, ");
+        buildClassArguments(builder, type.parameterArray());
+        builder.append(");");
+        return builder.toString();
+    }
+
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Catch.java
=====================================
@@ -26,6 +26,7 @@ import java.lang.invoke.MethodType;
  */
 public class Catch extends Transform {
 
+    public static final String EXCEPTION_HANDLER_JAVA = "<exception handler>";
     private final Class<? extends Throwable> throwable;
     private final MethodHandle function;
 
@@ -45,4 +46,11 @@ public class Catch extends Transform {
     public String toString() {
         return "catch exception type " + throwable + " using " + function;
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = MethodHandles.catchException(handle, ");
+        buildClassArgument(builder, throwable);
+        builder.append(", ").append(EXCEPTION_HANDLER_JAVA).append(");");
+        return builder.toString();
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Collect.java
=====================================
@@ -18,12 +18,13 @@ package com.headius.invokebinder.transform;
 import com.headius.invokebinder.Binder;
 
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 
 /**
  * An argument-boxing transform with a fixed incoming size.
  *
- * Equivalent call: MethodHandle.asCollector(Class, int)
+ * Equivalent call: MethodHandle.asCollector(Class, int) or MethodHandles.collectArguments
  */
 public class Collect extends Transform {
 
@@ -31,12 +32,22 @@ public class Collect extends Transform {
     private final int index;
     private final int count;
     private final Class<?> arrayType;
+    private final MethodHandle collector;
 
     public Collect(MethodType source, int index, Class<?> arrayType) {
         this.source = source;
         this.index = index;
         this.count = source.parameterCount() - index;
         this.arrayType = arrayType;
+        this.collector = null;
+    }
+
+    public Collect(MethodType source, int index, Class<?> arrayType, MethodHandle collector) {
+        this.source = source;
+        this.index = index;
+        this.count = source.parameterCount() - index;
+        this.arrayType = arrayType;
+        this.collector = collector;
     }
 
     public Collect(MethodType source, int index, int count, Class<?> arrayType) {
@@ -44,38 +55,40 @@ public class Collect extends Transform {
         this.index = index;
         this.count = count;
         this.arrayType = arrayType;
+        this.collector = null;
+    }
+
+    public Collect(MethodType source, int index, int count, Class<?> arrayType, MethodHandle collector) {
+        this.source = source;
+        this.index = index;
+        this.count = count;
+        this.arrayType = arrayType;
+        this.collector = collector;
     }
 
     public MethodHandle up(MethodHandle target) {
-        if (index + count == source.parameterCount()) {
+        if (onlyTail()) {
             // fast path for tail args
-            return target.asCollector(arrayType, count);
-        } else {
-            int[] movePermute = new int[source.parameterCount()];
-            int[] moveBackPermute = new int[target.type().parameterCount()];
-            // pre
-            for (int i = 0; i < index; i++) {
-                movePermute[i] = i;
-                moveBackPermute[i] = i;
+            if (collector == null) {
+                return target.asCollector(arrayType, count);
             }
 
-            // post
-            int shifted = 0;
-            for (int i = index; i + count < movePermute.length; i++, shifted++) movePermute[i] = i + count;
-            for (int i = index; i + 1 < moveBackPermute.length; i++) moveBackPermute[i + 1] = i;
-
-            // collected args
-            for (int i = index + shifted; i < movePermute.length; i++) movePermute[i] = i - shifted;
-            moveBackPermute[index] = moveBackPermute.length - 1;
+            return MethodHandles.collectArguments(target, index, collector);
+        } else {
+            Permutes permutes = buildPermutes(source, target.type());
 
-            return Binder.from(source)
-                    .permute(movePermute)
-                    .collect(source.parameterCount() - count, arrayType)
-                    .permute(moveBackPermute)
-                    .invoke(target);
+            Binder binder = preparePermuteBinder(permutes);
+            return binder.invoke(target);
         }
     }
 
+    private Binder preparePermuteBinder(Permutes permutes) {
+        return Binder.from(source)
+                .permute(permutes.movePermute)
+                .collect(source.parameterCount() - count, arrayType, collector)
+                .permute(permutes.moveBackPermute);
+    }
+
     public MethodType down(MethodType type) {
         assertTypesAreCompatible();
 
@@ -96,4 +109,67 @@ public class Collect extends Transform {
     public String toString() {
         return "collect at " + index + " into " + arrayType.getName();
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder();
+        if (onlyTail()) {
+            if (collector == null) {
+                builder.append("handle = handle.asCollector(");
+                buildClassArgument(builder, arrayType);
+                builder
+                        .append(", ")
+                        .append(count)
+                        .append(");");
+            } else {
+                builder.append("handle = MethodHandles.collectArguments(");
+
+                builder
+                        .append("handle, ")
+                        .append(count)
+                        .append(", ");
+
+                buildClassArgument(builder, arrayType);
+
+                builder.append(");");
+            }
+        } else {
+            Permutes permutes = buildPermutes(source, incoming);
+
+            Binder binder = preparePermuteBinder(permutes);
+            return binder.toJava(incoming);
+        }
+        return builder.toString();
+    }
+
+    private boolean onlyTail() {
+        return index + count == source.parameterCount();
+    }
+
+    private static class Permutes {
+        private final int[] movePermute;
+        private final int[] moveBackPermute;
+
+        private Permutes(MethodType source, MethodType target, int index, int count) {
+            movePermute = new int[source.parameterCount()];
+            moveBackPermute = new int[target.parameterCount()];
+            // pre
+            for (int i = 0; i < index; i++) {
+                movePermute[i] = i;
+                moveBackPermute[i] = i;
+            }
+
+            // post
+            int shifted = 0;
+            for (int i = index; i + count < movePermute.length; i++, shifted++) movePermute[i] = i + count;
+            for (int i = index; i + 1 < moveBackPermute.length; i++) moveBackPermute[i + 1] = i;
+
+            // collected args
+            for (int i = index + shifted; i < movePermute.length; i++) movePermute[i] = i - shifted;
+            moveBackPermute[index] = moveBackPermute.length - 1;
+        }
+    }
+
+    private Permutes buildPermutes(MethodType source, MethodType target) {
+        return new Permutes(source, target, index, count);
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Convert.java
=====================================
@@ -53,4 +53,14 @@ public class Convert extends Transform {
     public String toString() {
         return "convert args to " + type;
     }
+
+    public String toJava(MethodType incoming) {
+        String methodTypeJava = generateMethodType(type);
+
+        if (incoming.returnType() == void.class) {
+            return "handle = MethodHandles.explicitCastArguments(handle.asType(" + methodTypeJava + ", " + methodTypeJava + ");";
+        }
+
+        return "handle = handle.asType(" + methodTypeJava + ");";
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Drop.java
=====================================
@@ -46,4 +46,14 @@ public class Drop extends Transform {
     public String toString() {
         return "drop " + Arrays.toString(types) + " at " + position;
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = MethodHandles.dropArguments(handle, ");
+
+        builder.append(position).append(", ");
+        buildClassArguments(builder, types);
+        builder.append(");");
+
+        return builder.toString();
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Filter.java
=====================================
@@ -26,10 +26,11 @@ import java.util.Arrays;
  * Equivalent call: MethodHandles.filterArguments(MethodHandle, int, MethodHandle...).
  */
 public class Filter extends Transform {
-
     private final int index;
     private final MethodHandle[] functions;
 
+    public static final String FILTER_FUNCTIONS_JAVA = "<filter functions>";
+
     public Filter(int index, MethodHandle... functions) {
         this.index = index;
         this.functions = functions;
@@ -49,4 +50,8 @@ public class Filter extends Transform {
     public String toString() {
         return "fold args from " + index + " with " + Arrays.toString(functions);
     }
+
+    public String toJava(MethodType incoming) {
+        return "handle = MethodHandles.filterArguments(handle, " + index + ", " + FILTER_FUNCTIONS_JAVA + ");";
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/FilterReturn.java
=====================================
@@ -27,9 +27,10 @@ import java.lang.invoke.MethodType;
  * Equivalent call: MethodHandles.filterReturn(MethodHandle, MethodHandle).
  */
 public class FilterReturn extends Transform {
-
     private final MethodHandle function;
 
+    public static final String FILTER_FUNCTION_JAVA = "<filter function>";
+
     public FilterReturn(MethodHandle function) {
         this.function = function;
     }
@@ -53,4 +54,8 @@ public class FilterReturn extends Transform {
     public String toString() {
         return "filter return with " + function;
     }
+
+    public String toJava(MethodType incoming) {
+        return "handle = MethodHandles.filterReturnValue(handle, " + FILTER_FUNCTION_JAVA + ");";
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Fold.java
=====================================
@@ -25,9 +25,10 @@ import java.lang.invoke.MethodType;
  * Equivalent call: MethodHandles.foldArguments(MethodHandle, MethodHandle).
  */
 public class Fold extends Transform {
-
     private final MethodHandle function;
 
+    public static final String FOLD_FUNCTION_JAVA = "<fold function>";
+
     public Fold(MethodHandle function) {
         this.function = function;
     }
@@ -44,4 +45,8 @@ public class Fold extends Transform {
     public String toString() {
         return "fold args with " + function;
     }
+
+    public String toJava(MethodType incoming) {
+        return "handle = MethodHandles.foldArguments(handle, " + FOLD_FUNCTION_JAVA + ");";
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Insert.java
=====================================
@@ -107,6 +107,30 @@ public class Insert extends Transform {
         return "insert " + Arrays.toString(types()) + " at " + position;
     }
 
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = MethodHandles.insertArguments(handle, ");
+        builder
+                .append(position)
+                .append(", ");
+
+        // we cast all arguments since natural type will frequently be wrong
+        boolean second = false;
+        for (int i = 0; i < types.length; i++) {
+            if (second) builder.append(", ");
+            second = true;
+
+            buildClassCast(builder, types[i]);
+            if (types[i].isPrimitive()) {
+                buildPrimitiveJava(builder, values[i]);
+            } else {
+                builder.append("value").append(i + 1);
+            }
+        }
+        builder.append(");");
+
+        return builder.toString();
+    }
+
     private Class<?>[] types() {
         Class<?>[] types = new Class<?>[values.length];
         for (int i = 0; i < types.length; i++) {


=====================================
src/main/java/com/headius/invokebinder/transform/Permute.java
=====================================
@@ -57,4 +57,16 @@ public class Permute extends Transform {
     public String toString() {
         return "permute " + source + " with " + Arrays.toString(reorder);
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = MethodHandles.permuteArguments(handle, ");
+
+        String reorder = Arrays.toString(this.reorder);
+        reorder = reorder.substring(1, reorder.length() - 1);
+
+        builder.append(generateMethodType(source) + ", new int[] {" + reorder + "});");
+
+        return builder.toString();
+    }
+
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Spread.java
=====================================
@@ -52,4 +52,17 @@ public class Spread extends Transform {
     public String toString() {
         return "spread " + source + " to " + down(source);
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = handle.asSpreader(");
+
+        buildClassArgument(builder, source.parameterType(source.parameterCount() - 1));
+        builder
+                .append(", ")
+                .append(spreadTypes.length);
+
+        builder.append(");");
+
+        return builder.toString();
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Transform.java
=====================================
@@ -46,4 +46,109 @@ public abstract class Transform {
      * @return a string representation of this transform
      */
     public abstract String toString();
+
+    /**
+     * Return a Java code representation of this transform.
+     *
+     * @return a Java code representation of this transform.
+     */
+    public abstract String toJava(MethodType incoming);
+
+    /**
+     * Build a list of argument type classes suitable for inserting into Java code.
+     *
+     * This will be an argument list of the form "pkg.Cls1.class, pkg.Cls2[].class, primtype.class, ..."
+     *
+     * @param builder the builder in which to build the argument list
+     * @param types the classes from which to create the argument list
+     */
+    protected static void buildClassArguments(StringBuilder builder, Class<?>[] types) {
+        boolean second = false;
+        for (Class cls : types) {
+            if (second) builder.append(", ");
+            second = true;
+            buildClassArgument(builder, cls);
+        }
+    }
+
+    /**
+     * Build Java code to represent a single .class reference.
+     *
+     * This will be an argument of the form "pkg.Cls1.class" or "pkg.Cls2[].class" or "primtype.class"
+     *
+     * @param builder the builder in which to build the argument
+     * @param cls the type for the argument
+     */
+    protected static void buildClassArgument(StringBuilder builder, Class cls) {
+        buildClass(builder, cls);
+        builder.append(".class");
+    }
+
+    /**
+     * Build Java code to represent a cast to the given type.
+     *
+     * This will be an argument of the form "(pkg.Cls1)" or "(pkg.Cls2[])" or "(primtype)"
+     *
+     * @param builder the builder in which to build the argument
+     * @param cls the type for the argument
+     */
+    protected static void buildClassCast(StringBuilder builder, Class cls) {
+        builder.append('(');
+        buildClass(builder, cls);
+        builder.append(')');
+    }
+
+    /**
+     * Build Java code to represent a literal primitive.
+     *
+     * This will append L or F as appropriate for long and float primitives.
+     *
+     * @param builder the builder in which to generate the code
+     * @param value the primitive value to generate from
+     */
+    protected static void buildPrimitiveJava(StringBuilder builder, Object value) {
+        builder.append(value.toString());
+        if (value.getClass() == Float.class) builder.append('F');
+        if (value.getClass() == Long.class) builder.append('L');
+    }
+
+    /**
+     * Build Java code to represent a type reference to the given class.
+     *
+     * This will be of the form "pkg.Cls1" or "pkc.Cls2[]" or "primtype"..
+     *
+     * @param builder the builder in which to build the type reference
+     * @param cls the type for the reference
+     */
+    private static void buildClass(StringBuilder builder, Class cls) {
+        int arrayDims = 0;
+        Class tmp = cls;
+        while (tmp.isArray()) {
+            arrayDims++;
+            tmp = tmp.getComponentType();
+        }
+        builder.append(tmp.getName());
+        if (arrayDims > 0) {
+            for (; arrayDims > 0 ; arrayDims--) {
+                builder.append("[]");
+            }
+        }
+    }
+
+    /**
+     * Build Java code appropriate for standing up the given MethodType.
+     *
+     * @param source the MethodType for which to build Java code
+     * @return Java code suitable for building the given MethodType
+     */
+    public static String generateMethodType(MethodType source) {
+        StringBuilder builder = new StringBuilder("MethodType.methodType(");
+        buildClassArgument(builder, source.returnType());
+        if (source.parameterCount() > 0) {
+            builder.append(", ");
+            buildClassArguments(builder, source.parameterArray());
+        }
+        builder.append(")");
+        return builder.toString();
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/TryFinally.java
=====================================
@@ -16,10 +16,12 @@
 package com.headius.invokebinder.transform;
 
 import com.headius.invokebinder.Binder;
+import com.headius.invokebinder.Util;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationTargetException;
 
 /**
  * An try-finally transform.
@@ -34,11 +36,15 @@ public class TryFinally extends Transform {
 
     private final MethodHandle post;
 
+    private static final MethodHandle tryFinallyJava9;
+
     public TryFinally(MethodHandle post) {
         this.post = post;
     }
 
     public MethodHandle up(MethodHandle target) {
+        if (Util.isJava9()) return nativeTryFinally(target, post);
+
         MethodHandle exceptionHandler = Binder
                 .from(target.type().insertParameterTypes(0, Throwable.class).changeReturnType(void.class))
                 .drop(0)
@@ -72,6 +78,36 @@ public class TryFinally extends Transform {
         return MethodHandles.foldArguments(realPost, target);
     }
 
+    private MethodHandle nativeTryFinally(MethodHandle target, MethodHandle post) {
+        MethodType targetType = target.type();
+        boolean voidReturn = targetType.returnType() == Void.TYPE;
+        MethodType finallyType = targetType.insertParameterTypes(0, Throwable.class);
+        int dropCount = 1;
+
+        if (!voidReturn) {
+            finallyType = finallyType.insertParameterTypes(1, targetType.returnType());
+            dropCount = 2;
+        }
+
+        MethodHandle wrapPost = Binder
+                .from(finallyType)
+                .drop(0, dropCount)
+                .invoke(post);
+
+        if (!voidReturn) {
+            wrapPost = Binder.from(finallyType)
+                    .foldVoid(wrapPost)
+                    .permute(1)
+                    .identity();
+        }
+
+        try {
+            return (MethodHandle) tryFinallyJava9.invokeExact(target, wrapPost);
+        } catch (Throwable t) {
+            throw new RuntimeException("Java 9 detected but MethodHandles.tryFinally missing", t);
+        }
+    }
+
     public MethodType down(MethodType type) {
         return type;
     }
@@ -79,4 +115,16 @@ public class TryFinally extends Transform {
     public String toString() {
         return "try/finally with " + post;
     }
+
+    public String toJava(MethodType incoming) {
+        throw new RuntimeException("TryFinally does not yet support toJava");
+    }
+
+    static {
+        if (Util.isJava9()) {
+            tryFinallyJava9 = Binder.from(MethodHandle.class, MethodHandle.class, MethodHandle.class).invokeStaticQuiet(MethodHandles.lookup(), MethodHandles.class, "tryFinally");
+        } else {
+            tryFinallyJava9 = null;
+        }
+    }
 }


=====================================
src/main/java/com/headius/invokebinder/transform/Varargs.java
=====================================
@@ -59,4 +59,15 @@ public class Varargs extends Transform {
     public String toString() {
         return "varargs at " + index + " into " + arrayType.getName();
     }
+
+    public String toJava(MethodType incoming) {
+        StringBuilder builder = new StringBuilder("handle = handle.asVarargsCollector(");
+
+        buildClassArgument(builder, arrayType);
+        builder.append(").asType(");
+        builder.append(generateMethodType(source));
+        builder.append(");");
+
+        return builder.toString();
+    }
 }


=====================================
src/main/java/module-info.java
=====================================
@@ -0,0 +1,6 @@
+module com.headius.invokebinder {
+    requires java.base;
+    requires java.logging;
+
+    exports com.headius.invokebinder;
+}
\ No newline at end of file


=====================================
src/test/java/com/headius/invokebinder/BinderTest.java
=====================================
@@ -1,6 +1,8 @@
 package com.headius.invokebinder;
 
 import org.junit.Test;
+
+import static java.lang.invoke.MethodType.methodType;
 import static org.junit.Assert.*;
 
 import java.io.ByteArrayOutputStream;
@@ -8,7 +10,7 @@ import java.io.PrintStream;
 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.VarHandle;
 import java.lang.reflect.Method;
 
 /**
@@ -16,6 +18,7 @@ import java.lang.reflect.Method;
  */
 public class BinderTest {
     private static final Lookup LOOKUP = MethodHandles.lookup();
+
     @Test
     public void testFrom() throws Throwable {
         MethodHandle target = Subjects.concatHandle();
@@ -29,7 +32,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class), handle.type());
+        assertEquals(methodType(String.class, String.class, Object.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", new Object()));
     }
 
@@ -38,35 +41,35 @@ public class BinderTest {
         Binder b1 = Binder
                 .from(void.class)
                 .insert(0, true);
-        assertEquals(MethodType.methodType(void.class, boolean.class), b1.type());
+        assertEquals(methodType(void.class, boolean.class), b1.type());
         Binder b2 = Binder
                 .from(void.class)
                 .insert(0, (byte)1);
-        assertEquals(MethodType.methodType(void.class, byte.class), b2.type());
+        assertEquals(methodType(void.class, byte.class), b2.type());
         Binder b3 = Binder
                 .from(void.class)
                 .insert(0, (short)1);
-        assertEquals(MethodType.methodType(void.class, short.class), b3.type());
+        assertEquals(methodType(void.class, short.class), b3.type());
         Binder b4 = Binder
                 .from(void.class)
                 .insert(0, (char)1);
-        assertEquals(MethodType.methodType(void.class, char.class), b4.type());
+        assertEquals(methodType(void.class, char.class), b4.type());
         Binder b5 = Binder
                 .from(void.class)
                 .insert(0, 1);
-        assertEquals(MethodType.methodType(void.class, int.class), b5.type());
+        assertEquals(methodType(void.class, int.class), b5.type());
         Binder b6 = Binder
                 .from(void.class)
                 .insert(0, 1L);
-        assertEquals(MethodType.methodType(void.class, long.class), b6.type());
+        assertEquals(methodType(void.class, long.class), b6.type());
         Binder b7 = Binder
                 .from(void.class)
                 .insert(0, 1.0F);
-        assertEquals(MethodType.methodType(void.class, float.class), b7.type());
+        assertEquals(methodType(void.class, float.class), b7.type());
         Binder b8 = Binder
                 .from(void.class)
                 .insert(0, 1.0);
-        assertEquals(MethodType.methodType(void.class, double.class), b8.type());
+        assertEquals(methodType(void.class, double.class), b8.type());
 
         MethodHandle target = intLongHandle();
 
@@ -75,7 +78,7 @@ public class BinderTest {
                 .insert(0, new Class[]{int.class, long.class}, 1, 1L)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class), handle.type());
+        assertEquals(methodType(String.class), handle.type());
         assertEquals("intLong ok", (String) handle.invokeExact());
     }
 
@@ -93,9 +96,9 @@ public class BinderTest {
         
         Binder newBinder = thisBinder.to(otherBinder);
         
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), otherBinder.type());
-        assertEquals(MethodType.methodType(String.class, String.class, int.class), thisBinder.type());
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), newBinder.type());
+        assertEquals(methodType(String.class, String.class, String.class), otherBinder.type());
+        assertEquals(methodType(String.class, String.class, int.class), thisBinder.type());
+        assertEquals(methodType(String.class, String.class, String.class), newBinder.type());
         
         MethodHandle target = newBinder.invoke(Subjects.concatHandle());
         
@@ -107,12 +110,12 @@ public class BinderTest {
         Binder binder = Binder
                 .from(String.class, String.class, Integer.class);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Integer.class), binder.type());
+        assertEquals(methodType(String.class, String.class, Integer.class), binder.type());
 
         binder = binder
                 .drop(1);
 
-        assertEquals(MethodType.methodType(String.class, String.class), binder.type());
+        assertEquals(methodType(String.class, String.class), binder.type());
     }
 
     @Test
@@ -142,7 +145,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, "));
 
         MethodHandle target2 = Subjects.concatCharSequenceHandle();
@@ -151,7 +154,7 @@ public class BinderTest {
                 .insert(1, CharSequence.class, "world")
                 .invoke(target2);
 
-        assertEquals(MethodType.methodType(String.class, String.class), handle2.type());
+        assertEquals(methodType(String.class, String.class), handle2.type());
         assertEquals("Hello, world", (String) handle2.invokeExact("Hello, "));
     }
 
@@ -164,7 +167,7 @@ public class BinderTest {
                 .drop(1)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class), handle.type());
+        assertEquals(methodType(String.class, String.class, Object.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", new Object()));
 
         MethodHandle target2 = Subjects.concatCharSequenceHandle();
@@ -174,7 +177,7 @@ public class BinderTest {
                 .drop(1)
                 .invoke(target2);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class), handle2.type());
+        assertEquals(methodType(String.class, String.class, Object.class), handle2.type());
         assertEquals("Hello, world", (String) handle2.invokeExact("Hello, ", new Object()));
     }
 
@@ -187,7 +190,7 @@ public class BinderTest {
                 .drop(1)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, String.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact(new Object(), "world"));
 
         MethodHandle target2 = Subjects.concatHandle();
@@ -197,7 +200,7 @@ public class BinderTest {
                 .drop(1)
                 .invoke(target2);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, String.class), handle2.type());
+        assertEquals(methodType(String.class, Object.class, String.class), handle2.type());
         assertEquals("Hello, world", (String) handle2.invokeExact(new Object(), "world"));
     }
 
@@ -210,7 +213,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class), handle.type());
+        assertEquals(methodType(String.class, String.class, Object.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", new Object()));
     }
     
@@ -223,7 +226,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class), handle.type());
+        assertEquals(methodType(String.class, String.class, Object.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", new Object()));
 
         handle = Binder
@@ -232,7 +235,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class, double.class), handle.type());
+        assertEquals(methodType(String.class, String.class, Object.class, double.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", new Object(), 1.0));
     }
 
@@ -245,7 +248,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, String.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact(new Object(), "Hello, "));
 
         handle = Binder
@@ -254,7 +257,7 @@ public class BinderTest {
                 .insert(1, "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, double.class, String.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, double.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact(new Object(), 1.0, "Hello, "));
     }
 
@@ -267,7 +270,7 @@ public class BinderTest {
                 .insert(0, "Hello, ", "world")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Object.class), handle.type());
+        assertEquals(methodType(String.class, String.class, Object.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", new Object()));
     }
 
@@ -279,7 +282,7 @@ public class BinderTest {
                 .convert(target.type())
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, Integer.class, Float.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, Integer.class, Float.class), handle.type());
         assertEquals(null, (String) handle.invokeExact((Object) "foo", (Integer) 5, (Float) 5.0f));
     }
 
@@ -291,7 +294,7 @@ public class BinderTest {
                 .convert(target.type().returnType(), target.type().parameterArray())
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, Integer.class, Float.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, Integer.class, Float.class), handle.type());
         assertEquals(null, (String)handle.invokeExact((Object)"foo", (Integer)5, (Float)5.0f));
     }
 
@@ -303,7 +306,7 @@ public class BinderTest {
                 .cast(target.type())
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, byte.class, int.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, byte.class, int.class), handle.type());
         assertEquals(null, (String)handle.invokeExact((Object)"foo", (byte)5, 5));
     }
 
@@ -315,7 +318,7 @@ public class BinderTest {
                 .cast(target.type().returnType(), target.type().parameterArray())
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Object.class, byte.class, int.class), handle.type());
+        assertEquals(methodType(String.class, Object.class, byte.class, int.class), handle.type());
         assertEquals(null, (String)handle.invokeExact((Object)"foo", (byte)5, 5));
     }
 
@@ -328,7 +331,7 @@ public class BinderTest {
                 .permute(0, 0)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, Integer.class, Float.class, String.class), handle.type());
+        assertEquals(methodType(String.class, Integer.class, Float.class, String.class), handle.type());
         assertEquals("foofoo", (String)handle.invokeExact((Integer) 0, (Float) 0.0f, "foo"));
     }
 
@@ -340,7 +343,7 @@ public class BinderTest {
                 .spread(String.class, String.class)
                 .invoke(target);
         
-        assertEquals(MethodType.methodType(String.class, Object[].class), handle.type());
+        assertEquals(methodType(String.class, Object[].class), handle.type());
         assertEquals("foobar", (String)handle.invokeExact(new Object[] {"foo", "bar"}));
     }
 
@@ -352,7 +355,7 @@ public class BinderTest {
                 .spread(2)
                 .invoke(target);
         
-        assertEquals(MethodType.methodType(String.class, String[].class), handle.type());
+        assertEquals(methodType(String.class, String[].class), handle.type());
         assertEquals("foobar", (String)handle.invokeExact(new String[] {"foo", "bar"}));
     }
 
@@ -363,7 +366,7 @@ public class BinderTest {
                 .collect(1, String[].class)
                 .invokeStatic(LOOKUP, BinderTest.class, "varargs");
 
-        assertEquals(MethodType.methodType(String[].class, String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String[].class, String.class, String.class, String.class), handle.type());
         String[] ary = (String[])handle.invokeExact("one", "two", "three");
         assertEquals(2, ary.length);
         assertEquals("two", ary[0]);
@@ -374,10 +377,38 @@ public class BinderTest {
                 .collect(1, 3, Integer[].class)
                 .invoke(Subjects.StringIntegersStringHandle);
 
-        assertEquals(MethodType.methodType(String.class, String.class, Integer.class, Integer.class, Integer.class, String.class), handle2.type());
+        assertEquals(methodType(String.class, String.class, Integer.class, Integer.class, Integer.class, String.class), handle2.type());
         assertEquals("[foo, [1, 2, 3], bar]", (String)handle2.invokeExact("foo", new Integer(1), new Integer(2), new Integer(3), "bar"));
     }
 
+    public static String[] newStringArray(String s1, String s2) {
+        return new String[] {s1, s2};
+    }
+
+    public static MethodHandle newStringArrayHandle() throws Exception {
+        return LOOKUP.findStatic(BinderTest.class, "newStringArray", methodType(String[].class, String.class, String.class));
+    }
+
+    @Test
+    public void testCollectWithCollector() throws Throwable {
+        Binder binder = Binder
+                .from(String[].class, String.class, String.class, String.class)
+                .collect(1, String[].class, newStringArrayHandle());
+
+        String toJava = binder.toJava(methodType(String[].class, String.class, String.class, String.class));
+
+        assertTrue(toJava.contains("collectArguments"));
+
+        MethodHandle handle = binder
+                .invokeStatic(LOOKUP, BinderTest.class, "varargs");
+
+        assertEquals(methodType(String[].class, String.class, String.class, String.class), handle.type());
+        String[] ary = (String[])handle.invokeExact("one", "two", "three");
+        assertEquals(2, ary.length);
+        assertEquals("two", ary[0]);
+        assertEquals("three", ary[1]);
+    }
+
     @Test
     public void testVarargs() throws Throwable {
         MethodHandle handle = Binder
@@ -385,14 +416,14 @@ public class BinderTest {
                 .varargs(1, String[].class)
                 .invokeStatic(LOOKUP, BinderTest.class, "varargs");
 
-        assertEquals(MethodType.methodType(String[].class, String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String[].class, String.class, String.class, String.class), handle.type());
         String[] ary = (String[])handle.invokeExact("one", "two", "three");
         assertEquals(2, ary.length);
         assertEquals("two", ary[0]);
         assertEquals("three", ary[1]);
 
         // from #2
-        MethodHandle foo = Binder.from(MethodType.methodType(String.class, String.class))
+        MethodHandle foo = Binder.from(methodType(String.class, String.class))
                 .varargs(0, Object[].class)
                 .invokeStatic(MethodHandles.publicLookup(), getClass(), "varargs");
 
@@ -405,7 +436,7 @@ public class BinderTest {
                 .from(String.class)
                 .constant("hello");
 
-        assertEquals(MethodType.methodType(String.class), handle.type());
+        assertEquals(methodType(String.class), handle.type());
         assertEquals("hello", (String)handle.invokeExact());
     }
 
@@ -415,7 +446,7 @@ public class BinderTest {
                 .from(Object.class)
                 .constant("hello");
 
-        assertEquals(MethodType.methodType(Object.class), handle.type());
+        assertEquals(methodType(Object.class), handle.type());
         assertEquals("hello", (Object)handle.invokeExact());
     }
 
@@ -425,7 +456,7 @@ public class BinderTest {
                 .from(String.class, String.class)
                 .identity();
 
-        assertEquals(MethodType.methodType(String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class), handle.type());
         assertEquals("hello", (String)handle.invokeExact("hello"));
     }
 
@@ -441,7 +472,7 @@ public class BinderTest {
                 .fold(fold)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class), handle.type());
         assertEquals("yahoofoo", (String)handle.invokeExact("foo"));
     }
 
@@ -453,7 +484,7 @@ public class BinderTest {
                 .foldStatic(BinderTest.class, "alwaysYahooStatic")
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class), handle.type());
         assertEquals("yahoofoo", (String)handle.invokeExact("foo"));
     }
 
@@ -467,23 +498,36 @@ public class BinderTest {
                 .drop(1)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class), handle.type());
         assertEquals("yahoofoo", (String)handle.invokeExact("foo"));
     }
 
     @Test
     public void testFilter() throws Throwable {
         MethodHandle target = Subjects.concatHandle();
-        MethodHandle filter = LOOKUP.findStatic(BinderTest.class, "addBaz", MethodType.methodType(String.class, String.class));
+        MethodHandle filter = LOOKUP.findStatic(BinderTest.class, "addBaz", methodType(String.class, String.class));
         MethodHandle handle = Binder
                 .from(String.class, String.class, String.class)
                 .filter(0, filter, filter)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class, String.class), handle.type());
         assertEquals("foobazbarbaz", (String)handle.invokeExact("foo", "bar"));
     }
 
+    @Test
+    public void testFilterForward() throws Throwable {
+        MethodHandle target = LOOKUP.findStatic(Subjects.class, "twoIntsToString", methodType(String.class, int.class, int.class));
+        MethodHandle[] filters = {Subjects.nextInt, Subjects.nextInt};
+        MethodHandle handle = Binder.from(String.class, int.class, int.class)
+                .filterForward(0, filters)
+                .invoke(target);
+
+        int first = Subjects.counter.get();
+
+        assertEquals("(" + first + ", " + (first + 1) + ")", (String) handle.invokeExact(0, 0));
+    }
+
     @Test
     public void testInvoke() throws Throwable {
         MethodHandle target = Subjects.concatHandle();
@@ -491,7 +535,7 @@ public class BinderTest {
                 .from(String.class, String.class, String.class)
                 .invoke(target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", "world"));
     }
 
@@ -502,7 +546,7 @@ public class BinderTest {
                 .from(String.class, String.class, String.class)
                 .invoke(LOOKUP, target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", "world"));
     }
 
@@ -513,7 +557,7 @@ public class BinderTest {
                 .from(String.class, String.class, String.class)
                 .invokeQuiet(LOOKUP, target);
 
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", "world"));
     }
 
@@ -523,7 +567,7 @@ public class BinderTest {
                 .from(String.class, String.class, String.class)
                 .invokeStatic(LOOKUP, Subjects.class, "concatStatic");
 
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", "world"));
     }
 
@@ -533,7 +577,7 @@ public class BinderTest {
                 .from(String.class, String.class, String.class)
                 .invokeStaticQuiet(LOOKUP, Subjects.class, "concatStatic");
 
-        assertEquals(MethodType.methodType(String.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact("Hello, ", "world"));
     }
 
@@ -543,7 +587,7 @@ public class BinderTest {
                 .from(String.class, BinderTest.class, String.class, String.class)
                 .invokeVirtual(LOOKUP, "concatVirtual");
 
-        assertEquals(MethodType.methodType(String.class, BinderTest.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, BinderTest.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact(this, "Hello, ", "world"));
     }
 
@@ -553,7 +597,7 @@ public class BinderTest {
                 .from(String.class, BinderTest.class, String.class, String.class)
                 .invokeVirtualQuiet(LOOKUP, "concatVirtual");
 
-        assertEquals(MethodType.methodType(String.class, BinderTest.class, String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, BinderTest.class, String.class, String.class), handle.type());
         assertEquals("Hello, world", (String) handle.invokeExact(this, "Hello, ", "world"));
     }
 
@@ -563,7 +607,7 @@ public class BinderTest {
                 .from(Constructable.class, String.class, String.class)
                 .invokeConstructor(LOOKUP, Constructable.class);
 
-        assertEquals(MethodType.methodType(Constructable.class, String.class, String.class), handle.type());
+        assertEquals(methodType(Constructable.class, String.class, String.class), handle.type());
         assertEquals(new Constructable("foo", "bar"), (Constructable) handle.invokeExact("foo", "bar"));
     }
 
@@ -573,7 +617,7 @@ public class BinderTest {
                 .from(Constructable.class, String.class, String.class)
                 .invokeConstructorQuiet(LOOKUP, Constructable.class);
 
-        assertEquals(MethodType.methodType(Constructable.class, String.class, String.class), handle.type());
+        assertEquals(methodType(Constructable.class, String.class, String.class), handle.type());
         assertEquals(new Constructable("foo", "bar"), (Constructable) handle.invokeExact("foo", "bar"));
     }
 
@@ -584,7 +628,7 @@ public class BinderTest {
                 .from(String.class, Fields.class)
                 .getField(LOOKUP, "instanceField");
         
-        assertEquals(MethodType.methodType(String.class, Fields.class), handle.type());
+        assertEquals(methodType(String.class, Fields.class), handle.type());
         assertEquals("initial", (String)handle.invokeExact(fields));
     }
 
@@ -595,7 +639,7 @@ public class BinderTest {
                 .from(String.class, Fields.class)
                 .getFieldQuiet(LOOKUP, "instanceField");
 
-        assertEquals(MethodType.methodType(String.class, Fields.class), handle.type());
+        assertEquals(methodType(String.class, Fields.class), handle.type());
         assertEquals("initial", (String)handle.invokeExact(fields));
     }
 
@@ -605,7 +649,7 @@ public class BinderTest {
                 .from(String.class)
                 .getStatic(LOOKUP, Fields.class, "staticField");
 
-        assertEquals(MethodType.methodType(String.class), handle.type());
+        assertEquals(methodType(String.class), handle.type());
         assertEquals("initial", (String)handle.invokeExact());
     }
 
@@ -615,7 +659,7 @@ public class BinderTest {
                 .from(String.class)
                 .getStaticQuiet(LOOKUP, Fields.class, "staticField");
 
-        assertEquals(MethodType.methodType(String.class), handle.type());
+        assertEquals(methodType(String.class), handle.type());
         assertEquals("initial", (String)handle.invokeExact());
     }
 
@@ -626,7 +670,7 @@ public class BinderTest {
                 .from(void.class, Fields.class, String.class)
                 .setField(LOOKUP, "instanceField");
 
-        assertEquals(MethodType.methodType(void.class, Fields.class, String.class), handle.type());
+        assertEquals(methodType(void.class, Fields.class, String.class), handle.type());
         handle.invokeExact(fields, "modified");
         assertEquals("modified", fields.instanceField);
     }
@@ -638,7 +682,7 @@ public class BinderTest {
                 .from(void.class, Fields.class, String.class)
                 .setFieldQuiet(LOOKUP, "instanceField");
 
-        assertEquals(MethodType.methodType(void.class, Fields.class, String.class), handle.type());
+        assertEquals(methodType(void.class, Fields.class, String.class), handle.type());
         handle.invokeExact(fields, "modified");
         assertEquals("modified", fields.instanceField);
     }
@@ -650,7 +694,7 @@ public class BinderTest {
                     .from(void.class, String.class)
                     .setStatic(LOOKUP, Fields.class, "staticField");
 
-            assertEquals(MethodType.methodType(void.class, String.class), handle.type());
+            assertEquals(methodType(void.class, String.class), handle.type());
             handle.invokeExact("modified");
             assertEquals("modified", Fields.staticField);
         } finally {
@@ -665,7 +709,7 @@ public class BinderTest {
                     .from(void.class, String.class)
                     .setStaticQuiet(LOOKUP, Fields.class, "staticField");
 
-            assertEquals(MethodType.methodType(void.class, String.class), handle.type());
+            assertEquals(methodType(void.class, String.class), handle.type());
             handle.invokeExact("modified");
             assertEquals("modified", Fields.staticField);
         } finally {
@@ -679,7 +723,7 @@ public class BinderTest {
                 .from(void.class, int.class, String.class)
                 .nop();
         
-        assertEquals(MethodType.methodType(void.class, int.class, String.class), handle.type());
+        assertEquals(methodType(void.class, int.class, String.class), handle.type());
         try {
             handle.invokeExact(1, "foo");
         } catch (Throwable t) {
@@ -693,7 +737,7 @@ public class BinderTest {
                 .from(void.class, BlahException.class)
                 .throwException();
         
-        assertEquals(MethodType.methodType(void.class, BlahException.class), handle.type());
+        assertEquals(methodType(void.class, BlahException.class), handle.type());
         try {
             handle.invokeExact(new BlahException());
             assertTrue("should not reach here", false);
@@ -712,7 +756,7 @@ public class BinderTest {
                 .tryFinally(post)
                 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFoo");
 
-        assertEquals(MethodType.methodType(void.class, String[].class), handle.type());
+        assertEquals(methodType(void.class, String[].class), handle.type());
         String[] stringAry = new String[1];
         handle.invokeExact(stringAry);
         assertEquals("foofinally", stringAry[0]);
@@ -729,7 +773,7 @@ public class BinderTest {
                 .tryFinally(post)
                 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFooAndRaise");
 
-        assertEquals(MethodType.methodType(void.class, String[].class), handle.type());
+        assertEquals(methodType(void.class, String[].class), handle.type());
         String[] stringAry = new String[1];
         try {
             handle.invokeExact(stringAry);
@@ -755,7 +799,7 @@ public class BinderTest {
                 .catchException(BlahException.class, ignoreException)
                 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFooAndRaise");
 
-        assertEquals(MethodType.methodType(void.class, String[].class), handle.type());
+        assertEquals(methodType(void.class, String[].class), handle.type());
         String[] stringAry = new String[1];
         try {
             handle.invokeExact(stringAry);
@@ -776,7 +820,7 @@ public class BinderTest {
                 .tryFinally(post)
                 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFooReturnInt");
 
-        assertEquals(MethodType.methodType(int.class, String[].class), handle.type());
+        assertEquals(methodType(int.class, String[].class), handle.type());
         String[] stringAry = new String[1];
         assertEquals(1, (int)handle.invokeExact(stringAry));
         assertEquals("foofinally", stringAry[0]);
@@ -793,7 +837,7 @@ public class BinderTest {
                 .tryFinally(post)
                 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFooReturnIntAndRaise");
 
-        assertEquals(MethodType.methodType(int.class, String[].class), handle.type());
+        assertEquals(methodType(int.class, String[].class), handle.type());
         String[] stringAry = new String[1];
         try {
             int x = (int)handle.invokeExact(stringAry);
@@ -820,7 +864,7 @@ public class BinderTest {
                 .catchException(BlahException.class, ignoreException)
                 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFooReturnIntAndRaise");
 
-        assertEquals(MethodType.methodType(int.class, String[].class), handle.type());
+        assertEquals(methodType(int.class, String[].class), handle.type());
         String[] stringAry = new String[1];
         try {
             assertEquals(1, (int)handle.invokeExact(stringAry));
@@ -836,7 +880,7 @@ public class BinderTest {
                 .from(void.class, Object[].class, int.class, Object.class)
                 .arraySet();
 
-        assertEquals(MethodType.methodType(void.class, Object[].class, int.class, Object.class), handle.type());
+        assertEquals(methodType(void.class, Object[].class, int.class, Object.class), handle.type());
         Object[] ary = new Object[1];
         handle.invokeExact(ary, 0, (Object)"foo");
         assertEquals(ary[0], "foo");
@@ -848,10 +892,46 @@ public class BinderTest {
                 .from(Object.class, Object[].class, int.class)
                 .arrayGet();
 
-        assertEquals(MethodType.methodType(Object.class, Object[].class, int.class), handle.type());
+        assertEquals(methodType(Object.class, Object[].class, int.class), handle.type());
         Object[] ary = new Object[] {"foo"};
         assertEquals(handle.invokeExact(ary, 0), "foo");
     }
+
+    public static final VarHandle.AccessMode[] GET_ACCESS_MODES = new VarHandle.AccessMode[]{
+            VarHandle.AccessMode.GET,
+            VarHandle.AccessMode.GET_VOLATILE,
+            VarHandle.AccessMode.GET_ACQUIRE,
+            VarHandle.AccessMode.GET_OPAQUE};
+
+    public static final VarHandle.AccessMode[] SET_ACCESS_MODES = new VarHandle.AccessMode[]{
+            VarHandle.AccessMode.SET,
+            VarHandle.AccessMode.SET_VOLATILE,
+            VarHandle.AccessMode.SET_RELEASE,
+            VarHandle.AccessMode.SET_OPAQUE};
+
+    @Test
+    public void testArrayAccess() throws Throwable {
+        for (VarHandle.AccessMode mode : GET_ACCESS_MODES) {
+            MethodHandle handle = Binder
+                    .from(Object.class, Object[].class, int.class)
+                    .arrayAccess(mode);
+
+            assertEquals(methodType(Object.class, Object[].class, int.class), handle.type());
+            Object[] ary = new Object[]{"foo"};
+            assertEquals(handle.invokeExact(ary, 0), "foo");
+        }
+
+        for (VarHandle.AccessMode mode : SET_ACCESS_MODES) {
+            MethodHandle handle = Binder
+                    .from(void.class, Object[].class, int.class, Object.class)
+                    .arrayAccess(mode);
+
+            assertEquals(methodType(void.class, Object[].class, int.class, Object.class), handle.type());
+            Object[] ary = new Object[1];
+            handle.invokeExact(ary, 0, (Object) "foo");
+            assertEquals(ary[0], "foo");
+        }
+    }
     
     @Test
     public void testBranch() throws Throwable {
@@ -869,7 +949,7 @@ public class BinderTest {
                                 .invokeStatic(LOOKUP, BinderTest.class, "addBaz")
                 );
         
-        assertEquals(MethodType.methodType(String.class, String.class), handle.type());
+        assertEquals(methodType(String.class, String.class), handle.type());
         assertEquals("foobar", (String)handle.invokeExact("foo"));
         assertEquals("quuxbaz", (String)handle.invokeExact("quux"));
     }
@@ -877,7 +957,7 @@ public class BinderTest {
     ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
     public static MethodHandle intLongHandle() throws Exception {
-        return LOOKUP.findStatic(BinderTest.class, "intLong", MethodType.methodType(String.class, int.class, long.class));
+        return LOOKUP.findStatic(BinderTest.class, "intLong", methodType(String.class, int.class, long.class));
     }
 
     public String concatVirtual(String a, String b) {
@@ -957,7 +1037,7 @@ public class BinderTest {
     }
 
     public static MethodHandle mixedHandle() throws Exception {
-        return LOOKUP.findStatic(BinderTest.class, "mixed", MethodType.methodType(void.class, String.class, int.class, float.class));
+        return LOOKUP.findStatic(BinderTest.class, "mixed", methodType(void.class, String.class, int.class, float.class));
     }
 
     public static void mixed(String a, int b, float c) {


=====================================
src/test/java/com/headius/invokebinder/Subjects.java
=====================================
@@ -4,6 +4,7 @@ import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Created by headius on 1/25/14.
@@ -80,5 +81,17 @@ public class Subjects {
     public static String upperCase(String x) {
         return x.toUpperCase();
     }
+
+    public static String twoIntsToString(int a, int b) {
+        return "(" + a + ", " + b + ")";
+    }
+
+    public static int nextInt(int ignored) {
+        return counter.getAndIncrement();
+    }
+
+    public static MethodHandle nextInt = Binder.from(int.class, int.class).invokeStaticQuiet(LOOKUP, Subjects.class, "nextInt");
+
+    public static final AtomicInteger counter = new AtomicInteger(0);
     
 }


=====================================
src/test/java/com/headius/invokebinder/ToJavaTest.java
=====================================
@@ -0,0 +1,162 @@
+package com.headius.invokebinder;
+
+import com.headius.invokebinder.transform.Cast;
+import com.headius.invokebinder.transform.Catch;
+import com.headius.invokebinder.transform.Collect;
+import com.headius.invokebinder.transform.Convert;
+import com.headius.invokebinder.transform.Drop;
+import com.headius.invokebinder.transform.Filter;
+import com.headius.invokebinder.transform.FilterReturn;
+import com.headius.invokebinder.transform.Fold;
+import com.headius.invokebinder.transform.Insert;
+import com.headius.invokebinder.transform.Spread;
+import com.headius.invokebinder.transform.Transform;
+import com.headius.invokebinder.transform.Varargs;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class ToJavaTest {
+    @Test
+    public void testCast() {
+        Cast cast = new Cast(MethodType.methodType(String.class, Integer[].class, float.class));
+
+        String toJava = cast.toJava(null);
+
+        Assert.assertEquals("handle = MethodHandles.explicitCastArguments(handle, java.lang.Integer[].class, float.class);", toJava);
+    }
+
+    @Test
+    public void testCatch() {
+        Catch ctch = new Catch(RuntimeException.class, DUMMY_HANDLE);
+
+        String toJava = ctch.toJava(null);
+
+        Assert.assertEquals("handle = MethodHandles.catchException(handle, java.lang.RuntimeException.class, " + Catch.EXCEPTION_HANDLER_JAVA + ");", toJava);
+    }
+
+    @Test
+    public void testCollect() {
+        MethodType source = MethodType.methodType(void.class, int.class, int.class, int.class);
+        MethodType incoming = MethodType.methodType(void.class, int.class, int[].class);
+
+        Collect collect = new Collect(source, 1, int[].class);
+
+        String toJava = collect.toJava(incoming);
+
+        Assert.assertEquals("handle = handle.asCollector(int[].class, 2);", toJava);
+
+        collect = new Collect(source, 1, 1, int[].class);
+
+        toJava = collect.toJava(incoming);
+
+        String expected =
+                "handle = MethodHandles.permuteArguments(handle, MethodType.methodType(void.class, int.class, int.class, int[].class), new int[] {0, 1});\n" +
+                "handle = handle.asCollector(int[].class, 1);\n" +
+                "handle = MethodHandles.permuteArguments(handle, MethodType.methodType(void.class, int.class, int.class, int.class), new int[] {0, 2, 1});";
+
+        Assert.assertEquals(expected, toJava);
+    }
+
+    @Test
+    public void testConvert() {
+        MethodType source = MethodType.methodType(void.class, String.class);
+        MethodType incoming = MethodType.methodType(void.class, Object.class);
+
+        Convert convert = new Convert(source);
+
+        String toJava = convert.toJava(incoming);
+
+        String expected = "handle = MethodHandles.explicitCastArguments(handle.asType(MethodType.methodType(void.class, java.lang.String.class), MethodType.methodType(void.class, java.lang.String.class));";
+
+        Assert.assertEquals(expected, toJava);
+
+        source = MethodType.methodType(long.class, String.class);
+        incoming = MethodType.methodType(int.class, Object.class);
+
+        convert = new Convert(source);
+
+        toJava = convert.toJava(incoming);
+
+        Assert.assertEquals("handle = handle.asType(MethodType.methodType(long.class, java.lang.String.class));", toJava);
+    }
+
+    @Test
+    public void testDrop() {
+        MethodType incoming = MethodType.methodType(void.class);
+
+        Drop drop = new Drop(0, int.class, int.class, int.class);
+
+        String toJava = drop.toJava(incoming);
+
+        Assert.assertEquals("handle = MethodHandles.dropArguments(handle, 0, int.class, int.class, int.class);", toJava);
+    }
+
+    @Test
+    public void testFilter() {
+        Filter filter = new Filter(0, null);
+
+        String toJava = filter.toJava(DUMMY_HANDLE.type());
+
+        Assert.assertEquals("handle = MethodHandles.filterArguments(handle, 0, " + Filter.FILTER_FUNCTIONS_JAVA + ");", toJava);
+    }
+
+    @Test
+    public void testFilterReturn() {
+        FilterReturn filterReturn = new FilterReturn(DUMMY_HANDLE);
+
+        String toJava = filterReturn.toJava(null);
+
+        Assert.assertEquals("handle = MethodHandles.filterReturnValue(handle, " + FilterReturn.FILTER_FUNCTION_JAVA + ");", toJava);
+    }
+
+    @Test
+    public void testFold() {
+        Fold fold = new Fold(DUMMY_HANDLE);
+
+        String toJava = fold.toJava(null);
+
+        Assert.assertEquals("handle = MethodHandles.foldArguments(handle, " + Fold.FOLD_FUNCTION_JAVA + ");", toJava);
+    }
+
+    @Test
+    public void testInsert() {
+        Insert insert = new Insert(0, new Class[] {int.class, double.class, Object.class}, 1L, 1.0F, "hello");
+
+        String toJava = insert.toJava(null);
+
+        Assert.assertEquals("handle = MethodHandles.insertArguments(handle, 0, (int)1L, (double)1.0F, (java.lang.Object)value3);", toJava);
+    }
+
+    @Test
+    public void testSpread() {
+        Spread spread = new Spread(OBJECTARRAY_HANDLE.type(), Object.class, String.class, Integer.class);
+
+        String toJava = spread.toJava(null);
+
+        Assert.assertEquals("handle = handle.asSpreader(java.lang.Object[].class, 3);", toJava);
+    }
+
+    @Test
+    public void testVarargs() {
+        Varargs varargs = new Varargs(OBJECTS_HANDLE.type(), 0, Object[].class);
+
+        String toJava = varargs.toJava(null);
+
+        Assert.assertEquals("handle = handle.asVarargsCollector(java.lang.Object[].class).asType(" + Transform.generateMethodType(OBJECTS_HANDLE.type()) + ");", toJava);
+    }
+
+    public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    private static final MethodHandle DUMMY_HANDLE = Binder.from(void.class).invokeStaticQuiet(LOOKUP, ToJavaTest.class, "dummy");
+    private static void dummy() {}
+
+    private static final MethodHandle OBJECTARRAY_HANDLE = Binder.from(void.class, Object[].class).invokeStaticQuiet(LOOKUP, ToJavaTest.class, "objectArray");
+    private static void objectArray(Object[] ary) {}
+
+    private static final MethodHandle OBJECTS_HANDLE = Binder.from(void.class, Object.class, Object.class, Object.class).invokeStaticQuiet(LOOKUP, ToJavaTest.class, "objects");
+    private static void objects(Object o, Object p, Object q) {}
+}



View it on GitLab: https://salsa.debian.org/java-team/invokebinder/-/commit/11486fa1b7bc5b3c407ebe988f890599c1b5beea

-- 
View it on GitLab: https://salsa.debian.org/java-team/invokebinder/-/commit/11486fa1b7bc5b3c407ebe988f890599c1b5beea
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20221026/afd55dd0/attachment.htm>


More information about the pkg-java-commits mailing list