diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lvm.js | 292 |
1 files changed, 279 insertions, 13 deletions
@@ -3,6 +3,7 @@ const BytecodeParser = require("./lundump.js"); const OC = require('./lopcodes.js'); +const CT = require('./lua.js').constant_types; class LuaVM { @@ -10,24 +11,46 @@ class LuaVM { this.L = L; } - RA(base, a) { - return base + a; + RA(base, i) { + return base + i.A; } - RB(base, opcode, b) { - return base + b; + RB(base, i) { + return base + i.B; } - RC(base, c) { - return base + c; + RC(base, i) { + return base + i.C; } - RKB(base, k, b) { - return OC.ISK(b) ? k[OC.INDEXK(b)] : base + b; + RKB(base, k, i) { + return OC.ISK(b) ? k[OC.INDEXK(b)] : base + i.B; } - RKC(base, k, c) { - return OC.ISK(c) ? k[OC.INDEXK(c)] : base + c; + RKC(base, k, i) { + return OC.ISK(c) ? k[OC.INDEXK(c)] : base + i.C; + } + + static tonumber(v) { + if (v.type === CT.LUA_TNUMFLT) + return { + type: v.type, + value: v.value + }; + + if (v.type === CT.LUA_TNUMINT) + return { + type: CT.LUA_TNUMFLT, + value: v.value + }; + + if (v.type === CT.LUA_TSHRSTR || v.type === CT.LUA_TLNGSTR) + return { + type: CT.LUA_TNUMFLT, + value: parseFloat(v.value) // TODO 0x or other exotic form + }; + + return false; } execute() { @@ -41,20 +64,36 @@ class LuaVM { let base = ci.base; let i = ci.savedpc[ci.pcOff++]; - let ra = this.RA(base, i.A); - + let ra = this.RA(base, i); + + var op1, op2, numberop1, numberop2, op, numberop; switch (OC.OpCodes[i.opcode]) { case "OP_MOVE": - L.stack[ra] = L.stack[this.RB(base, i.opcode, i.B)]; + L.stack[ra] = L.stack[this.RB(base, i)]; break; case "OP_LOADK": L.stack[ra] = k[i.Bx]; break; case "OP_LOADKX": + assert(OC.OpCodes[ci.savedpc[ci.pcOff].opcode] === "OP_EXTRAARG"); + L.stack[ra] = k[ci.savedpc[ci.pcOff++].Ax]; break; case "OP_LOADBOOL": + L.stack[ra] = { + type: CT.LUA_TBOOLEAN, + value: i.B !== 0 + }; + + if (i.C !== 0) + ci.pcOff++; /* skip next instruction (if C) */ + break; case "OP_LOADNIL": + for (let j = 0; j <= i.B; j++) + L.stack[ra + j] = { + type: CT.LUA_TNIL, + value: null + } break; case "OP_GETUPVAL": break; @@ -73,34 +112,261 @@ class LuaVM { case "OP_SELF": break; case "OP_ADD": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value + k[i.C].value + }; + } else if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: k[i.B].value + k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_SUB": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value - k[i.C].value + }; + } else if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: k[i.B].value - k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_MUL": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value * k[i.C].value + }; + } else if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: k[i.B].value * k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_MOD": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value % k[i.C].value + }; + } else if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: k[i.B].value % k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_POW": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: Math.pow(k[i.B].value, k[i.C].value) + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_DIV": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: k[i.B].value / k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_IDIV": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: Math.floor(k[i.B].value / k[i.C].value) + }; + } else if (numberop1 !== false && numberop2 !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: Math.floor(k[i.B].value / k[i.C].value) + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_BAND": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value & k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_BOR": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value | k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_BXOR": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value ^ k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_SHL": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value << k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_SHR": + op1 = k[i.B]; + op2 = k[i.C]; + numberop1 = LuaVM.tonumber(op1); + numberop2 = LuaVM.tonumber(op2); + + if (op1.type === CT.LUA_TNUMINT && op2.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: k[i.B].value >> k[i.C].value + }; + } else { + // Metamethod + throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_UNM": + op = L.stack[this.RB(base, i)]; + numberop = LuaVM.tonumber(op); + + if (op.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: -L.stack[this.RB(base, i)].value + }; + } else if (numberop !== false) { + L.stack[ra] = { + type: CT.LUA_TNUMFLT, + value: -L.stack[this.RB(base, i)].value + }; + } else { + // Metamethod + throw new Error(`Can't perform unary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_BNOT": + op = L.stack[this.RB(base, i)]; + numberop = LuaVM.tonumber(op); + + if (op.type === CT.LUA_TNUMINT) { + L.stack[ra] = { + type: CT.LUA_TNUMINT, + value: ~L.stack[this.RB(base, i)].value + }; + } else { + // Metamethod + throw new Error(`Can't perform unary operation on ${k[i.B].value} and ${k[i.C].value}`); + } break; case "OP_NOT": + op = L.stack[this.RB(base, i)]; + L.stack[ra] = { + type: CT.LUA_TBOOLEAN, + value: !!((op.type === CT.LUA_TBOOLEAN && !op.value) || op.type === CT.LUA_TNIL) + } break; case "OP_LEN": break; |