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 java.util.function.Supplier;
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.AccessorSupport;
import scotch.runtime.Callable;
import scotch.runtime.SuppliedThunk;
@EqualsAndHashCode(callSuper = false)
@ToString
public class IntermediateAccessor extends IntermediateValue {
private final List<String> captures;
private final IntermediateValue target;
private final String fieldName;
private final String methodName;
IntermediateAccessor(List<String> captures, IntermediateValue target, String fieldName, String methodName) {
this.captures = ImmutableList.copyOf(captures);
this.target = target;
this.fieldName = fieldName;
this.methodName = methodName;
}
@Override
public CodeBlock generateBytecode(BytecodeGenerator generator) {
List<Integer> argumentOffsets = generator.getArgumentOffsets();
Class<?>[] argumentTypes = getArgumentTypes(argumentOffsets);
return new CodeBlock() {{
newobj(p(SuppliedThunk.class));
dup();
argumentOffsets.forEach(this::aload);
lambda(generator.currentClass(), new LambdaBlock(generator.reserveAccess()) {{
function(p(Supplier.class), "get", sig(Object.class));
specialize(sig(Callable.class));
capture(argumentTypes);
delegateTo(ACC_STATIC, sig(Callable.class, argumentTypes), new CodeBlock() {{
List<String> arguments = generator.getArguments();
generator.beginMethod(arguments);
append(target.generateBytecode(generator));
ldc(methodName);
invokestatic(p(AccessorSupport.class), "access", sig(Callable.class, Callable.class, String.class));
areturn();
generator.endMethod();
}});
}});
invokespecial(p(SuppliedThunk.class), "<init>", sig(void.class, Supplier.class));
}};
}
private Class<?>[] getArgumentTypes(List<Integer> offsets) {
Class<?>[] types = new Class<?>[offsets.size()];
for (int i = 0; i < offsets.size(); i++) {
types[i] = Callable.class;
}
return types;
}
}