diff options
-rw-r--r-- | src/lapi.js | 4 | ||||
-rw-r--r-- | src/ldblib.js | 91 | ||||
-rw-r--r-- | src/ldebug.js | 8 | ||||
-rw-r--r-- | tests/ldblib.js | 84 |
4 files changed, 181 insertions, 6 deletions
diff --git a/src/lapi.js b/src/lapi.js index fcb07d6..894b5bb 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -230,7 +230,7 @@ const lua_pushlstring = function(L, s, len) { }; const lua_pushstring = function (L, s) { - assert(Array.isArray(s), "lua_pushstring expects array of byte"); + assert(Array.isArray(s) || s === undefined || s === null, "lua_pushstring expects array of byte"); if (s === undefined || s === null) L.stack[L.top] = new TValue(CT.LUA_TNIL, null); @@ -245,7 +245,7 @@ const lua_pushstring = function (L, s) { }; const lua_pushliteral = function (L, s) { - assert(typeof s === "string", "lua_pushliteral expects a JS string"); + assert(typeof s === "string" || s === undefined || s === null, "lua_pushliteral expects a JS string"); if (s === undefined || s === null) L.stack[L.top] = new TValue(CT.LUA_TNIL, null); diff --git a/src/ldblib.js b/src/ldblib.js index 318e616..ea080c0 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -3,6 +3,7 @@ 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'); @@ -37,6 +38,95 @@ const getthread = function(L) { } }; +/* +** 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. +*/ +const settabss = function(L, k, v) { + lapi.lua_pushstring(L, v); + lapi.lua_setfield(L, -2, k); +}; + +const settabsi = function(L, k, v) { + lapi.lua_pushinteger(L, v); + lapi.lua_setfield(L, -2, k); +}; + +const settabsb = function(L, k, v) { + lapi.lua_pushboolean(L, v); + lapi.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'. +*/ +const treatstackoption = function(L, L1, fname) { + if (L == L1) + lapi.lua_rotate(L, -2, 1); /* exchange object and table */ + else + lapi.lua_xmove(L1, L, 1); /* move object to the "main" stack */ + lapi.lua_setfield(L, -2, fname); /* put object into table */ +}; + +/* +** Calls 'lua_getinfo' and collects all results in a new table. +** L1 needs stack space for an optional input (function) plus +** two optional outputs (function and line table) from function +** 'lua_getinfo'. +*/ +const db_getinfo = function(L) { + let ar = new lua.lua_Debug(); + let thread = getthread(L); + let arg = thread.arg; + let L1 = thread.thread; + let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu")); + checkstack(L, L1, 3); + if (lapi.lua_isfunction(L, arg + 1)) { /* info about a function? */ + options = [char['>']].concat(options); /* add '>' to 'options' */ + lapi.lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ + lapi.lua_xmove(L, L1, 1); + } else { /* stack level */ + if (!ldebug.lua_getstack(L1, lauxlib.luaL_checkinteger(L, arg + 1), ar)) { + lapi.lua_pushnil(L); /* level out of range */ + return 1; + } + } + + if (!ldebug.lua_getinfo(L1, options, ar)) + lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option")); + 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); + } + if (options.indexOf(char['l']) > -1) + settabsi(L, lua.to_luastring("currentline"), 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); + 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); + } + if (options.indexOf(char['t']) > - 1) + settabsb(L, lua.to_luastring("istailcall"), ar.istailcall); + if (options.indexOf(char['L']) > - 1) + treatstackoption(L, L1, lua.to_luastring("activelines")); + if (options.indexOf(char['f']) > - 1) + treatstackoption(L, L1, lua.to_luastring("func")); + return 1; /* return table */ +}; + const db_getlocal = function(L) { let thread = getthread(L); let L1 = thread.thread; @@ -99,6 +189,7 @@ const db_traceback = function(L) { }; const dblib = { + "getinfo": db_getinfo, "getlocal": db_getlocal, "traceback": db_traceback, "upvalueid": db_upvalueid diff --git a/src/ldebug.js b/src/ldebug.js index ce62abc..713a0fb 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -179,13 +179,13 @@ const auxgetinfo = function(L, what, ar, f, ci) { break; } case 'u': { - ar.nups = f === null ? 0 : f.c.nupvalues; - if (f === null || f.c.type === CT.LUA_TCCL) { + ar.nups = f === null ? 0 : f.nupvalues; + if (f === null || f.type === CT.LUA_TCCL) { ar.isvararg = true; ar.nparams = 0; } else { - ar.isvararg = f.l.p.is_vararg; - ar.nparams = f.l.p.numparams; + ar.isvararg = f.p.is_vararg; + ar.nparams = f.p.numparams; } break; } diff --git a/tests/ldblib.js b/tests/ldblib.js index c95ab9e..a770c76 100644 --- a/tests/ldblib.js +++ b/tests/ldblib.js @@ -206,3 +206,87 @@ test('debug.traceback (with a upvalue)', function (t) { ); }); + +test('debug.getinfo', function (t) { + let luaCode = ` + local alocal = function(p1, p2) end + global = function() return alocal end + + local d1 = debug.getinfo(alocal) + local d2 = debug.getinfo(global) + + print(d1.short_src, d1.nups, d1.what, d1.nparams, + d2.short_src, d2.nups, d2.what, d2.nparams) + + return d1.short_src, d1.nups, d1.what, d1.nparams, + d2.short_src, d2.nups, d2.what, d2.nparams + `, L; + + t.plan(10); + + t.doesNotThrow(function () { + + L = lauxlib.luaL_newstate(); + + linit.luaL_openlibs(L); + + luaCode = lua.to_luastring(luaCode); + lauxlib.luaL_loadbuffer(L, luaCode, luaCode.length, lua.to_luastring("getinfo-test")); + + }, "Lua program loaded without error"); + + t.doesNotThrow(function () { + + lapi.lua_call(L, 0, -1); + + }, "Lua program ran without error"); + + t.strictEqual( + lapi.lua_tojsstring(L, -8), + `[string "getinfo-test"]`, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -7), + 0, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tojsstring(L, -6), + `Lua`, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -5), + 2, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tojsstring(L, -4), + `[string "getinfo-test"]`, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -3), + 1, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tojsstring(L, -2), + `Lua`, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -1), + 0, + "Correct element(s) on the stack" + ); + +}); |