aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lvm.js292
1 files changed, 279 insertions, 13 deletions
diff --git a/src/lvm.js b/src/lvm.js
index 964abe7..0c52eaf 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -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;