diff options
Diffstat (limited to 'src/ldblib.js')
-rw-r--r-- | src/ldblib.js | 251 |
1 files changed, 227 insertions, 24 deletions
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`); } |