From 74dda64eab7951da520dc451a1f3bbb8c7d62706 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 28 Feb 2017 21:15:16 +0100 Subject: Bytecode generation --- src/lapi.js | 8 +- src/lbaselib.js | 2 +- src/lcode.js | 1001 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/ldebug.js | 6 +- src/ldo.js | 2 +- src/lfunc.js | 2 +- src/lobject.js | 91 ++++- src/lopcodes.js | 2 +- src/lparser.js | 18 +- src/lstate.js | 2 +- src/lua.js | 33 ++ src/lvm.js | 65 ++-- tests/llex.js | 4 +- todo-temp.md | 25 -- 14 files changed, 1160 insertions(+), 101 deletions(-) delete mode 100644 todo-temp.md diff --git a/src/lapi.js b/src/lapi.js index c4a7bbe..9a05638 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -149,7 +149,7 @@ const reverse = function(L, from, to) { /* ** Let x = AB, where A is a prefix of length 'n'. Then, -** rotate x n == BA. But BA == (A^r . B^r)^r. +** rotate x n === BA. But BA === (A^r . B^r)^r. */ const lua_rotate = function(L, idx, n) { let t = L.stack[L.top - 1]; @@ -586,7 +586,7 @@ const f_call = function(L, ud) { const lua_type = function(L, idx) { let o = index2addr(L, idx); - return o.ttnov(); // TODO: isvalid ? luaO_nilobject != nil tvalue ? + return o.ttnov(); // TODO: isvalid ? luaO_nilobject !== nil tvalue ? }; const lua_typename = function(L, t) { @@ -662,7 +662,7 @@ const lua_callk = function(L, nargs, nresults, ctx, k) { ldo.luaD_callnoyield(L, func, nresults); } - if (nresults == lua.LUA_MULTRET && L.ci.top < L.top) + if (nresults === lua.LUA_MULTRET && L.ci.top < L.top) L.ci.top = L.top; }; @@ -713,7 +713,7 @@ const lua_pcallk = function(L, nargs, nresults, errfunc, ctx, k) { status = TS.LUA_OK; } - if (nresults == lua.LUA_MULTRET && L.ci.top < L.top) + if (nresults === lua.LUA_MULTRET && L.ci.top < L.top) L.ci.top = L.top; return status; diff --git a/src/lbaselib.js b/src/lbaselib.js index b663b87..77638d0 100644 --- a/src/lbaselib.js +++ b/src/lbaselib.js @@ -90,7 +90,7 @@ const luaB_rawset = function(L) { const luaB_type = function(L) { let t = lapi.lua_type(L, 1); - lauxlib.luaL_argcheck(L, t != CT.LUA_TNONE, 1, "value expected"); + lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, "value expected"); lapi.lua_pushstring(L, lapi.lua_typename(L, t)); return 1; }; diff --git a/src/lcode.js b/src/lcode.js index a2dc43b..c2134ff 100644 --- a/src/lcode.js +++ b/src/lcode.js @@ -2,10 +2,19 @@ const assert = require('assert'); -const lopcode = require('./lopcode.js'); const llex = require('./llex.js'); -const lcode = require('./lcode.js'); +const llimit = require('./llimit.js'); +const lobject = require('./lobject.js'); +const lopcode = require('./lopcode.js'); +const lparser = require('./lparser.js'); +const lua = require('./lua.js'); +const lvm = require('./lvm.js'); +const CT = lua.constants_type; const OpCodesI = lopcode.OpCodesI; +const TValue = lobject.TValue; + +/* Maximum number of registers in a Lua function (must fit in 8 bits) */ +const MAXREGS = 255; /* ** Marks the end of a patch list. It is an invalid value both as an absolute @@ -46,6 +55,62 @@ const UnOpr = { OPR_NOUNOPR: 4 }; +const hasjumps = function(e) { + return e.t !== e.f; +}; + +/* +** If expression is a numeric constant, fills 'v' with its value +** and returns true. Otherwise, returns false. +*/ +const tonumeral = function(e, v) { + let ek = lparser.expkind; + if (hasjumps(e)) + return false; /* not a numeral */ + switch (e.k) { + case ek.VKINT: + if (v) { + v.type = CT.LUA_TNUMINT; + v.value = e.u.ival; + } + return true; + case ek.VKFLT: + if (v) { + v.type = CT.LUA_TNUMFLT; + v.value = e.u.nval; + } + return true; + default: return false; + } +}; + +/* +** Create a OP_LOADNIL instruction, but try to optimize: if the previous +** instruction is also OP_LOADNIL and ranges are compatible, adjust +** range of previous instruction instead of emitting a new one. (For +** instance, 'local a; local b' will generate a single opcode.) +*/ +const luaK_nil = function(fs, from, n) { + let previous; + let l = from + n - 1; /* last register to set nil */ + if (fs.pc > fs.lasttarget) { /* no jumps to current position? */ + previous = fs.f.code[fs.pc-1]; + if (previous.opcode === OpCodesI.OP_LOADNIL) { /* previous is LOADNIL? */ + let pfrom = previous.A; /* get previous range */ + let pl = pfrom + previous.B; + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ + if (pl > l) l = pl; /* l = max(l, pl) */ + lopcode.SETARG_A(previous, from); + lopcode.SETARG_B(previous, l - from); + return; + } + } /* else go through */ + } + luaK_codeABC(fs, OpCodesI.OP_LOADNIL, from, n - 1, 0); /* else no optimization */ +}; + const getinstruction = function(fs, e) { return fs.f.code[e.u.info]; }; @@ -109,6 +174,10 @@ const luaK_jump = function (fs) { return j; }; +const luaK_jumpto = function(fs, t) { + return luaK_patchlist(fs, luaK_jump(fs), t); +}; + /* ** Code a 'return' instruction */ @@ -116,6 +185,15 @@ const luaK_ret = function(fs, first, nret) { luaK_codeABC(fs, OpCodesI.OP_RETURN, first, nret + 1, 0); }; +/* +** Code a "conditional jump", that is, a test or comparison opcode +** followed by a jump. Return jump position. +*/ +const condjump = function(fs, op, A, B, C) { + luaK_codeABC(fs, op, A, B, C); + return luaK_jump(fs); +}; + /* ** returns current 'pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). @@ -158,6 +236,14 @@ const patchtestreg = function(fs, node, reg) { return true; }; +/* +** Traverse a list of tests ensuring no one produces a value +*/ +const removevalues = function(fs, list) { + for (; list !== NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, lopcode.NO_REG); +}; + /* ** Traverse a list of tests, patching their destination address and ** registers: tests producing values jump to 'vtarget' (and put their @@ -174,6 +260,16 @@ const patchlistaux = function(fs, list, vtarget, reg, dtarget) { } }; +/* +** Ensure all pending jumps to current position are fixed (jumping +** to current position with no values) and reset list of pending +** jumps +*/ +const dischargejpc = function(fs) { + patchlistaux(fs, fs.jpc, fs.pc, lopcode.NO_REG, fs.pc); + fs.jpc = NO_JUMP; +}; + /* ** Add elements in 'list' to list of pending jumps to "here" ** (current position) @@ -217,7 +313,7 @@ const luaK_patchclose = function(fs, list, level) { */ const luaK_code = function(fs, i) { let f = fs.f; - lcode.dischargejpc(fs); /* 'pc' will change */ + dischargejpc(fs); /* 'pc' will change */ /* put new instruction in code array */ f.code[fs.pc] = i; f.lineinfo[fs.pc] = fs.ls.lastline; @@ -229,9 +325,9 @@ const luaK_code = function(fs, i) { ** of parameters versus opcode.) */ const luaK_codeABC = function(fs, o, a, b, c) { - assert(lopcode.getOpMode(o) == lopcode.iABC); - assert(lopcode.getBMode(o) != lopcode.OpArgN || b === 0); - assert(lopcode.getCMode(o) != lopcode.OpArgN || c === 0); + assert(lopcode.getOpMode(o) === lopcode.iABC); + assert(lopcode.getBMode(o) !== lopcode.OpArgN || b === 0); + assert(lopcode.getCMode(o) !== lopcode.OpArgN || c === 0); assert(a <= lopcode.MAXARG_A && b <= lopcode.MAXARG_B && c <= lopcode.MAXARG_C); return luaK_code(fs, lopcode.CREATE_ABC(o, a, b, c)); }; @@ -240,8 +336,8 @@ const luaK_codeABC = function(fs, o, a, b, c) { ** Format and emit an 'iABx' instruction. */ const luaK_codeABx = function(fs, o, a, bc) { - assert(lopcode.getOpMode(o) == lopcode.iABx || lopcode.getOpMode(o) == lopcode.iAsBx); - assert(lopcode.getCMode(o) == lopcode.OpArgN); + assert(lopcode.getOpMode(o) === lopcode.iABx || lopcode.getOpMode(o) === lopcode.iAsBx); + assert(lopcode.getCMode(o) === lopcode.OpArgN); assert(a <= lopcode.MAXARG_A && bc <= lopcode.MAXARG_Bx); return luaK_code(fs, lopcode.CREATE_ABx(o, a, bc)); }; @@ -250,16 +346,879 @@ const luaK_codeAsBx = function(fs,o,A,sBx) { return luaK_codeABx(fs, o, A, (sBx) + lopcode.MAXARG_sBx); }; -module.exports.BinOpr = BinOpr; -module.exports.NO_JUMP = NO_JUMP; -module.exports.UnOpr = UnOpr; -module.exports.getinstruction = getinstruction; -module.exports.luaK_codeABC = luaK_codeABC; -module.exports.luaK_codeABx = luaK_codeABx; -module.exports.luaK_codeAsBx = luaK_codeAsBx; -module.exports.luaK_getlabel = luaK_getlabel; -module.exports.luaK_jump = luaK_jump; -module.exports.luaK_patchclose = luaK_patchclose; -module.exports.luaK_patchlist = luaK_patchlist; -module.exports.luaK_patchtohere = luaK_patchtohere; -module.exports.luaK_ret = luaK_ret; \ No newline at end of file +/* +** Emit an "extra argument" instruction (format 'iAx') +*/ +const codeextraarg = function(fs, a) { + assert(a <= lopcode.MAXARG_Ax); + return luaK_code(fs, lopcode.CREATE_Ax(OpCodesI.OP_EXTRAARG, a)); +}; + +/* +** Emit a "load constant" instruction, using either 'OP_LOADK' +** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' +** instruction with "extra argument". +*/ +const luaK_codek = function(fs, reg, k) { + if (k <= lopcode.MAXARG_Bx) + return luaK_codeABx(fs, OpCodesI.OP_LOADK, reg, k); + else { + let p = luaK_codeABx(fs, OpCodesI.OP_LOADKX, reg, 0); + codeextraarg(fs, k); + return p; + } +}; + +/* +** Check register-stack level, keeping track of its maximum size +** in field 'maxstacksize' +*/ +const luaK_checkstack = function(fs, n) { + let newstack = fs.freereg + n; + if (newstack > fs.f.maxstacksize) { + if (newstack >= MAXREGS) + llex.luaX_syntaxerror(fs.ls, "function or expression needs to many registers"); + fs.f.maxstacksize = newstack; + } +}; + +/* +** Reserve 'n' registers in register stack +*/ +const luaK_reserveregs = function(fs, n) { + luaK_checkstack(fs, n); + fs.freereg += n; +}; + +/* +** Free register 'reg', if it is neither a constant index nor +** a local variable. +*/ +const freereg = function(fs, reg) { + if (!lopcode.ISK(reg) && reg >= fs.nactvar) { + fs.freereg--; + assert(reg === fs.freereg); + } +}; + +/* +** Free register used by expression 'e' (if any) +*/ +const freeexp = function(fs, e) { + if (e.k === lparser.expkind.VNONRELOC) + freereg(fs, e.u.info); +}; + +/* +** Free registers used by expressions 'e1' and 'e2' (if any) in proper +** order. +*/ +const freeexps = function(fs, e1, e2) { + let r1 = (e1.k === lparser.expkind.VNONRELOC) ? e1.u.info : -1; + let r2 = (e2.k === lparser.expkind.VNONRELOC) ? e2.u.info : -1; + if (r1 > r2) { + freereg(fs, r1); + freereg(fs, r2); + } + else { + freereg(fs, r2); + freereg(fs, r1); + } +}; + + +/* +** Add constant 'v' to prototype's list of constants (field 'k'). +** Use scanner's table to cache position of constants in constant list +** and try to reuse constants. Because some values should not be used +** as keys (nil cannot be a key, integer keys can collapse with float +** keys), the caller must provide a useful 'key' for indexing the cache. +*/ +const addk = function(fs, key, v) { + let f = fs.f; + let idx = fs.ls.h.__index(fs.ls.h, key); /* index scanner table */ + if (idx) { /* is there an index there? */ + /* correct value? (warning: must distinguish floats from integers!) */ + if (f.k[idx].ttype() === v.ttype() && f.k[idx].value === v.value) + return idx; /* reuse index */ + } + /* constant not found; create a new entry */ + let k = fs.nk; + fs.ls.h.__newindex(fs.ls.h, key, k); + f.k[k] = v; + fs.nk++; + return k; +}; + +/* +** Add a string to list of constants and return its index. +*/ +const luaK_stringK = function(fs, s) { + let o = new TValue(CT.LUA_TLNGSTR, s); + return addk(fs, o, o); /* use string itself as key */ +}; + + +/* +** Add an integer to list of constants and return its index. +** Integers use userdata as keys to avoid collision with floats with +** same value; conversion to 'void*' is used only for hashing, so there +** are no "precision" problems. +*/ +const luaK_intK = function(fs, n) { + let k = new TValue(CT.LUA_TLNGSTR, `n`); + let o = new TValue(CT.LUA_TNUMINT, n); + return addk(fs, k, o); +}; + +/* +** Add a float to list of constants and return its index. +*/ +const luaK_numberK = function(fs, r) { + let o = new TValue(CT.LUA_TNUMFLT, r); + return addk(fs, o, o); /* use number itself as key */ +}; + + +/* +** Add a boolean to list of constants and return its index. +*/ +const boolK = function(fs, b) { + let o = new TValue(CT.LUA_TBOOLEAN, b); + return addk(fs, o, o); /* use boolean itself as key */ +}; + + +/* +** Add nil to list of constants and return its index. +*/ +const nilK = function(fs) { + let o = new TValue(CT.LUA_TNIL, null); + return addk(fs, o, o); +}; + +/* +** Fix an expression to return the number of results 'nresults'. +** Either 'e' is a multi-ret expression (function call or vararg) +** or 'nresults' is LUA_MULTRET (as any expression can satisfy that). +*/ +const luaK_setreturns = function(fs, e, nresults) { + let ek = lparser.expkind; + if (e.k === ek.VCALL) { /* expression is an open function call? */ + lopcode.SETARG_C(getinstruction(fs, e), nresults + 1); + } + else if (e.k === ek.VVARARG) { + let pc = getinstruction(fs, e); + lopcode.SETARG_B(pc, nresults + 1); + lopcode.SETARG_A(pc, fs.freereg); + luaK_reserveregs(fs, 1); + } + else assert(nresults === lua.LUA_MULTRET); +}; + +const luaK_setmultret = function(fs, e) { + luaK_setreturns(fs, e, lua.LUA_MULTRET); +}; + +/* +** Fix an expression to return one result. +** If expression is not a multi-ret expression (function call or +** vararg), it already returns one result, so nothing needs to be done. +** Function calls become VNONRELOC expressions (as its result comes +** fixed in the base register of the call), while vararg expressions +** become VRELOCABLE (as OP_VARARG puts its results where it wants). +** (Calls are created returning one result, so that does not need +** to be fixed.) +*/ +const luaK_setoneret = function(fs, e) { + let ek = lparser.expkind; + if (e.k === ek.VCALL) { /* expression is an open function call? */ + /* already returns 1 value */ + assert(getinstruction(fs, e).C === 2); + e.k = ek.VNONRELOC; /* result has fixed position */ + e.u.info = getinstruction(fs, e).A; + } else if (e.k === ek.VVARARG) { + lopcode.SETARG_B(getinstruction(fs, e), 2); + e.k = ek.VRELOCABLE; /* can relocate its simple result */ + } +}; + +/* +** Ensure that expression 'e' is not a variable. +*/ +const luaK_dischargevars = function(fs, e) { + let ek = lparser.expkind; + + switch (e.k) { + case ek.VLOCAL: { /* already in a register */ + e.k = ek.VNONRELOC; /* becomes a non-relocatable value */ + break; + } + case ek.VUPVAL: { /* move value to some (pending) register */ + e.u.info = luaK_codeABC(fs, OpCodesI.OP_GETUPVAL, 0, e.u.info, 0); + e.k = ek.VRELOCABLE; + break; + } + case ek.VINDEXED: { + let op; + freereg(fs, e.u.ind.idx); + if (e.u.ind.vt === ek.VLOCAL) { /* is 't' in a register? */ + freereg(fs, e.u.ind.t); + op = OpCodesI.OP_GETTABLE; + } else { + assert(e.u.ind.vt === ek.VUPVAL); + op = OpCodesI.OP_GETTABUP; /* 't' is in an upvalue */ + } + e.u.info = luaK_codeABC(fs, op, 0, e.u.ind.t, e.u.ind.idx); + e.k = OpCodesI.VRELOCABLE; + break; + } + case ek.VVARARG: case ek.VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +}; + +const code_loadbool = function(fs, A, b, jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OpCodesI.OP_LOADBOOL, A, b, jump); +}; + +/* +** Ensures expression value is in register 'reg' (and therefore +** 'e' will become a non-relocatable expression). +*/ +const discharge2reg = function(fs, e, reg) { + let ek = lparser.expkind; + luaK_dischargevars(fs, e); + switch (e.k) { + case ek.VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case ek.VFALSE: case ek.VTRUE: { + luaK_codeABC(fs, OpCodesI.OP_LOADBOOL, reg, e.k === ek.VTRUE, 0); + break; + } + case ek.VK: { + luaK_codek(fs, reg, e.u.info); + break; + } + case ek.VKFLT: { + luaK_codek(fs, reg, luaK_numberK(fs, e.u.nval)); + break; + } + case ek.VKINT: { + luaK_codek(fs, reg, luaK_intK(fs, e.u.ival)); + break; + } + case ek.VRELOCABLE: { + let pc = getinstruction(fs, e); + lopcode.SETARG_A(pc, reg); /* instruction will put result in 'reg' */ + break; + } + case ek.VNONRELOC: { + if (reg !== e.u.info) + luaK_codeABC(fs, OpCodesI.OP_MOVE, reg, e.u.info, 0); + break; + } + default: { + assert(e.k === ek.VJMP); + return; /* nothing to do... */ + } + } + e.u.info = reg; + e.k = ek.VNONRELOC; +}; + +/* +** Ensures expression value is in any register. +*/ +const discharge2anyreg = function(fs, e) { + if (e.k !== lparser.expkind.VNONRELOC) { /* no fixed register yet? */ + luaK_reserveregs(fs, 1); /* get a register */ + discharge2reg(fs, e, fs.freereg-1); /* put value there */ + } +}; + +/* +** check whether list has any jump that do not produce a value +** or produce an inverted value +*/ +const need_value = function(fs, list) { + for (; list !== NO_JUMP; list = getjump(fs, list)) { + let i = getjumpcontrol(fs, list); + if (i.opcode !== OpCodesI.OP_TESTSET) return true; + } + return false; /* not found */ +}; + +/* +** Ensures final expression result (including results from its jump +** lists) is in register 'reg'. +** If expression has jumps, need to patch these jumps either to +** its final position or to "load" instructions (for those tests +** that do not produce values). +*/ +const exp2reg = function(fs, e, reg) { + let ek = lparser.expkind; + discharge2reg(fs, e, reg); + if (e.k === ek.VJMP) /* expression itself is a test? */ + e.t = luaK_concat(fs, e.t, e.u.info); /* put this jump in 't' list */ + if (hasjumps(e)) { + let final; /* position after whole expression */ + let p_f = NO_JUMP; /* position of an eventual LOAD false */ + let p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e.t) || need_value(fs, e.f)) { + let fj = (e.k === ek.VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_loadbool(fs, reg, 0, 1); + p_t = code_loadbool(fs, reg, 1, 0); + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e.f, final, reg, p_f); + patchlistaux(fs, e.t, final, reg, p_t); + } + e.f = e.t = NO_JUMP; + e.u.info = reg; + e.k = ek.VNONRELOC; +}; + +/* +** Ensures final expression result (including results from its jump +** lists) is in next available register. +*/ +const luaK_exp2nextreg = function(fs, e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs.freereg - 1); +}; + + +/* +** Ensures final expression result (including results from its jump +** lists) is in some (any) register and return that register. +*/ +const luaK_exp2anyreg = function(fs, e) { + luaK_dischargevars(fs, e); + if (e.k === lparser.expkind.VNONRELOC) { /* expression already has a register? */ + if (!hasjumps(e)) /* no jumps? */ + return e.u.info; /* result is already in a register */ + if (e.u.info >= fs.nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e.u.info); /* put final result in it */ + return e.u.info; + } + } + luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ + return e.u.info; +}; + +/* +** Ensures final expression result is either in a register or in an +** upvalue. +*/ +const luaK_exp2anyregup = function(fs, e) { + if (e.k !== lparser.expkind.VUPVAL || hasjumps(e)) + luaK_exp2anyreg(fs, e); +}; + +/* +** Ensures final expression result is either in a register or it is +** a constant. +*/ +const luaK_exp2val = function(fs, e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +}; + +/* +** Ensures final expression result is in a valid R/K index +** (that is, it is either in a register or in 'k' with an index +** in the range of R/K indices). +** Returns R/K index. +*/ +const luaK_exp2RK = function(fs, e) { + let ek = lparser.expkind; + let vk = false; + luaK_exp2val(fs, e); + switch (e.k) { /* move constants to 'k' */ + case ek.VTRUE: e.u.info = boolK(fs, 1); vk = true; break; + case ek.VFALSE: e.u.info = boolK(fs, 0); vk = true; break; + case ek.VNIL: e.u.info = nilK(fs); vk = true; break; + case ek.VKINT: e.u.info = luaK_intK(fs, e.u.ival); vk = true; break; + case ek.VKFLT: e.u.info = luaK_numberK(fs, e.u.nval); vk = true; break; + case ek.VK: vk = true; break; + default: break; + } + + if (vk) { + e.k = ek.VK; + if (e.u.info <= lopcode.MAXINDEXRK) /* constant fits in 'argC'? */ + return lopcode.RKASK(e.u.info); + } + + /* not a constant in the right range: put it in a register */ + return luaK_exp2anyreg(fs, e); +}; + +/* +** Generate code to store result of expression 'ex' into variable 'var'. +*/ +const luaK_storevar = function(fs, vr, ex) { + let ek = lparser.expkind; + switch (vr.k) { + case ek.VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, vr.u.info); /* compute 'ex' into proper place */ + return; + } + case ek.VUPVAL: { + let e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OpCodesI.OP_SETUPVAL, e, vr.u.info, 0); + break; + } + case ek.VINDEXED: { + let op = (vr.u.ind.vt === ek.VLOCAL) ? OpCodesI.OP_SETTABLE : OpCodesI.OP_SETTABUP; + let e = luaK_exp2RK(fs, ex); + luaK_codeABC(fs, op, vr.u.ind.t, vr.u.ind.idx, e); + break; + } + } + freeexp(fs, ex); +}; + + +/* +** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). +*/ +const luaK_self = function(fs, e, key) { + luaK_exp2anyreg(fs, e); + let ereg = e.u.info; /* register where 'e' was placed */ + freeexp(fs, e); + e.u.info = fs.freereg; /* base register for op_self */ + e.k = lparser.expkind.VNONRELOC; /* self expression has a fixed register */ + luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ + luaK_codeABC(fs, OpCodesI.OP_SELF, e.u.info, ereg, luaK_exp2RK(fs, key)); + freeexp(fs, key); +}; + +/* +** Negate condition 'e' (where 'e' is a comparison). +*/ +const negatecondition = function(fs, e) { + let pc = getjumpcontrol(fs, e.u.info); + assert(lopcode.testTMode(pc.opcode) && pc.opcode !== OpCodesI.OP_TESTSET && pc.opcode !== OpCodesI.OP_TEST); + lopcode.SETARG_A(pc, !(pc.A)); +}; + +/* +** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' +** is true, code will jump if 'e' is true.) Return jump position. +** Optimize when 'e' is 'not' something, inverting the condition +** and removing the 'not'. +*/ +const jumponcond = function(fs, e, cond) { + if (e.k === lparser.expkind.VRELOCABLE) { + let ie = getinstruction(fs, e); + if (ie.opcode === OpCodesI.OP_NOT) { + fs.pc--; /* remove previous OP_NOT */ + return condjump(fs, OpCodesI.OP_TEST, ie.B, 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OpCodesI.OP_TESTSET, lopcode.NO_REG, e.u.info, cond); +}; + +/* +** Emit code to go through if 'e' is true, jump otherwise. +*/ +const luaK_goiftrue = function(fs, e) { + let ek = lparser.expkind; + let pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e.k) { + case ek.VJMP: { /* condition? */ + negatecondition(fs, e); /* jump when it is false */ + pc = e.u.info; /* save jump position */ + break; + } + case ek.VK: case ek.VKFLT: case ek.VKINT: case ek.VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 0); /* jump when false */ + break; + } + } + e.f = luaK_concat(fs, e.f, pc); /* insert new jump in false list */ + luaK_patchtohere(fs, e.t); /* true list jumps to here (to go through) */ + e.t = NO_JUMP; +}; + +/* +** Emit code to go through if 'e' is false, jump otherwise. +*/ +const luaK_goiffalse = function(fs, e) { + let ek = lparser.expkind; + let pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e.k) { + case ek.VJMP: { + pc = e.u.info; /* already jump if true */ + break; + } + case ek.VNIL: case ek.VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 1); /* jump if true */ + break; + } + } + e.t = luaK_concat(fs, e.t, pc); /* insert new jump in 't' list */ + luaK_patchtohere(fs, e.f); /* false list jumps to here (to go through) */ + e.f = NO_JUMP; +}; + +/* +** Code 'not e', doing constant folding. +*/ +const codenot = function(fs, e) { + let ek = lparser.expkind; + luaK_dischargevars(fs, e); + switch (e.k) { + case ek.VNIL: case ek.VFALSE: { + e.k = ek.VTRUE; /* true === not nil === not false */ + break; + } + case ek.VK: case ek.VKFLT: case ek.VKINT: case ek.VTRUE: { + e.k = ek.VFALSE; /* false === not "x" === not 0.5 === not 1 === not true */ + break; + } + case ek.VJMP: { + negatecondition(fs, e); + break; + } + case ek.VRELOCABLE: + case ek.VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e.u.info = luaK_codeABC(fs, OpCodesI.OP_NOT, 0, e.u.info, 0); + e.k = ek.VRELOCABLE; + break; + } + } + /* interchange true and false lists */ + { let temp = e.f; e.f = e.t; e.t = temp; } + removevalues(fs, e.f); /* values are useless when negated */ + removevalues(fs, e.t); +}; + +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. +*/ +const luaK_indexed = function(fs, t, k) { + let ek = lparser.expkind; + assert(!hasjumps(t) && (lparser.vkisinreg(t.k) || t.k === ek.VUPVAL)); + t.u.ind.t = t.u.info; /* register or upvalue index */ + t.u.ind.idx = luaK_exp2RK(fs, k); /* R/K index for key */ + t.u.ind.vt = (t.k === ek.VUPVAL) ? ek.VUPVAL : ek.VLOCAL; + t.k = ek.VINDEXED; +}; + +/* +** Return false if folding can raise an error. +** Bitwise operations need operands convertible to integers; division +** operations cannot have 0 as divisor. +*/ +const validop = function(op, v1, v2) { + switch (op) { + case lua.LUA_OPBAND: case lua.LUA_OPBOR: case lua.LUA_OPBXOR: + case lua.LUA_OPSHL: case lua.LUA_OPSHR: case lua.LUA_OPBNOT: { /* conversion errors */ + return (lvm.tointeger(v1) && lvm.tointeger(v2)); + } + case lua.LUA_OPDIV: case lua.LUA_OPIDIV: case lua.LUA_OPMOD: /* division by 0 */ + return (v2.value !== 0); + default: return 1; /* everything else is valid */ + } +}; + +/* +** Try to "constant-fold" an operation; return 1 iff successful. +** (In this case, 'e1' has the final result.) +*/ +const constfolding = function(fs, op, e1, e2) { + let ek = lparser.expkind; + let v1 = new TValue(); + let v2 = new TValue(); + let res = new TValue(); + if (!tonumeral(e1, v1) || !tonumeral(e2, v2) || !validop(op, v1, v2)) + return 0; /* non-numeric operands or not safe to fold */ + lobject.luaO_arith(fs.ls.L, op, v1, v2, res); /* does operation */ + if (res.ttisinteger()) { + e1.k = ek.VKINT; + e1.u.ival = res.value; + } + else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ + let n = res.value; + if (isNaN(n) || n === 0) + return false; + e1.k = ek.VKFLT; + e1.u.nval = n; + } + return true; +}; + +/* +** Emit code for unary expressions that "produce values" +** (everything but 'not'). +** Expression to produce final result will be encoded in 'e'. +*/ +const codeunexpval = function(fs, op, e, line) { + let r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ + freeexp(fs, e); + e.u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ + e.k = lparser.expkind.VRELOCABLE; /* all those operations are relocatable */ + luaK_fixline(fs, line); +}; + +/* +** Emit code for binary expressions that "produce values" +** (everything but logical operators 'and'/'or' and comparison +** operators). +** Expression to produce final result will be encoded in 'e1'. +** Because 'luaK_exp2RK' can free registers, its calls must be +** in "stack order" (that is, first on 'e2', which may have more +** recent registers to be released). +*/ +const codebinexpval = function(fs, op, e1, e2, line) { + let rk2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */ + let rk1 = luaK_exp2RK(fs, e1); + freeexps(fs, e1, e2); + e1.u.info = luaK_codeABC(fs, op, 0, rk1, rk2); /* generate opcode */ + e1.k = lparser.expkind.VRELOCABLE; /* all those operations are relocatable */ + luaK_fixline(fs, line); +}; + + +/* +** Emit code for comparisons. +** 'e1' was already put in R/K form by 'luaK_infix'. +*/ +const codecomp = function(fs, opr, e1, e2) { + let ek = lparser.expkind; + let rk1 = (e1.k === ek.VK) ? lopcode.RKASK(e1.u.info) : llimit.check_exp(e1.k === ek.VNONRELOC, e1.u.info); + let rk2 = luaK_exp2RK(fs, e2); + freeexps(fs, e1, e2); + switch (opr) { + case BinOpr.OPR_NE: { /* '(a ~= b)' ==> 'not (a === b)' */ + e1.u.info = condjump(fs, OpCodesI.OP_EQ, 0, rk1, rk2); + break; + } + case BinOpr.OPR_GT: case BinOpr.OPR_GE: { + /* '(a > b)' ==> '(b < a)'; '(a >= b)' ==> '(b <= a)' */ + let op = (opr - BinOpr.OPR_NE) + OpCodesI.OP_EQ; + e1.u.info = condjump(fs, op, 1, rk2, rk1); /* invert operands */ + break; + } + default: { /* '==', '<', '<=' use their own opcodes */ + let op = (opr - BinOpr.OPR_EQ) + OpCodesI.OP_EQ; + e1.u.info = condjump(fs, op, 1, rk1, rk2); + break; + } + } + e1.k = ek.VJMP; +}; + +/* +** Apply prefix operation 'op' to expression 'e'. +*/ +const luaK_prefix = function(fs, op, e, line) { + let ef = new lparser.expdesc(); + ef.k = lparser.expkind.VKINT; + ef.u.ival = e.u.nval = e.u.info = 0; + e.t = NO_JUMP; + e.f = NO_JUMP; + switch (op) { + case UnOpr.OPR_MINUS: case UnOpr.OPR_BNOT: /* use 'ef' as fake 2nd operand */ + if (constfolding(fs, op + lua.LUA_OPUNM, e, ef)) + break; + /* FALLTHROUGH */ + case UnOpr.OPR_LEN: + codeunexpval(fs, op + UnOpr.OP_UNM, e, line); + break; + case UnOpr.OPR_NOT: codenot(fs, e); break; + } +}; + +/* +** Process 1st operand 'v' of binary operation 'op' before reading +** 2nd operand. +*/ +const luaK_infix = function(fs, op, v) { + switch (op) { + case BinOpr.OPR_AND: { + luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ + break; + } + case BinOpr.OPR_OR: { + luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ + break; + } + case BinOpr.OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the 'stack' */ + break; + } + case BinOpr.OPR_ADD: case BinOpr.OPR_SUB: + case BinOpr.OPR_MUL: case BinOpr.OPR_DIV: case BinOpr.OPR_IDIV: + case BinOpr.OPR_MOD: case BinOpr.OPR_POW: + case BinOpr.OPR_BAND: case BinOpr.OPR_BOR: case BinOpr.OPR_BXOR: + case BinOpr.OPR_SHL: case BinOpr.OPR_SHR: { + if (!tonumeral(v, null)) + luaK_exp2RK(fs, v); + /* else keep numeral, which may be folded with 2nd operand */ + break; + } + default: { + luaK_exp2RK(fs, v); + break; + } + } +}; + +/* +** Finalize code for binary operation, after reading 2nd operand. +** For '(a .. b .. c)' (which is '(a .. (b .. c))', because +** concatenation is right associative), merge second CONCAT into first +** one. +*/ +const luaK_posfix = function(fs, op, e1, e2, line) { + let ek = lparser.expkind; + switch (op) { + case BinOpr.OPR_AND: { + assert(e1.t === NO_JUMP); /* list closed by 'luK_infix' */ + luaK_dischargevars(fs, e2); + e2.f = luaK_concat(fs, e2.f, e1.f); + // WARN: *e1 = *e2; + break; + } + case BinOpr.OPR_OR: { + assert(e1.f === NO_JUMP); /* list closed by 'luK_infix' */ + luaK_dischargevars(fs, e2); + e2.t = luaK_concat(fs, e2.t, e1.t); + // WARN: *e1 = *e2; + break; + } + case BinOpr.OPR_CONCAT: { + let ins = getinstruction(fs, e2); + luaK_exp2val(fs, e2); + if (e2.k === ek.VRELOCABLE && ins.opcode === OpCodesI.OP_CONCAT) { + assert(e1.u.info === ins.B - 1); + freeexp(fs, e1); + lopcode.SETARG_B(ins, e1.u.info); + e1.k = ek.VRELOCABLE; e1.u.info = e2.u.info; + } + else { + luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ + codebinexpval(fs, OpCodesI.OP_CONCAT, e1, e2, line); + } + break; + } + case BinOpr.OPR_ADD: case BinOpr.OPR_SUB: case BinOpr.OPR_MUL: case BinOpr.OPR_DIV: + case BinOpr.OPR_IDIV: case BinOpr.OPR_MOD: case BinOpr.OPR_POW: + case BinOpr.OPR_BAND: case BinOpr.OPR_BOR: case BinOpr.OPR_BXOR: + case BinOpr.OPR_SHL: case BinOpr.OPR_SHR: { + if (!constfolding(fs, op + lua.LUA_OPADD, e1, e2)) + codebinexpval(fs, op + OpCodesI.OP_ADD, e1, e2, line); + break; + } + case BinOpr.OPR_EQ: case BinOpr.OPR_LT: case BinOpr.OPR_LE: + case BinOpr.OPR_NE: case BinOpr.OPR_GT: case BinOpr.OPR_GE: { + codecomp(fs, op, e1, e2); + break; + } + } +}; + +/* +** Change line information associated with current position. +*/ +const luaK_fixline = function(fs, line) { + fs.f.lineinfo[fs.pc - 1] = line; +}; + +/* +** Emit a SETLIST instruction. +** 'base' is register that keeps table; +** 'nelems' is #table plus those to be stored now; +** 'tostore' is number of values (in registers 'base + 1',...) to add to +** table (or LUA_MULTRET to add up to stack top). +*/ +const luaK_setlist = function(fs, base, nelems, tostore) { + let c = (nelems - 1)/lopcode.LFIELDS_PER_FLUSH + 1; + let b = (tostore === lua.LUA_MULTRET) ? 0 : tostore; + assert(tostore !== 0 && tostore <= lopcode.LFIELDS_PER_FLUSH); + if (c <= lopcode.MAXARG_C) + luaK_codeABC(fs, OpCodesI.OP_SETLIST, base, b, c); + else if (c <= lopcode.MAXARG_Ax) { + luaK_codeABC(fs, OpCodesI.OP_SETLIST, base, b, 0); + codeextraarg(fs, c); + } + else + llex.luaX_syntaxerror(fs.ls, "constructor too long"); + fs.freereg = base + 1; /* free registers with list values */ +}; + + +module.exports.BinOpr = BinOpr; +module.exports.NO_JUMP = NO_JUMP; +module.exports.UnOpr = UnOpr; +module.exports.getinstruction = getinstruction; +module.exports.luaK_checkstack = luaK_checkstack; +module.exports.luaK_code = luaK_code; +module.exports.luaK_codeABC = luaK_codeABC; +module.exports.luaK_codeABx = luaK_codeABx; +module.exports.luaK_codeAsBx = luaK_codeAsBx; +module.exports.luaK_codek = luaK_codek; +module.exports.luaK_concat = luaK_concat; +module.exports.luaK_dischargevars = luaK_dischargevars; +module.exports.luaK_exp2RK = luaK_exp2RK; +module.exports.luaK_exp2anyreg = luaK_exp2anyreg; +module.exports.luaK_exp2anyregup = luaK_exp2anyregup; +module.exports.luaK_exp2nextreg = luaK_exp2nextreg; +module.exports.luaK_exp2val = luaK_exp2val; +module.exports.luaK_fixline = luaK_fixline; +module.exports.luaK_getlabel = luaK_getlabel; +module.exports.luaK_goiffalse = luaK_goiffalse; +module.exports.luaK_goiftrue = luaK_goiftrue; +module.exports.luaK_indexed = luaK_indexed; +module.exports.luaK_infix = luaK_infix; +module.exports.luaK_intK = luaK_intK; +module.exports.luaK_jump = luaK_jump; +module.exports.luaK_jumpto = luaK_jumpto; +module.exports.luaK_nil = luaK_nil; +module.exports.luaK_numberK = luaK_numberK; +module.exports.luaK_patchclose = luaK_patchclose; +module.exports.luaK_patchlist = luaK_patchlist; +module.exports.luaK_patchtohere = luaK_patchtohere; +module.exports.luaK_posfix = luaK_posfix; +module.exports.luaK_prefix = luaK_prefix; +module.exports.luaK_reserveregs = luaK_reserveregs; +module.exports.luaK_ret = luaK_ret; +module.exports.luaK_self = luaK_self; +module.exports.luaK_setlist = luaK_setlist; +module.exports.luaK_setmultret = luaK_setmultret; +module.exports.luaK_setoneret = luaK_setoneret; +module.exports.luaK_setreturns = luaK_setreturns; +module.exports.luaK_storevar = luaK_storevar; +module.exports.luaK_stringK = luaK_stringK; \ No newline at end of file diff --git a/src/ldebug.js b/src/ldebug.js index f6d0633..091b896 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -60,7 +60,7 @@ const upvalname = function(p, uv) { }; const funcinfo = function(ar, cl) { - if (cl === null || cl.type == CT.LUA_TCCL) { + if (cl === null || cl.type === CT.LUA_TCCL) { ar.source = "=[JS]"; ar.linedefined = -1; ar.lastlinedefined = -1; @@ -77,7 +77,7 @@ const funcinfo = function(ar, cl) { }; const collectvalidlines = function(L, f) { - if (f === null || f.c.type == CT.LUA_TCCL) { + if (f === null || f.c.type === CT.LUA_TCCL) { L.stack[L.top++] = ldo.nil; assert(L.top <= L.ci.top, "stack overflow"); } else { @@ -123,7 +123,7 @@ const auxgetinfo = function(L, what, ar, f, ci) { } case 'u': { ar.nups = f === null ? 0 : f.c.nupvalues; - if (f === null || f.c.type == CT.LUA_TCCL) { + if (f === null || f.c.type === CT.LUA_TCCL) { ar.isvararg = true; ar.nparams = 0; } else { diff --git a/src/ldo.js b/src/ldo.js index 8d12c9f..4345b15 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -509,7 +509,7 @@ const f_parser = function(L, data) { let p = new BytecodeParser(data); let cl = p.luaU_undump(L); - assert(cl.nupvalues == cl.p.upvalues.length); + assert(cl.nupvalues === cl.p.upvalues.length); lfunc.luaF_initupvals(L, cl); }; diff --git a/src/lfunc.js b/src/lfunc.js index fde910b..37239b2 100644 --- a/src/lfunc.js +++ b/src/lfunc.js @@ -119,7 +119,7 @@ const luaF_getlocalname = function(f, local_number, pc) { for (let i = 0; i < f.locvars.length && f.locvars[i].startpc <= pc; i++) { if (pc < f.locvars[i].endpc) { /* is variable active? */ local_number--; - if (local_number == 0) + if (local_number === 0) return f.locvars[i].varname; } } diff --git a/src/lobject.js b/src/lobject.js index 1a0df1c..f3078d1 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -4,6 +4,9 @@ const assert = require('assert'); const ljstype = require('./ljstype.js'); +const ltm = require('./ltm.js'); +const lua = require('./lua.js'); +const lvm = require('./lvm.js'); const CT = require('./lua.js').constant_types; const UpVal = require('./lfunc.js').UpVal; @@ -125,6 +128,7 @@ class Table extends TValue { // Those lua values are used by value, others by reference if (key instanceof TValue && [CT.LUA_TNIL, + CT.LUA_TBOOLEAN, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, @@ -349,7 +353,7 @@ const luaO_str2num = function(s) { /* ** converts an integer to a "floating point byte", represented as ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if -** eeeee != 0 and (xxx) otherwise. +** eeeee !== 0 and (xxx) otherwise. */ const luaO_int2fb = function(x) { let e = 0; /* exponent */ @@ -365,13 +369,90 @@ const luaO_int2fb = function(x) { return ((e+1) << 3) | (x - 8); }; +const intarith = function(L, op, v1, v2) { + switch (op) { + case lua.LUA_OPADD: return (v1 + v2)|0; + case lua.LUA_OPSUB: return (v1 - v2)|0; + case lua.LUA_OPMUL: return (v1 * v2)|0; + case lua.LUA_OPMOD: return (v1 % v2)|0; + case lua.LUA_OPIDIV: return (v1 / v2)|0; + case lua.LUA_OPBAND: return (v1 & v2)|0; + case lua.LUA_OPBOR: return (v1 | v2)|0; + case lua.LUA_OPBXOR: return (v1 ^ v2)|0; + case lua.LUA_OPSHL: return (v1 << v2)|0; + case lua.LUA_OPSHR: return (v1 >> v2)|0; + case lua.LUA_OPUNM: return (-v1)|0; + case lua.LUA_OPBNOT: return (~v1)|0; + } +}; + + +const numarith = function(L, op, v1, v2) { + switch (op) { + case lua.LUA_OPADD: return v1 + v2; + case lua.LUA_OPSUB: return v1 - v2; + case lua.LUA_OPMUL: return v1 * v2; + case lua.LUA_OPDIV: return v1 / v2; + case lua.LUA_OPPOW: return Math.pow(v1, v2); + case lua.LUA_OPIDIV: return (v1 / v2)|0; + case lua.LUA_OPUNM: return -v1; + case lua.LUA_OPMOD: return v1 % v2; + } +}; + +const luaO_arith = function(L, op, p1, p2, res) { + switch (op) { + case lvm.LUA_OPBAND: case lvm.LUA_OPBOR: case lvm.LUA_OPBXOR: + case lvm.LUA_OPSHL: case lvm.LUA_OPSHR: + case lvm.LUA_OPBNOT: { /* operate only on integers */ + let i1 = lvm.tointeger(p1); + let i2 = lvm.tointeger(p2); + if (i1 !== false && i2 !== false) { + res.type = CT.LUA_TNUMINT; + res.value = intarith(L, op, i1, i2); + return; + } + else break; /* go to the end */ + } + case lvm.LUA_OPDIV: case lvm.LUA_OPPOW: { /* operate only on floats */ + let n1 = lvm.tonumber(p1); + let n2 = lvm.tonumber(p2); + if (n1 !== false && n2 !== false) { + res.type = CT.LUA_TNUMFLT; + res.value = numarith(L, op, n1, n2); + return; + } + else break; /* go to the end */ + } + default: { /* other operations */ + let n1 = lvm.tonumber(p1); + let n2 = lvm.tonumber(p2); + if (p1.ttisinteger() && p2.ttisinteger()) { + res.type = CT.LUA_TNUMINT; + res.value = intarith(L, op, p1.value, p2.value); + return; + } + else if (n1 !== false && n2 !== false) { + res.type = CT.LUA_TNUMFLT; + res.value = numarith(L, op, n1, n2); + return; + } + else break; /* go to the end */ + } + } + /* could not perform raw operation; try metamethod */ + assert(L !== null); /* should not fail when folding (compile time) */ + ltm.luaT_trybinTM(L, p1, p2, res, (op - lua.LUA_OPADD) + ltm.TMS.TM_ADD); +}; + module.exports.CClosure = CClosure; module.exports.LClosure = LClosure; +module.exports.TValue = TValue; +module.exports.Table = Table; +module.exports.UTF8BUFFSZ = UTF8BUFFSZ; +module.exports.luaO_arith = luaO_arith; module.exports.luaO_chunkid = luaO_chunkid; module.exports.luaO_hexavalue = luaO_hexavalue; module.exports.luaO_int2fb = luaO_int2fb; module.exports.luaO_str2num = luaO_str2num; -module.exports.luaO_utf8desc = luaO_utf8desc; -module.exports.Table = Table; -module.exports.TValue = TValue; -module.exports.UTF8BUFFSZ = UTF8BUFFSZ; \ No newline at end of file +module.exports.luaO_utf8desc = luaO_utf8desc; \ No newline at end of file diff --git a/src/lopcodes.js b/src/lopcodes.js index d83897c..3f7f63b 100644 --- a/src/lopcodes.js +++ b/src/lopcodes.js @@ -299,7 +299,7 @@ const CREATE_ABx = function(o, a, bc) { return fullins(o << POS_OP | a << POS_A | bc << POS_Bx); }; -const CREATE_Ax = function(o a) { +const CREATE_Ax = function(o, a) { return fullins(o << POS_OP | a << POS_Ax); }; diff --git a/src/lparser.js b/src/lparser.js index 154310b..074542a 100644 --- a/src/lparser.js +++ b/src/lparser.js @@ -22,7 +22,7 @@ const UpVal = lfunc.UpVal; const MAXVARS = 200; const hasmultret = function(k) { - return k == expkind.VCALL || k == expkind.VVARARG; + return k === expkind.VCALL || k === expkind.VVARARG; }; class BlockCnt { @@ -65,6 +65,10 @@ const vkisvar = function(k) { return expkind.VLOCAL <= k && k <= expkind.VINDEXED; }; +const vkisinreg = function(k) { + return k === expkind.VNONRELOC || k === expkind.VLOCAL; +}; + class expdesc { constructor() { this.k = NaN; @@ -586,7 +590,7 @@ const recfield = function(ls, cc) { if (ls.t.token === R.TK_NAME) { checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, "items in a constructor"); checkname(ls, key); - } else /* ls->t.token == '[' */ + } else /* ls->t.token === '[' */ yindex(ls, key); cc.nh++; checknext(ls, '='); @@ -1334,9 +1338,9 @@ const funcname = function(ls, v) { /* funcname -> NAME {fieldsel} [':' NAME] */ let ismethod = 0; singlevar(ls, v); - while (ls.t.token == '.') + while (ls.t.token === '.') fieldsel(ls, v); - if (ls.t.token == ':') { + if (ls.t.token === ':') { ismethod = 1; fieldsel(ls, v); } @@ -1392,7 +1396,7 @@ const retstat = function(ls) { else { lcode.luaK_exp2nextreg(fs, e); /* values must go to the stack */ first = fs.nactvar; /* return all active values */ - assert(nret == fs.freereg - first); + assert(nret === fs.freereg - first); } } } @@ -1507,4 +1511,6 @@ const luaY_parser = function(L, z, buff, dyd, name, firstchar) { }; -module.exports.luaY_parser = luaY_parser; \ No newline at end of file +module.exports.expkind = expkind; +module.exports.luaY_parser = luaY_parser; +module.exports.vkisinreg = vkisinreg; \ No newline at end of file diff --git a/src/lstate.js b/src/lstate.js index 9a9c9e0..1fc7765 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -105,7 +105,7 @@ const init_registry = function(L, g) { /* ** open parts of the state that may cause memory-allocation errors. -** ('g->version' != NULL flags that the state was completely build) +** ('g->version' !== NULL flags that the state was completely build) */ const f_luaopen = function(L) { let g = L.l_G; diff --git a/src/lua.js b/src/lua.js index c7d1fd7..60cc78e 100644 --- a/src/lua.js +++ b/src/lua.js @@ -62,6 +62,25 @@ constant_types.LUA_TLCL = constant_types.LUA_TFUNCTION | (0 << 4); /* Lua closu constant_types.LUA_TLCF = constant_types.LUA_TFUNCTION | (1 << 4); /* light C function */ constant_types.LUA_TCCL = constant_types.LUA_TFUNCTION | (2 << 4); /* C closure */ +/* +** Comparison and arithmetic functions +*/ + +const LUA_OPADD = 0; /* ORDER TM, ORDER OP */ +const LUA_OPSUB = 1; +const LUA_OPMUL = 2; +const LUA_OPMOD = 3; +const LUA_OPPOW = 4; +const LUA_OPDIV = 5; +const LUA_OPIDIV = 6; +const LUA_OPBAND = 7; +const LUA_OPBOR = 8; +const LUA_OPBXOR = 9; +const LUA_OPSHL = 10; +const LUA_OPSHR = 11; +const LUA_OPUNM = 12; +const LUA_OPBNOT = 13; + const LUA_OPEQ = 0; const LUA_OPLT = 1; const LUA_OPLE = 2; @@ -121,9 +140,23 @@ module.exports.LUA_INIT_VAR = LUA_INIT_VAR; module.exports.LUA_MINSTACK = LUA_MINSTACK; module.exports.LUA_MULTRET = -1; module.exports.LUA_NUMTAGS = LUA_NUMTAGS; +module.exports.LUA_OPADD = LUA_OPADD; +module.exports.LUA_OPBAND = LUA_OPBAND; +module.exports.LUA_OPBNOT = LUA_OPBNOT; +module.exports.LUA_OPBOR = LUA_OPBOR; +module.exports.LUA_OPBXOR = LUA_OPBXOR; +module.exports.LUA_OPDIV = LUA_OPDIV; module.exports.LUA_OPEQ = LUA_OPEQ; +module.exports.LUA_OPIDIV = LUA_OPIDIV; module.exports.LUA_OPLE = LUA_OPLE; module.exports.LUA_OPLT = LUA_OPLT; +module.exports.LUA_OPMOD = LUA_OPMOD; +module.exports.LUA_OPMUL = LUA_OPMUL; +module.exports.LUA_OPPOW = LUA_OPPOW; +module.exports.LUA_OPSHL = LUA_OPSHL; +module.exports.LUA_OPSHR = LUA_OPSHR; +module.exports.LUA_OPSUB = LUA_OPSUB; +module.exports.LUA_OPUNM = LUA_OPUNM; module.exports.LUA_REGISTRYINDEX = LUA_REGISTRYINDEX; module.exports.LUA_RELEASE = LUA_RELEASE; module.exports.LUA_RIDX_GLOBALS = LUA_RIDX_GLOBALS; diff --git a/src/lvm.js b/src/lvm.js index d987cc1..0ff433e 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -792,6 +792,10 @@ const luaV_equalobj = function(L, t1, t2) { return L.stack[L.top].l_isfalse() ? 0 : 1; }; +const luaV_rawequalobj = function(t1, t2) { + return luaV_equalobj(null, t1, t2); +}; + const forlimit = function(obj, step) { let stopnow = false; let ilimit = luaV_tointeger(obj, step < 0 ? 2 : 1); @@ -818,9 +822,9 @@ const forlimit = function(obj, step) { /* ** try to convert a value to an integer, rounding according to 'mode': -** mode == 0: accepts only integral values -** mode == 1: takes the floor of the number -** mode == 2: takes the ceil of the number +** mode === 0: accepts only integral values +** mode === 1: takes the floor of the number +** mode === 2: takes the ceil of the number */ const luaV_tointeger = function(obj, mode) { if (obj.ttisfloat()) { @@ -831,7 +835,7 @@ const luaV_tointeger = function(obj, mode) { if (mode === 0) return false; /* fails if mode demands integral value */ else if (mode > 1) /* needs ceil? */ - f += 1; /* convert floor to ceil (remember: n != f) */ + f += 1; /* convert floor to ceil (remember: n !== f) */ } return f|0; @@ -1072,29 +1076,30 @@ const luaV_finishset = function(L, t, key, val, slot, recur) { } -module.exports.dojump = dojump; -module.exports.donextjump = donextjump; -module.exports.forlimit = forlimit; -module.exports.gettable = gettable; -module.exports.l_strcmp = l_strcmp; -module.exports.LEintfloat = LEintfloat; -module.exports.LEnum = LEnum; -module.exports.LTintfloat = LTintfloat; -module.exports.LTnum = LTnum; -module.exports.luaV_concat = luaV_concat; -module.exports.luaV_equalobj = luaV_equalobj; -module.exports.luaV_execute = luaV_execute; -module.exports.luaV_finishOp = luaV_finishOp; -module.exports.luaV_finishset = luaV_finishset; -module.exports.luaV_lessequal = luaV_lessequal; -module.exports.luaV_lessthan = luaV_lessthan; -module.exports.luaV_objlen = luaV_objlen; -module.exports.luaV_tointeger = luaV_tointeger; -module.exports.RA = RA; -module.exports.RB = RB; -module.exports.RC = RC; -module.exports.RKB = RKB; -module.exports.RKC = RKC; -module.exports.settable = settable; -module.exports.tointeger = tointeger; -module.exports.tonumber = tonumber; \ No newline at end of file +module.exports.LEintfloat = LEintfloat; +module.exports.LEnum = LEnum; +module.exports.LTintfloat = LTintfloat; +module.exports.LTnum = LTnum; +module.exports.RA = RA; +module.exports.RB = RB; +module.exports.RC = RC; +module.exports.RKB = RKB; +module.exports.RKC = RKC; +module.exports.dojump = dojump; +module.exports.donextjump = donextjump; +module.exports.forlimit = forlimit; +module.exports.gettable = gettable; +module.exports.l_strcmp = l_strcmp; +module.exports.luaV_concat = luaV_concat; +module.exports.luaV_equalobj = luaV_equalobj; +module.exports.luaV_execute = luaV_execute; +module.exports.luaV_finishOp = luaV_finishOp; +module.exports.luaV_finishset = luaV_finishset; +module.exports.luaV_lessequal = luaV_lessequal; +module.exports.luaV_lessthan = luaV_lessthan; +module.exports.luaV_objlen = luaV_objlen; +module.exports.luaV_rawequalobj = luaV_rawequalobj; +module.exports.luaV_tointeger = luaV_tointeger; +module.exports.settable = settable; +module.exports.tointeger = tointeger; +module.exports.tonumber = tonumber; \ No newline at end of file diff --git a/tests/llex.js b/tests/llex.js index 725d3f8..1457af3 100644 --- a/tests/llex.js +++ b/tests/llex.js @@ -45,7 +45,7 @@ test('basic lexing: TK_RETURN, TK_STRING', function (t) { readTokens, [R.TK_RETURN, R.TK_STRING], "Correct tokens found" - ) + ); }); @@ -93,6 +93,6 @@ test('TK_LOCAL, TK_NAME, TK_INT', function (t) { R.TK_RETURN, R.TK_NAME, '(', R.TK_STRING, ')', '+', R.TK_INT ], "Correct tokens found" - ) + ); }); \ No newline at end of file diff --git a/todo-temp.md b/todo-temp.md deleted file mode 100644 index d86e75c..0000000 --- a/todo-temp.md +++ /dev/null @@ -1,25 +0,0 @@ -- [ ] luaK_checkstack -- [ ] luaK_codek -- [ ] luaK_dischargevars -- [ ] luaK_exp2RK -- [ ] luaK_exp2anyreg -- [ ] luaK_exp2anyregup -- [ ] luaK_exp2nextreg -- [ ] luaK_exp2val -- [ ] luaK_fixline -- [ ] luaK_goiffalse -- [ ] luaK_goiftrue -- [ ] luaK_indexed -- [ ] luaK_infix -- [ ] luaK_intK -- [ ] luaK_jumpto -- [ ] luaK_posfix -- [ ] luaK_prefix -- [ ] luaK_reserveregs -- [ ] luaK_self -- [ ] luaK_setlist -- [ ] luaK_setmultret -- [ ] luaK_setoneret -- [ ] luaK_setreturns -- [ ] luaK_storevar -- [ ] luaK_stringK \ No newline at end of file -- cgit v1.2.3-54-g00ecf