import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

import static java.lang.invoke.MethodHandles.identity;
import static java.lang.invoke.MethodHandles.loop;

/**
 * @author Anastasiya Solodkaya.
 */
public class EmptyBodyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException {
        Increment increment = new Increment();
        MethodHandles.whileLoop(increment.initHandle(), increment.predHandle(), null);
        MethodHandles.doWhileLoop(increment.initHandle(), null, increment.predHandle());
        whileImplementation(increment.initHandle(), increment.predHandle(), null);
        doWhileImplementation(increment.initHandle(), increment.predHandle(), null);
    }


    /**
     * See implementation at {@link MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle)}
     */
    private static MethodHandle whileImplementation(MethodHandle init, MethodHandle pred, MethodHandle body) {
        MethodHandle[]
                checkExit = {null, null, pred, identity(init.type().returnType())},
                varBody = {init, body};
        return loop(checkExit, varBody);
    }

    /**
     * See implementation at {@link MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle)}
     */
    private static MethodHandle doWhileImplementation(MethodHandle init, MethodHandle pred, MethodHandle body) {
        MethodHandle[] clause = {init, body, pred, identity(init.type().returnType())};
        return loop(clause);
    }

    private static class Increment {
        int init(int k) {
            return 1;
        }

        boolean pred(int i, int k) {
            return i < k;
        }

        MethodHandle initHandle() throws NoSuchMethodException, IllegalAccessException {
            return MethodHandles.lookup().findVirtual(getClass(), "init", MethodType.methodType(int.class, int.class)).bindTo(this);
        }

        MethodHandle predHandle() throws NoSuchMethodException, IllegalAccessException {
            return MethodHandles.lookup().findVirtual(getClass(), "pred", MethodType.methodType(boolean.class, int.class, int.class)).bindTo(this);
        }
    }
}
