Tue, 06 Apr 2010 14:49:52 +0100
Add a bytecode interpreter \o/
lvm.js | file | annotate | diff | comparison | revisions |
--- a/lvm.js Tue Apr 06 14:47:21 2010 +0100 +++ b/lvm.js Tue Apr 06 14:49:52 2010 +0100 @@ -56,35 +56,126 @@ return new LValue("function", func); } -var default_environment = new LValue("table", {}); - -var print; -if(typeof(document) == "object") - print = function (a) { document.write(a+"<br/>") }; // Browser -else - print = require("sys").puts; // Nodejs - -default_environment.setIndex(LValueFromString("print"), LValueFromFunction(print)); - -function LFunction() +function LBinaryChunk(chunk) { - this.constants = []; + this.chunk = chunk; + this.pos = 12; + + this.sourceName = this.readString(); + this.lineDefined = this.readInt(); + this.lastLineDefined = this.readInt(); + this.numUpvalues = this.readByte(); + this.numParameters = this.readByte(); + this.isVararg = this.readByte(); + this.maxStackSize = this.readByte(); + this.instructions = []; - this.environment = default_environment; + + this.numInstructions = this.readInt(); + for(var i=0;i<this.numInstructions;i++) + { + var ins = this.readInt(); + this.instructions.push([ + ins&0x3F, // Opcode + (ins>>6)&0xFF, // Field A + (ins>>23)&0x1FF, // Field B + (ins>>14)&0x1FF // Field C + ]); + if(debugMode) + { + var pi = this.instructions[this.instructions.length-1]; + sys.puts("Pos: "+(this.pos-4)+" Ins: "+ins+" OP: "+INS_OPCODE(pi)+" A: "+INS_A(pi)+" B: "+INS_B(pi)+" C: "+INS_C(pi)+" Bx: "+INS_Bx(pi)); + } + } + + this.constants = []; + + this.numConstants = this.readInt(); + for(var i=0;i<this.numConstants;i++) + { + var type = this.readByte(); + switch(type) + { + case 0: // Nil + this.constants.push(new LValue("nil", null)); + break; + case 1: // Boolean + this.constants.push(new LValue("boolean", this.readByte())); + break; + case 3: // Number + break; + case 4: // String + this.constants.push(LValueFromString(this.readString())); + break; + default: + throw "Invalid constant type "+type+" in bytecode"; + } + } + return this; } -LFunction.prototype = { - addInstruction: function (instruction) +LBinaryChunk.prototype = { + readBytes: function (n) + { + return this.chunk.slice(this.pos, this.pos+=n); + }, + readByte: function () { - this.instructions.push(instruction); + return this.readBytes(1).charCodeAt(0); + }, + readInt: function () + { + //FIXME: Endianness + return this.readByte() | (this.readByte()<<8) + | (this.readByte()<<16) | (this.readByte()<<24); + }, + readString: function () + { + var len = this.readInt(); + return this.readBytes(len).substring(0,len-1); }, - addConstant: function (constant) - { - this.constants.push(constant); - } }; +function INS_OPCODE(ins) +{ + return ins[0]; +} + +function INS_A(ins) +{ + return ins[1]; +} + +function INS_B(ins) +{ + return ins[2]; +} + +function INS_C(ins) +{ + return ins[3]; +} + +function INS_Bx(ins) +{ + return ((INS_C(ins))|(INS_B(ins)<<9)); +} + +function INS_sBx(ins) +{ + return INS_Bx(ins)+0x1FFFE; +} + +function LFunction(chunk, env) +{ + function F() {}; + F.prototype = chunk; + var o = new F(); + o.environment = env; + return o; +} + function LVM() { this.callstack = []; @@ -101,7 +192,12 @@ while(this.callstack.length>0) { instruction = this.frame.f.instructions[this.frame.pc++]; - switch(instruction[0]) + if(debugMode) + { + sys.puts("PC: "+(this.frame.pc-1)+" OP: "+instruction[0]); + sys.puts("STACK: "+JSON.stringify(this.frame.reg)); + } + switch(INS_OPCODE(instruction)) { case OP_MOVE: this.frame.reg[instruction[1]] = this.frame.reg[instruction[2]]; @@ -145,23 +241,25 @@ }; var testvm = new LVM(); -var mycode = new LFunction(); -mycode.addConstant(LValueFromString("hello")); -mycode.addConstant(LValueFromString("print")); +var fs=require("fs"); +var sys=require("sys"); +var c = new LBinaryChunk(fs.readFileSync("luac.out", "binary")); + +var default_environment = new LValue("table", {}); -mycode.addInstruction([OP_LOADK, 0, 0]); -mycode.addInstruction([OP_LOADNIL, 1, 1]); -mycode.addInstruction([OP_MOVE, 1, 0]); -mycode.addInstruction([OP_GETGLOBAL, 2, 1]); -mycode.addInstruction([OP_MOVE, 3, 1]); -mycode.addInstruction([OP_CALL, 2, 2, 1]); -mycode.addInstruction([OP_RETURN, 0, 1]); +var print; +if(typeof(document) == "object") + print = function (a) { document.write(a+"<br/>") }; // Browser +else + print = require("sys").puts; // Nodejs +default_environment.setIndex(LValueFromString("print"), LValueFromFunction(print)); +var f = new LFunction(c, default_environment); try{ - testvm.run(mycode); + testvm.run(f); } catch(e) {