presto-spi/src/main/java/io/prestosql/spi/block/MethodHandleUtil.java/MethodHandleUtil/compose
class MethodHandleUtil:    public static MethodHandle compose(MethodHandle f, MethodHandle g, MethodHandle h)
    {
        if (f.type().parameterCount() != 2) {
            throw new IllegalArgumentException(format("f.parameterCount != 2. f: %s", f.type()));
        }
        if (f.type().parameterType(0) != g.type().returnType()) {
            throw new IllegalArgumentException(format("f.parameter(0) != g.return. f: %s  g: %s", f.type(), g.type()));
        }
        if (f.type().parameterType(1) != h.type().returnType()) {
            throw new IllegalArgumentException(format("f.parameter(0) != h.return. f: %s  h: %s", f.type(), h.type()));
        }

        // (V, T1, T2, ..., Tn, U)R
        MethodType typeVTU = f.type().dropParameterTypes(0, 1).appendParameterTypes(h.type().parameterList()).appendParameterTypes(f.type().parameterType(0));
        // Semantics: f => f
        // Type: (U, V)R => (V, T1, T2, ..., Tn, U)R
        MethodHandle fVTU = MethodHandles.permuteArguments(f, typeVTU, h.type().parameterCount() + 1, 0);
        // Semantics: f => fh
        // Type: (V, T1, T2, ..., Tn, U)R => (T1, T2, ..., Tn, U)R
        MethodHandle fhTU = MethodHandles.foldArguments(fVTU, h);

        // reorder: [m+1, m+2, ..., m+n, 0]
        int[] reorder = new int[fhTU.type().parameterCount()];
        for (int i = 0; i < reorder.length - 1; i++) {
            reorder[i] = i + 1 + g.type().parameterCount();
        }
        reorder[reorder.length - 1] = 0;

        // (U, S1, S2, ..., Sm, T1, T2, ..., Tn)R
        MethodType typeUST = f.type().dropParameterTypes(1, 2).appendParameterTypes(g.type().parameterList()).appendParameterTypes(h.type().parameterList());
        // Semantics: f.h => f.h
        // Type: (T1, T2, ..., Tn, U)R => (U, S1, S2, ..., Sm, T1, T2, ..., Tn)R
        MethodHandle fhUST = MethodHandles.permuteArguments(fhTU, typeUST, reorder);

        // Semantics: fh => fgh
        // Type: (U, S1, S2, ..., Sm, T1, T2, ..., Tn)R => (S1, S2, ..., Sm, T1, T2, ..., Tn)R
        return MethodHandles.foldArguments(fhUST, g);
    }