Add a bytecode interpreter \o/

Tue, 06 Apr 2010 14:49:52 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Tue, 06 Apr 2010 14:49:52 +0100
changeset 9
3f055c9ab80e
parent 8
e7de6d1fee96
child 10
ce2f27fa25a4

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)
 {

mercurial