aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <giann008@gmail.com>2017-02-28 21:15:16 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-03-01 09:32:28 +0100
commit74dda64eab7951da520dc451a1f3bbb8c7d62706 (patch)
tree26930f74654a0be55cc418c64fa2ba2713e875c6
parentdfac155a3f8b650222fa784303e599f4e50abe67 (diff)
downloadfengari-74dda64eab7951da520dc451a1f3bbb8c7d62706.tar.gz
fengari-74dda64eab7951da520dc451a1f3bbb8c7d62706.tar.bz2
fengari-74dda64eab7951da520dc451a1f3bbb8c7d62706.zip
Bytecode generation
-rw-r--r--src/lapi.js8
-rw-r--r--src/lbaselib.js2
-rw-r--r--src/lcode.js1001
-rw-r--r--src/ldebug.js6
-rw-r--r--src/ldo.js2
-rw-r--r--src/lfunc.js2
-rw-r--r--src/lobject.js91
-rw-r--r--src/lopcodes.js2
-rw-r--r--src/lparser.js18
-rw-r--r--src/lstate.js2
-rw-r--r--src/lua.js33
-rw-r--r--src/lvm.js65
-rw-r--r--tests/llex.js4
-rw-r--r--todo-temp.md25
14 files changed, 1160 insertions, 101 deletions
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
*/
@@ -117,6 +186,15 @@ const luaK_ret = function(fs, first, nret) {
};
/*
+** 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).
*/
@@ -159,6 +237,14 @@ const patchtestreg = function(fs, node, reg) {
};
/*
+** 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
** values in 'reg'), other tests jump to 'dtarget'.
@@ -175,6 +261,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