package scotch.compiler.target; import static java.util.stream.Collectors.toList; import static me.qmx.jitescript.util.CodegenUtils.ci; import static me.qmx.jitescript.util.CodegenUtils.p; import static me.qmx.jitescript.util.CodegenUtils.sig; import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static scotch.compiler.output.GeneratedClass.ClassType.DATA_CONSTRUCTOR; import static scotch.compiler.output.GeneratedClass.ClassType.DATA_TYPE; import static scotch.compiler.output.GeneratedClass.ClassType.MODULE; import static scotch.compiler.syntax.reference.DefinitionReference.rootRef; import static scotch.symbol.Symbol.moduleClass; import static scotch.symbol.Symbol.toJavaName; import static scotch.util.Pair.pair; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.collect.ImmutableList; import me.qmx.jitescript.CodeBlock; import me.qmx.jitescript.JDKVersion; import me.qmx.jitescript.JiteClass; import scotch.compiler.intermediate.IntermediateGraph; import scotch.compiler.output.GeneratedClass; import scotch.compiler.output.GeneratedClass.ClassType; import scotch.compiler.syntax.reference.DefinitionReference; import scotch.runtime.Callable; import scotch.runtime.Copyable; import scotch.symbol.Symbol; import scotch.symbol.Symbol.QualifiedSymbol; import scotch.symbol.type.TypeDescriptor; import scotch.util.Pair; public class BytecodeGenerator { private final IntermediateGraph graph; private final Map<String, JiteClass> moduleClasses; private final Deque<Pair<JiteClass, ClassType>> classes; private final List<Pair<JiteClass, ClassType>> finishedClasses; private final Deque<List<String>> argumentOffsets; private int lambdas; private int applies; private int accesses; public BytecodeGenerator(IntermediateGraph graph) { this.graph = graph; this.moduleClasses = new HashMap<>(); this.classes = new ArrayDeque<>(); this.finishedClasses = new ArrayList<>(); this.argumentOffsets = new ArrayDeque<>(); } public void beginConstant(Symbol symbol) { JiteClass jiteClass = new JiteClass( currentClass().getClassName() + "$" + toJavaName(symbol.getMemberName()), currentClass().getClassName(), new String[0] ); pushClass(jiteClass, DATA_CONSTRUCTOR); jiteClass.defineDefaultConstructor(); jiteClass.defineMethod("call", ACC_PUBLIC, sig(Object.class), new CodeBlock() {{ aload(0); areturn(); }}); } public void beginConstructor(Symbol symbol) { JiteClass jiteClass = new JiteClass( currentClass().getClassName() + "$" + toJavaName(symbol.getMemberName()), currentClass().getClassName(), new String[] { p(Copyable.class) }); pushClass(jiteClass, DATA_CONSTRUCTOR); } public List<Integer> getArgumentOffsets() { AtomicInteger counter = new AtomicInteger(); return argumentOffsets.peek().stream() .map(argument -> counter.getAndIncrement()) .collect(toList()); } public void defineField(String name) { currentClass().defineField(toJavaName(name), ACC_PRIVATE | ACC_FINAL, ci(Callable.class), null); } public List<String> getArguments() { return ImmutableList.copyOf(argumentOffsets.peek()); } public void method(String name, int access, String signature, CodeBlock body) { currentClass().defineMethod(name, access, signature, body); } public void pushClass(JiteClass jiteClass, ClassType dataType) { classes.push(pair(jiteClass, dataType)); } public void beginData(Symbol symbol) { JiteClass dataClass = new JiteClass(symbol.getClassName()) {{ defineDefaultConstructor(); }}; pushClass(dataClass, DATA_TYPE); } public void beginMethod(List<String> captures) { argumentOffsets.push(ImmutableList.copyOf(captures)); } public void beginMethod(List<String> captures, String argument) { argumentOffsets.push(new ArrayList<String>() {{ addAll(ImmutableList.copyOf(captures)); add(argument); }}); } public void beginModule(String moduleName) { lambdas = 0; applies = 0; accesses = 0; JiteClass jiteClass = new JiteClass(moduleClass(moduleName)) {{ defineDefaultConstructor(ACC_PRIVATE); }}; moduleClasses.put(moduleName, jiteClass); pushClass(jiteClass, MODULE); } public void createValue(Symbol symbol, CodeBlock valueBody) { moduleClasses.get(((QualifiedSymbol) symbol).getModuleName()) .defineMethod(symbol.getMethodName(), ACC_STATIC | ACC_PUBLIC, sig(Callable.class), valueBody); } public void createValueType(Symbol symbol, CodeBlock valueTypeBody) { moduleClasses.get(((QualifiedSymbol) symbol).getModuleName()) .defineMethod(symbol.getMethodName() + "$type", ACC_STATIC | ACC_PUBLIC, sig(TypeDescriptor.class), valueTypeBody); } public JiteClass currentClass() { return classes.peek().getLeft(); } public void endClass() { finishedClasses.add(classes.pop()); } public void endMethod() { argumentOffsets.pop(); } public List<GeneratedClass> generateBytecode() { generateBytecode(rootRef()); return finishedClasses.stream() .map(pair -> pair.into( (jiteClass, type) -> new GeneratedClass(type, jiteClass.getClassName().replace("/", "."), jiteClass.toBytes(JDKVersion.V1_8)))) .sorted() .collect(toList()); } public void generateBytecode(DefinitionReference reference) { graph.getDefinition(reference) .orElseThrow(IllegalStateException::new) .generateBytecode(this); } public int offsetOf(String argument) { return argumentOffsets.peek().indexOf(argument); } public String reserveAccess() { return "access$" + accesses++; } public String reserveApply() { return "apply$" + applies++; } public String reserveLambda() { return "lambda$" + lambdas++; } public void storeOffset(String variable) { argumentOffsets.peek().add(variable); } }