Fri, 19 Nov 2010 16:02:55 +0000
Large overhaul - every LValue (and LBinaryChunk) now has a valid 'vm' property, so we know in which LVM to call it or its metamethods
lvm.js | file | annotate | diff | comparison | revisions |
--- 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);