diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lapi.js | 74 | ||||
-rw-r--r-- | src/lauxlib.js | 32 | ||||
-rw-r--r-- | src/lbaselib.js | 34 | ||||
-rw-r--r-- | src/lcode.js | 6 | ||||
-rw-r--r-- | src/lcorolib.js | 2 | ||||
-rw-r--r-- | src/ldblib.js | 251 | ||||
-rw-r--r-- | src/ldebug.js | 129 | ||||
-rw-r--r-- | src/ldo.js | 50 | ||||
-rw-r--r-- | src/lfunc.js | 3 | ||||
-rw-r--r-- | src/linit.js | 4 | ||||
-rw-r--r-- | src/llex.js | 26 | ||||
-rw-r--r-- | src/lmathlib.js | 20 | ||||
-rw-r--r-- | src/lobject.js | 21 | ||||
-rw-r--r-- | src/loslib.js | 78 | ||||
-rw-r--r-- | src/lparser.js | 44 | ||||
-rw-r--r-- | src/lstate.js | 4 | ||||
-rw-r--r-- | src/lstrlib.js | 74 | ||||
-rw-r--r-- | src/ltable.js | 16 | ||||
-rw-r--r-- | src/ltablib.js | 24 | ||||
-rw-r--r-- | src/ltm.js | 54 | ||||
-rw-r--r-- | src/lua.js | 38 | ||||
-rw-r--r-- | src/lualib.js | 2 | ||||
-rw-r--r-- | src/lutf8lib.js | 20 | ||||
-rw-r--r-- | src/lvm.js | 88 |
24 files changed, 796 insertions, 298 deletions
diff --git a/src/lapi.js b/src/lapi.js index 894b5bb..72c35f0 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -221,7 +221,7 @@ const lua_pushlstring = function(L, s, len) { assert(Array.isArray(s), "lua_pushlstring expects array of byte"); assert(typeof len === "number"); - let ts = len === 0 ? L.l_G.intern(lua.to_luastring("")) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len)); + let ts = len === 0 ? L.l_G.intern(lua.to_luastring("", true)) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len)); L.stack[L.top++] = ts; assert(L.top <= L.ci.top, "stack overflow"); @@ -346,7 +346,7 @@ const auxsetstr = function(L, t, k) { }; const lua_setglobal = function(L, name) { - auxsetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS - 1), name); + auxsetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS), name); }; const lua_setmetatable = function(L, objindex) { @@ -403,6 +403,15 @@ const lua_rawset = function(L, idx) { L.top -= 2; }; +const lua_rawsetp = function(L, idx, p) { + assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack"); + let o = index2addr(L, idx); + assert(L, o.ttistable(), "table expected"); + let k = p; + o.__newindex(o, k, L.stack[L.top - 1]); + L.top--; +}; + /* ** get functions (Lua -> stack) */ @@ -436,6 +445,15 @@ const lua_rawgeti = function(L, idx, n) { return L.stack[L.top - 1].ttnov(); }; +const lua_rawgetp = function(L, idx, p) { + let t = index2addr(L, idx); + assert(t.ttistable(), "table expected"); + let k = p; + L.stack[L.top++] = t.__index(t, k); + assert(L.top <= L.ci.top, "stack overflow"); + return L.stack[L.top - 1].ttnov(); +}; + const lua_rawget = function(L, idx) { let t = index2addr(L, idx); @@ -537,6 +555,15 @@ const lua_getmetatable = function(L, objindex) { return res; }; +const lua_getuservalue = function(L, idx) { + let o = index2addr(L, idx); + assert(L, o.ttisfulluserdata(), "full userdata expected"); + L.stack[L.top].type = o.type; + L.stack[L.top++].value = o.value; + assert(L.top <= L.ci.top, "stack overflow"); + return L.stack[L.top - 1].ttnov(); +}; + const lua_gettable = function(L, idx) { let t = index2addr(L, idx); lvm.gettable(L, t, L.stack[L.top - 1], L.top - 1); @@ -562,7 +589,7 @@ const lua_geti = function(L, idx, n) { }; const lua_getglobal = function(L, name) { - return auxgetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS - 1), name); + return auxgetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS), name); }; /* @@ -710,6 +737,11 @@ const lua_typename = function(L, t) { return ltm.ttypename(t); }; +const lua_iscfunction = function(L, idx) { + let o = index2addr(L, idx); + return o.ttislcf(o) || o.ttisCclosure(); +}; + const lua_isnil = function(L, n) { return lua_type(L, n) === CT.LUA_TNIL; }; @@ -773,7 +805,7 @@ const lua_load = function(L, reader, data, chunckname, mode) { if (f.nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ let reg = L.l_G.l_registry; - let gt = reg.value.get(lua.LUA_RIDX_GLOBALS - 1); + let gt = reg.value.get(lua.LUA_RIDX_GLOBALS); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ f.upvals[0].u.value = gt; } @@ -793,6 +825,16 @@ const lua_status = function(L) { return L.status; }; +const lua_setuservalue = function(L, idx) { + assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack"); + let o = index2addr(L, idx); + assert(L, o.ttisfulluserdata(), "full userdata expected"); + L.stack[L.top - 1].type = o.type; + L.stack[L.top - 1].value = o.value; + L.top--; +}; + + const lua_callk = function(L, nargs, nresults, ctx, k) { assert(k === null || !(L.ci.callstatus & lstate.CIST_LUA), "cannot use continuations inside hooks"); assert(nargs + 1 < L.top - L.ci.funcOff, "not enough elements in the stack"); @@ -851,7 +893,7 @@ const lua_pcallk = function(L, nargs, nresults, errfunc, ctx, k) { ci.extra = c.funcOff; ci.u.c.old_errfunc = L.errfunc; L.errfunc = func; - // TODO: setoah(ci->callstatus, L->allowhook); + ci.callstatus &= ~lstate.CIST_OAH | L.allowhook; ci.callstatus |= lstate.CIST_YPCALL; /* function can do error recovery */ ldo.luaD_call(L, c.funcOff, nresults); /* do the call */ ci.callstatus &= ~lstate.CIST_YPCALL; @@ -896,7 +938,7 @@ const lua_concat = function(L, n) { if (n >= 2) lvm.luaV_concat(L, n); else if (n === 0) { - L.stack[L.top++] = L.l_G.intern(lua.to_luastring("")); + L.stack[L.top++] = L.l_G.intern(lua.to_luastring("", true)); assert(L.top <= L.ci.top, "stack overflow"); } }; @@ -914,7 +956,8 @@ const getupvalref = function(L, fidx, n, pf) { assert(1 <= n && n <= f.p.upvalues.length, "invalid upvalue index"); return { closure: f, - upval: f.upvals[n - 1] + upval: f.upvals[n - 1], + upvalOff: n - 1 }; }; @@ -936,6 +979,17 @@ const lua_upvalueid = function(L, fidx, n) { } }; +const lua_upvaluejoin = function(L, fidx1, n1, fidx2, n2) { + let ref1 = getupvalref(L, fidx1, n1); + let ref2 = getupvalref(L, fidx2, n2); + let up1 = ref1.upvalOff; + let up2 = ref2.upval; + let f1 = ref1.closure; + + f1.upvals[up1] = up2; + up2.u.open.touched = true; // TODO: useful +}; + // This functions are only there for compatibility purposes const lua_gc = function () {}; @@ -972,7 +1026,9 @@ module.exports.lua_getmetatable = lua_getmetatable; module.exports.lua_gettable = lua_gettable; module.exports.lua_gettop = lua_gettop; module.exports.lua_getupvalue = lua_getupvalue; +module.exports.lua_getuservalue = lua_getuservalue; module.exports.lua_insert = lua_insert; +module.exports.lua_iscfunction = lua_iscfunction; module.exports.lua_isfunction = lua_isfunction; module.exports.lua_isinteger = lua_isinteger; module.exports.lua_isnil = lua_isnil; @@ -1011,8 +1067,10 @@ module.exports.lua_pushvalue = lua_pushvalue; module.exports.lua_rawequal = lua_rawequal; module.exports.lua_rawget = lua_rawget; module.exports.lua_rawgeti = lua_rawgeti; +module.exports.lua_rawgetp = lua_rawgetp; module.exports.lua_rawlen = lua_rawlen; module.exports.lua_rawset = lua_rawset; +module.exports.lua_rawsetp = lua_rawsetp; module.exports.lua_remove = lua_remove; module.exports.lua_replace = lua_replace; module.exports.lua_rotate = lua_rotate; @@ -1023,6 +1081,7 @@ module.exports.lua_setmetatable = lua_setmetatable; module.exports.lua_settable = lua_settable; module.exports.lua_settop = lua_settop; module.exports.lua_setupvalue = lua_setupvalue; +module.exports.lua_setuservalue = lua_setuservalue; module.exports.lua_status = lua_status; module.exports.lua_stringtonumber = lua_stringtonumber; module.exports.lua_toboolean = lua_toboolean; @@ -1040,5 +1099,6 @@ module.exports.lua_touserdata = lua_touserdata; module.exports.lua_type = lua_type; module.exports.lua_typename = lua_typename; module.exports.lua_upvalueid = lua_upvalueid; +module.exports.lua_upvaluejoin = lua_upvaluejoin; module.exports.lua_version = lua_version; module.exports.lua_xmove = lua_xmove; diff --git a/src/lauxlib.js b/src/lauxlib.js index 8e263fe..3d75282 100644 --- a/src/lauxlib.js +++ b/src/lauxlib.js @@ -59,7 +59,7 @@ const findfield = function(L, objidx, level) { const pushglobalfuncname = function(L, ar) { let top = lapi.lua_gettop(L); ldebug.lua_getinfo(L, [char['f']], ar); /* push function */ - lapi.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(LUA_LOADED_TABLE)); + lapi.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(LUA_LOADED_TABLE, true)); if (findfield(L, top + 1, 2)) { let name = lapi.lua_tostring(L, -1); if (lobject.jsstring(name).startsWith("_G.")) { /* name start with '_G.'? */ @@ -79,7 +79,7 @@ const sv = s => s ? s : []; const pushfuncname = function(L, ar) { if (pushglobalfuncname(L, ar)) { /* try first a global name */ - lapi.lua_pushstring(L, lua.to_luastring("function '").concat(lapi.lua_tostring(L, -1)).concat([char["'"]])); + lapi.lua_pushstring(L, lua.to_luastring("function '", true).concat(lapi.lua_tostring(L, -1)).concat([char["'"]])); lapi.lua_remove(L, -2); /* remove name */ } else if (ar.namewhat) /* is there a name from code? */ @@ -87,7 +87,7 @@ const pushfuncname = function(L, ar) { else if (ar.what && ar.what[0] === char['m']) /* main? */ lapi.lua_pushliteral(L, "main chunk"); else if (ar.what && ar.what[0] != char['C']) /* for Lua functions, use <file:line> */ - lapi.lua_pushstring(L, lua.to_luastring("function <").concat(...sv(ar.short_src), char[':'], ...lua.to_luastring(`${ar.linedefined}>`))); + lapi.lua_pushstring(L, lua.to_luastring("function <", true).concat(...sv(ar.short_src), char[':'], ...lua.to_luastring(`${ar.linedefined}>`))); else /* nothing left... */ lapi.lua_pushliteral(L, "?"); }; @@ -121,7 +121,7 @@ const luaL_traceback = function(L, L1, msg, level) { lapi.lua_pushliteral(L, "\n\t..."); /* add a '...' */ level = last - LEVELS2 + 1; /* and skip to last ones */ } else { - ldebug.lua_getinfo(L1, lua.to_luastring("Slnt"), ar); + ldebug.lua_getinfo(L1, lua.to_luastring("Slnt", true), ar); lapi.lua_pushstring(L, [char['\n'], char['\t'], char['.'], char['.'], char['.']].concat(ar.short_src)); if (ar.currentline > 0) lapi.lua_pushliteral(L, `${ar.currentline}:`); @@ -147,7 +147,7 @@ const luaL_argerror = function(L, arg, extramsg) { ldebug.lua_getinfo(L, 'n', ar); - if (ar.namewhat === lua.to_luastring('method')) { + if (ar.namewhat === lua.to_luastring('method', true)) { arg--; /* do not count 'self' */ if (arg === 0) /* error is in the self argument itself? */ return luaL_error(L, lua.to_luastring(`calling '${lobject.jsstring(ar.name)}' on bad self (${lobject.jsstring(extramsg)})`)); @@ -161,10 +161,10 @@ const luaL_argerror = function(L, arg, extramsg) { const typeerror = function(L, arg, tname) { let typearg; - if (luaL_getmetafield(L, arg, lua.to_luastring("__name")) === CT.LUA_TSTRING) + if (luaL_getmetafield(L, arg, lua.to_luastring("__name", true)) === CT.LUA_TSTRING) typearg = lapi.lua_tostring(L, -1); else if (lapi.lua_type(L, arg) === CT.LUA_TLIGHTUSERDATA) - typearg = lua.to_luastring("light userdata"); + typearg = lua.to_luastring("light userdata", true); else typearg = luaL_typename(L, arg); @@ -175,7 +175,7 @@ const typeerror = function(L, arg, tname) { const luaL_where = function(L, level) { let ar = new lua.lua_Debug(); if (ldebug.lua_getstack(L, level, ar)) { - ldebug.lua_getinfo(L, lua.to_luastring("Sl"), ar); + ldebug.lua_getinfo(L, lua.to_luastring("Sl", true), ar); if (ar.currentline > 0) { lapi.lua_pushstring(L, lua.to_luastring(`${lobject.jsstring(ar.short_src)}:${ar.currentline}:`)); return; @@ -220,7 +220,7 @@ const luaL_argcheck = function(L, cond, arg, extramsg) { const luaL_checkany = function(L, arg) { if (lapi.lua_type(L, arg) === CT.LUA_TNONE) - luaL_argerror(L, arg, lua.to_luastring("value expected")); + luaL_argerror(L, arg, lua.to_luastring("value expected", true)); }; const luaL_checktype = function(L, arg, t) { @@ -248,7 +248,7 @@ const luaL_optstring = luaL_optlstring; const interror = function(L, arg) { if (lapi.lua_isnumber(L, arg)) - luaL_argerror(L, arg, lua.to_luastring("number has no integer representation")); + luaL_argerror(L, arg, lua.to_luastring("number has no integer representation", true)); else tag_error(L, arg, CT.LUA_TNUMBER); }; @@ -357,15 +357,15 @@ const luaL_len = function(L, idx) { lapi.lua_len(L, idx); let l = lapi.lua_tointegerx(L, -1); if (l === false) - luaL_error(L, lua.to_luastring("object length is not an integer")); + luaL_error(L, lua.to_luastring("object length is not an integer", true)); lapi.lua_pop(L, 1); /* remove object */ return l; }; const luaL_tolstring = function(L, idx) { - if (luaL_callmeta(L, idx, lua.to_luastring("__tostring"))) { + if (luaL_callmeta(L, idx, lua.to_luastring("__tostring", true))) { if (!lapi.lua_isstring(L, -1)) - luaL_error(L, lua.to_luastring("'__tostring' must return a string")); + luaL_error(L, lua.to_luastring("'__tostring' must return a string", true)); } else { switch(lapi.lua_type(L, idx)) { case CT.LUA_TNUMBER: @@ -379,7 +379,7 @@ const luaL_tolstring = function(L, idx) { lapi.lua_pushstring(L, lua.to_luastring(`nil`)); break; default: - let tt = luaL_getmetafield(L, idx, lua.to_luastring("__name")); + let tt = luaL_getmetafield(L, idx, lua.to_luastring("__name", true)); let kind = tt === CT.LUA_TSTRING ? lapi.lua_tostring(L, -1) : luaL_typename(L, idx); lapi.lua_pushstring(L, lua.to_luastring(`${lobject.jsstring(kind)}: 0x${lapi.index2addr(L, -1).id.toString(16)}`)); if (tt !== CT.LUA_TNIL) @@ -438,7 +438,7 @@ const luaL_getsubtable = function(L, idx, fname) { ** Returns with only the table at the stack. */ const luaL_setfuncs = function(L, l, nup) { - luaL_checkstack(L, nup, lua.to_luastring("too many upvalues")); + luaL_checkstack(L, nup, lua.to_luastring("too many upvalues", true)); for (let lib in l) { /* fill the table with given functions */ for (let i = 0; i < nup; i++) /* copy upvalues to the top */ lapi.lua_pushvalue(L, -nup); @@ -460,7 +460,7 @@ const luaL_checkstack = function(L, space, msg) { if (msg) luaL_error(L, lua.to_luastring(`stack overflow (${lobject.jsstring(msg)})`)); else - luaL_error(L, lua.to_luastring('stack overflow')); + luaL_error(L, lua.to_luastring('stack overflow', true)); } }; diff --git a/src/lbaselib.js b/src/lbaselib.js index 931a285..af7c3cc 100644 --- a/src/lbaselib.js +++ b/src/lbaselib.js @@ -13,14 +13,14 @@ const luaB_print = function(L) { let n = lapi.lua_gettop(L); /* number of arguments */ let str = []; - lapi.lua_getglobal(L, lua.to_luastring("tostring")); + lapi.lua_getglobal(L, lua.to_luastring("tostring", true)); for (let i = 1; i <= n; i++) { lapi.lua_pushvalue(L, -1); /* function to be called */ lapi.lua_pushvalue(L, i); /* value to print */ lapi.lua_call(L, 1, 1); let s = lapi.lua_tolstring(L, -1); if (s === null) - return lauxlib.luaL_error(L, lua.to_luastring("'tostring' must return a string to 'print'")); + return lauxlib.luaL_error(L, lua.to_luastring("'tostring' must return a string to 'print'", true)); if (i > 1) s = ["\t".charCodeAt(0)].concat(s); str = str.concat(s); lapi.lua_pop(L, 1); @@ -45,16 +45,16 @@ const luaB_getmetatable = function(L) { lapi.lua_pushnil(L); return 1; /* no metatable */ } - lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable")); + lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable", true)); return 1; /* returns either __metatable field (if present) or metatable */ }; const luaB_setmetatable = function(L) { let t = lapi.lua_type(L, 2); lauxlib.luaL_checktype(L, 1, CT.LUA_TTABLE); - lauxlib.luaL_argcheck(L, t === CT.LUA_TNIL || t === CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected")); - if (lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable")) !== CT.LUA_TNIL) - return lauxlib.luaL_error(L, lua.to_luastring("cannot change a protected metatable")); + lauxlib.luaL_argcheck(L, t === CT.LUA_TNIL || t === CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected", true)); + if (lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable", true)) !== CT.LUA_TNIL) + return lauxlib.luaL_error(L, lua.to_luastring("cannot change a protected metatable", true)); lapi.lua_settop(L, 2); lapi.lua_setmetatable(L, 1); return 1; @@ -69,7 +69,7 @@ const luaB_rawequal = function(L) { const luaB_rawlen = function(L) { let t = lapi.lua_type(L, 1); - lauxlib.luaL_argcheck(L, t === CT.LUA_TTABLE || t === CT.LUA_TSTRING, 1, lua.to_luastring("table or string expected")); + lauxlib.luaL_argcheck(L, t === CT.LUA_TTABLE || t === CT.LUA_TSTRING, 1, lua.to_luastring("table or string expected", true)); lapi.lua_pushinteger(L, lapi.lua_rawlen(L, 1)); return 1; }; @@ -93,7 +93,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, lua.to_luastring("value expected")); + lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, lua.to_luastring("value expected", true)); lapi.lua_pushstring(L, lapi.lua_typename(L, t)); return 1; }; @@ -124,7 +124,7 @@ const luaB_next = function(L) { }; const luaB_pairs = function(L) { - return pairsmeta(L, lua.to_luastring("__pairs"), 0, luaB_next); + return pairsmeta(L, lua.to_luastring("__pairs", true), 0, luaB_next); }; /* @@ -166,7 +166,7 @@ const luaB_tonumber = function(L) { let base = lauxlib.luaL_checkinteger(L, 2); lauxlib.luaL_checktype(L, 1, CT.LUA_TSTRING); /* no numbers as strings */ let s = lapi.lua_tostring(L, 1); - lauxlib.luaL_argcheck(L, 2 <= base && base <= 36, 2, lua.to_luastring("base out of range")); + lauxlib.luaL_argcheck(L, 2 <= base && base <= 36, 2, lua.to_luastring("base out of range", true)); let n = parseInt(lobject.jsstring(s), base); if (!isNaN(n)) { lapi.lua_pushinteger(L, n); @@ -210,7 +210,7 @@ const luaB_select = function(L) { let i = lauxlib.luaL_checkinteger(L, 1); if (i < 0) i = n + i; else if (i > n) i = n; - lauxlib.luaL_argcheck(L, 1 <= i, 1, lua.to_luastring("index out of range")); + lauxlib.luaL_argcheck(L, 1 <= i, 1, lua.to_luastring("index out of range", true)); return n - i; } }; @@ -284,28 +284,28 @@ const RESERVEDSLOT = 5; ** reserved slot inside the stack. */ const generic_reader = function(L, ud) { - lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many nested functions")); + lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many nested functions", true)); lapi.lua_pushvalue(L, 1); /* get function */ lapi.lua_call(L, 0, 1); /* call it */ if (lapi.lua_isnil(L, -1)) { lapi.lua_pop(L, 1); /* pop result */ return null; } else if (!lapi.lua_isstring(L, -1)) - lauxlib.luaL_error(L, lua.to_luastring("reader function must return a string")); + lauxlib.luaL_error(L, lua.to_luastring("reader function must return a string", true)); lapi.lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ return lapi.lua_tostring(L, RESERVEDSLOT); }; const luaB_load = function(L) { let s = lapi.lua_tostring(L, 1); - let mode = lauxlib.luaL_optstring(L, 3, lua.to_luastring("bt")); + let mode = lauxlib.luaL_optstring(L, 3, lua.to_luastring("bt", true)); let env = !lapi.lua_isnone(L, 4) ? 4 : 0; /* 'env' index or 0 if no 'env' */ let status; if (s !== null) { /* loading a string? */ let chunkname = lauxlib.luaL_optstring(L, 2, s); status = lauxlib.luaL_loadbufferx(L, s, chunkname, mode); } else { /* loading from a reader function */ - let chunkname = lauxlib.luaL_optstring(L, 2, lua.to_luastring("=(load)")); + let chunkname = lauxlib.luaL_optstring(L, 2, lua.to_luastring("=(load)", true)); lauxlib.luaL_checktype(L, 1, CT.LUA_TFUNCTION); lapi.lua_settop(L, RESERVEDSLOT); /* create reserved slot */ status = lapi.lua_load(L, generic_reader, null, chunkname, mode); @@ -378,10 +378,10 @@ const luaopen_base = function(L) { lauxlib.luaL_setfuncs(L, base_funcs, 0); /* set global _G */ lapi.lua_pushvalue(L, -1); - lapi.lua_setfield(L, -2, lua.to_luastring("_G")); + lapi.lua_setfield(L, -2, lua.to_luastring("_G", true)); /* set global _VERSION */ lapi.lua_pushliteral(L, lua.LUA_VERSION); - lapi.lua_setfield(L, -2, lua.to_luastring("_VERSION")); + lapi.lua_setfield(L, -2, lua.to_luastring("_VERSION", true)); return 1; }; diff --git a/src/lcode.js b/src/lcode.js index 0b3d9bd..ec233c6 100644 --- a/src/lcode.js +++ b/src/lcode.js @@ -183,7 +183,7 @@ const fixjump = function(fs, pc, dest) { let offset = dest - (pc + 1); assert(dest !== NO_JUMP); if (Math.abs(offset) > lopcode.MAXARG_sBx) - llex.luaX_syntaxerror(fs.ls, lua.to_luastring("control structure too long")); + llex.luaX_syntaxerror(fs.ls, lua.to_luastring("control structure too long", true)); lopcode.SETARG_sBx(jmp, offset); }; @@ -424,7 +424,7 @@ const luaK_checkstack = function(fs, n) { let newstack = fs.freereg + n; if (newstack > fs.f.maxstacksize) { if (newstack >= MAXREGS) - llex.luaX_syntaxerror(fs.ls, lua.to_luastring("function or expression needs to many registers")); + llex.luaX_syntaxerror(fs.ls, lua.to_luastring("function or expression needs to many registers", true)); fs.f.maxstacksize = newstack; } }; @@ -1230,7 +1230,7 @@ const luaK_setlist = function(fs, base, nelems, tostore) { codeextraarg(fs, c); } else - llex.luaX_syntaxerror(fs.ls, lua.to_luastring("constructor too long")); + llex.luaX_syntaxerror(fs.ls, lua.to_luastring("constructor too long", true)); fs.freereg = base + 1; /* free registers with list values */ }; diff --git a/src/lcorolib.js b/src/lcorolib.js index 58d061c..b7c4555 100644 --- a/src/lcorolib.js +++ b/src/lcorolib.js @@ -14,7 +14,7 @@ const TS = lua.thread_status; const getco = function(L) { let co = lapi.lua_tothread(L, 1); - lauxlib.luaL_argcheck(L, co, 1, lua.to_luastring("thread expected")); + lauxlib.luaL_argcheck(L, co, 1, lua.to_luastring("thread expected", true)); return co; }; diff --git a/src/ldblib.js b/src/ldblib.js index ea080c0..c637af2 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -18,6 +18,44 @@ const checkstack = function(L, L1, n) { lauxlib.luaL_error(L, "stack overflow"); }; +const db_getregistry = function(L) { + lapi.lua_pushvalue(L, lua.LUA_REGISTRYINDEX); + return 1; +}; + +const db_getmetatable = function(L) { + lauxlib.luaL_checkany(L, 1); + if (!lapi.lua_getmetatable(L, 1)) { + lapi.lua_pushnil(L); /* no metatable */ + } + return 1; +}; + +const db_setmetatable = function(L) { + const t = lapi.lua_type(L, 2); + lauxlib.luaL_argcheck(L, t == lua.CT.LUA_TNIL || t == lua.CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected", true)); + lapi.lua_settop(L, 2); + lapi.lua_setmetatable(L, 1); + return 1; /* return 1st argument */ +}; + +const db_getuservalue = function(L) { + if (lapi.lua_type(L, 1) !== lua.CT.LUA_TUSERDATA) + lapi.lua_pushnil(L); + else + lapi.lua_getuservalue(L, 1); + return 1; +}; + + +const db_setuservalue = function(L) { + lauxlib.luaL_checktype(L, 1, lua.CT.LUA_TUSERDATA); + lauxlib.luaL_checkany(L, 2); + lapi.lua_settop(L, 2); + lapi.lua_setuservalue(L, 1); + return 1; +}; + /* ** Auxiliary function used by several library functions: check for ** an optional thread as function's first argument and set 'arg' with @@ -85,7 +123,7 @@ const db_getinfo = function(L) { let thread = getthread(L); let arg = thread.arg; let L1 = thread.thread; - let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu")); + let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu", true)); checkstack(L, L1, 3); if (lapi.lua_isfunction(L, arg + 1)) { /* info about a function? */ options = [char['>']].concat(options); /* add '>' to 'options' */ @@ -99,31 +137,31 @@ const db_getinfo = function(L) { } if (!ldebug.lua_getinfo(L1, options, ar)) - lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option")); + lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option", true)); lapi.lua_newtable(L); /* table to collect results */ if (options.indexOf(char['S']) > -1) { - settabss(L, lua.to_luastring("source"), ar.source.value); - settabss(L, lua.to_luastring("short_src"), ar.short_src); - settabss(L, lua.to_luastring("linedefined"), lua.to_luastring(`${ar.linedefined}`)); - settabss(L, lua.to_luastring("lastlinedefined"), lua.to_luastring(`${ar.lastlinedefined}`)); - settabss(L, lua.to_luastring("what"), ar.what); + settabss(L, lua.to_luastring("source", true), ar.source.value); + settabss(L, lua.to_luastring("short_src", true), ar.short_src); + settabss(L, lua.to_luastring("linedefined", true), lua.to_luastring(`${ar.linedefined}`)); + settabss(L, lua.to_luastring("lastlinedefined", true), lua.to_luastring(`${ar.lastlinedefined}`)); + settabss(L, lua.to_luastring("what", true), ar.what); } if (options.indexOf(char['l']) > -1) - settabsi(L, lua.to_luastring("currentline"), ar.currentline); + settabsi(L, lua.to_luastring("currentline", true), ar.currentline); if (options.indexOf(char['u']) > -1) - settabsi(L, lua.to_luastring("nups"), ar.nups); - settabsi(L, lua.to_luastring("nparams"), ar.nparams); - settabsb(L, lua.to_luastring("isvararg"), ar.isvararg); + settabsi(L, lua.to_luastring("nups", true), ar.nups); + settabsi(L, lua.to_luastring("nparams", true), ar.nparams); + settabsb(L, lua.to_luastring("isvararg", true), ar.isvararg); if (options.indexOf(char['n']) > - 1) { - settabss(L, lua.to_luastring("name"), ar.name ? ar.name.value : null); - settabss(L, lua.to_luastring("namewhat"), ar.namewhat ? ar.namewhat : null); + settabss(L, lua.to_luastring("name", true), ar.name ? ar.name : null); + settabss(L, lua.to_luastring("namewhat", true), ar.namewhat ? ar.namewhat : null); } if (options.indexOf(char['t']) > - 1) - settabsb(L, lua.to_luastring("istailcall"), ar.istailcall); + settabsb(L, lua.to_luastring("istailcall", true), ar.istailcall); if (options.indexOf(char['L']) > - 1) - treatstackoption(L, L1, lua.to_luastring("activelines")); + treatstackoption(L, L1, lua.to_luastring("activelines", true)); if (options.indexOf(char['f']) > - 1) - treatstackoption(L, L1, lua.to_luastring("func")); + treatstackoption(L, L1, lua.to_luastring("func", true)); return 1; /* return table */ }; @@ -140,7 +178,7 @@ const db_getlocal = function(L) { } else { /* stack-level argument */ let level = lauxlib.luaL_checkinteger(L, arg + 1); if (!ldebug.lua_getstack(L1, level, ar)) /* out of range? */ - return lauxlib.luaL_argerror(L, arg+1, lapi.to_luastring("level out of range")); + return lauxlib.luaL_argerror(L, arg+1, lapi.to_luastring("level out of range", true)); checkstack(L, L1, 1); let name = ldebug.lua_getlocal(L1, ar, nvar); if (name) { @@ -156,6 +194,49 @@ const db_getlocal = function(L) { } }; +const db_setlocal = function(L) { + let thread = getthread(L); + let L1 = thread.thread; + let arg = thread.arg; + let ar = new lua.lua_Debug(); + let level = lauxlib.luaL_checkinteger(L, arg + 1); + let nvar = lauxlib.luaL_checkinteger(L, arg + 2); + if (!ldebug.lua_getstack(L1, level, ar)) /* out of range? */ + return lauxlib.luaL_argerror(L, arg + 1, "level out of range"); + lauxlib.luaL_checkany(L, arg + 3); + lapi.lua_settop(L, arg + 3); + checkstack(L, L1, 1); + lapi.lua_xmove(L, L1, 1); + let name = ldebug.lua_setlocal(L1, ar, nvar); + if (name === null) + lapi.lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lapi.lua_pushstring(L, name.value); + return 1; +}; + +/* +** get (if 'get' is true) or set an upvalue from a closure +*/ +const auxupvalue = function(L, get) { + let n = lauxlib.luaL_checkinteger(L, 2); /* upvalue index */ + lauxlib.luaL_checktype(L, 1, lua.CT.LUA_TFUNCTION); /* closure */ + let name = get ? lapi.lua_getupvalue(L, 1, n) : lapi.lua_setupvalue(L, 1, n); + if (name === null) return 0; + lapi.lua_pushstring(L, name); + lapi.lua_insert(L, -(get+1)); /* no-op if get is false */ + return get + 1; +}; + + +const db_getupvalue = function(L) { + return auxupvalue(L, 1); +}; + +const db_setupvalue = function(L) { + lauxlib.luaL_checkany(L, 3); + return auxupvalue(L, 0); +}; + /* ** Check whether a given upvalue from a given closure exists and ** returns its index @@ -163,17 +244,128 @@ const db_getlocal = function(L) { const checkupval = function(L, argf, argnup) { let nup = lauxlib.luaL_checkinteger(L, argnup); /* upvalue index */ lauxlib.luaL_checktype(L, argf, lua.CT.LUA_TFUNCTION); /* closure */ - lauxlib.luaL_argcheck(L, (lapi.lua_getupvalue(L, argf, nup) !== null), argnup, lua.to_luastring("invalid upvalue index")); + lauxlib.luaL_argcheck(L, (lapi.lua_getupvalue(L, argf, nup) !== null), argnup, lua.to_luastring("invalid upvalue index", true)); return nup; }; - const db_upvalueid = function(L) { let n = checkupval(L, 1, 2); lapi.lua_pushlightuserdata(L, lapi.lua_upvalueid(L, 1, n)); return 1; }; +const db_upvaluejoin = function(L) { + let n1 = checkupval(L, 1, 2); + let n2 = checkupval(L, 3, 4); + lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 1), 1, lua.to_luastring("Lua function expected", true)); + lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 3), 3, lua.to_luastring("Lua function expected", true)); + lapi.lua_upvaluejoin(L, 1, n1, 3, n2); + return 0; +}; + +/* +** The hook table at registry[HOOKKEY] maps threads to their current +** hook function. (We only need the unique address of 'HOOKKEY'.) +*/ +const HOOKKEY = lua.to_luastring("__hooks__", true); + +const hooknames = ["call", "return", "line", "count", "tail call"].map(e => lua.to_luastring(e)); + +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ +const hookf = function(L, ar) { + lapi.lua_rawgetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY); + lapi.lua_pushthread(L); + if (lapi.lua_rawget(L, -2) === lua.CT.LUA_TFUNCTION) { /* is there a hook function? */ + lapi.lua_pushstring(L, hooknames[ar.event]); /* push event name */ + if (ar.currentline >= 0) + lapi.lua_pushinteger(L, ar.currentline); /* push current line */ + else lapi.lua_pushnil(L); + assert(ldebug.lua_getinfo(L, [char["l"], char["S"]], ar)); + lapi.lua_call(L, 2, 0); /* call hook function */ + } +}; + +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ +const makemask = function(smask, count) { + let mask = 0; + if (smask.indexOf(char["c"]) > -1) mask |= lua.LUA_MASKCALL; + if (smask.indexOf(char["r"]) > -1) mask |= lua.LUA_MASKRET; + if (smask.indexOf(char["l"]) > -1) mask |= lua.LUA_MASKLINE; + if (count > 0) mask |= lua.LUA_MASKCOUNT; + return mask; +}; + +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ +const unmakemask = function(mask, smask) { + let i = 0; + if (mask & lua.LUA_MASKCALL) smask[i++] = char["c"]; + if (mask & lua.LUA_MASKRET) smask[i++] = char["r"]; + if (mask & lua.LUA_MASKLINE) smask[i++] = char["l"]; + return smask; +}; + +const db_sethook = function(L) { + let mask, count, func; + let thread = getthread(L); + let L1 = thread.thread; + let arg = thread.arg; + if (lapi.lua_isnoneornil(L, arg+1)) { /* no hook? */ + lapi.lua_settop(L, arg+1); + func = null; mask = 0; count = 0; /* turn off hooks */ + } + else { + const smask = lauxlib.luaL_checkstring(L, arg + 2); + lauxlib.luaL_checktype(L, arg+1, lua.CT.LUA_TFUNCTION); + count = lauxlib.luaL_optinteger(L, arg + 3, 0); + func = hookf; mask = makemask(smask, count); + } + if (lapi.lua_rawgetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY) === lua.CT.LUA_TNIL) { + lapi.lua_createtable(L, 0, 2); /* create a hook table */ + lapi.lua_pushvalue(L, -1); + lapi.lua_rawsetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY); /* set it in position */ + lapi.lua_pushstring(L, [char["k"]]); + lapi.lua_setfield(L, -2, lua.to_luastring("__mode", true)); /** hooktable.__mode = "k" */ + lapi.lua_pushvalue(L, -1); + lapi.lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */ + } + checkstack(L, L1, 1); + lapi.lua_pushthread(L1); lapi.lua_xmove(L1, L, 1); /* key (thread) */ + lapi.lua_pushvalue(L, arg + 1); /* value (hook function) */ + lapi.lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + ldebug.lua_sethook(L1, func, mask, count); + return 0; +}; + +const db_gethook = function(L) { + let thread = getthread(L); + let L1 = thread.thread; + let arg = thread.arg; + let buff = []; + let mask = ldebug.lua_gethookmask(L1); + let hook = ldebug.lua_gethook(L1); + if (hook === null) /* no hook? */ + lapi.lua_pushnil(L); + else if (hook !== hookf) /* external hook? */ + lapi.lua_pushliteral(L, "external hook"); + else { /* hook table must exist */ + lapi.lua_rawgetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY); + checkstack(L, L1, 1); + lapi.lua_pushthread(L1); lapi.lua_xmove(L1, L, 1); + lapi.lua_rawget(L, -2); /* 1st result = hooktable[L1] */ + lapi.lua_remove(L, -2); /* remove hook table */ + } + lapi.lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lapi.lua_pushinteger(L, ldebug.lua_gethookcount(L1)); /* 3rd result = count */ + return 3; +}; + const db_traceback = function(L) { let thread = getthread(L); let L1 = thread.thread; @@ -189,10 +381,21 @@ const db_traceback = function(L) { }; const dblib = { - "getinfo": db_getinfo, - "getlocal": db_getlocal, - "traceback": db_traceback, - "upvalueid": db_upvalueid + "gethook": db_gethook, + "getinfo": db_getinfo, + "getlocal": db_getlocal, + "getmetatable": db_getmetatable, + "getregistry": db_getregistry, + "getupvalue": db_getupvalue, + "getuservalue": db_getuservalue, + "sethook": db_sethook, + "setlocal": db_setlocal, + "setmetatable": db_setmetatable, + "setupvalue": db_setupvalue, + "setuservalue": db_setuservalue, + "traceback": db_traceback, + "upvalueid": db_upvalueid, + "upvaluejoin": db_upvaluejoin }; // Only with Node @@ -220,7 +423,7 @@ if (typeof require === "function") { continue; let buffer = lua.to_luastring(input); - if (lauxlib.luaL_loadbuffer(L, buffer, buffer.length, lua.to_luastring("=(debug command)")) + if (lauxlib.luaL_loadbuffer(L, buffer, buffer.length, lua.to_luastring("=(debug command)", true)) || lapi.lua_pcall(L, 0, 0, 0)) { lauxlib.lua_writestringerror(`${lapi.lua_tojsstring(L, -1)}\n`); } diff --git a/src/ldebug.js b/src/ldebug.js index 713a0fb..16449e2 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -38,6 +38,33 @@ const swapextra = function(L) { } }; +const lua_sethook = function(L, func, mask, count) { + if (func === null || mask === 0) { /* turn off hooks? */ + mask = 0; + func = null; + } + if (L.ci.callstatus & lstate.CIST_LUA) + L.oldpc = L.ci.pcOff; + L.hook = func; + L.basehookcount = count; + L.hookcount = L.basehookcount; + L.hookmask = mask; +}; + +const lua_gethook = function(L) { + return L.hook; +}; + + +const lua_gethookmask = function(L) { + return L.hookmask; +}; + + +const lua_gethookcount = function(L) { + return L.basehookcount; +}; + const lua_getstack = function(L, level, ar) { let ci; let status; @@ -52,10 +79,11 @@ const lua_getstack = function(L, level, ar) { return status; }; +// TODO: returns TValue or String array ? const upvalname = function(p, uv) { assert(uv < p.upvalues.length); let s = p.upvalues[uv].name; - if (s === null) return "?".charCodeAt(0); + if (s === null) return ["?".charCodeAt(0)]; return s; }; @@ -66,7 +94,7 @@ const findvararg = function(ci, n, pos) { else { return { pos: ci.funcOff + nparams + n, - name: lua.to_luastring("(*vararg)") /* generic name for any vararg */ + name: lua.to_luastring("(*vararg)", true) /* generic name for any vararg */ }; } }; @@ -87,7 +115,7 @@ const findlocal = function(L, ci, n) { if (name === null) { /* no 'standard' name? */ let limit = ci === L.ci ? L.top : ci.next.func; if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */ - name = lua.to_luastring("(*temporary)"); /* generic name for any valid slot */ + name = lua.to_luastring("(*temporary)", true); /* generic name for any valid slot */ else return null; /* no name */ } @@ -116,18 +144,32 @@ const lua_getlocal = function(L, ar, n) { return name; }; +const lua_setlocal = function(L, ar, n) { + swapextra(L); + let local = findlocal(L, ar.i_ci, n); + let name = local.name; + let pos = local.pos; + if (name) { + L.stack[pos].type = L.stack[L.top - 1].type; + L.stack[pos].value = L.stack[L.top - 1].value; + L.top--; /* pop value */ + } + swapextra(L); + return name; +}; + const funcinfo = function(ar, cl) { if (cl === null || cl.type === CT.LUA_TCCL) { - ar.source = lua.to_luastring("=[JS]"); + ar.source = lua.to_luastring("=[JS]", true); ar.linedefined = -1; ar.lastlinedefined = -1; ar.what = "J"; } else { let p = cl.p; - ar.source = p.source ? p.source : lua.to_luastring("=?"); + ar.source = p.source ? p.source : lua.to_luastring("=?", true); ar.linedefined = p.linedefined; ar.lastlinedefined = p.lastlinedefined; - ar.what = ar.linedefined === 0 ? lua.to_luastring("main") : lua.to_luastring("Lua"); + ar.what = ar.linedefined === 0 ? lua.to_luastring("main", true) : lua.to_luastring("Lua", true); } ar.short_src = lobject.luaO_chunkid(ar.source, luaconf.LUA_IDSIZE); @@ -156,8 +198,8 @@ const getfuncname = function(L, ci) { if (ci === null) return null; else if (ci.callstatus & lstate.CIST_FIN) { /* is this a finalizer? */ - r.name = lua.to_luastring("__gc"); - r.funcname = lua.to_luastring("metamethod"); /* report it as such */ + r.name = lua.to_luastring("__gc", true); + r.funcname = lua.to_luastring("metamethod", true); /* report it as such */ return r; } /* calling function is a known Lua function? */ @@ -260,8 +302,8 @@ const kname = function(p, pc, c) { /* else no reasonable name found */ } else { /* 'c' is a register */ let what = getobjname(p, pc, c); /* search for 'c' */ - if (what && what.name[0] === 'c'.charCodeAt(0)) { - return what; + if (what && what.funcname[0] === 'c'.charCodeAt(0)) { /* found a constant name? */ + return what; /* 'name' already filled */ } /* else no reasonable name found */ } @@ -331,7 +373,7 @@ const getobjname = function(p, lastpc, reg) { }; if (r.name) { /* is a local? */ - r.funcname = lua.to_luastring("local"); + r.funcname = lua.to_luastring("local", true); return r; } @@ -352,13 +394,14 @@ const getobjname = function(p, lastpc, reg) { let k = i.C; /* key index */ let t = i.B; /* table index */ let vn = op === 'OP_GETTABLE' ? lfunc.luaF_getlocalname(p, t + 1, pc) : upvalname(p, t); - r.name = kname(p, pc, k); - r.funcname = vn && vn === lua.to_luastring("_ENV") ? lua.to_luastring("global") : lua.to_luastring("field"); + vn = vn ? vn.jsstring() : null; + r.name = kname(p, pc, k).name; + r.funcname = vn && vn === "_ENV" ? lua.to_luastring("global", true) : lua.to_luastring("field", true); return r; } case 'OP_GETUPVAL': { r.name = upvalname(p, i.B); - r.funcname = lua.to_luastring("upvalue"); + r.funcname = lua.to_luastring("upvalue", true); return r; } case 'OP_LOADK': @@ -366,15 +409,15 @@ const getobjname = function(p, lastpc, reg) { let b = op === 'OP_LOADK' ? i.Bx : p.code[pc + 1].Ax; if (p.k[b].ttisstring()) { r.name = p.k[b].value; - r.funcname = lua.to_luastring("constant"); + r.funcname = lua.to_luastring("constant", true); return r; } break; } case 'OP_SELF': { let k = i.C; - r.name = kname(p, pc, k); - r.funcname = lua.to_luastring("method"); + r.name = kname(p, pc, k).name; + r.funcname = lua.to_luastring("method", true); return r; } default: break; @@ -403,7 +446,7 @@ const funcnamefromcode = function(L, ci) { if (ci.callstatus & lstate.CIST_HOOKED) { r.name = [lua.char["?"]]; - r.funcname = lua.to_luastring("hook"); + r.funcname = lua.to_luastring("hook", true); return r; } @@ -412,8 +455,8 @@ const funcnamefromcode = function(L, ci) { case 'OP_TAILCALL': return getobjname(p, pc, i.A); /* get function name */ case 'OP_TFORCALL': - r.name = lua.to_luastring("for iterator"); - r.funcname = lua.to_luastring("for iterator"); + r.name = lua.to_luastring("for iterator", true); + r.funcname = lua.to_luastring("for iterator", true); return r; /* other instructions can do calls through metamethods */ case 'OP_SELF': @@ -449,7 +492,7 @@ const funcnamefromcode = function(L, ci) { } r.name = L.l_G.tmname[tm]; - r.funcname = lua.to_luastring("metamethod"); + r.funcname = lua.to_luastring("metamethod", true); return r; }; @@ -473,7 +516,7 @@ const getupvalname = function(L, ci, o, name) { if (c.upvals[i].val(L) === o) { return { name: upvalname(c.p, i), - funcname: lua.to_luastring('upvalue') + funcname: lua.to_luastring('upvalue', true) }; } } @@ -501,7 +544,7 @@ const luaG_typeerror = function(L, o, op) { const luaG_concaterror = function(L, p1, p2) { if (p1.ttisstring() || p1.ttisnumber()) p1 = p2; - luaG_typeerror(L, p1, lua.to_luastring('concatenate')); + luaG_typeerror(L, p1, lua.to_luastring('concatenate', true)); }; /* @@ -563,6 +606,40 @@ const luaG_tointerror = function(L, p1, p2) { luaG_runerror(L, lua.to_luastring(`number${lobject.jsstring(varinfo(L, p2))} has no integer representation`)); }; +const luaG_traceexec = function(L) { + let ci = L.ci; + let mask = L.hookmask; + let counthook = (--L.hookcount === 0 && (mask & lua.LUA_MASKCOUNT)); + if (counthook) + L.hookcount = L.basehookcount; /* reset count */ + else if (!(mask & lua.LUA_MASKLINE)) + return; /* no line hook and count != 0; nothing to be done */ + if (ci.callstatus & lstate.CIST_HOOKYIELD) { /* called hook last time? */ + ci.callstatus &= ~lstate.CIST_HOOKYIELD; /* erase mark */ + return; /* do not call hook again (VM yielded, so it did not move) */ + } + if (counthook) + ldo.luaD_hook(L, lua.LUA_HOOKCOUNT, -1); /* call count hook */ + if (mask & lua.LUA_MASKLINE) { + let p = ci.func.p; + let npc = ci.pcOff; // pcRel(ci.u.l.savedpc, p); + let newline = p.lineinfo ? p.lineinfo[npc] : -1; + if (npc === 0 || /* call linehook when enter a new function, */ + ci.pcOff <= L.oldpc || /* when jump back (loop), or when */ + newline !== p.lineinfo ? p.lineinfo[L.oldpc] : -1) /* enter a new line */ + ldo.luaD_hook(L, lua.LUA_HOOKLINE, newline); /* call line hook */ + } + L.oldpc = ci.pcOff; + if (L.status === TS.LUA_YIELD) { /* did hook yield? */ + if (counthook) + L.hookcount = 1; /* undo decrement to zero */ + ci.u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci.callstatus |= lstate.CIST_HOOKYIELD; /* mark that it yielded */ + ci.func = L.top - 1; /* protect stack below results */ + ldo.luaD_throw(L, TS.LUA_YIELD); + } +}; + module.exports.luaG_addinfo = luaG_addinfo; module.exports.luaG_concaterror = luaG_concaterror; module.exports.luaG_errormsg = luaG_errormsg; @@ -570,7 +647,13 @@ module.exports.luaG_opinterror = luaG_opinterror; module.exports.luaG_ordererror = luaG_ordererror; module.exports.luaG_runerror = luaG_runerror; module.exports.luaG_tointerror = luaG_tointerror; +module.exports.luaG_traceexec = luaG_traceexec; module.exports.luaG_typeerror = luaG_typeerror; +module.exports.lua_gethook = lua_gethook; +module.exports.lua_gethookcount = lua_gethookcount; +module.exports.lua_gethookmask = lua_gethookmask; module.exports.lua_getinfo = lua_getinfo; module.exports.lua_getlocal = lua_getlocal; module.exports.lua_getstack = lua_getstack; +module.exports.lua_sethook = lua_sethook; +module.exports.lua_setlocal = lua_setlocal; @@ -23,11 +23,11 @@ const TValue = lobject.TValue; const seterrorobj = function(L, errcode, oldtop) { switch (errcode) { case TS.LUA_ERRMEM: { - L.stack[oldtop] = L.l_G.intern(lua.to_luastring("not enough memory")); + L.stack[oldtop] = L.l_G.intern(lua.to_luastring("not enough memory", true)); break; } case TS.LUA_ERRERR: { - L.stack[oldtop] = L.l_G.intern(lua.to_luastring("error in error handling")); + L.stack[oldtop] = L.l_G.intern(lua.to_luastring("error in error handling", true)); break; } default: { @@ -73,7 +73,8 @@ const luaD_precall = function(L, off, nresults) { ci.funcOff = off; ci.top = L.top + lua.LUA_MINSTACK; ci.callstatus = 0; - // TODO: hook + if (L.hookmask & lua.LUA_MASKCALL) + luaD_hook(L, lua.LUA_HOOKCALL, -1); let n = f(L); /* do the actual call */ assert(n < L.top - L.ci.funcOff, "not enough elements in the stack"); @@ -129,6 +130,13 @@ const luaD_precall = function(L, off, nresults) { const luaD_poscall = function(L, ci, firstResult, nres) { let wanted = ci.nresults; + + if (L.hookmask & (lua.LUA_MASKRET | lua.LUA_MASKLINE)) { + if (L.hookmask & lua.LUA_MASKRET) + luaD_hook(L, lua.LUA_HOOKRET, -1); + L.oldpc = ci.previous.pcOff; /* 'oldpc' for caller function */ + } + let res = ci.funcOff; L.ci = ci.previous; L.ciOff--; @@ -171,6 +179,33 @@ const moveresults = function(L, firstResult, res, nres, wanted) { return true; }; +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which triggers this +** function, can be changed asynchronously by signals.) +*/ +const luaD_hook = function(L, event, line) { + let hook = L.hook; + if (hook && L.allowhook) { /* make sure there is a hook */ + let ci = L.ci; + let top = L.top; + let ci_top = ci.top; + let ar = new lua.lua_Debug(); + ar.event = event; + ar.currentline = line; + ar.i_ci = ci; + ci.top = L.top + lua.LUA_MINSTACK; + L.allowhook = 0; /* cannot call hooks inside a hook */ + ci.callstatus |= lstate.CIST_HOOKED; + hook(L, ar); + assert(!L.allowhook); + L.allowhook = 1; + ci.top = ci_top; + L.top = top; + ci.callstatus &= ~lstate.CIST_HOOKED; + } +}; + const adjust_varargs = function(L, p, actual) { let nfixargs = p.numparams; /* move fixed parameters to final position */ @@ -448,9 +483,9 @@ const lua_yieldk = function(L, nresults, ctx, k) { if (L.nny > 0) { if (L !== L.l_G.mainthread) - ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield across a JS-call boundary")); + ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield across a JS-call boundary", true)); else - ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield from outside a coroutine")); + ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield from outside a coroutine", true)); } L.status = TS.LUA_YIELD; @@ -529,10 +564,10 @@ const f_parser = function(L, p) { let cl; let c = p.z.getc(); /* read first character */ if (c === lua.LUA_SIGNATURE.charCodeAt(0)) { - checkmode(L, p.mode, lua.to_luastring("binary")); + checkmode(L, p.mode, lua.to_luastring("binary", true)); cl = new BytecodeParser(L, p.z.buffer).luaU_undump(); } else { - checkmode(L, p.mode, lua.to_luastring("text")); + checkmode(L, p.mode, lua.to_luastring("text", true)); cl = lparser.luaY_parser(L, p.z, p.buff, p.dyd, p.name, c); } @@ -566,6 +601,7 @@ module.exports.SParser = SParser; module.exports.adjust_varargs = adjust_varargs; module.exports.luaD_call = luaD_call; module.exports.luaD_callnoyield = luaD_callnoyield; +module.exports.luaD_hook = luaD_hook; module.exports.luaD_pcall = luaD_pcall; module.exports.luaD_poscall = luaD_poscall; module.exports.luaD_precall = luaD_precall; diff --git a/src/lfunc.js b/src/lfunc.js index 032f653..bc88702 100644 --- a/src/lfunc.js +++ b/src/lfunc.js @@ -45,7 +45,6 @@ class UpVal { setval(L, ra) { if (this.v !== null) { this.L.stack[this.v] = L.stack[ra]; - this.v = ra; } else this.u.value = L.stack[ra]; } @@ -138,4 +137,4 @@ module.exports.findupval = findupval; module.exports.luaF_close = luaF_close; module.exports.luaF_getlocalname = luaF_getlocalname; module.exports.luaF_initupvals = luaF_initupvals; -module.exports.luaF_newLclosure = luaF_newLclosure;
\ No newline at end of file +module.exports.luaF_newLclosure = luaF_newLclosure; diff --git a/src/linit.js b/src/linit.js index 9425ae7..fa827cb 100644 --- a/src/linit.js +++ b/src/linit.js @@ -12,15 +12,17 @@ const lstrlib = require('./lstrlib.js'); const ltablib = require('./ltablib.js'); const lutf8lib = require('./lutf8lib.js'); const ldblib = require('./ldblib.js'); +const loslib = require('./loslib.js'); const lualib = require('./lualib.js'); const loadedlibs = { [lualib.LUA_COLIBNAME]: lcorolib.luaopen_coroutine, + [lualib.LUA_DBLIBNAME]: ldblib.luaopen_debug, [lualib.LUA_MATHLIBNAME]: lmathlib.luaopen_math, + [lualib.LUA_OSLIBNAME]: loslib.luaopen_os, [lualib.LUA_STRLIBNAME]: lstrlib.luaopen_string, [lualib.LUA_TABLIBNAME]: ltablib.luaopen_table, [lualib.LUA_UTF8LIBNAME]: lutf8lib.luaopen_utf8, - [lualib.LUA_DBLIBNAME]: ldblib.luaopen_debug, "_G": lbaselib.luaopen_base }; diff --git a/src/llex.js b/src/llex.js index 3ed0e56..b038619 100644 --- a/src/llex.js +++ b/src/llex.js @@ -152,7 +152,7 @@ const save = function(ls, c) { let b = ls.buff; if (b.n + 1 > b.buffer.length) { if (b.buffer.length >= Number.MAX_SAFE_INTEGER/2) - lexerror(ls, lua.to_luastring("lexical element too long"), 0); + lexerror(ls, lua.to_luastring("lexical element too long", true), 0); } b.buffer[b.n++] = c < 0 ? 255 + c + 1 : c; }; @@ -193,7 +193,7 @@ const inclinenumber = function(ls) { if (currIsNewline(ls) && ls.current !== old) next(ls); /* skip '\n\r' or '\r\n' */ if (++ls.linenumber >= Number.MAX_SAFE_INTEGER) - lexerror(ls, lua.to_luastring("chunk has too many lines"), 0); + lexerror(ls, lua.to_luastring("chunk has too many lines", true), 0); }; const luaX_setinput = function(L, ls, z, source, firstchar) { @@ -220,7 +220,7 @@ const luaX_setinput = function(L, ls, z, source, firstchar) { ls.linenumber = 1; ls.lastline = 1; ls.source = source; - ls.envn = L.l_G.intern(lua.to_luastring("_ENV")); + ls.envn = L.l_G.intern(lua.to_luastring("_ENV", true)); }; const check_next1 = function(ls, c) { @@ -267,7 +267,7 @@ const read_numeral = function(ls, seminfo) { let obj = lobject.luaO_str2num(ls.buff.buffer); if (obj === false) /* format error? */ - lexerror(ls, lua.to_luastring("malformed number"), R.TK_FLT); + lexerror(ls, lua.to_luastring("malformed number", true), R.TK_FLT); if (obj.ttisinteger()) { seminfo.i = obj.value; return R.TK_INT; @@ -373,7 +373,7 @@ const esccheck = function(ls, c, msg) { const gethexa = function(ls) { save_and_next(ls); - esccheck(ls, ljstype.lisxdigit(ls.current), lua.to_luastring("hexadecimal digit expected")); + esccheck(ls, ljstype.lisxdigit(ls.current), lua.to_luastring("hexadecimal digit expected", true)); return lobject.luaO_hexavalue(ls.current); }; @@ -387,17 +387,17 @@ const readhexaesc = function(ls) { const readutf8desc = function(ls) { let i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ save_and_next(ls); /* skip 'u' */ - esccheck(ls, ls.current === char['{'], lua.to_luastring("missing '{'")); + esccheck(ls, ls.current === char['{'], lua.to_luastring("missing '{'", true)); let r = gethexa(ls); /* must have at least one digit */ save_and_next(ls); while (ljstype.lisxdigit(ls.current)) { i++; r = (r << 4) + lobject.luaO_hexavalue(ls.current); - esccheck(ls, r <= 0x10FFFF, lua.to_luastring("UTF-8 value too large")); + esccheck(ls, r <= 0x10FFFF, lua.to_luastring("UTF-8 value too large", true)); save_and_next(ls); } - esccheck(ls, ls.current === char['}'], lua.to_luastring("missing '}'")); + esccheck(ls, ls.current === char['}'], lua.to_luastring("missing '}'", true)); next(ls); /* skip '}' */ ls.buff.n -= i; /* remove saved chars from buffer */ return r; @@ -417,7 +417,7 @@ const readdecesc = function(ls) { r = 10 * r + ls.current - char['0']; save_and_next(ls); } - esccheck(ls, r <= 255, lua.to_luastring("decimal escape too large")); + esccheck(ls, r <= 255, lua.to_luastring("decimal escape too large", true)); ls.buff.n -= i; /* remove read digits from buffer */ return r; }; @@ -428,11 +428,11 @@ const read_string = function(ls, del, seminfo) { while (ls.current !== del) { switch (ls.current) { case -1: - lexerror(ls, lua.to_luastring("unfinished string"), R.TK_EOS); + lexerror(ls, lua.to_luastring("unfinished string", true), R.TK_EOS); break; case char['\n']: case char['\r']: - lexerror(ls, lua.to_luastring("unfinished string"), R.TK_STRING); + lexerror(ls, lua.to_luastring("unfinished string", true), R.TK_STRING); break; case char['\\']: { /* escape sequences */ save_and_next(ls); /* keep '\\' for error messages */ @@ -463,7 +463,7 @@ const read_string = function(ls, del, seminfo) { will = 'no_save'; break; } default: { - esccheck(ls, ljstype.lisdigit(ls.current), lua.to_luastring("invalid escape sequence")); + esccheck(ls, ljstype.lisdigit(ls.current), lua.to_luastring("invalid escape sequence", true)); c = readdecesc(ls); /* digital escape '\ddd' */ will = 'only_save'; break; } @@ -537,7 +537,7 @@ const llex = function(ls, seminfo) { read_long_string(ls, seminfo, sep); return R.TK_STRING; } else if (sep !== -1) /* '[=...' missing second bracket */ - lexerror(ls, lua.to_luastring("invalid long string delimiter"), R.TK_STRING); + lexerror(ls, lua.to_luastring("invalid long string delimiter", true), R.TK_STRING); return char['[']; } case char['=']: { diff --git a/src/lmathlib.js b/src/lmathlib.js index 500331b..c052fcb 100644 --- a/src/lmathlib.js +++ b/src/lmathlib.js @@ -37,13 +37,13 @@ const math_random = function(L) { up = lauxlib.luaL_checkinteger(L, 2); break; } - default: return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments")); + default: return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments", true)); } /* random integer in the interval [low, up] */ - lauxlib.luaL_argcheck(L, low <= up, 1, lua.to_luastring("interval is empty")); + lauxlib.luaL_argcheck(L, low <= up, 1, lua.to_luastring("interval is empty", true)); lauxlib.luaL_argcheck(L, low >= 0 || up <= Number.MAX_SAFE_INTEGER + low, 1, - lua.to_luastring("interval too large")); + lua.to_luastring("interval too large", true)); r *= (up - low) + 1; lapi.lua_pushinteger(L, r + low); @@ -173,7 +173,7 @@ const math_rad = function(L) { const math_min = function(L) { let n = lapi.lua_gettop(L); /* number of arguments */ let imin = 1; /* index of current minimum value */ - lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected")); + lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected", true)); for (let i = 2; i <= n; i++){ if (lapi.lua_compare(L, i, imin, lua.LUA_OPLT)) imin = i; @@ -185,7 +185,7 @@ const math_min = function(L) { const math_max = function(L) { let n = lapi.lua_gettop(L); /* number of arguments */ let imax = 1; /* index of current minimum value */ - lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected")); + lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected", true)); for (let i = 2; i <= n; i++){ if (lapi.lua_compare(L, imax, i, lua.LUA_OPLT)) imax = i; @@ -211,7 +211,7 @@ const math_fmod = function(L) { if (lapi.lua_isinteger(L, 1) && lapi.lua_isinteger(L, 2)) { let d = lapi.lua_tointeger(L, 2); if (Math.abs(d) + 1 <= 1) { - lauxlib.luaL_argcheck(L, d !== 0, 2, lua.to_luastring("zero")); + lauxlib.luaL_argcheck(L, d !== 0, 2, lua.to_luastring("zero", true)); lapi.lua_pushinteger(L, 0); } else lapi.lua_pushinteger(L, lapi.lua_tointeger(L, 1) % d); @@ -265,13 +265,13 @@ const mathlib = { const luaopen_math = function(L) { lauxlib.luaL_newlib(L, mathlib); lapi.lua_pushnumber(L, Math.PI); - lapi.lua_setfield(L, -2, lua.to_luastring("pi")); + lapi.lua_setfield(L, -2, lua.to_luastring("pi", true)); lapi.lua_pushnumber(L, Number.MAX_VALUE); - lapi.lua_setfield(L, -2, lua.to_luastring("huge")); + lapi.lua_setfield(L, -2, lua.to_luastring("huge", true)); lapi.lua_pushinteger(L, Number.MAX_SAFE_INTEGER); - lapi.lua_setfield(L, -2, lua.to_luastring("maxinteger")); + lapi.lua_setfield(L, -2, lua.to_luastring("maxinteger", true)); lapi.lua_pushinteger(L, Number.MIN_SAFE_INTEGER); - lapi.lua_setfield(L, -2, lua.to_luastring("mininteger")); + lapi.lua_setfield(L, -2, lua.to_luastring("mininteger", true)); return 1; }; diff --git a/src/lobject.js b/src/lobject.js index 618e801..52ca385 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -207,22 +207,13 @@ class Table extends TValue { __newindex(table, key, value) { key = Table.keyValue(key); - if (typeof key === 'number' && key > 0) { - table.value.set(key - 1, value); // Lua array starts at 1 - } else { - table.value.set(key, value); - } + table.value.set(key, value); } __index(table, key) { key = Table.keyValue(key); - let v = luaO_nilobject; - if (typeof key === 'number' && key > 0) { - v = table.value.get(key - 1); // Lua array starts at 1 - } else { - v = table.value.get(key); - } + let v = table.value.get(key); return v ? v : luaO_nilobject; } @@ -283,7 +274,7 @@ class LocVar { } } -const RETS = lua.to_luastring("..."); +const RETS = lua.to_luastring("...", true); const PRE = lua.to_luastring("[string \""); const POS = lua.to_luastring("\"]"); @@ -445,7 +436,7 @@ const l_str2d = function(s) { return null; let end = l_str2dloc(s, mode); /* try to convert */ if (end === null) { /* failed? may be a different locale */ - throw new Error("Locale not available to handle number"); // TODO + // throw new Error("Locale not available to handle number"); // TODO } return end; }; @@ -483,7 +474,7 @@ const l_str2int = function(s) { while (ljstype.lisspace(s[0])) s = s.slice(1); /* skip trailing spaces */ - if (empty || s[0] !== 0) return null; /* something wrong in the numeral */ + if (empty || (s.length > 0 && s[0] !== 0)) return null; /* something wrong in the numeral */ else { return neg ? -a : a; } @@ -549,7 +540,7 @@ const intarith = function(L, op, v1, v2) { case lua.LUA_OPADD: return (v1 + v2); case lua.LUA_OPSUB: return (v1 - v2); case lua.LUA_OPMUL: return (v1 * v2); - case lua.LUA_OPMOD: return (v1 % v2); + case lua.LUA_OPMOD: return (v1 - Math.floor(v1 / v2) * v2); // % semantic on negative numbers is different in js case lua.LUA_OPIDIV: return (v1 / v2); case lua.LUA_OPBAND: return (v1 & v2); case lua.LUA_OPBOR: return (v1 | v2); diff --git a/src/loslib.js b/src/loslib.js new file mode 100644 index 0000000..20675a1 --- /dev/null +++ b/src/loslib.js @@ -0,0 +1,78 @@ +"use strict"; + +const assert = require('assert'); + +const lua = require('./lua.js'); +const char = lua.char; +const lapi = require('./lapi.js'); +const lauxlib = require('./lauxlib.js'); +const ldebug = require('./ldebug.js'); + +const setfield = function(L, key, value) { + lapi.lua_pushinteger(L, value); + lapi.lua_setfield(L, -2, key); +}; + +const setallfields = function(L, time) { + setfield(L, "sec", time.getSeconds()); + setfield(L, "min", time.getMinutes()); + setfield(L, "hour", time.getHours()); + setfield(L, "day", time.getDate()); + setfield(L, "month", time.getMonth()); + setfield(L, "year", time.getYear()); + setfield(L, "wday", time.getDay()); + let now = new Date(); + setfield(L, "yday", Math.floor((now - (new Date(now.getFullYear(), 0, 0))) / (1000 * 60 * 60 * 24))); + // setboolfield(L, "isdst", time.get); +}; + +const L_MAXDATEFIELD = (Number.MAX_SAFE_INTEGER / 2); + +const getfield = function(L, key, d, delta) { + let t = lapi.lua_getfield(L, -1, lua.to_luastring(key)); /* get field and its type */ + let res = lapi.lua_tointegerx(L, -1); + if (res !== false) { /* field is not an integer? */ + if (t != lua.CT.LUA_TNIL) /* some other value? */ + return lauxlib.luaL_error(L, `field '${key}' is not an integer`); + else if (d < 0) /* absent field; no default? */ + return lauxlib.luaL_error(L, `field '${key}' missing in date table`); + res = d; + } + else { + if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) + return lauxlib.luaL_error(L, `field '${key}' is out-of-bound`); + res -= delta; + } + lapi.lua_pop(L, 1); + return res; +}; + +const os_time = function(L) { + let t = new Date(); + if (!lapi.lua_isnoneornil(L, 1)) /* called with arg */{ + lauxlib.luaL_checktype(L, 1, lua.CT.LUA_TTABLE); /* make sure table is at the top */ + lapi.lua_settop(L, 1); + t.setSeconds(getfield(L, "sec", 0, 0)); + t.setSeconds(getfield(L, "min", 0, 0)); + t.setSeconds(getfield(L, "hour", 12, 0)); + t.setSeconds(getfield(L, "day", -1, 0)); + t.setSeconds(getfield(L, "month", -1, 1)); + t.setSeconds(getfield(L, "year", -1, 1900)); + setallfields(L, t); + } + + lapi.lua_pushinteger(L, Math.floor(t / 1000)); + return 1; +}; + + +const syslib = { + "time": os_time +}; + +const luaopen_os = function(L) { + lauxlib.luaL_newlib(L, syslib); + return 1; +}; + +module.exports.luaopen_os = luaopen_os; diff --git a/src/lparser.js b/src/lparser.js index bdaa160..cf5f55b 100644 --- a/src/lparser.js +++ b/src/lparser.js @@ -242,7 +242,7 @@ const new_localvar = function(ls, name) { let fs = ls.fs; let dyd = ls.dyd; let reg = registerlocalvar(ls, name); - checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, lua.to_luastring("local variables")); + checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, lua.to_luastring("local variables", true)); dyd.actvar.arr[dyd.actvar.n] = new Vardesc(); dyd.actvar.arr[dyd.actvar.n].idx = reg; dyd.actvar.n++; @@ -282,7 +282,7 @@ const searchupvalue = function(fs, name) { const newupvalue = function(fs, name, v) { let f = fs.f; - checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, lua.to_luastring("upvalues")); + checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, lua.to_luastring("upvalues", true)); f.upvalues[fs.nups] = new UpVal(fs.ls.L); f.upvalues[fs.nups].instack = v.k === expkind.VLOCAL; f.upvalues[fs.nups].idx = v.u.info; @@ -373,7 +373,7 @@ const adjust_assign = function(ls, nvars, nexps, e) { const enterlevel = function(ls) { let L = ls.L; ++L.nCcalls; - checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels")); + checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels", true)); }; const leavelevel = function(ls) { @@ -480,7 +480,7 @@ const enterblock = function(fs, bl, isloop) { ** create a label named 'break' to resolve break statements */ const breaklabel = function(ls) { - let n = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break")); + let n = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break", true)); let l = newlabelentry(ls, ls.dyd.label, n, 0, ls.fs.pc); findgotos(ls, ls.dyd.label.arr[l]); }; @@ -639,7 +639,7 @@ const recfield = function(ls, cc) { let val = new expdesc(); if (ls.t.token === R.TK_NAME) { - checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor")); + checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor", true)); checkname(ls, key); } else /* ls->t.token === '[' */ yindex(ls, key); @@ -677,7 +677,7 @@ const lastlistfield = function(fs, cc) { const listfield = function(ls, cc) { /* listfield -> exp */ expr(ls, cc.v); - checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor")); + checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor", true)); cc.na++; cc.tostore++; }; @@ -749,7 +749,7 @@ const parlist = function(ls) { f.is_vararg = 1; /* declared vararg */ break; } - default: llex.luaX_syntaxerror(ls, lua.to_luastring("<name> or '...' expected")); + default: llex.luaX_syntaxerror(ls, lua.to_luastring("<name> or '...' expected", true)); } } while(!f.is_vararg && testnext(ls, char[','])); } @@ -767,7 +767,7 @@ const body = function(ls, e, ismethod, line) { open_func(ls, new_fs, bl); checknext(ls, char['(']); if (ismethod) { - new_localvarliteral(ls, lua.to_luastring("self")); /* create 'self' parameter */ + new_localvarliteral(ls, lua.to_luastring("self", true)); /* create 'self' parameter */ adjustlocalvars(ls, 1); } parlist(ls); @@ -816,7 +816,7 @@ const funcargs = function(ls, f, line) { break; } default: { - llex.luaX_syntaxerror(ls, lua.to_luastring("function arguments expected")); + llex.luaX_syntaxerror(ls, lua.to_luastring("function arguments expected", true)); } } assert(f.k === expkind.VNONRELOC); @@ -856,7 +856,7 @@ const primaryexp = function(ls, v) { return; } default: { - llex.luaX_syntaxerror(ls, lua.to_luastring("unexpected symbol")); + llex.luaX_syntaxerror(ls, lua.to_luastring("unexpected symbol", true)); } } }; @@ -930,7 +930,7 @@ const simpleexp = function(ls, v) { } case R.TK_DOTS: { /* vararg */ let fs = ls.fs; - check_condition(ls, fs.f.is_vararg, lua.to_luastring("cannot use '...' outside a vararg function")); + check_condition(ls, fs.f.is_vararg, lua.to_luastring("cannot use '...' outside a vararg function", true)); init_exp(v, expkind.VVARARG, lcode.luaK_codeABC(fs, OpCodesI.OP_VARARG, 0, 1, 0)); break; } @@ -1102,14 +1102,14 @@ const check_conflict = function(ls, lh, v) { const assignment = function(ls, lh, nvars) { let e = new expdesc(); - check_condition(ls, vkisvar(lh.v.k), lua.to_luastring("syntax error")); + check_condition(ls, vkisvar(lh.v.k), lua.to_luastring("syntax error", true)); if (testnext(ls, char[','])) { /* assignment -> ',' suffixedexp assignment */ let nv = new LHS_assign(); nv.prev = lh; suffixedexp(ls, nv.v); if (nv.v.k !== expkind.VINDEXED) check_conflict(ls, lh, nv.v); - checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels")); + checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels", true)); assignment(ls, nv, nvars + 1); } else { /* assignment -> '=' explist */ checknext(ls, char['=']); @@ -1142,7 +1142,7 @@ const gotostat = function(ls, pc) { label = str_checkname(ls); else { llex.luaX_next(ls); /* skip break */ - label = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break")); + label = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break", true)); } let g = newlabelentry(ls, ls.dyd.gt, label, line, pc); findlabel(ls, g); /* close it if label already defined */ @@ -1253,9 +1253,9 @@ const fornum = function(ls, varname, line) { /* fornum -> NAME = exp1,exp1[,exp1] forbody */ let fs = ls.fs; let base = fs.freereg; - new_localvarliteral(ls, lua.to_luastring("(for index)")); - new_localvarliteral(ls, lua.to_luastring("(for limit)")); - new_localvarliteral(ls, lua.to_luastring("(for step)")); + new_localvarliteral(ls, lua.to_luastring("(for index)", true)); + new_localvarliteral(ls, lua.to_luastring("(for limit)", true)); + new_localvarliteral(ls, lua.to_luastring("(for step)", true)); new_localvar(ls, varname); checknext(ls, char['=']); exp1(ls); /* initial value */ @@ -1277,9 +1277,9 @@ const forlist = function(ls, indexname) { let nvars = 4; /* gen, state, control, plus at least one declared var */ let base = fs.freereg; /* create control variables */ - new_localvarliteral(ls, lua.to_luastring("(for generator)")); - new_localvarliteral(ls, lua.to_luastring("(for state)")); - new_localvarliteral(ls, lua.to_luastring("(for control)")); + new_localvarliteral(ls, lua.to_luastring("(for generator)", true)); + new_localvarliteral(ls, lua.to_luastring("(for state)", true)); + new_localvarliteral(ls, lua.to_luastring("(for control)", true)); /* create declared variables */ new_localvar(ls, indexname); while (testnext(ls, char[','])) { @@ -1303,7 +1303,7 @@ const forstat = function(ls, line) { switch (ls.t.token) { case char['=']: fornum(ls, varname, line); break; case char[',']: case R.TK_IN: forlist(ls, varname); break; - default: llex.luaX_syntaxerror(ls, lua.to_luastring("'=' or 'in' expected")); + default: llex.luaX_syntaxerror(ls, lua.to_luastring("'=' or 'in' expected", true)); } check_match(ls, R.TK_END, R.TK_FOR, line); leaveblock(fs); /* loop scope ('break' jumps to this point) */ @@ -1421,7 +1421,7 @@ const exprstat= function(ls) { assignment(ls, v, 1); } else { /* stat -> func */ - check_condition(ls, v.v.k === expkind.VCALL, lua.to_luastring("syntax error")); + check_condition(ls, v.v.k === expkind.VCALL, lua.to_luastring("syntax error", true)); lopcode.SETARG_C(lcode.getinstruction(fs, v.v), 1); /* call statement uses no results */ } }; diff --git a/src/lstate.js b/src/lstate.js index 74c908d..e18e7aa 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -109,8 +109,8 @@ const stack_init = function(L1, L) { const init_registry = function(L, g) { let registry = new lobject.Table(); g.l_registry = registry; - registry.value.set(lua.LUA_RIDX_MAINTHREAD - 1, L); - registry.value.set(lua.LUA_RIDX_GLOBALS - 1, new lobject.Table()); + registry.value.set(lua.LUA_RIDX_MAINTHREAD, L); + registry.value.set(lua.LUA_RIDX_GLOBALS, new lobject.Table()); }; /* diff --git a/src/lstrlib.js b/src/lstrlib.js index 73fc068..c2ee817 100644 --- a/src/lstrlib.js +++ b/src/lstrlib.js @@ -103,11 +103,11 @@ const num2straux = function(x) { let buff = []; /* if 'inf' or 'NaN', format it like '%g' */ if (Object.is(x, Infinity)) - return lua.to_luastring('inf'); + return lua.to_luastring('inf', true); else if (Object.is(x, -Infinity)) - return lua.to_luastring('-inf'); + return lua.to_luastring('-inf', true); else if (Number.isNaN(x)) - return lua.to_luastring('nan'); + return lua.to_luastring('nan', true); else if (x === 0) { /* can be -0... */ /* create "0" or "-0" followed by exponent */ let zero = sprintf(luaconf.LUA_NUMBER_FMT + "x0p+0", x).split('').map(e => e.charCodeAt(0)); @@ -144,7 +144,7 @@ const lua_number2strx = function(L, fmt, x) { for (let i = 0; i < buff.length; i++) buff[i] = char[String.fromCharCode(buff[i]).toUpperCase()]; } else if (fmt[SIZELENMOD] !== char['a']) - lauxlib.luaL_error(L, lua.to_luastring("modifiers for format '%a'/'%A' not implemented")); + lauxlib.luaL_error(L, lua.to_luastring("modifiers for format '%a'/'%A' not implemented", true)); return buff; }; @@ -239,7 +239,7 @@ const addliteral = function(L, b, arg) { break; } default: { - lauxlib.luaL_argerror(L, arg, lua.to_luastring("value has no literal form")); + lauxlib.luaL_argerror(L, arg, lua.to_luastring("value has no literal form", true)); } } }; @@ -248,7 +248,7 @@ const scanformat = function(L, strfrmt, form) { let p = strfrmt; while (p[0] !== 0 && FLAGS.indexOf(p[0]) >= 0) p = p.slice(1); /* skip flags */ if (strfrmt.length - p.length >= FLAGS.length) - lauxlib.luaL_error(L, lua.to_luastring("invalid format (repeated flags)")); + lauxlib.luaL_error(L, lua.to_luastring("invalid format (repeated flags)", true)); if (isdigit(p[0])) p = p.slice(1); /* skip width */ if (isdigit(p[0])) p = p.slice(1); /* (2 digits at most) */ if (p[0] === char['.']) { @@ -257,7 +257,7 @@ const scanformat = function(L, strfrmt, form) { if (isdigit(p[0])) p = p.slice(1); /* (2 digits at most) */ } if (isdigit(p[0])) - lauxlib.luaL_error(L, lua.to_luastring("invalid format (width or precision too long)")); + lauxlib.luaL_error(L, lua.to_luastring("invalid format (width or precision too long)", true)); form[0] = char["%"]; for (let i = 0; i < strfrmt.length - p.length + 1; i++) form[i + 1] = strfrmt[i]; @@ -298,7 +298,7 @@ const str_format = function(L) { } else { /* format item */ let form = []; /* to store the format ('%...') */ if (++arg > top) - lauxlib.luaL_argerror(L, arg, lua.to_luastring("no value")); + lauxlib.luaL_argerror(L, arg, lua.to_luastring("no value", true)); let f = scanformat(L, strfrmt, form); strfrmt = f.p; form = f.form; @@ -343,7 +343,7 @@ const str_format = function(L) { concat(b, s); /* keep entire string */ lapi.lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ } else { - lauxlib.luaL_argcheck(L, s.length === strlen(s), arg, lua.to_luastring("string contains zeros")); + lauxlib.luaL_argcheck(L, s.length === strlen(s), arg, lua.to_luastring("string contains zeros", true)); if (form.indexOf(char['.']) < 0 && s.length >= 100) { /* no precision and string is too long to be formatted */ concat(b, s); /* keep entire string */ @@ -468,7 +468,7 @@ const getoption = function(h, fmt) { case char['c']: { r.size = getnum(fmt, -1); if (r.size === -1) - lauxlib.luaL_error(h.L, lua.to_luastring("missing size for format option 'c'")); + lauxlib.luaL_error(h.L, lua.to_luastring("missing size for format option 'c'", true)); r.opt = KOption.Kchar; return r; } @@ -509,13 +509,13 @@ const getdetails = function(h, totalsize, fmt) { let align = r.size; /* usually, alignment follows size */ if (r.opt === KOption.Kpaddalign) { /* 'X' gets alignment from following option */ if (fmt.s[0] === 0) - lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'")); + lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'", true)); else { let o = getoption(h, fmt); align = o.size; o = o.opt; if (o === KOption.Kchar || align === 0) - lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'")); + lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'", true)); } } if (align <= 1 || r.opt === KOption.Kchar) /* need no alignment? */ @@ -524,7 +524,7 @@ const getdetails = function(h, totalsize, fmt) { if (align > h.maxalign) /* enforce maximum alignment */ align = h.maxalign; if ((align & (align -1)) !== 0) /* is 'align' not a power of 2? */ - lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("format asks for alignment not power of 2")); + lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("format asks for alignment not power of 2", true)); r.ntoalign = (align - (totalsize & (align - 1))) & (align - 1); } return r; @@ -585,7 +585,7 @@ const str_pack = function(L) { let n = lauxlib.luaL_checkinteger(L, arg); if (size < SZINT) { /* need overflow check? */ let lim = 1 << (size * 8) - 1; - lauxlib.luaL_argcheck(L, -lim <= n && n < lim, arg, lua.to_luastring("integer overflow")); + lauxlib.luaL_argcheck(L, -lim <= n && n < lim, arg, lua.to_luastring("integer overflow", true)); } packint(b, n, h.islittle, size, n < 0); break; @@ -593,7 +593,7 @@ const str_pack = function(L) { case KOption.Kuint: { /* unsigned integers */ let n = lauxlib.luaL_checkinteger(L, arg); if (size < SZINT) - lauxlib.luaL_argcheck(L, n < (1 << (size * NB)), arg, lua.to_luastring("unsigned overflow")); + lauxlib.luaL_argcheck(L, n < (1 << (size * NB)), arg, lua.to_luastring("unsigned overflow", true)); packint(b, n, h.islittle, size, false); break; } @@ -605,7 +605,7 @@ const str_pack = function(L) { case KOption.Kchar: { /* fixed-size string */ let s = lauxlib.luaL_checkstring(L, arg); let len = s.length; - lauxlib.luaL_argcheck(L, len <= size, arg, lua.to_luastring("string long than given size")); + lauxlib.luaL_argcheck(L, len <= size, arg, lua.to_luastring("string long than given size", true)); b.push(...s); /* add string */ while (len++ < size) /* pad extra space */ b.push(LUAL_PACKPADBYTE); @@ -614,7 +614,7 @@ const str_pack = function(L) { case KOption.Kstring: { /* strings with length count */ let s = lauxlib.luaL_checkstring(L, arg); let len = s.length; - lauxlib.luaL_argcheck(L, size >= NB || len < (1 << size * NB), arg, lua.to_luastring("string length does not fit in given size")); + lauxlib.luaL_argcheck(L, size >= NB || len < (1 << size * NB), arg, lua.to_luastring("string length does not fit in given size", true)); packint(b, len, h.islittle, size, 0); /* pack length */ b.push(...s); totalsize += len; @@ -623,7 +623,7 @@ const str_pack = function(L) { case KOption.Kzstr: { /* zero-terminated string */ let s = lauxlib.luaL_checkstring(L, arg); let len = s.length; - lauxlib.luaL_argcheck(L, s.length === String.fromCharCode(...s).length, arg, lua.to_luastring("strings contains zeros")); + lauxlib.luaL_argcheck(L, s.length === String.fromCharCode(...s).length, arg, lua.to_luastring("strings contains zeros", true)); b.push(...s); b.push(0); /* add zero at the end */ totalsize += len + 1; @@ -662,7 +662,7 @@ const str_rep = function(L) { let sep = lauxlib.luaL_optstring(L, 3, []); if (s.length + sep.length < s.length || s.length + sep.length > MAXSIZE / n) /* may overflow? */ - return lauxlib.luaL_error(L, lua.to_luastring("resulting string too large")); + return lauxlib.luaL_error(L, lua.to_luastring("resulting string too large", true)); let r = []; for (let i = 0; i < n - 1; i++) @@ -683,10 +683,10 @@ const str_byte = function(L) { if (pose > l) pose = l; if (posi > pose) return 0; /* empty interval; return no values */ if (pose - posi >= Number.MAX_SAFE_INTEGER) /* arithmetic overflow? */ - return lauxlib.luaL_error(L, lua.to_luastring("string slice too long")); + return lauxlib.luaL_error(L, lua.to_luastring("string slice too long", true)); let n = (pose - posi) + 1; - lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long")); + lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long", true)); for (let i = 0; i < n; i++) lapi.lua_pushinteger(L, s[posi + i - 1]); return n; @@ -707,12 +707,12 @@ const str_packsize = function(L) { let size = details.size; let ntoalign = details.ntoalign; size += ntoalign; /* total space used by option */ - lauxlib.luaL_argcheck(L, totalsize <= MAXSIZE - size - 1, lua.to_luastring("format result too large")); + lauxlib.luaL_argcheck(L, totalsize <= MAXSIZE - size - 1, lua.to_luastring("format result too large", true)); totalsize += size; switch (opt) { case KOption.Kstring: /* strings with length count */ case KOption.Kzstr: /* zero-terminated string */ - lauxlib.luaL_argerror(L, 1, lua.to_luastring("variable-length format")); + lauxlib.luaL_argerror(L, 1, lua.to_luastring("variable-length format", true)); default: break; } } @@ -771,17 +771,17 @@ const str_unpack = function(L) { let ld = data.length; let pos = posrelat(lauxlib.luaL_optinteger(L, 3, 1), ld) - 1; let n = 0; /* number of results */ - lauxlib.luaL_argcheck(L, pos <= ld, 3, lua.to_luastring("initial position out of string")); + lauxlib.luaL_argcheck(L, pos <= ld, 3, lua.to_luastring("initial position out of string", true)); while (fmt.s.length - 1 > 0) { let details = getdetails(h, pos, fmt); let opt = details.opt; let size = details.size; let ntoalign = details.ntoalign; if (/*ntoalign + size > ~pos ||*/ pos + ntoalign + size > ld) - lauxlib.luaL_argerror(L, 2, lua.to_luastring("data string too short")); + lauxlib.luaL_argerror(L, 2, lua.to_luastring("data string too short", true)); pos += ntoalign; /* skip alignment */ /* stack space for item + next position */ - lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many results")); + lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many results", true)); n++; switch (opt) { case KOption.Kint: @@ -801,7 +801,7 @@ const str_unpack = function(L) { } case KOption.Kstring: { let len = unpackint(L, data.slice(pos), h.islittle, size, 0); - lauxlib.luaL_argcheck(L, pos + len + size <= ld, 2, lua.to_luastring("data string too short")); + lauxlib.luaL_argcheck(L, pos + len + size <= ld, 2, lua.to_luastring("data string too short", true)); lapi.lua_pushstring(L, data.slice(pos + size, pos + size + len)); pos += len; /* skip string */ break; @@ -852,21 +852,21 @@ const capture_to_close = function(ms) { let level = ms.level; for (level--; level >= 0; level--) if (ms.capture[level].len === CAP_UNFINISHED) return level; - return lauxlib.luaL_error(ms.L, lua.to_luastring("invalid pattern capture")); + return lauxlib.luaL_error(ms.L, lua.to_luastring("invalid pattern capture", true)); }; const classend = function(ms, p) { switch(ms.p[p++]) { case L_ESC: { if (p === ms.p_end) - lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (ends with '%')")); + lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (ends with '%')", true)); return p + 1; } case char['[']: { if (ms.p[p] === char['^']) p++; do { /* look for a ']' */ if (p === ms.p_end) - lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing ']')")); + lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing ']')", true)); if (ms.p[p++] === L_ESC && p < ms.p_end) p++; /* skip escapes (e.g. '%]') */ } while (ms.p[p] !== char[']']); @@ -933,7 +933,7 @@ const singlematch = function(ms, s, p, ep) { const matchbalance = function(ms, s, p) { if (p >= ms.p_end - 1) - lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing arguments to '%b'")); + lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing arguments to '%b'", true)); if (ms.src[s] !== ms.p[p]) return null; else { @@ -976,7 +976,7 @@ const min_expand = function(ms, s, p, ep) { const start_capture = function(ms, s, p, what) { let level = ms.level; - if (level >= LUA_MAXCAPTURES) lauxlib.luaL_error(ms.L, lua.to_luastring("too many captures")); + if (level >= LUA_MAXCAPTURES) lauxlib.luaL_error(ms.L, lua.to_luastring("too many captures", true)); ms.capture[level] = ms.capture[level] ? ms.capture[level] : {}; ms.capture[level].init = s; ms.capture[level].len = what; @@ -1009,7 +1009,7 @@ const match = function(ms, s, p) { let gotoinit = true; if (ms.matchdepth-- === 0) - lauxlib.luaL_error(ms.L, lua.to_luastring("pattern too complex")); + lauxlib.luaL_error(ms.L, lua.to_luastring("pattern too complex", true)); while (gotoinit || gotodefault) { gotoinit = false; @@ -1118,7 +1118,7 @@ const push_onecapture = function(ms, i, s, e) { lauxlib.luaL_error(ms.L, lua.to_luastring(`invalid capture index %${i + 1}`)); } else { let l = ms.capture[i].len; - if (l === CAP_UNFINISHED) lauxlib.luaL_error(ms.L, lua.to_luastring("unfinished capture")); + if (l === CAP_UNFINISHED) lauxlib.luaL_error(ms.L, lua.to_luastring("unfinished capture", true)); if (l === CAP_POSITION) lapi.lua_pushinteger(ms.L, ms.src_init + 1); else @@ -1128,7 +1128,7 @@ const push_onecapture = function(ms, i, s, e) { const push_captures = function(ms, s, e) { let nlevels = ms.level === 0 && ms.src.slice(s) ? 1 : ms.level; - lauxlib.luaL_checkstack(ms.L, nlevels, lua.to_luastring("too many catpures")); + lauxlib.luaL_checkstack(ms.L, nlevels, lua.to_luastring("too many catpures", true)); for (let i = 0; i < nlevels; i++) push_onecapture(ms, i, s, e); return nlevels; /* number of strings pushed */ @@ -1340,7 +1340,7 @@ const str_gsub = function(L) { let ms = new MatchState(L); let b = new lauxlib.luaL_Buffer(L); lauxlib.luaL_argcheck(L, tr === CT.LUA_TNUMBER || tr === CT.LUA_TSTRING || tr === CT.LUA_TFUNCTION || tr === CT.LUA_TTABLE, 3, - lua.to_luastring("string/function/table expected")); + lua.to_luastring("string/function/table expected", true)); lauxlib.luaL_buffinit(L, b); if (anchor) { p = p.slice(1); lp--; /* skip anchor character */ @@ -1392,7 +1392,7 @@ const createmetatable = function(L) { lapi.lua_setmetatable(L, -2); /* set table as metatable for strings */ lapi.lua_pop(L, 1); /* pop dummy string */ lapi.lua_pushvalue(L, -2); /* get string library */ - lapi.lua_setfield(L, -2, lua.to_luastring("__index")); /* metatable.__index = string */ + lapi.lua_setfield(L, -2, lua.to_luastring("__index", true)); /* metatable.__index = string */ lapi.lua_pop(L, 1); /* pop metatable */ }; diff --git a/src/ltable.js b/src/ltable.js index 039dd6d..6ba8236 100644 --- a/src/ltable.js +++ b/src/ltable.js @@ -13,7 +13,7 @@ const TValue = lobject.TValue; Table.prototype.ordered_intindexes = function() { return [...this.value.keys()] - .filter(e => typeof e === 'number' && e % 1 === 0) // Only integer indexes + .filter(e => typeof e === 'number' && e % 1 === 0 && e > 0) // Only integer indexes .sort(function (a, b) { return a > b ? 1 : -1; }); @@ -22,8 +22,8 @@ Table.prototype.ordered_intindexes = function() { Table.prototype.ordered_indexes = function() { return [...this.value.keys()] .sort(function(a, b) { - if (typeof a !== "number") return 1; - if (typeof b !== "number") return -1; + if (typeof a !== "number" || a <= 0) return 1; + if (typeof b !== "number" || b <= 0) return -1; return a > b ? 1 : -1; }); }; @@ -33,16 +33,18 @@ Table.prototype.ordered_indexes = function() { ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). */ Table.prototype.luaH_getn = function() { - // TODO: is this costly ? let indexes = this.ordered_intindexes(); let len = indexes.length; + // If first index != 1, length is 0 + if (indexes[0] !== 1) return 0; + for (let i = 0; i < len; i++) { let key = indexes[i]; - if (!this.__index(this, key).ttisnil() // t[i] is non-nil - && (i === len - 1 || this.__index(this, indexes[i + 1]).ttisnil())) { // t[i+1] is nil or is the last integer indexed element - return indexes[i] + 1; + if (!this.__index(this, key).ttisnil() // t[key] is non-nil + && (indexes[i + 1] - key > 1 || this.__index(this, indexes[i + 1]).ttisnil())) { // gap with next key or next value is nil + return indexes[i]; } } diff --git a/src/ltablib.js b/src/ltablib.js index eaaf24d..0794887 100644 --- a/src/ltablib.js +++ b/src/ltablib.js @@ -36,9 +36,9 @@ const checktab = function(L, arg, what) { if (lapi.lua_type(L, arg) !== CT.LUA_TTABLE) { /* is it not a table? */ let n = 1; if (lapi.lua_getmetatable(L, arg) && /* must have metatable */ - (!(what & TAB_R) || checkfield(L, lua.to_luastring("__index"), ++n)) && - (!(what & TAB_W) || checkfield(L, lua.to_luastring("__newindex"), ++n)) && - (!(what & TAB_L) || checkfield(L, lua.to_luastring("__len"), ++n))) { + (!(what & TAB_R) || checkfield(L, lua.to_luastring("__index", true), ++n)) && + (!(what & TAB_W) || checkfield(L, lua.to_luastring("__newindex", true), ++n)) && + (!(what & TAB_L) || checkfield(L, lua.to_luastring("__len", true), ++n))) { lapi.lua_pop(L, n); /* pop metatable and tested metamethods */ } else @@ -68,7 +68,7 @@ const tinsert = function(L) { break; case 3: { pos = lauxlib.luaL_checkinteger(L, 2); /* 2nd argument is the position */ - lauxlib.luaL_argcheck(L, 1 <= pos && pos <= e, 2, lua.to_luastring("position out of bounds")); + lauxlib.luaL_argcheck(L, 1 <= pos && pos <= e, 2, lua.to_luastring("position out of bounds", true)); for (let i = e; i > pos; i--) { /* move up elements */ lapi.lua_geti(L, 1, i - 1); lapi.lua_seti(L, 1, i); /* t[i] = t[i - 1] */ @@ -76,7 +76,7 @@ const tinsert = function(L) { break; } default: { - return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments to 'insert'")); + return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments to 'insert'", true)); } } @@ -88,7 +88,7 @@ const tremove = function(L) { let size = aux_getn(L, 1, TAB_RW); let pos = lauxlib.luaL_optinteger(L, 2, size); if (pos !== size) /* validate 'pos' if given */ - lauxlib.luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, lua.to_luastring("position out of bounds")); + lauxlib.luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, lua.to_luastring("position out of bounds", true)); lapi.lua_geti(L, 1, pos); /* result = t[pos] */ for (; pos < size; pos++) { lapi.lua_geti(L, 1, pos + 1); @@ -113,9 +113,9 @@ const tmove = function(L) { checktab(L, 1, TAB_R); checktab(L, tt, TAB_W); if (e >= f) { /* otherwise, nothing to move */ - lauxlib.luaL_argcheck(L, f > 0 || e < llimit.LUA_MAXINTEGER + f, 3, lua.to_luastring("too many elements to move")); + lauxlib.luaL_argcheck(L, f > 0 || e < llimit.LUA_MAXINTEGER + f, 3, lua.to_luastring("too many elements to move", true)); let n = e - f + 1; /* number of elements to move */ - lauxlib.luaL_argcheck(L, t <= llimit.LUA_MAXINTEGER - n + 1, 4, lua.to_luastring("destination wrap around")); + lauxlib.luaL_argcheck(L, t <= llimit.LUA_MAXINTEGER - n + 1, 4, lua.to_luastring("destination wrap around", true)); if (t > e || t <= f || (tt !== 1 && lapi.lua_compare(L, 1, tt, lua.LUA_OPEQ) !== 1)) { for (let i = 0; i < n; i++) { @@ -173,7 +173,7 @@ const unpack = function(L) { if (i > e) return 0; /* empty range */ let n = e - i; /* number of elements minus 1 (avoid overflows) */ if (n >= Number.MAX_SAFE_INTEGER || !lapi.lua_checkstack(L, ++n)) - return lauxlib.luaL_error(L, lua.to_luastring("too many results to unpack")); + return lauxlib.luaL_error(L, lua.to_luastring("too many results to unpack", true)); for (; i < e; i++) /* push arg[i..e - 1] (to avoid overflows) */ lapi.lua_geti(L, 1, i); lapi.lua_geti(L, 1, e); /* push last element */ @@ -192,7 +192,7 @@ const auxsort = function(L) { else if (typeof b[0] !== 'number') return -1; return lapi.lua_compare_(L, a[1], b[1], lua.LUA_OPLT) === 1 ? -1 : 1; /* a < b */ }) - .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i, e[1]) : true); + .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i + 1, e[1]) : true); } else { [...t.value.entries()] .sort(function (a, b) { @@ -207,14 +207,14 @@ const auxsort = function(L) { lapi.lua_pop(L, 1); /* pop result */ return res ? -1 : 1; }) - .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i, e[1]) : true); + .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i + 1, e[1]) : true); } }; const sort = function(L) { let n = aux_getn(L, 1, TAB_RW); if (n > 1) { /* non-trivial interval? */ - lauxlib.luaL_argcheck(L, n < Number.MAX_SAFE_INTEGER, 1, lua.to_luastring("array too big")); + lauxlib.luaL_argcheck(L, n < Number.MAX_SAFE_INTEGER, 1, lua.to_luastring("array too big", true)); if (!lapi.lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ lauxlib.luaL_checktype(L, 2, CT.LUA_TFUNCTION); /* must be a function */ lapi.lua_settop(L, 2); /* make sure there are two arguments */ @@ -15,30 +15,30 @@ const CT = lua.constant_types; const TMS = { - TM_INDEX: lua.to_luastring("__index"), - TM_NEWINDEX: lua.to_luastring("__newindex"), - TM_GC: lua.to_luastring("__gc"), - TM_MODE: lua.to_luastring("__mode"), - TM_LEN: lua.to_luastring("__len"), - TM_EQ: lua.to_luastring("__eq"), /* last tag method with fast access */ - TM_ADD: lua.to_luastring("__add"), - TM_SUB: lua.to_luastring("__sub"), - TM_MUL: lua.to_luastring("__mul"), - TM_MOD: lua.to_luastring("__mod"), - TM_POW: lua.to_luastring("__pow"), - TM_DIV: lua.to_luastring("__div"), - TM_IDIV: lua.to_luastring("__idiv"), - TM_BAND: lua.to_luastring("__band"), - TM_BOR: lua.to_luastring("__bor"), - TM_BXOR: lua.to_luastring("__bxor"), - TM_SHL: lua.to_luastring("__shl"), - TM_SHR: lua.to_luastring("__shr"), - TM_UNM: lua.to_luastring("__unm"), - TM_BNOT: lua.to_luastring("__bnot"), - TM_LT: lua.to_luastring("__lt"), - TM_LE: lua.to_luastring("__le"), - TM_CONCAT: lua.to_luastring("__concat"), - TM_CALL: lua.to_luastring("__call") + TM_INDEX: lua.to_luastring("__index", true), + TM_NEWINDEX: lua.to_luastring("__newindex", true), + TM_GC: lua.to_luastring("__gc", true), + TM_MODE: lua.to_luastring("__mode", true), + TM_LEN: lua.to_luastring("__len", true), + TM_EQ: lua.to_luastring("__eq", true), /* last tag method with fast access */ + TM_ADD: lua.to_luastring("__add", true), + TM_SUB: lua.to_luastring("__sub", true), + TM_MUL: lua.to_luastring("__mul", true), + TM_MOD: lua.to_luastring("__mod", true), + TM_POW: lua.to_luastring("__pow", true), + TM_DIV: lua.to_luastring("__div", true), + TM_IDIV: lua.to_luastring("__idiv", true), + TM_BAND: lua.to_luastring("__band", true), + TM_BOR: lua.to_luastring("__bor", true), + TM_BXOR: lua.to_luastring("__bxor", true), + TM_SHL: lua.to_luastring("__shl", true), + TM_SHR: lua.to_luastring("__shr", true), + TM_UNM: lua.to_luastring("__unm", true), + TM_BNOT: lua.to_luastring("__bnot", true), + TM_LT: lua.to_luastring("__lt", true), + TM_LE: lua.to_luastring("__le", true), + TM_CONCAT: lua.to_luastring("__concat", true), + TM_CALL: lua.to_luastring("__call", true) }; const luaT_typenames_ = [ @@ -71,7 +71,7 @@ const luaT_init = function(L) { */ const luaT_objtypename = function(L, o) { if ((o.ttistable() && o.metatable !== null) || (o.ttisfulluserdata() && o.metatable !== null)) { - let name = o.__index(o, lua.to_luastring('__name')); + let name = o.__index(o, lua.to_luastring('__name', true)); if (name.ttisstring()) return name.jsstring(); } @@ -123,10 +123,10 @@ const luaT_trybinTM = function(L, p1, p2, res, event) { if (n1 !== false && n2 !== false) ldebug.luaG_tointerror(L, p1, p2); else - ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform bitwise operation on")); + ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform bitwise operation on", true)); } default: - ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform arithmetic on")); + ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform arithmetic on", true)); } } }; @@ -140,9 +140,16 @@ class lua_Debug { } -const to_luastring = function(str, maxBytesToWrite) { +const to_luastring_cache = {}; + +const to_luastring = function(str, cache, maxBytesToWrite) { assert(typeof str === "string", "to_luastring expect a js string"); + if (cache) { + let cached = to_luastring_cache[str]; + if (Array.isArray(cached)) return cached; + } + maxBytesToWrite = maxBytesToWrite !== undefined ? maxBytesToWrite : Number.MAX_SAFE_INTEGER; let outU8Array = []; @@ -195,9 +202,29 @@ const to_luastring = function(str, maxBytesToWrite) { } // Null-terminate the pointer to the buffer. // outU8Array[outIdx] = 0; + + if (cache) to_luastring_cache[str] = outU8Array; return outU8Array; }; +/* +** Event codes +*/ +const LUA_HOOKCALL = 0; +const LUA_HOOKRET = 1; +const LUA_HOOKLINE = 2; +const LUA_HOOKCOUNT = 3; +const LUA_HOOKTAILCALL = 4; + + +/* +** Event masks +*/ +const LUA_MASKCALL = (1 << LUA_HOOKCALL); +const LUA_MASKRET = (1 << LUA_HOOKRET); +const LUA_MASKLINE = (1 << LUA_HOOKLINE); +const LUA_MASKCOUNT = (1 << LUA_HOOKCOUNT); + module.exports.CT = CT; module.exports.FENGARI_AUTHORS = FENGARI_AUTHORS; module.exports.FENGARI_COPYRIGHT = FENGARI_COPYRIGHT; @@ -209,8 +236,17 @@ module.exports.FENGARI_VERSION_NUM = FENGARI_VERSION_NUM; module.exports.FENGARI_VERSION_RELEASE = FENGARI_VERSION_RELEASE; module.exports.LUA_AUTHORS = LUA_AUTHORS; module.exports.LUA_COPYRIGHT = LUA_COPYRIGHT; +module.exports.LUA_HOOKCALL = LUA_HOOKCALL; +module.exports.LUA_HOOKCOUNT = LUA_HOOKCOUNT; +module.exports.LUA_HOOKLINE = LUA_HOOKLINE; +module.exports.LUA_HOOKRET = LUA_HOOKRET; +module.exports.LUA_HOOKTAILCALL = LUA_HOOKTAILCALL; module.exports.LUA_INITVARVERSION = LUA_INITVARVERSION; module.exports.LUA_INIT_VAR = LUA_INIT_VAR; +module.exports.LUA_MASKCALL = LUA_MASKCALL; +module.exports.LUA_MASKCOUNT = LUA_MASKCOUNT; +module.exports.LUA_MASKLINE = LUA_MASKLINE; +module.exports.LUA_MASKRET = LUA_MASKRET; module.exports.LUA_MINSTACK = LUA_MINSTACK; module.exports.LUA_MULTRET = -1; module.exports.LUA_NUMTAGS = LUA_NUMTAGS; diff --git a/src/lualib.js b/src/lualib.js index 9751302..327673e 100644 --- a/src/lualib.js +++ b/src/lualib.js @@ -20,7 +20,7 @@ module.exports.LUA_IOLIBNAME = LUA_IOLIBNAME; const LUA_OSLIBNAME = "os"; module.exports.LUA_OSLIBNAME = LUA_OSLIBNAME; -// module.exports[LUA_OSLIBNAME] = require("./loslib.js").luaopen_os; +module.exports[LUA_OSLIBNAME] = require("./loslib.js").luaopen_os; const LUA_STRLIBNAME = "string"; module.exports.LUA_STRLIBNAME = LUA_STRLIBNAME; diff --git a/src/lutf8lib.js b/src/lutf8lib.js index ef9739f..f58223a 100644 --- a/src/lutf8lib.js +++ b/src/lutf8lib.js @@ -87,7 +87,7 @@ const utflen = function(L) { const pushutfchar = function(L, arg) { let code = lauxlib.luaL_checkinteger(L, arg); - lauxlib.luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, lua.to_luastring("value out of range")); + lauxlib.luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, lua.to_luastring("value out of range", true)); lapi.lua_pushstring(L, lua.to_luastring(String.fromCharCode(code))); }; @@ -121,14 +121,14 @@ const byteoffset = function(L) { let posi = n >= 0 ? 1 : s.length + 1; posi = u_posrelat(lauxlib.luaL_optinteger(L, 3, posi), s.length); - lauxlib.luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, lua.to_luastring("position ot ouf range")); + lauxlib.luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, lua.to_luastring("position ot ouf range", true)); if (n === 0) { /* find beginning of current byte sequence */ while (posi > 0 && iscont(s[posi])) posi--; } else { if (iscont(s[posi])) - lauxlib.luaL_error(L, lua.to_luastring("initial position is a continuation byte")); + lauxlib.luaL_error(L, lua.to_luastring("initial position is a continuation byte", true)); if (n < 0) { while (n < 0 && posi > 0) { /* move back */ @@ -166,19 +166,19 @@ const codepoint = function(L) { let posi = u_posrelat(lauxlib.luaL_optinteger(L, 2, 1), s.length); let pose = u_posrelat(lauxlib.luaL_optinteger(L, 3, posi), s.length); - lauxlib.luaL_argcheck(L, posi >= 1, 2, lua.to_luastring("out of range")); - lauxlib.luaL_argcheck(L, pose <= s.length, 3, lua.to_luastring("out of range")); + lauxlib.luaL_argcheck(L, posi >= 1, 2, lua.to_luastring("out of range", true)); + lauxlib.luaL_argcheck(L, pose <= s.length, 3, lua.to_luastring("out of range", true)); if (posi > pose) return 0; /* empty interval; return no values */ if (pose - posi >= Number.MAX_SAFE_INTEGER) - return lauxlib.luaL_error(L, lua.to_luastring("string slice too long")); + return lauxlib.luaL_error(L, lua.to_luastring("string slice too long", true)); let n = (pose - posi) + 1; - lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long")); + lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long", true)); n = 0; for (s = s.slice(posi - 1); n < pose - posi;) { let dec = utf8_decode(s); if (dec === null) - return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code")); + return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code", true)); s = dec.string; let code = dec.code; lapi.lua_pushinteger(L, code); @@ -207,7 +207,7 @@ const iter_aux = function(L) { let code = dec ? dec.code : null; let next = dec ? dec.string : null; if (next === null || iscont(next[0])) - return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code")); + return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code", true)); lapi.lua_pushinteger(L, n + 1); lapi.lua_pushinteger(L, code); return 2; @@ -236,7 +236,7 @@ const UTF8PATT = "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"; const luaopen_utf8 = function(L) { lauxlib.luaL_newlib(L, funcs); lapi.lua_pushstring(L, lua.to_luastring(UTF8PATT)); - lapi.lua_setfield(L, -2, lua.to_luastring("charpattern")); + lapi.lua_setfield(L, -2, lua.to_luastring("charpattern", true)); return 1; }; @@ -123,8 +123,15 @@ const luaV_execute = function(L) { base = ci.u.l.base; i = ci.u.l.savedpc[ci.pcOff++]; + + if (L.hookmask & (lua.LUA_MASKLINE | lua.LUA_MASKCOUNT)) { + ldebug.luaG_traceexec(L); + base = ci.u.l.base; + } + + ra = RA(L, base, i); - opcode = i.opcode + opcode = i.opcode; } if (i.breakpoint) // TODO: remove, used until lapi @@ -228,7 +235,7 @@ const luaV_execute = function(L) { if (op1.ttisinteger() && op2.ttisinteger()) { L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value + op2.value)); } else if (numberop1 !== false && numberop2 !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value + op2.value); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, numberop1 + numberop2); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_ADD); base = ci.u.l.base; @@ -244,7 +251,7 @@ const luaV_execute = function(L) { if (op1.ttisinteger() && op2.ttisinteger()) { L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value - op2.value)); } else if (numberop1 !== false && numberop2 !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value - op2.value); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, numberop1 - numberop2); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SUB); base = ci.u.l.base; @@ -260,7 +267,7 @@ const luaV_execute = function(L) { if (op1.ttisinteger() && op2.ttisinteger()) { L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value * op2.value)); } else if (numberop1 !== false && numberop2 !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value * op2.value); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, numberop1 * numberop2); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MUL); base = ci.u.l.base; @@ -274,9 +281,9 @@ const luaV_execute = function(L) { let numberop2 = tonumber(op2); if (op1.ttisinteger() && op2.ttisinteger()) { - L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value % op2.value)); + L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value - Math.floor(op1.value / op2.value) * op2.value)); } else if (numberop1 !== false && numberop2 !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value % op2.value); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, (numberop1 - Math.floor(numberop1 / numberop2) * numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MOD); base = ci.u.l.base; @@ -290,7 +297,7 @@ const luaV_execute = function(L) { let numberop2 = tonumber(op2); if (numberop1 !== false && numberop2 !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.pow(op1.value, op2.value)); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.pow(numberop1, numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_POW); base = ci.u.l.base; @@ -320,7 +327,7 @@ const luaV_execute = function(L) { if (op1.ttisinteger() && op2.ttisinteger()) { L.stack[ra] = new TValue(CT.LUA_TNUMINT, Math.floor(op1.value / op2.value)); } else if (numberop1 !== false && numberop2 !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.floor(op1.value / op2.value)); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.floor(numberop1 / numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_IDIV); base = ci.u.l.base; @@ -330,11 +337,11 @@ const luaV_execute = function(L) { case OCi.OP_BAND: { let op1 = RKB(L, base, k, i); let op2 = RKC(L, base, k, i); - let numberop1 = tonumber(op1); - let numberop2 = tonumber(op2); + let numberop1 = tointeger(op1); + let numberop2 = tointeger(op2); - if (op1.ttisinteger() && op2.ttisinteger()) { - L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value & op2.value)); + if (numberop1 !== false && numberop2) { + L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 & numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BAND); base = ci.u.l.base; @@ -344,11 +351,11 @@ const luaV_execute = function(L) { case OCi.OP_BOR: { let op1 = RKB(L, base, k, i); let op2 = RKC(L, base, k, i); - let numberop1 = tonumber(op1); - let numberop2 = tonumber(op2); + let numberop1 = tointeger(op1); + let numberop2 = tointeger(op2); - if (op1.ttisinteger() && op2.ttisinteger()) { - L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value | op2.value)); + if (numberop1 !== false && numberop2) { + L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 | numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BOR); base = ci.u.l.base; @@ -358,11 +365,11 @@ const luaV_execute = function(L) { case OCi.OP_BXOR: { let op1 = RKB(L, base, k, i); let op2 = RKC(L, base, k, i); - let numberop1 = tonumber(op1); - let numberop2 = tonumber(op2); + let numberop1 = tointeger(op1); + let numberop2 = tointeger(op2); - if (op1.ttisinteger() && op2.ttisinteger()) { - L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value ^ op2.value)); + if (numberop1 !== false && numberop2) { + L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 ^ numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BXOR); base = ci.u.l.base; @@ -372,11 +379,11 @@ const luaV_execute = function(L) { case OCi.OP_SHL: { let op1 = RKB(L, base, k, i); let op2 = RKC(L, base, k, i); - let numberop1 = tonumber(op1); - let numberop2 = tonumber(op2); + let numberop1 = tointeger(op1); + let numberop2 = tointeger(op2); - if (op1.ttisinteger() && op2.ttisinteger()) { - L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value << op2.value)); + if (numberop1 !== false && numberop2) { + L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 << numberop2)); // TODO: luaV_shiftl ? } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHL); base = ci.u.l.base; @@ -386,11 +393,11 @@ const luaV_execute = function(L) { case OCi.OP_SHR: { let op1 = RKB(L, base, k, i); let op2 = RKC(L, base, k, i); - let numberop1 = tonumber(op1); - let numberop2 = tonumber(op2); + let numberop1 = tointeger(op1); + let numberop2 = tointeger(op2); - if (op1.ttisinteger() && op2.ttisinteger()) { - L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value >> op2.value)); + if (numberop1 !== false && numberop2) { + L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 >> numberop2)); } else { ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHR); base = ci.u.l.base; @@ -404,7 +411,7 @@ const luaV_execute = function(L) { if (op.ttisinteger()) { L.stack[ra] = new TValue(CT.LUA_TNUMINT, -op.value); } else if (numberop !== false) { - L.stack[ra] = new TValue(CT.LUA_TNUMFLT, -op.value); + L.stack[ra] = new TValue(CT.LUA_TNUMFLT, -numberop); } else { ltm.luaT_trybinTM(L, op, op, ra, ltm.TMS.TM_UNM); base = ci.u.l.base; @@ -595,19 +602,19 @@ const luaV_execute = function(L) { let nstep = tonumber(pstep); if (nlimit === false) - ldebug.luaG_runerror(L, lua.to_luastring("'for' limit must be a number")); + ldebug.luaG_runerror(L, lua.to_luastring("'for' limit must be a number", true)); plimit.type = CT.LUA_TNUMFLT; plimit.value = nlimit; if (nstep === false) - ldebug.luaG_runerror(L, lua.to_luastring("'for' step must be a number")); + ldebug.luaG_runerror(L, lua.to_luastring("'for' step must be a number", true)); pstep.type = CT.LUA_TNUMFLT; pstep.value = nstep; if (ninit === false) - ldebug.luaG_runerror(L, lua.to_luastring("'for' initial value must be a number")); + ldebug.luaG_runerror(L, lua.to_luastring("'for' initial value must be a number", true)); init.type = CT.LUA_TNUMFLT; init.value = ninit - nstep; @@ -768,9 +775,10 @@ const luaV_equalobj = function(L, t1, t2) { switch(t1.ttype()) { case CT.LUA_TNIL: return 1; + case CT.LUA_TBOOLEAN: + return t1.value == t2.value ? 1 : 0; // Might be 1 or true case CT.LUA_TNUMINT: case CT.LUA_TNUMFLT: - case CT.LUA_TBOOLEAN: case CT.LUA_TLCF: return t1.value === t2.value ? 1 : 0; case CT.LUA_TSHRSTR: @@ -861,7 +869,7 @@ const luaV_tointeger = function(obj, mode) { } else if (obj.ttisinteger()) { return obj.value; } else if (obj.ttisstring()) { - return luaV_tointeger(new TValue(CT.LUA_TNUMFLT, parseFloat(obj.jsstring())), mode); // TODO: luaO_str2num + return luaV_tointeger(lobject.luaO_str2num(obj.value), mode); } return false; @@ -876,7 +884,7 @@ const tonumber = function(v) { return v.value; if (v.ttnov() === CT.LUA_TSTRING) - return parseFloat(v.value); // TODO: luaO_str2num + return lobject.luaO_str2num(v.value); return false; }; @@ -980,7 +988,7 @@ const luaV_objlen = function(L, ra, rb) { default: { tm = ltm.luaT_gettmbyobj(L, rb, ltm.TMS.TM_LEN); if (tm.ttisnil()) - ldebug.luaG_typeerror(L, rb, lua.to_luastring("get length of")); + ldebug.luaG_typeerror(L, rb, lua.to_luastring("get length of", true)); break; } } @@ -1045,7 +1053,7 @@ const gettable = function(L, table, key, ra, recur) { recur = recur ? recur : 0; if (recur >= MAXTAGRECUR) - ldebug.luaG_runerror(L, lua.to_luastring("'__index' chain too long; possible loop")); + ldebug.luaG_runerror(L, lua.to_luastring("'__index' chain too long; possible loop", true)); if (table.ttistable()) { let element = table.__index(table, key); @@ -1066,7 +1074,7 @@ const luaV_finishget = function(L, t, key, val, slot, recur) { assert(!t.ttistable()); tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX); if (tm.ttisnil()) - ldebug.luaG_typeerror(L, t, lua.to_luastring('index')); + ldebug.luaG_typeerror(L, t, lua.to_luastring('index', true)); } else { /* 't' is a table */ assert(slot.ttisnil()); tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX); // TODO: fasttm @@ -1088,7 +1096,7 @@ const settable = function(L, table, key, v, recur) { recur = recur ? recur : 0; if (recur >= MAXTAGRECUR) - ldebug.luaG_runerror(L, lua.to_luastring("'__newindex' chain too long; possible loop")); + ldebug.luaG_runerror(L, lua.to_luastring("'__newindex' chain too long; possible loop", true)); if (table.ttistable()) { let element = table.__index(table, key); @@ -1115,7 +1123,7 @@ const luaV_finishset = function(L, t, key, val, slot, recur) { } else { /* not a table; check metamethod */ tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_NEWINDEX); if (tm.ttisnil()) - ldebug.luaG_typeerror(L, t, lua.to_luastring('index')); + ldebug.luaG_typeerror(L, t, lua.to_luastring('index', true)); } if (tm.ttisfunction()) { |