package scotch.compiler.intermediate; import static me.qmx.jitescript.util.CodegenUtils.p; import static me.qmx.jitescript.util.CodegenUtils.sig; import java.util.List; import com.google.common.collect.ImmutableList; import lombok.EqualsAndHashCode; import lombok.ToString; import me.qmx.jitescript.CodeBlock; import me.qmx.jitescript.LambdaBlock; import scotch.compiler.target.BytecodeGenerator; import scotch.runtime.Applicable; import scotch.runtime.Callable; @EqualsAndHashCode(callSuper = false) @ToString public class IntermediateFunction extends IntermediateValue { private final List<String> captures; private final String argument; private final IntermediateValue body; public IntermediateFunction(List<String> captures, String argument, IntermediateValue body) { this.captures = ImmutableList.copyOf(captures); this.argument = argument; this.body = body; } @Override public CodeBlock generateBytecode(BytecodeGenerator generator) { return new CodeBlock() {{ captures.forEach(capture -> aload(generator.offsetOf(capture))); lambda(generator.currentClass(), new LambdaBlock(generator.reserveLambda()) {{ function(p(Applicable.class), "apply", sig(Callable.class, Callable.class)); capture(getCaptureTypes()); delegateTo(ACC_STATIC, sig(Callable.class, getLambdaArgumentTypes()), new CodeBlock() {{ generator.beginMethod(captures, argument); append(body.generateBytecode(generator)); areturn(); generator.endMethod(); }}); }}); }}; } private Class<?>[] getLambdaArgumentTypes() { return getCallables(captures.size() + 1); } private Class<?>[] getCallables(int size) { Class<?>[] callables = new Class<?>[size]; for (int i = 0; i < size; i++) { callables[i] = Callable.class; } return callables; } private Class<?>[] getCaptureTypes() { return getCallables(captures.size()); } }