summaryrefslogtreecommitdiff
path: root/src/ldblib.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/ldblib.js')
-rw-r--r--src/ldblib.js251
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`);
}