lvm.js

changeset 73
6b43d68abc94
parent 72
dc73a60b3c06
child 74
a4a6adee1459
--- a/lvm.js	Fri Nov 19 13:09:25 2010 +0000
+++ b/lvm.js	Fri Nov 19 16:02:55 2010 +0000
@@ -25,8 +25,9 @@
 
 var debugMode = false;
 
-function LValue(type, value)
+function LValue(vm, type, value)
 {
+	this.vm = vm;
 	this.type = type||"nil";
 	this.value = value;
 }
@@ -59,15 +60,15 @@
 				return this.value[key.value];
 			else if(raw != true && this.metatable && this.metatable.type != "nil")
 			{
-				var __index = this.metatable.index(new LValue("string", "__index"));
+				var __index = this.metatable.index(this.vm.LValue("__index"));
 				if(__index.type == "function")
 				{
-					return LValueFromValue(__index.call([this, key])[0]);
+					return this.vm.LValue(__index.call([this, key])[0]);
 				}
 				else if(__index.type != "nil")
 					return __index.index(key);
 			}
-			return new LValue("nil", null);
+			return this.vm.LValue(null);
 		}
 		else
 			throw "Attempt to index a " + this.type + " value";
@@ -103,7 +104,7 @@
 	add: function (op2)
 	{
 		var metamethod;
-		var __add = LValueFromString("__add");
+		var __add = this.vm.LValue("__add");
 		if(this.metatable)
 			metamethod = this.metatable.index(__add);
 		if((!metamethod || metamethod.type == "nil") && op2.metatable)
@@ -122,44 +123,7 @@
 	}
 };
 
-function LValueFromString(string)
-{
-	return new LValue("string", string);
-}
-
-function LValueFromFunction(vm, func)
-{
-	var val = new LValue("function", func);
-	val.vm = vm;
-	return val;
-}
-
-function LValueFromValue(value)
-{
-	switch(typeof(value))
-	{
-	case "number":
-		return new LValue("number", value);
-	case "string":
-		return new LValueFromString(value);
-	case "function":
-		return new LValueFromFunction(value);
-	case "object":
-		if(value == null)
-			return new LValue("nil", null);
-		else
-			return new LValue("table", value);
-	case "undefined":
-		return new LValue("nil", null);
-	default:
-		sys.puts( "Not able to convert type " +
-			typeof(value)+" from Javascript to Lua: "+sys.inspect(value));
-		throw "Not able to convert type " +
-			typeof(value)+" from Javascript to Lua";
-	}
-}
-
-function LBinaryChunk(chunk, start)
+function LBinaryChunk(vm, chunk, start)
 {
 	this.chunk = chunk;
 	this.pos = start||12;
@@ -200,16 +164,16 @@
 		switch(type)
 		{
 		case 0: // Nil
-			this.constants.push(new LValue("nil", null));
+			this.constants.push(new LValue(vm, "nil", null));
 			break;
 		case 1: // Boolean
-			this.constants.push(new LValue("boolean", this.readByte()));
+			this.constants.push(new LValue(vm, "boolean", this.readByte())); // FIXME type
 			break;
 		case 3: // Number
-			this.constants.push(new LValue("number", this.readNumber()));
+			this.constants.push(new LValue(vm, "number", this.readNumber()));
 			break;
 		case 4: // String
-			this.constants.push(LValueFromString(this.readString()));
+			this.constants.push(new LValue(vm, "string", this.readString()));
 			break;
 		default:
 			throw "Invalid constant type "+type+" in bytecode";
@@ -221,7 +185,7 @@
 	this.numPrototypes = this.readInt();
 	for(var i=0;i<this.numPrototypes;i++)
 	{
-		var p = new LBinaryChunk(chunk, this.pos);
+		var p = new LBinaryChunk(vm, chunk, this.pos);
 		this.pos = p.pos;
 		this.prototypes.push(p);
 	}
@@ -352,13 +316,46 @@
 }
 
 LVM.prototype = {
-	call: function (lfFunction)
+	LValue: function (value)
 	{
-		var frame = {f:lfFunction,pc:0,reg:[],entry:true};
-		this.callstack.push(frame);
-		for(var i=0;i<lfFunction.maxStackSize;i++)
-			frame.reg[i] = new LValue("nil", null);
-		return this.run(frame);
+		switch(typeof(value))
+		{
+		case "number":
+			return new LValue(this, "number", value);
+		case "string":
+			return new LValue(this, "string", value);
+		case "function":
+			return new LValue(this, "function", value);
+		case "object":
+			if(value == null)
+				return new LValue(this, "nil", value);
+			else
+				return new LValue(this, "table", value);
+		case "undefined":
+			return new LValue(this, "nil", null);
+		default:
+			throw "Not able to convert type " +
+				typeof(value)+" from Javascript to Lua";
+		}
+	},
+	call: function (lfFunction, args)
+	{
+		if(typeof(lfFunction) == "function")
+		{
+			return lfFunction.apply(this, args);
+		}
+		else
+		{
+			var frame = {f:lfFunction,pc:0,entry:true};
+			if(args)
+				frame.reg = args.slice(0);
+			else
+				frame.reg = [];
+			this.callstack.push(frame);
+			for(var i=0;i<lfFunction.maxStackSize;i++)
+				frame.reg[i] = this.LValue(null);
+			return this.run(frame);
+		}
 	},
 	run: function(frame)
 	{
@@ -378,10 +375,10 @@
 				break;
 			case OP_LOADNIL:
 				for(var i = INS_A(instruction);i<=INS_B(instruction);i++)
-					frame.reg[i] = new LValue("nil", null);
+					frame.reg[i] = new LValue(this, "nil", null);
 				break;
 			case OP_LOADBOOL:
-				frame.reg[INS_A(instruction)] = new LValue("boolean", INS_B(instruction)!=0);
+				frame.reg[INS_A(instruction)] = new LValue(this, "boolean", INS_B(instruction)!=0);
 				if(INS_C(instruction)!=0)
 					frame.pc++;
 				break;
@@ -404,17 +401,17 @@
 				break;
 			case OP_LOADK:
 				var constant = frame.f.constants[INS_Bx(instruction)];
-				frame.reg[INS_A(instruction)] = new LValue(constant.type, constant.value);
+				frame.reg[INS_A(instruction)] = new LValue(this, constant.type, constant.value);
 				break;
 			case OP_NEWTABLE:
-				frame.reg[INS_A(instruction)] = new LValue("table", {});
+				frame.reg[INS_A(instruction)] = new LValue(this, "table", {});
 				break;
 			case OP_GETTABLE:
 				var C = INS_C(instruction);
 				var keysource = (C&0x100)?frame.f.constants:frame.reg;
 				var key = keysource[C&0xff];
 				var value = frame.reg[INS_B(instruction)].index(key).value;
-				frame.reg[INS_A(instruction)] = new LValueFromValue(value);
+				frame.reg[INS_A(instruction)] = this.LValue(value);
 				break;
 			case OP_SETTABLE:
 				var C = INS_C(instruction);
@@ -432,17 +429,17 @@
 				var undefined;
 				var args = frame.reg.slice(A+1, B==0?undefined:(A+B));
 				for(var i=args.length+1;i<f.maxStackSize;i++)
-					args[i] = new LValue("nil", null);
+					args[i] = new LValue(this, "nil", null);
 				if(typeof(f) == "function")
 				{
 					// JS native function
-					var ret = f.apply(this, [args]);
+					var ret = this.call(f, args);
 					for(var i = 0; i < (C-1); i++) //FIXME: Handle C == 0
 					{
 						if(i < ret && i < args.length)
 							frame.reg[A+i] = args[i];
 						else
-							frame.reg[A+i] = new LValue("nil", null);
+							frame.reg[A+i] = new LValue(this, "nil", null);
 					}
 				}
 				else
@@ -458,7 +455,7 @@
 				var prototype_id = INS_Bx(instruction);
 				var chunk = frame.f.chunk.prototypes[prototype_id];
 				var f = new LFunction(this, chunk, frame.f.environment);
-				frame.reg[INS_A(instruction)] = new LValue("function", f);
+				frame.reg[INS_A(instruction)] = new LValue(this, "function", f);
 				for(var i=0;i<chunk.numUpvalues;i++)
 				{
 					var upval_instruction = frame.f.instructions[frame.pc++];
@@ -506,7 +503,7 @@
 				frame.pc+=(INS_sBx(instruction));
 				var A = INS_A(instruction);
 				frame.reg[A].value -= frame.reg[A+2].value;
-				frame.reg[A+3] = new LValue("number", null);
+				frame.reg[A+3] = new LValue(this, "number", null);
 				break;
 			case OP_FORLOOP:
 				var A = INS_A(instruction);
@@ -543,7 +540,7 @@
 			case OP_SUB:
 				var RB = frame.reg[INS_B(instruction)];
 				var RC = frame.reg[INS_C(instruction)];
-				frame.reg[INS_A(instruction)] = new LValue("number", RB.value - RC.value);
+				frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.value - RC.value);
 				break;
 			case OP_LT:
 				var RB = frame.reg[INS_B(instruction)];
@@ -563,9 +560,9 @@
 
 var fs=require("fs");
 var sys=require("sys");
-var c = new LBinaryChunk(fs.readFileSync("luac.out", "binary"));
+var c = new LBinaryChunk(testvm, fs.readFileSync("luac.out", "binary"));
 
-var default_environment = new LValue("table", {});
+var default_environment = testvm.LValue({});
 
 // Standard library
 
@@ -589,14 +586,14 @@
 
 for(var name in baselib)
 {
-	default_environment.setIndex(LValueFromString(name), LValueFromFunction(testvm, baselib[name]));
+	default_environment.setIndex(testvm.LValue(name), testvm.LValue(baselib[name]));
 }
 
 // Metatable on environment to print out nil global accesses
-var mt = new LValue("table", {});
+var mt = testvm.LValue({});
 mt.setIndex(
-	LValueFromString("__index"),
-	LValueFromFunction(testvm, function (t, k) { sys.puts("Access of nil global: "+k); })
+	testvm.LValue("__index"),
+	testvm.LValue(function (t, k) { sys.puts("Access of nil global: "+k); })
 );
 default_environment.setMetatable(mt);
 

mercurial