From ea8b3d63af92085d1563c670968152c7dbbb7642 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 11 Apr 2017 12:25:30 +0200 Subject: debug.getlocal --- README.md | 5 +---- src/lapi.js | 10 +++++++++ src/ldblib.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ldebug.js | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- tests/ldblib.js | 48 ++++++++++++++++++++++++++++++++++++++++ 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" + ); + +}); -- cgit v1.2.3-54-g00ecf