summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--src/lapi.js10
-rw-r--r--src/ldblib.js60
-rw-r--r--src/ldebug.js68
-rw-r--r--tests/ldblib.js48
5 files changed, 182 insertions, 9 deletions
diff --git a/README.md b/README.md
index 38f0abe..23e5f15 100644
--- a/README.md
+++ b/README.md
@@ -31,15 +31,12 @@
- [ ] lua_gethookmask
- [ ] lua_geti
- [ ] lua_getinfo
- - [ ] lua_getlocal
- [ ] lua_getstack
- [ ] lua_getupvalue
- [ ] lua_getuservalue
- [ ] lua_isboolean
- [ ] lua_iscfunction
- - [ ] lua_isfunction
- [ ] lua_islightuserdata
- - [ ] lua_isthread
- [ ] lua_isuserdata
- [ ] lua_pcallk
- [ ] lua_pushfstring
@@ -98,4 +95,4 @@
- [Source code for Lua 5.3](lua.org/source/5.3/)
- [Lua 5.2 Bytecode and Virtual Machine](http://files.catwell.info/misc/mirror/lua-5.2-bytecode-vm-dirk-laurie/lua52vm.html)
- [Lua 5.3 Bytecode Reference](http://the-ravi-programming-language.readthedocs.io/en/latest/lua_bytecode_reference.html)
-- [A No-Frills Introduction to Lua 5.1 VM Instructions](http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf) \ No newline at end of file
+- [A No-Frills Introduction to Lua 5.1 VM Instructions](http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf)
diff --git a/src/lapi.js b/src/lapi.js
index 3fbcee9..00db461 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -736,6 +736,14 @@ const lua_isuserdata = function(L, idx) {
return o.ttisfulluserdata(o) || o.ttislightuserdata();
};
+const lua_isthread = function(L, idx) {
+ return lua_type(L, idx) === CT.LUA_TTHREAD;
+};
+
+const lua_isfunction = function(L, idx) {
+ return lua_type(L, idx) === CT.LUA_TFUNCTION;
+};
+
const lua_rawequal = function(L, index1, index2) {
let o1 = index2addr(L, index1);
let o2 = index2addr(L, index2);
@@ -927,6 +935,7 @@ module.exports.lua_getmetatable = lua_getmetatable;
module.exports.lua_gettable = lua_gettable;
module.exports.lua_gettop = lua_gettop;
module.exports.lua_insert = lua_insert;
+module.exports.lua_isfunction = lua_isfunction;
module.exports.lua_isinteger = lua_isinteger;
module.exports.lua_isnil = lua_isnil;
module.exports.lua_isnone = lua_isnone;
@@ -934,6 +943,7 @@ module.exports.lua_isnoneornil = lua_isnoneornil;
module.exports.lua_isnumber = lua_isnumber;
module.exports.lua_isstring = lua_isstring;
module.exports.lua_istable = lua_istable;
+module.exports.lua_isthread = lua_isthread;
module.exports.lua_isuserdata = lua_isuserdata;
module.exports.lua_len = lua_len;
module.exports.lua_load = lua_load;
diff --git a/src/ldblib.js b/src/ldblib.js
index 39b9574..efab872 100644
--- a/src/ldblib.js
+++ b/src/ldblib.js
@@ -5,9 +5,69 @@ const assert = require('assert');
const lua = require('./lua.js');
const lapi = require('./lapi.js');
const lauxlib = require('./lauxlib.js');
+const ldebug = require('./ldebug.js');
+/*
+** If L1 != L, L1 can be in any state, and therefore there are no
+** guarantees about its stack space; any push in L1 must be
+** checked.
+*/
+const checkstack = function(L, L1, n) {
+ if (L !== L1 && !lapi.lua_checkstack(L1, n))
+ lauxlib.luaL_error(L, "stack overflow");
+};
+
+/*
+** 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)
+*/
+const getthread = function(L) {
+ if (lapi.lua_isthread(L, 1)) {
+ return {
+ arg: 1,
+ thread: lapi.lua_tothread(L, 1)
+ };
+ } else {
+ return {
+ arg: 0,
+ thread: L
+ }; /* function will operate over current thread */
+ }
+};
+
+const db_getlocal = function(L) {
+ let thread = getthread(L);
+ let L1 = thread.thread;
+ let arg = thread.arg;
+ let ar = new lua.lua_Debug();
+ let nvar = lauxlib.luaL_checkinteger(L, arg + 2); /* local-variable index */
+ if (lapi.lua_isfunction(L, arg + 1)) {
+ lapi.lua_pushvalue(L, arg + 1); /* push function */
+ lapi.lua_pushstring(L, ldebug.lua_getlocal(L, null, nvar)); /* push local name */
+ return 1; /* return only name (there is no value) */
+ } 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"));
+ checkstack(L, L1, 1);
+ let name = ldebug.lua_getlocal(L1, ar, nvar);
+ if (name) {
+ lapi.lua_xmove(L1, L, 1); /* move local value */
+ lapi.lua_pushstring(L, name.value); /* push name */
+ lapi.lua_rotate(L, -2, 1); /* re-order */
+ return 2;
+ }
+ else {
+ lapi.lua_pushnil(L); /* no name (nor value) */
+ return 1;
+ }
+ }
+};
const dblib = {
+ "getlocal": db_getlocal
};
// Only with Node
diff --git a/src/ldebug.js b/src/ldebug.js
index 3d66d85..5a5fc34 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -59,6 +59,63 @@ const upvalname = function(p, uv) {
return s;
};
+const findvararg = function(ci, n, pos) {
+ let nparams = ci.func.p.numparams;
+ if (n >= ci.u.l.base - ci.funcOff - nparams)
+ return null; /* no such vararg */
+ else {
+ return {
+ pos: ci.funcOff + nparams + n,
+ name: lua.to_luastring("(*vararg)") /* generic name for any vararg */
+ };
+ }
+};
+
+const findlocal = function(L, ci, n) {
+ let base, name = null;
+
+ if (ci.callstatus & lstate.CIST_LUA) {
+ if (n < 0) /* access to vararg values? */
+ return findvararg(ci, -n);
+ else {
+ base = ci.u.l.base;
+ name = lfunc.luaF_getlocalname(ci.func.p, n, ci.pcOff);
+ }
+ } else
+ base = ci.funcOff + 1;
+
+ if (name === null) { /* no 'standard' name? */
+ let limit = ci === L.ci ? L.top : ci.next.func;
+ if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
+ name = lua.to_luastring("(*temporary)"); /* generic name for any valid slot */
+ else
+ return null; /* no name */
+ }
+ return {
+ pos: base + (n - 1),
+ name: name
+ };
+};
+
+const lua_getlocal = function(L, ar, n) {
+ let name;
+ swapextra(L);
+ if (ar === null) { /* information about non-active function? */
+ if (!L.stack[L.top - 1].ttisLclosure()) /* not a Lua function? */
+ name = null;
+ else /* consider live variables at function start (parameters) */
+ name = lfunc.luaF_getlocalname(L.stack[L.top - 1].value.p, n, 0);
+ } else { /* active function; get information through 'ar' */
+ let local = findlocal(L, ar.i_ci, n);
+ name = local.name;
+ let pos = L.stack[local.pos];
+ if (name)
+ L.stack[L.top++] = new TValue(pos.type, pos.value);
+ }
+ swapextra(L);
+ return name;
+};
+
const funcinfo = function(ar, cl) {
if (cl === null || cl.type === CT.LUA_TCCL) {
ar.source = lua.to_luastring("=[JS]");
@@ -503,13 +560,14 @@ const luaG_tointerror = function(L, p1, p2) {
luaG_runerror(L, lua.to_luastring(`number${lobject.jsstring(varinfo(L, p2))} has no integer representation`));
};
-module.exports.lua_getstack = lua_getstack;
-module.exports.lua_getinfo = lua_getinfo;
-module.exports.luaG_errormsg = luaG_errormsg;
module.exports.luaG_addinfo = luaG_addinfo;
-module.exports.luaG_runerror = luaG_runerror;
-module.exports.luaG_typeerror = luaG_typeerror;
module.exports.luaG_concaterror = luaG_concaterror;
+module.exports.luaG_errormsg = luaG_errormsg;
module.exports.luaG_opinterror = luaG_opinterror;
module.exports.luaG_ordererror = luaG_ordererror;
+module.exports.luaG_runerror = luaG_runerror;
module.exports.luaG_tointerror = luaG_tointerror;
+module.exports.luaG_typeerror = luaG_typeerror;
+module.exports.lua_getinfo = lua_getinfo;
+module.exports.lua_getlocal = lua_getlocal;
+module.exports.lua_getstack = lua_getstack;
diff --git a/tests/ldblib.js b/tests/ldblib.js
index 5e5c298..579912d 100644
--- a/tests/ldblib.js
+++ b/tests/ldblib.js
@@ -1,7 +1,55 @@
"use strict";
+const test = require('tape');
+
const lapi = require("../src/lapi.js");
const lauxlib = require("../src/lauxlib.js");
const lua = require('../src/lua.js');
const linit = require('../src/linit.js');
+test('debug.getlocal', function (t) {
+ let luaCode = `
+ local alocal = "alocal"
+ local another = "another"
+
+ local result = ""
+
+ local l = function()
+ local infunction = "infunction"
+ local anotherin = "anotherin"
+ result = table.concat(table.pack(debug.getlocal(2, 1)), " ")
+ .. table.concat(table.pack(debug.getlocal(2, 2)), " ")
+ .. table.concat(table.pack(debug.getlocal(1, 1)), " ")
+ .. table.concat(table.pack(debug.getlocal(1, 2)), " ")
+ end
+
+ l()
+
+ return result
+ `, L;
+
+ t.plan(3);
+
+ t.doesNotThrow(function () {
+
+ L = lauxlib.luaL_newstate();
+
+ linit.luaL_openlibs(L);
+
+ lauxlib.luaL_loadstring(L, lua.to_luastring(luaCode));
+
+ }, "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, -1),
+ "alocal alocalanother anotherinfunction infunctionanotherin anotherin",
+ "Correct element(s) on the stack"
+ );
+
+});