aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ldo.js81
-rw-r--r--src/lfunc.js4
-rw-r--r--src/lobject.js85
-rw-r--r--src/ltm.js97
-rw-r--r--src/lvm.js310
5 files changed, 378 insertions, 199 deletions
diff --git a/src/ldo.js b/src/ldo.js
index 488bc86..85bd3d3 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -1,23 +1,21 @@
/*jshint esversion: 6 */
"use strict";
-const OC = require('./lopcodes.js');
const lua = require('./lua.js');
const CT = lua.constant_types;
const LUA_MULTRET = lua.LUA_MULTRET;
const lobject = require('./lobject.js');
const TValue = lobject.TValue;
-const Table = lobject.Table;
-const LClosure = lobject.LClosure;
-const lfunc = require('./lfunc.js');
-const UpVal = lfunc.UpVal;
const lstate = require('./lstate.js');
const CallInfo = lstate.CallInfo;
const llimit = require('./llimit.js');
+const ltm = require('./ltm.js');
+const TMS = ltm.TMS;
const nil = new TValue(CT.LUA_TNIL, null);
-const precall = function(L, off, func, nresults) {
+const luaD_precall = function(L, off, nresults) {
+ let func = L.stack[off];
let ci;
switch(func.type) {
@@ -56,28 +54,30 @@ const precall = function(L, off, func, nresults) {
}
ci.nresults = nresults;
ci.func = func;
+ ci.funcOff = off;
ci.u.l.base = base;
ci.top = base + fsize;
L.top = ci.top;
ci.u.l.savedpc = p.code;
+ ci.pcOff = 0;
ci.callstatus = lstate.CIST_LUA;
return false;
break;
}
default:
- // __call
- return false;
+ tryfuncTM(L, off, func);
+ return luaD_precall(L, off, nresults);
}
-}
+};
-const postcall = function(L, ci, firstResult, nres) {
+const luaD_poscall = function(L, ci, firstResult, nres) {
let wanted = ci.nresults;
let res = ci.funcOff;
L.ci = ci.previous;
L.ciOff--;
return moveresults(L, firstResult, res, nres, wanted);
-}
+};
const moveresults = function(L, firstResult, res, nres, wanted) {
switch (wanted) {
@@ -98,7 +98,11 @@ const moveresults = function(L, firstResult, res, nres, wanted) {
default: {
let i;
if (wanted <= nres) {
- for (i = 0; i < wanted; i++)
+ for (i = 0; i < wanted; i++) {
+ L.stack[res + i] = L.stack[firstResult + i];
+ }
+ } else {
+ for (i = 0; i < nres; i++)
L.stack[res + i] = L.stack[firstResult + i];
for (; i < wanted; i++)
L.stack[res + i] = nil;
@@ -107,8 +111,9 @@ const moveresults = function(L, firstResult, res, nres, wanted) {
}
}
+ L.top = res + wanted; /* top points after the last result */
return true;
-}
+};
const adjust_varargs = function(L, p, actual) {
let nfixargs = p.numparams;
@@ -126,38 +131,24 @@ const adjust_varargs = function(L, p, actual) {
L.stack[L.top++] = nil;
return base;
+};
+
+const tryfuncTM = function(L, off, func) {
+ let tm = ltm.luaT_gettmbyobj(L, func, TMS.TM_CALL);
+ if (!tm.ttisfunction(tm))
+ throw new Error("__call metatable member is not a function") // TODO: luaG_typeerror
+ /* Open a hole inside the stack at 'func' */
+ for (p = L.top; p > off; p--)
+ L.stack[p] = L.stack[p-1];
+ L.top++; /* slot ensured by caller */
+ L.stack[off] = tm; /* tag method is the new function to be called */
}
-
-/*
-** Check appropriate error for stack overflow ("regular" overflow or
-** overflow while handling stack overflow). If 'nCalls' is larger than
-** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but
-** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to
-** allow overflow handling to work)
-*/
-const stackerror = function(L) {
- if (L.nCcalls === LUAI_MAXCCALLS)
- throw new Error("JS stack overflow");
- else if (L.nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) /* error while handing stack error */
- throw new Error("stack overflow") // TODO: luaD_throw(L, LUA_ERRERR);
-}
-
-/*
-** Call a function (JS or Lua). The function to be called is at func.
-** The arguments are on the stack, right after the function.
-** When returns, all the results are on the stack, starting at the original
-** function position.
-*/
-// const luaD_call = function(L, func, nResults) {
-// if (++L->nCcalls >= LUAI_MAXCCALLS)
-// stackerror(L);
-// if ()
-// }
-
module.exports = {
- precall: precall,
- postcall: postcall,
- moveresults: moveresults,
- adjust_varargs: adjust_varargs
-} \ No newline at end of file
+ nil: nil,
+ luaD_precall: luaD_precall,
+ luaD_poscall: luaD_poscall,
+ moveresults: moveresults,
+ adjust_varargs: adjust_varargs,
+ tryfuncTM: tryfuncTM
+}; \ No newline at end of file
diff --git a/src/lfunc.js b/src/lfunc.js
index 77eb853..2e0a70a 100644
--- a/src/lfunc.js
+++ b/src/lfunc.js
@@ -47,7 +47,7 @@ class UpVal {
return this.v !== null;
}
-}
+};
const findupval = function(L, level) {
let pp = L.openupval;
@@ -73,7 +73,7 @@ const findupval = function(L, level) {
// Thread with upvalue list business ? lfunc.c:75
return uv;
-}
+};
const luaF_close = function(L, level) {
while (L.openupval !== null && L.openupval.v >= level) {
diff --git a/src/lobject.js b/src/lobject.js
index 4c3290d..713a734 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -104,6 +104,7 @@ class TValue {
}
+const nil = new TValue(CT.LUA_TNIL, null);
class LClosure extends TValue {
@@ -138,6 +139,17 @@ class TString extends TValue {
}
+class Userdata extends TValue {
+
+ constructor(jsObject) {
+ super(CT.LUA_TUSERDATA, jsObject);
+
+ this.metatable = null;
+ }
+
+}
+
+
class Table extends TValue {
constructor(array, hash) {
@@ -146,43 +158,44 @@ class Table extends TValue {
hash: new Map(hash)
});
- this.usermetatable = null;
-
- this.metatable = {
- __newindex: function (table, key, value) {
- if (key instanceof TValue) {
- // Those lua values are used by value, tables and functions by reference
- if ([CT.LUA_TNUMBER, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, CT.LUA_TNUMFLT, CT.LUA_TNUMINT].indexOf(key.type) > -1) {
- key = key.value;
- }
- }
-
- if (typeof key === 'number') {
- table.value.array[key] = value;
- } else {
- table.value.hash.set(key, value);
- }
- },
-
- __index: function (table, key) {
- if (key instanceof TValue) {
- // Those lua values are used by value, tables and functions by reference
- if ([CT.LUA_TNUMBER, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, CT.LUA_TNUMFLT, CT.LUA_TNUMINT].indexOf(key.type) > -1) {
- key = key.value;
- }
- }
-
- if (typeof key === 'number') {
- return table.value.array[key];
- } else {
- return table.value.hash.get(key);
- }
- },
-
- __len: function (table) {
- return t.value.array.length;
+ this.metatable = null;
+ }
+
+ __newindex(table, key, value) {
+ if (key instanceof TValue) {
+ // Those lua values are used by value, tables and functions by reference
+ if ([CT.LUA_TNUMBER, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, CT.LUA_TNUMFLT, CT.LUA_TNUMINT].indexOf(key.type) > -1) {
+ key = key.value;
+ }
+ }
+
+ if (typeof key === 'number') {
+ table.value.array[key] = value;
+ } else {
+ table.value.hash.set(key, value);
+ }
+ }
+
+ __index(table, key) {
+ if (key instanceof TValue) {
+ // Those lua values are used by value, tables and functions by reference
+ if ([CT.LUA_TNUMBER, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, CT.LUA_TNUMFLT, CT.LUA_TNUMINT].indexOf(key.type) > -1) {
+ key = key.value;
}
- };
+ }
+
+ let v = nil;
+ if (typeof key === 'number') {
+ v = table.value.array[key];
+ } else {
+ v = table.value.hash.get(key);
+ }
+
+ return v ? v : nil;
+ }
+
+ __len(table) {
+ return t.value.array.length;
}
}
diff --git a/src/ltm.js b/src/ltm.js
index ebc0348..5d93e28 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -4,7 +4,102 @@
const lobject = require('./lobject.js');
const TValue = lobject.TValue;
const Table = lobject.Table;
+const ldo = require('./ldo.js');
+const lstate = require('./lstate.js');
+const lua = require('./lua.js');
+const CT = lua.constant_types;
+const TMS = {
+ TM_INDEX: "__index",
+ TM_NEWINDEX: "__newindex",
+ TM_GC: "__gc",
+ TM_MODE: "__mode",
+ TM_LEN: "__len",
+ TM_EQ: "__eq", /* last tag method with fast access */
+ TM_ADD: "__add",
+ TM_SUB: "__sub",
+ TM_MUL: "__mul",
+ TM_MOD: "__mod",
+ TM_POW: "__pow",
+ TM_DIV: "__div",
+ TM_IDIV: "__idiv",
+ TM_BAND: "__band",
+ TM_BOR: "__bor",
+ TM_BXOR: "__bxor",
+ TM_SHL: "__shl",
+ TM_SHR: "__shr",
+ TM_UNM: "__unm",
+ TM_BNOT: "__bnot",
+ TM_LT: "__lt",
+ TM_LE: "__le",
+ TM_CONCAT: "__concat",
+ TM_CALL: "__call",
+ TM_N: 26
+};
-module.exports = {} \ No newline at end of file
+const luaT_callTM = function(L, f, p1, p2, p3, hasres) {
+ let result = p3;
+ let func = L.top;
+
+ L.stack[L.top] = f; /* push function (assume EXTRA_STACK) */
+ L.stack[L.top + 1] = p1; /* 1st argument */
+ L.stack[L.top + 2] = p2; /* 2nd argument */
+ L.top += 3;
+
+ if (!hasres) /* no result? 'p3' is third argument */
+ L.stack[L.top++] = p3; /* 3rd argument */
+
+ if (ci.callstatus & lstate.CIST_LUA)
+ ldo.luaD_call(L, func, hasres);
+ else
+ ldo.luaD_callnoyield(L, func, hasres);
+
+ if (hasres) {
+ L.stack[result] = L.stack[--L.top];
+ }
+};
+
+const luaT_callbinTM = function(L, p1, p2, res, event) {
+ let tm = luaT_gettmbyobj(L, p1, event);
+ if (tm.ttisnil())
+ tm = luaT_gettmbyobj(L, p2, event);
+ if (tm.ttisnil()) return false;
+ luaT_callTM(L, tm, p1, p2, res, 1);
+ return true;
+}
+
+const luaT_trybinTM = function(L, p1, p2, res, event) {
+ if (!luaT_gettmbyobj(L, p1, p2, res, event)) {
+ throw new Error("TM error"); // TODO: luaG_error
+ }
+}
+
+const luaT_callorderTM = function(L, p1, p2, event) {
+ if (!luaT_callbinTM(L, p2, p2, L.top, event))
+ return -1;
+ else
+ return !l_isfalse(L.stack[L.top]) ? 1 : 0;
+}
+
+const luaT_gettmbyobj = function(L, o, event) {
+ let mt;
+ switch(o.ttnov()) {
+ case CT.LUA_TTABLE:
+ case CT.LUA_TTUSERDATA:
+ mt = o.value.metatable;
+ default:
+ // TODO: mt = G(L)->mt[ttnov(o)];
+ }
+
+ return mt ? mt.__index(mt, event) : ldo.nil;
+}
+
+module.exports = {
+ TMS: TMS,
+ luaT_callTM: luaT_callTM,
+ luaT_callbinTM: luaT_callbinTM,
+ luaT_trybinTM: luaT_trybinTM,
+ luaT_callorderTM: luaT_callorderTM,
+ luaT_gettmbyobj: luaT_gettmbyobj
+} \ No newline at end of file
diff --git a/src/lvm.js b/src/lvm.js
index 6523482..c2cef1b 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -18,41 +18,54 @@ const lstate = require('./lstate.js');
const CallInfo = lstate.CallInfo;
const llimit = require('./llimit.js');
const ldo = require('./ldo.js');
+const ltm = require('./ltm.js');
+const TMS = ltm.TMS;
const RA = function(L, base, i) {
return base + i.A;
-}
+};
const RB = function(L, base, i) {
return base + i.B;
-}
+};
const RC = function(L, base, i) {
return base + i.C;
-}
+};
const RKB = function(L, base, k, i) {
return OC.ISK(i.B) ? k[OC.INDEXK(i.B)] : L.stack[base + i.B];
-}
+};
const RKC = function(L, base, k, i) {
return OC.ISK(i.C) ? k[OC.INDEXK(i.C)] : L.stack[base + i.C];
-}
+};
const luaV_execute = function(L) {
let ci = L.ci;
+ let specialCase = null; // To enable jump to specific opcode without reading current op/ra
+ let opcode, k, base, i, ra;
+ var cl;
+
ci.callstatus |= lstate.CIST_FRESH;
newframe:
for (;;) {
- ci = L.ci;
- var cl = ci.func;
- let k = cl.p.k;
- let base = ci.u.l.base
+ if (specialCase) {
+ opcode = specialCase;
+ specialCase = null;
+ } else {
+ ci = L.ci;
+ cl = ci.func;
+ k = cl.p.k;
+ base = ci.u.l.base
+
+ i = ci.u.l.savedpc[ci.pcOff++];
+ ra = RA(L, base, i);
- let i = ci.u.l.savedpc[ci.pcOff++];
- let ra = RA(L, base, i);
+ opcode = OC.OpCodes[i.opcode];
+ }
- switch (OC.OpCodes[i.opcode]) {
+ switch (opcode) {
case "OP_MOVE": {
L.stack[ra] = L.stack[RB(L, base, i)];
break;
@@ -91,10 +104,10 @@ const luaV_execute = function(L) {
let table = cl.upvals[i.B].val(L);
let key = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.metatable.__index(table, key)) {
+ // if (!table.ttistable() || !table.__index(table, key)) {
// // __index
// } else {
- L.stack[ra] = table.metatable.__index(table, key);
+ L.stack[ra] = table.__index(table, key);
// }
break;
}
@@ -103,10 +116,10 @@ const luaV_execute = function(L) {
let key = RKB(L, base, k, i);
let v = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.metatable.__index(table, key)) {
+ // if (!table.ttistable() || !table.__index(table, key)) {
// // __index
// } else {
- table.metatable.__newindex(table, key, v);
+ table.__newindex(table, key, v);
// }
break;
@@ -115,10 +128,10 @@ const luaV_execute = function(L) {
let table = RKB(L, base, k, i);
let key = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.metatable.__index(table, key)) {
+ // if (!table.ttistable() || !table.__index(table, key)) {
// // __index
// } else {
- L.stack[ra] = table.metatable.__index(table, key);
+ L.stack[ra] = table.__index(table, key);
// }
break;
}
@@ -127,10 +140,10 @@ const luaV_execute = function(L) {
let key = RKB(L, base, k, i);
let v = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.metatable.__index(table, key)) {
+ // if (!table.ttistable() || !table.__index(table, key)) {
// // __index
// } else {
- table.metatable.__newindex(table, key, v);
+ table.__newindex(table, key, v);
// }
break;
@@ -145,10 +158,10 @@ const luaV_execute = function(L) {
L.stack[ra + 1] = table;
- // if (!table.ttistable() || !table.metatable.__index(table, key)) {
+ // if (!table.ttistable() || !table.__index(table, key)) {
// // __index
// } else {
- L.stack[ra] = table.metatable.__index(table, key);
+ L.stack[ra] = table.__index(table, key);
// }
break;
@@ -164,8 +177,8 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value + op2.value);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${op1.value} and ${op2.value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_ADD);
+ base = ci.u.l.base;
}
break;
}
@@ -180,8 +193,8 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value - op2.value);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${op1.value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_SUB);
+ base = ci.u.l.base;
}
break;
}
@@ -196,8 +209,8 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value * op2.value);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_MUL);
+ base = ci.u.l.base;
}
break;
}
@@ -212,8 +225,8 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value % op2.value);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_MOD);
+ base = ci.u.l.base;
}
break;
}
@@ -226,8 +239,8 @@ const luaV_execute = function(L) {
if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.pow(op1.value, op2.value));
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_POW);
+ base = ci.u.l.base;
}
break;
}
@@ -240,8 +253,8 @@ const luaV_execute = function(L) {
if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value / op2.value);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_DIV);
+ base = ci.u.l.base;
}
break;
}
@@ -256,8 +269,8 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, (op1.value / op2.value)|0);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_IDIV);
+ base = ci.u.l.base;
}
break;
}
@@ -270,8 +283,8 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value & op2.value)|0);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_BAND);
+ base = ci.u.l.base;
}
break;
}
@@ -284,8 +297,8 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value | op2.value)|0);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_BOR);
+ base = ci.u.l.base;
}
break;
}
@@ -298,8 +311,8 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value ^ op2.value)|0);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_BXOR);
+ base = ci.u.l.base;
}
break;
}
@@ -312,8 +325,8 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value << op2.value)|0);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_SHL);
+ base = ci.u.l.base;
}
break;
}
@@ -326,8 +339,8 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value >> op2.value)|0);
} else {
- // Metamethod
- throw new Error(`Can't perform binary operation on ${k[i.B].value} and ${k[i.C].value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_SHR);
+ base = ci.u.l.base;
}
break;
}
@@ -340,8 +353,8 @@ const luaV_execute = function(L) {
} else if (numberop !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, -op.value);
} else {
- // Metamethod
- throw new Error(`Can't perform unary operation on ${op.value}`);
+ ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_UNM);
+ base = ci.u.l.base;
}
break;
}
@@ -352,8 +365,8 @@ const luaV_execute = function(L) {
if (op.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, ~op.value);
} else {
- // Metamethod
- throw new Error(`Can't perform unary operation on ${op.value}`);
+ ltm.luaT_trybinTM(L, op, op, ra, TMS.TM_BNOT);
+ base = ci.u.l.base;
}
break;
}
@@ -420,7 +433,7 @@ const luaV_execute = function(L) {
if (b !== 0)
L.top = ra+b;
- if (ldo.precall(L, ra, L.stack[ra], nresults)) {
+ if (ldo.luaD_precall(L, ra, nresults)) {
if (nresults >= 0)
L.top = ci.top;
base = ci.u.l.base;
@@ -433,7 +446,7 @@ const luaV_execute = function(L) {
}
case "OP_TAILCALL": {
if (i.B !== 0) L.top = ra + i.B;
- if (ldo.precall(L, ra, L.stack[ra], LUA_MULTRET)) { // JS function
+ if (ldo.luaD_precall(L, ra, LUA_MULTRET)) { // JS function
base = ci.u.l.base;
} else {
/* tail call: put called frame (n) in place of caller one (o) */
@@ -466,7 +479,7 @@ const luaV_execute = function(L) {
}
case "OP_RETURN": {
if (cl.p.p.length > 0) lfunc.luaF_close(L, base);
- let b = ldo.postcall(L, ci, ra, (i.B !== 0 ? i.B - 1 : L.top - ra));
+ let b = ldo.luaD_poscall(L, ci, ra, (i.B !== 0 ? i.B - 1 : L.top - ra));
if (ci.callstatus & lstate.CIST_FRESH)
return; /* external invocation: return */
@@ -540,12 +553,24 @@ const luaV_execute = function(L) {
break;
}
case "OP_TFORCALL": {
+ let cb = ra + 3 /* call base */
+ L.stack[cb + 2] = L.stack[ra + 2];
+ L.stack[cb + 1] = L.stack[ra + 1];
+ L.stack[cb] = L.stack[ra];
+ L.top = cb + 3; /* func. + 2 args (state and index) */
+ luaD_call(L, cb, i.C);
+ base = ci.u.l.base;
+ L.top = ci.top;
+ i = ci.u.l.savedpc[ci.pcOff++];
+ ra = RA(L, base, i);
+ assert(OC.OpCodes[i.opcode] === "OP_TFORLOOP");
+ specialCase = "OP_TFORLOOP";
break;
}
case "OP_TFORLOOP": {
if (!L.stack[ra + 1].ttisnil()) { /* continue loop? */
L.stack[ra] = L.stack[ra + 1]; /* save control variable */
- ci.cpOff += i.sBx; /* jump back */
+ ci.pcOff += i.sBx; /* jump back */
}
break;
}
@@ -616,51 +641,52 @@ const luaV_execute = function(L) {
}
}
}
-}
+};
const dojump = function(L, ci, i, e) {
let a = i.A;
if (a != 0) lfunc.luaF_close(L, ci.u.l.base + a - 1);
ci.pcOff += i.sBx + e;
-}
+};
const donextjump = function(L, ci) {
dojump(L, ci, ci.u.l.savedpc[ci.pcOff], 1);
-}
+};
-const luaV_lessequal = function(l, r) {
- if (l.ttisnumber() && r.ttisnumber())
- return LEnum(l, r);
- else if (l.ttisstring() && r.ttisstring())
- return l_strcmp(l, r) <= 0;
- // TODO: metatable
- // else if (l.metatable.__le || r.metatable.__le) {
- // let res = l.metatable.__le ? l.metatable.__le(l, r) : r.metatable.__le(l, r);
- // if (res >= 0)
- // return res;
- // } else {
- // L.ci.callstatus |= lstate.CIST_LEQ;
- // let res = l.metatable.__lt ? l.metatable.__lt(r, l) : r.metatable.__lt(r, l);
- // L.ci.callstatus ^= lstate.CIST_LEQ;
- // if (res < 0)
- // throw new Error("attempt to compare ...");
- // return !res;
- // }
-}
const luaV_lessthan = function(l, r) {
if (l.ttisnumber() && r.ttisnumber())
return LTnum(l, r);
else if (l.ttisstring() && r.ttisstring())
return l_strcmp(l, r) < 0;
- // TODO: metatable
- // else if (l.metatable.__lt || r.metatable.__lt) {
- // let res = l.metatable.__lt ? l.metatable.__lt(l, r) : r.metatable.__lt(l, r);
- // if (res < 0)
- // throw new Error("attempt to compare ...")
- // return res;
- // }
-}
+ else {
+ res = ltm.luatT_callorderTM(L, l, r, TMS.TM_LT);
+ if (res < 0)
+ throw new Error("TM order error"); // TODO: luaG_ordererror
+ return res;
+ }
+};
+
+const luaV_lessequal = function(l, r) {
+ let res;
+
+ if (l.ttisnumber() && r.ttisnumber())
+ return LEnum(l, r);
+ else if (l.ttisstring() && r.ttisstring())
+ return l_strcmp(l, r) <= 0;
+ else {
+ res = ltm.luatT_callorderTM(L, l, r, TMS.TM_LE);
+ if (res >= 0)
+ return res;
+ }
+
+ L.ci.callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
+ res = ltm.luatT_callorderTM(L, l, r, TMS.TM_LT);
+ L.ci.callstatus ^= CIST_LEQ; /* clear mark */
+ if (res < 0)
+ throw new Error("TM order error"); // TODO: luaG_ordererror
+ return res === 1;
+};
const luaV_equalobj = function(t1, t2) {
if (t1.ttype() !== t2.ttype()) { /* not the same variant? */
@@ -672,6 +698,8 @@ const luaV_equalobj = function(t1, t2) {
}
}
+ let tm;
+
/* values have same type and same variant */
switch(t1.ttype()) {
case CT.LUA_TNIL:
@@ -687,11 +715,23 @@ const luaV_equalobj = function(t1, t2) {
case CT.LUA_TUSERDATA:
case CT.LUA_TTABLE:
if (t1 === t2) return 1;
- // TODO: __eq
+ else if (L === null) return 1;
+
+ // TODO: fasttm ?
+ tm = ltm.luaT_gettmbyobj(L, t1, TMS.TM_EQ);
+ if (tm.ttisnil())
+ tm = ltm.luaT_gettmbyobj(L, t2, TMS.TM_EQ);
+ break
default:
return t1.value === t2.value ? 1 : 0;
}
-}
+
+ if (!tm || tm.ttisnil())
+ return 0;
+
+ ltm.luaT_callTM(L, tm, t1, t2, L.top, 1);
+ return !l_isfalse(L.stack[L.top]);
+};
const forlimit = function(obj, step) {
let stopnow = false;
@@ -715,7 +755,7 @@ const forlimit = function(obj, step) {
stopnow: stopnow,
ilimit: ilimit
}
-}
+};
/*
** try to convert a value to an integer, rounding according to 'mode':
@@ -743,7 +783,7 @@ const luaV_tointeger = function(obj, mode) {
}
return false;
-}
+};
const tonumber = function(v) {
if (v.type === CT.LUA_TNUMFLT)
@@ -756,7 +796,7 @@ const tonumber = function(v) {
return new TValue(CT.LUA_TNUMFLT, parseFloat(v.value)); // TODO: luaO_str2num
return false;
-}
+};
const LTnum = function(l, r) {
if (l.ttisinteger()) {
@@ -772,7 +812,7 @@ const LTnum = function(l, r) {
else
return !LEintfloat(r.value, l.value);
}
-}
+};
const LEnum = function(l, r) {
if (l.ttisinteger()) {
@@ -788,46 +828,86 @@ const LEnum = function(l, r) {
else
return !LTintfloat(r.value, l.value);
}
-}
+};
const LEintfloat = function(l, r) {
// TODO: LEintfloat
return l <= r ? 1 : 0;
-}
+};
const LTintfloat = function(l, r) {
// TODO: LTintfloat
return l < r ? 1 : 0;
-}
+};
const l_strcmp = function(ls, rs) {
// TODO: lvm.c:248 static int l_strcmp (const TString *ls, const TString *rs)
return ls.value === rs.value ? 0 : (ls.value < rs.value ? -1 : 1);
-}
+};
const l_isfalse = function(o) {
return o.ttisnil() || (o.ttisboolean() && o.value === false)
-}
+};
+
+/*
+** Check appropriate error for stack overflow ("regular" overflow or
+** overflow while handling stack overflow). If 'nCalls' is larger than
+** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but
+** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to
+** allow overflow handling to work)
+*/
+const stackerror = function(L) {
+ if (L.nCcalls === llimit.LUAI_MAXCCALLS)
+ throw new Error("JS stack overflow");
+ else if (L.nCcalls >= llimit.LUAI_MAXCCALLS + (llimit.LUAI_MAXCCALLS >> 3)) /* error while handing stack error */
+ throw new Error("stack overflow") // TODO: luaD_throw(L, LUA_ERRERR);
+};
+
+/*
+** Call a function (JS or Lua). The function to be called is at func.
+** The arguments are on the stack, right after the function.
+** When returns, all the results are on the stack, starting at the original
+** function position.
+*/
+const luaD_call = function(L, off, nResults) {
+ if (++L.nCcalls >= llimit.LUAI_MAXCCALLS)
+ stackerror(L);
+ if (!ldo.luaD_precall(L, off, nResults))
+ luaV_execute(L);
+ L.nCcalls--;
+};
+
+/*
+** Similar to 'luaD_call', but does not allow yields during the call
+*/
+const luaD_callnoyield = function(L, off, nResults) {
+ L.nny++;
+ luaD_call(L, off, nResults);
+ L.nny--;
+};
module.exports = {
- RA: RA,
- RB: RB,
- RC: RC,
- RKB: RKB,
- RKC: RKC,
- luaV_execute: luaV_execute,
- dojump: dojump,
- donextjump: donextjump,
- luaV_lessequal: luaV_lessequal,
- luaV_lessthan: luaV_lessthan,
- luaV_equalobj: luaV_equalobj,
- forlimit: forlimit,
- luaV_tointeger: luaV_tointeger,
- tonumber: tonumber,
- LTnum: LTnum,
- LEnum: LEnum,
- LEintfloat: LEintfloat,
- LTintfloat: LTintfloat,
- l_strcmp: l_strcmp,
- l_isfalse: l_isfalse
+ RA: RA,
+ RB: RB,
+ RC: RC,
+ RKB: RKB,
+ RKC: RKC,
+ luaV_execute: luaV_execute,
+ dojump: dojump,
+ donextjump: donextjump,
+ luaV_lessequal: luaV_lessequal,
+ luaV_lessthan: luaV_lessthan,
+ luaV_equalobj: luaV_equalobj,
+ forlimit: forlimit,
+ luaV_tointeger: luaV_tointeger,
+ tonumber: tonumber,
+ LTnum: LTnum,
+ LEnum: LEnum,
+ LEintfloat: LEintfloat,
+ LTintfloat: LTintfloat,
+ l_strcmp: l_strcmp,
+ l_isfalse: l_isfalse,
+ stackerror: stackerror,
+ luaD_call: luaD_call,
+ luaD_callnoyield: luaD_callnoyield,
}; \ No newline at end of file