|
|
@ -1,5 +1,5 @@ |
|
|
|
/*
|
|
|
|
** $Id: ldblib.c,v 1.137 2014/03/12 20:57:40 roberto Exp roberto $ |
|
|
|
** $Id: ldblib.c,v 1.138 2014/04/30 18:59:15 roberto Exp roberto $ |
|
|
|
** Interface from Lua to its debug API |
|
|
|
** See Copyright Notice in lua.h |
|
|
|
*/ |
|
|
@ -28,7 +28,7 @@ static int db_Csize (lua_State *L) { |
|
|
|
} sizes[] = { |
|
|
|
{'I', sizeof(lua_Integer)}, |
|
|
|
{'F', sizeof(lua_Number)}, |
|
|
|
{'b', CHAR_BIT}, |
|
|
|
{'b', CHAR_BIT}, /* here is number of bits (not bytes) */ |
|
|
|
{'h', sizeof(short)}, |
|
|
|
{'i', sizeof(int)}, |
|
|
|
{'l', sizeof(long)}, |
|
|
@ -91,24 +91,12 @@ static int db_setuservalue (lua_State *L) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void settabss (lua_State *L, const char *i, const char *v) { |
|
|
|
lua_pushstring(L, v); |
|
|
|
lua_setfield(L, -2, i); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void settabsi (lua_State *L, const char *i, int v) { |
|
|
|
lua_pushinteger(L, v); |
|
|
|
lua_setfield(L, -2, i); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void settabsb (lua_State *L, const char *i, int v) { |
|
|
|
lua_pushboolean(L, v); |
|
|
|
lua_setfield(L, -2, i); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Auxiliary function used by several library functions: check for |
|
|
|
** an optional thread as function's first argument and set 'arg' with |
|
|
|
** 1 if this argument is present (so that functions can skip it to |
|
|
|
** access their other arguments) |
|
|
|
*/ |
|
|
|
static lua_State *getthread (lua_State *L, int *arg) { |
|
|
|
if (lua_isthread(L, 1)) { |
|
|
|
*arg = 1; |
|
|
@ -116,44 +104,70 @@ static lua_State *getthread (lua_State *L, int *arg) { |
|
|
|
} |
|
|
|
else { |
|
|
|
*arg = 0; |
|
|
|
return L; |
|
|
|
return L; /* function will operate over current thread */ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Variations of 'lua_settable', used by 'db_getinfo' to put results |
|
|
|
** from 'lua_getinfo' into result table. Key is always a string; |
|
|
|
** value can be a string, an int, or a boolean. |
|
|
|
*/ |
|
|
|
static void settabss (lua_State *L, const char *k, const char *v) { |
|
|
|
lua_pushstring(L, v); |
|
|
|
lua_setfield(L, -2, k); |
|
|
|
} |
|
|
|
|
|
|
|
static void settabsi (lua_State *L, const char *k, int v) { |
|
|
|
lua_pushinteger(L, v); |
|
|
|
lua_setfield(L, -2, k); |
|
|
|
} |
|
|
|
|
|
|
|
static void settabsb (lua_State *L, const char *k, int v) { |
|
|
|
lua_pushboolean(L, v); |
|
|
|
lua_setfield(L, -2, k); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** In function 'db_getinfo', the call to 'lua_getinfo' may push |
|
|
|
** results on the stack; later it creates the result table to put |
|
|
|
** these objects. Function 'treatstackoption' puts the result from |
|
|
|
** 'lua_getinfo' on top of the result table so that it can call |
|
|
|
** 'lua_setfield'. |
|
|
|
*/ |
|
|
|
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { |
|
|
|
if (L == L1) { |
|
|
|
lua_pushvalue(L, -2); |
|
|
|
lua_remove(L, -3); |
|
|
|
} |
|
|
|
if (L == L1) |
|
|
|
lua_rotate(L, -2, 1); /* exchange object and table */ |
|
|
|
else |
|
|
|
lua_xmove(L1, L, 1); |
|
|
|
lua_setfield(L, -2, fname); |
|
|
|
lua_xmove(L1, L, 1); /* move object to the "main" stack */ |
|
|
|
lua_setfield(L, -2, fname); /* put object into table */ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Calls 'lua_getinfo' and collects all results in a new table. |
|
|
|
*/ |
|
|
|
static int db_getinfo (lua_State *L) { |
|
|
|
lua_Debug ar; |
|
|
|
int arg; |
|
|
|
lua_State *L1 = getthread(L, &arg); |
|
|
|
const char *options = luaL_optstring(L, arg+2, "flnStu"); |
|
|
|
if (lua_isnumber(L, arg+1)) { |
|
|
|
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { |
|
|
|
if (lua_isfunction(L, arg + 1)) { /* info about a function? */ |
|
|
|
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ |
|
|
|
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ |
|
|
|
lua_xmove(L, L1, 1); |
|
|
|
} |
|
|
|
else { /* stack level */ |
|
|
|
if (!lua_getstack(L1, luaL_checkint(L, arg + 1), &ar)) { |
|
|
|
lua_pushnil(L); /* level out of range */ |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (lua_isfunction(L, arg+1)) { |
|
|
|
lua_pushfstring(L, ">%s", options); |
|
|
|
options = lua_tostring(L, -1); |
|
|
|
lua_pushvalue(L, arg+1); |
|
|
|
lua_xmove(L, L1, 1); |
|
|
|
} |
|
|
|
else |
|
|
|
return luaL_argerror(L, arg+1, "function or level expected"); |
|
|
|
if (!lua_getinfo(L1, options, &ar)) |
|
|
|
return luaL_argerror(L, arg+2, "invalid option"); |
|
|
|
lua_createtable(L, 0, 2); |
|
|
|
lua_newtable(L); /* table to collect results */ |
|
|
|
if (strchr(options, 'S')) { |
|
|
|
settabss(L, "source", ar.source); |
|
|
|
settabss(L, "short_src", ar.short_src); |
|
|
@ -191,16 +205,16 @@ static int db_getlocal (lua_State *L) { |
|
|
|
if (lua_isfunction(L, arg + 1)) { /* function argument? */ |
|
|
|
lua_pushvalue(L, arg + 1); /* push function */ |
|
|
|
lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ |
|
|
|
return 1; |
|
|
|
return 1; /* return only name (there is no value) */ |
|
|
|
} |
|
|
|
else { /* stack-level argument */ |
|
|
|
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ |
|
|
|
return luaL_argerror(L, arg+1, "level out of range"); |
|
|
|
name = lua_getlocal(L1, &ar, nvar); |
|
|
|
if (name) { |
|
|
|
lua_xmove(L1, L, 1); /* push local value */ |
|
|
|
lua_xmove(L1, L, 1); /* move local value */ |
|
|
|
lua_pushstring(L, name); /* push name */ |
|
|
|
lua_pushvalue(L, -2); /* re-order */ |
|
|
|
lua_rotate(L, -2, 1); /* re-order */ |
|
|
|
return 2; |
|
|
|
} |
|
|
|
else { |
|
|
@ -225,14 +239,17 @@ static int db_setlocal (lua_State *L) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** get (if 'get' is true) or set an upvalue from a closure |
|
|
|
*/ |
|
|
|
static int auxupvalue (lua_State *L, int get) { |
|
|
|
const char *name; |
|
|
|
int n = luaL_checkint(L, 2); |
|
|
|
luaL_checktype(L, 1, LUA_TFUNCTION); |
|
|
|
int n = luaL_checkint(L, 2); /* upvalue index */ |
|
|
|
luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ |
|
|
|
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); |
|
|
|
if (name == NULL) return 0; |
|
|
|
lua_pushstring(L, name); |
|
|
|
lua_insert(L, -(get+1)); |
|
|
|
lua_insert(L, -(get+1)); /* no-op if get is false */ |
|
|
|
return get + 1; |
|
|
|
} |
|
|
|
|
|
|
@ -248,13 +265,15 @@ static int db_setupvalue (lua_State *L) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Check whether a given upvalue from a given closure exists and |
|
|
|
** returns its index |
|
|
|
*/ |
|
|
|
static int checkupval (lua_State *L, int argf, int argnup) { |
|
|
|
lua_Debug ar; |
|
|
|
int nup = luaL_checkint(L, argnup); |
|
|
|
luaL_checktype(L, argf, LUA_TFUNCTION); |
|
|
|
lua_pushvalue(L, argf); |
|
|
|
lua_getinfo(L, ">u", &ar); |
|
|
|
luaL_argcheck(L, 1 <= nup && nup <= ar.nups, argnup, "invalid upvalue index"); |
|
|
|
int nup = luaL_checkint(L, argnup); /* upvalue index */ |
|
|
|
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ |
|
|
|
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, |
|
|
|
"invalid upvalue index"); |
|
|
|
return nup; |
|
|
|
} |
|
|
|
|
|
|
@ -276,25 +295,36 @@ static int db_upvaluejoin (lua_State *L) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** The hook table (at registry[HOOKKEY]) maps threads to their current |
|
|
|
** hook function |
|
|
|
*/ |
|
|
|
#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY) |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Call hook function registered at hook table for the current |
|
|
|
** thread (if there is one) |
|
|
|
*/ |
|
|
|
static void hookf (lua_State *L, lua_Debug *ar) { |
|
|
|
static const char *const hooknames[] = |
|
|
|
{"call", "return", "line", "count", "tail call"}; |
|
|
|
gethooktable(L); |
|
|
|
lua_pushthread(L); |
|
|
|
if (lua_rawget(L, -2) == LUA_TFUNCTION) { |
|
|
|
lua_pushstring(L, hooknames[(int)ar->event]); |
|
|
|
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ |
|
|
|
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ |
|
|
|
if (ar->currentline >= 0) |
|
|
|
lua_pushinteger(L, ar->currentline); |
|
|
|
lua_pushinteger(L, ar->currentline); /* push current line */ |
|
|
|
else lua_pushnil(L); |
|
|
|
lua_assert(lua_getinfo(L, "lS", ar)); |
|
|
|
lua_call(L, 2, 0); |
|
|
|
lua_call(L, 2, 0); /* call hook function */ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Convert a string mask (for 'sethook') into a bit mask |
|
|
|
*/ |
|
|
|
static int makemask (const char *smask, int count) { |
|
|
|
int mask = 0; |
|
|
|
if (strchr(smask, 'c')) mask |= LUA_MASKCALL; |
|
|
@ -305,6 +335,9 @@ static int makemask (const char *smask, int count) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Convert a bit mask (for 'gethook') into a string mask |
|
|
|
*/ |
|
|
|
static char *unmakemask (int mask, char *smask) { |
|
|
|
int i = 0; |
|
|
|
if (mask & LUA_MASKCALL) smask[i++] = 'c'; |
|
|
@ -319,7 +352,7 @@ static int db_sethook (lua_State *L) { |
|
|
|
int arg, mask, count; |
|
|
|
lua_Hook func; |
|
|
|
lua_State *L1 = getthread(L, &arg); |
|
|
|
if (lua_isnoneornil(L, arg+1)) { |
|
|
|
if (lua_isnoneornil(L, arg+1)) { /* no hook? */ |
|
|
|
lua_settop(L, arg+1); |
|
|
|
func = NULL; mask = 0; count = 0; /* turn off hooks */ |
|
|
|
} |
|
|
@ -335,9 +368,9 @@ static int db_sethook (lua_State *L) { |
|
|
|
lua_pushvalue(L, -1); |
|
|
|
lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */ |
|
|
|
} |
|
|
|
lua_pushthread(L1); lua_xmove(L1, L, 1); |
|
|
|
lua_pushvalue(L, arg+1); |
|
|
|
lua_rawset(L, -3); /* set new hook */ |
|
|
|
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key */ |
|
|
|
lua_pushvalue(L, arg+1); /* value */ |
|
|
|
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ |
|
|
|
lua_sethook(L1, func, mask, count); /* set hooks */ |
|
|
|
return 0; |
|
|
|
} |
|
|
@ -354,11 +387,11 @@ static int db_gethook (lua_State *L) { |
|
|
|
else { |
|
|
|
gethooktable(L); |
|
|
|
lua_pushthread(L1); lua_xmove(L1, L, 1); |
|
|
|
lua_rawget(L, -2); /* get hook */ |
|
|
|
lua_rawget(L, -2); /* 1st result = hooktable[L1] */ |
|
|
|
lua_remove(L, -2); /* remove hook table */ |
|
|
|
} |
|
|
|
lua_pushstring(L, unmakemask(mask, buff)); |
|
|
|
lua_pushinteger(L, lua_gethookcount(L1)); |
|
|
|
lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ |
|
|
|
lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ |
|
|
|
return 3; |
|
|
|
} |
|
|
|
|
|
|
|