From 4bf190d1b51c8c2d3f5ab0b6355ecd971b735adc Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Sat, 11 Feb 2017 22:22:38 +0100 Subject: TFORCALL, TFORLOOP, luaD_call, tag methods --- src/ldo.js | 81 +++++++-------- src/lfunc.js | 4 +- src/lobject.js | 85 +++++++++------- src/ltm.js | 97 +++++++++++++++++- src/lvm.js | 310 ++++++++++++++++++++++++++++++++++++--------------------- 5 files changed, 378 insertions(+), 199 deletions(-) (limited to 'src') 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 -- cgit v1.2.3-70-g09d2