aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lapi.js4
-rw-r--r--src/ldblib.js91
-rw-r--r--src/ldebug.js8
-rw-r--r--tests/ldblib.js84
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"
+ );
+
+});