package edu.upol.paradise.tests.manual; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.Modifier; import javassist.bytecode.Bytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.InstructionPrinter; import javassist.bytecode.Opcode; public class Brainfuck { private ClassPool cp = ClassPool.getDefault(); private int counter = 0; private class StoredJmpPoint { public int jmpPoint; public int bcodePoint; } private String source; private Runnable compile() throws Throwable { String className = "edu.upol.paradise.tests.manual.BrainFuck$" + counter++; CtClass cls = cp.makeClass(className); cls.addInterface(cp.getCtClass("java/lang/Runnable")); CtField f = new CtField(CtClass.intType, "pointer", cls); f.setModifiers(Modifier.PUBLIC); cls.addField(f, "0"); f = new CtField(cp.get("[I"), "tape", cls); f.setModifiers(Modifier.PUBLIC); cls.addField(f, "new int[30000]"); CtMethod m = CtNewMethod.make("public void run(){}", cls); ConstPool pool = m.getMethodInfo().getConstPool(); Bytecode bc = new Bytecode(pool); compile(source, bc, pool, cls); CodeAttribute at = bc.toCodeAttribute(); at.computeMaxStack(); at.setMaxLocals(1); m.getMethodInfo().setCodeAttribute(at); InstructionPrinter.print(m, System.err); cls.addMethod(m); CtConstructor ct = CtNewConstructor.defaultConstructor(cls); cls.addConstructor(ct); return (Runnable) cls.toClass().newInstance(); } private void compile(String src, Bytecode bc, ConstPool pool, CtClass cls) { Queue bq = new LinkedList(); for (char c : src.toCharArray()){ switch (c){ case '>': movePointerRight(bc, pool, cls); break; case '<': movePointerLeft(bc, pool, cls); break; case '+': addToPointed(bc, pool, cls); break; case '-': subFromPointed(bc, pool, cls); break; case '.': print(bc, pool, cls); break; case ',': read(bc, pool, cls); break; case '[': beginloop(bc, pool, cls, bq); break; case ']': endloop(bc, pool, cls, bq); break; } } bc.addReturn(null); } private void endloop(Bytecode bc, ConstPool pool, CtClass cls, Queue bq) { StoredJmpPoint jp = bq.poll(); loadFromTape(bc, cls); bc.addOpcode(Opcode.IFNE); int point = bc.currentPc(); bc.add(0, 0); bc.write16bit(bc.currentPc()-2, (jp.jmpPoint - point + 1)); point = bc.currentPc(); bc.write16bit(jp.bcodePoint, point-jp.bcodePoint+1); } private void beginloop(Bytecode bc, ConstPool pool, CtClass cls, Queue bq) { StoredJmpPoint jp = new StoredJmpPoint(); jp.jmpPoint = bc.currentPc(); loadFromTape(bc, cls); bc.addOpcode(Opcode.IFEQ); jp.bcodePoint = bc.currentPc(); bc.add(0, 0); bq.add(jp); } private void read(Bytecode bc, ConstPool pool, CtClass cls) { // store bc.addAload(0); bc.addGetfield(cls, "tape", "[I"); bc.addAload(0); // for pointer bc.addGetfield(cls, "pointer", "I"); bc.addGetstatic("java/lang/System", "in", "Ljava/io/InputStream;"); bc.addInvokevirtual("java/io/InputStream", "read", "()I"); bc.addOpcode(Opcode.IASTORE); } private void print(Bytecode bc, ConstPool pool, CtClass cls) { bc.addGetstatic("java/lang/System", "out", "Ljava/io/PrintStream;"); // load loadFromTape(bc, cls); // print bc.addInvokevirtual("java/io/PrintStream", "print", "(C)V"); } private void addToPointed(Bytecode bc, ConstPool pool, CtClass cls) { // store bc.addAload(0); bc.addGetfield(cls, "tape", "[I"); bc.addAload(0); // for pointer bc.addGetfield(cls, "pointer", "I"); // load loadFromTape(bc, cls); // inc bc.addIconst(1); bc.addOpcode(Opcode.IADD); bc.addOpcode(Opcode.IASTORE); } private void subFromPointed(Bytecode bc, ConstPool pool, CtClass cls) { // store bc.addAload(0); bc.addGetfield(cls, "tape", "[I"); bc.addAload(0); // for pointer bc.addGetfield(cls, "pointer", "I"); // load loadFromTape(bc, cls); // inc bc.addIconst(1); bc.addOpcode(Opcode.ISUB); bc.addOpcode(Opcode.IASTORE); } private void loadFromTape(Bytecode bc, CtClass cls) { bc.addAload(0); bc.addGetfield(cls, "tape", "[I"); bc.addAload(0); // for pointer bc.addGetfield(cls, "pointer", "I"); bc.addOpcode(Opcode.IALOAD); } private void movePointerLeft(Bytecode bc, ConstPool pool, CtClass cls) { bc.addAload(0); // for putfield bc.addAload(0); // for pointer bc.addGetfield(cls, "pointer", "I"); bc.addIconst(1); bc.addOpcode(Opcode.ISUB); bc.addIconst(0); bc.addInvokestatic("java/lang/Math", "max", "(II)I"); bc.addPutfield(cls, "pointer", "I"); } private void movePointerRight(Bytecode bc, ConstPool pool, CtClass cls) { bc.addAload(0); // for putfield bc.addAload(0); // for pointer bc.addGetfield(cls, "pointer", "I"); bc.addIconst(1); bc.addOpcode(Opcode.IADD); addTapeMax(bc, pool, cls); bc.addInvokestatic("java/lang/Math", "min", "(II)I"); bc.addPutfield(cls, "pointer", "I"); } private void addTapeMax(Bytecode bc, ConstPool pool, CtClass cls) { bc.addAload(0); bc.addGetfield(cls, "tape", "[I"); bc.addOpcode(Opcode.ARRAYLENGTH); } private void setSource(String code) { this.source = code; } public static void main(String[] arg) throws Throwable{ String code = ",>>[-]<<[->>+<<]"; Brainfuck bf = new Brainfuck(); bf.setSource(code); Runnable r = bf.compile(); r.run(); } }