From d251efeb37778e2ab57a3d2c80b9df621a691796 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Thu, 13 Apr 2017 07:17:05 +0200 Subject: Table indexes are also starting at 1 internally Since we use a Map we don't have to care about indexes starting at 0 --- tests/lbaselib.js | 1 - tests/lexparse.js | 4 ++-- tests/ltablib.js | 2 +- tests/lvm.js | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/lbaselib.js b/tests/lbaselib.js index dd91449..5f39991 100644 --- a/tests/lbaselib.js +++ b/tests/lbaselib.js @@ -15,7 +15,6 @@ const lua = require('../src/lua.js'); const linit = require('../src/linit.js'); const CT = lua.constant_types; - test('print', function (t) { let luaCode = ` print("hello", "world", 123) diff --git a/tests/lexparse.js b/tests/lexparse.js index 92901b1..96c8fca 100644 --- a/tests/lexparse.js +++ b/tests/lexparse.js @@ -742,7 +742,7 @@ test('SETTABLE, GETTABLE', function (t) { }, "Lua program ran without error"); t.strictEqual( - lapi.lua_topointer(L, -1).get(0).jsstring(), + lapi.lua_topointer(L, -1).get(1).jsstring(), "hello", "Program output is correct" ); @@ -835,7 +835,7 @@ test('SETTABUP, GETTABUP', function (t) { }, "Lua program ran without error"); t.strictEqual( - lapi.lua_topointer(L, -1).get(0).jsstring(), + lapi.lua_topointer(L, -1).get(1).jsstring(), "hello", "Program output is correct" ); diff --git a/tests/ltablib.js b/tests/ltablib.js index 81ffcbd..60f9abc 100644 --- a/tests/ltablib.js +++ b/tests/ltablib.js @@ -21,7 +21,7 @@ const inttable2array = function(t) { t.forEach(function (v, k) { if (typeof k === 'number') - a[k] = v; + a[k - 1] = v; }); return a.map(e => e.value); diff --git a/tests/lvm.js b/tests/lvm.js index bda4c4f..0f91d81 100644 --- a/tests/lvm.js +++ b/tests/lvm.js @@ -496,7 +496,7 @@ test('SETTABLE, GETTABLE', function (t) { console.log(L.stack[L.top - 1]); t.deepEqual( - L.stack[L.top - 1].value.get(0).jsstring(), + L.stack[L.top - 1].value.get(1).jsstring(), "hello", "Program output is correct" ); @@ -559,7 +559,7 @@ test('SETTABUP, GETTABUP', function (t) { }, "Program executed without errors"); t.deepEqual( - L.stack[L.top - 1].value.get(0).jsstring(), + L.stack[L.top - 1].value.get(1).jsstring(), "hello", // "hello" "Program output is correct" ); -- cgit v1.2.3-54-g00ecf From b711f32eee2e0c696cb0e621f54e38c398c7090a Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 14 Apr 2017 07:50:36 +0200 Subject: debug.getuservalue, debug.setuservalue, debug.setlocal --- README.md | 9 +++----- src/lapi.js | 21 ++++++++++++++++++ src/ldblib.js | 40 ++++++++++++++++++++++++++++++++++ src/ldebug.js | 15 +++++++++++++ tests/ldblib.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/README.md b/README.md index 46a31ae..47c80bf 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ - [ ] lua_getinfo - [ ] lua_getstack - [ ] lua_getupvalue - - [ ] lua_getuservalue - [ ] lua_isboolean - [ ] lua_iscfunction - [ ] lua_islightuserdata @@ -48,8 +47,6 @@ - [ ] lua_register - [ ] lua_setallocf - [ ] lua_sethook - - [ ] lua_setlocal - - [ ] lua_setuservalue - [ ] lua_tocfunction - [ ] lua_upvaluejoin - [ ] Auxiliary library @@ -91,14 +88,14 @@ - [x] debug.getmetatable - [x] debug.getregistry - [x] debug.getupvalue + - [x] debug.getuservalue + - [x] debug.setlocal - [x] debug.setmetatable - [x] debug.setupvalue + - [x] debug.setuservalue - [x] debug.traceback - [ ] debug.gethook - - [ ] debug.getuservalue - [ ] debug.sethook - - [ ] debug.setlocal - - [ ] debug.setuservalue - [ ] debug.upvalueid - [ ] debug.upvaluejoin - [ ] Run [Lua test suite](https://github.com/lua/tests) diff --git a/src/lapi.js b/src/lapi.js index 048a6b7..b41cfbe 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -537,6 +537,15 @@ const lua_getmetatable = function(L, objindex) { return res; }; +const lua_getuservalue = function(L, idx) { + let o = index2addr(L, idx); + assert(L, o.ttisfulluserdata(), "full userdata expected"); + L.stack[L.top].type = o.type; + L.stack[L.top++].value = o.value; + assert(L.top <= L.ci.top, "stack overflow"); + return L.stack[L.top - 1].ttnov(); +}; + const lua_gettable = function(L, idx) { let t = index2addr(L, idx); lvm.gettable(L, t, L.stack[L.top - 1], L.top - 1); @@ -793,6 +802,16 @@ const lua_status = function(L) { return L.status; }; +const lua_setuservalue = function(L, idx) { + assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack"); + let o = index2addr(L, idx); + assert(L, o.ttisfulluserdata(), "full userdata expected"); + L.stack[L.top - 1].type = o.type; + L.stack[L.top - 1].value = o.value; + L.top--; +}; + + const lua_callk = function(L, nargs, nresults, ctx, k) { assert(k === null || !(L.ci.callstatus & lstate.CIST_LUA), "cannot use continuations inside hooks"); assert(nargs + 1 < L.top - L.ci.funcOff, "not enough elements in the stack"); @@ -972,6 +991,7 @@ module.exports.lua_getmetatable = lua_getmetatable; module.exports.lua_gettable = lua_gettable; module.exports.lua_gettop = lua_gettop; module.exports.lua_getupvalue = lua_getupvalue; +module.exports.lua_getuservalue = lua_getuservalue; module.exports.lua_insert = lua_insert; module.exports.lua_isfunction = lua_isfunction; module.exports.lua_isinteger = lua_isinteger; @@ -1023,6 +1043,7 @@ module.exports.lua_setmetatable = lua_setmetatable; module.exports.lua_settable = lua_settable; module.exports.lua_settop = lua_settop; module.exports.lua_setupvalue = lua_setupvalue; +module.exports.lua_setuservalue = lua_setuservalue; module.exports.lua_status = lua_status; module.exports.lua_stringtonumber = lua_stringtonumber; module.exports.lua_toboolean = lua_toboolean; diff --git a/src/ldblib.js b/src/ldblib.js index d3e3c7c..9fc6ce6 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -39,6 +39,23 @@ const db_setmetatable = function(L) { return 1; /* return 1st argument */ }; +const db_getuservalue = function(L) { + if (lapi.lua_type(L, 1) !== lua.CT.LUA_TUSERDATA) + lapi.lua_pushnil(L); + else + lapi.lua_getuservalue(L, 1); + return 1; +}; + + +const db_setuservalue = function(L) { + lauxlib.luaL_checktype(L, 1, lua.CT.LUA_TUSERDATA); + lauxlib.luaL_checkany(L, 2); + lapi.lua_settop(L, 2); + lapi.lua_setuservalue(L, 1); + return 1; +}; + /* ** Auxiliary function used by several library functions: check for ** an optional thread as function's first argument and set 'arg' with @@ -177,6 +194,26 @@ const db_getlocal = function(L) { } }; +const db_setlocal = function(L) { + let thread = getthread(L); + let L1 = thread.thread; + let arg = thread.arg; + let ar = new lua.lua_Debug(); + let level = lauxlib.luaL_checkinteger(L, arg + 1); + let nvar = lauxlib.luaL_checkinteger(L, arg + 2); + if (!ldebug.lua_getstack(L1, level, ar)) /* out of range? */ + return lauxlib.luaL_argerror(L, arg + 1, "level out of range"); + lauxlib.luaL_checkany(L, arg + 3); + lapi.lua_settop(L, arg + 3); + checkstack(L, L1, 1); + lapi.lua_xmove(L, L1, 1); + let name = ldebug.lua_setlocal(L1, ar, nvar); + if (name === null) + lapi.lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lapi.lua_pushstring(L, name.value); + return 1; +}; + /* ** get (if 'get' is true) or set an upvalue from a closure */ @@ -238,8 +275,11 @@ const dblib = { "getmetatable": db_getmetatable, "getregistry": db_getregistry, "getupvalue": db_getupvalue, + "getuservalue": db_getuservalue, + "setlocal": db_setlocal, "setmetatable": db_setmetatable, "setupvalue": db_setupvalue, + "setuservalue": db_setuservalue, "traceback": db_traceback, "upvalueid": db_upvalueid }; diff --git a/src/ldebug.js b/src/ldebug.js index 713a0fb..44410a6 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -116,6 +116,20 @@ const lua_getlocal = function(L, ar, n) { return name; }; +const lua_setlocal = function(L, ar, n) { + swapextra(L); + let local = findlocal(L, ar.i_ci, n); + let name = local.name; + let pos = local.pos; + if (name) { + L.stack[pos].type = L.stack[L.top - 1].type; + L.stack[pos].value = L.stack[L.top - 1].value; + L.top--; /* pop value */ + } + swapextra(L); + return name; +}; + const funcinfo = function(ar, cl) { if (cl === null || cl.type === CT.LUA_TCCL) { ar.source = lua.to_luastring("=[JS]"); @@ -574,3 +588,4 @@ module.exports.luaG_typeerror = luaG_typeerror; module.exports.lua_getinfo = lua_getinfo; module.exports.lua_getlocal = lua_getlocal; module.exports.lua_getstack = lua_getstack; +module.exports.lua_setlocal = lua_setlocal; diff --git a/tests/ldblib.js b/tests/ldblib.js index a770c76..30a3eb6 100644 --- a/tests/ldblib.js +++ b/tests/ldblib.js @@ -54,6 +54,72 @@ test('debug.getlocal', function (t) { }); +test('debug.setlocal', function (t) { + let luaCode = ` + local alocal = "alocal" + local another = "another" + + local l = function() + local infunction = "infunction" + local anotherin = "anotherin" + + debug.setlocal(2, 1, 1) + debug.setlocal(2, 2, 2) + debug.setlocal(1, 1, 3) + debug.setlocal(1, 2, 4) + + return infunction, anotherin + end + + local a, b = l() + + return alocal, another, a, b + `, L; + + t.plan(6); + + 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_tointeger(L, -4), + 1, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -3), + 2, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -2), + 3, + "Correct element(s) on the stack" + ); + + t.strictEqual( + lapi.lua_tointeger(L, -1), + 4, + "Correct element(s) on the stack" + ); + +}); + test('debug.upvalueid', function (t) { let luaCode = ` local upvalue = "upvalue" -- cgit v1.2.3-54-g00ecf From fd613ef1da5e3eeb10d13351ccf217e33b30b1dd Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 14 Apr 2017 08:18:27 +0200 Subject: debug.upvaluejoin --- README.md | 6 ++---- src/lapi.js | 21 ++++++++++++++++++++- src/ldblib.js | 13 +++++++++++-- tests/ldblib.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/README.md b/README.md index 47c80bf..e32f8f5 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ - [ ] lua_getstack - [ ] lua_getupvalue - [ ] lua_isboolean - - [ ] lua_iscfunction - [ ] lua_islightuserdata - [ ] lua_isuserdata - [ ] lua_pcallk @@ -48,7 +47,6 @@ - [ ] lua_setallocf - [ ] lua_sethook - [ ] lua_tocfunction - - [ ] lua_upvaluejoin - [ ] Auxiliary library - [x] ... - [ ] luaL_addsize @@ -94,10 +92,10 @@ - [x] debug.setupvalue - [x] debug.setuservalue - [x] debug.traceback + - [x] debug.upvalueid + - [x] debug.upvaluejoin - [ ] debug.gethook - [ ] debug.sethook - - [ ] debug.upvalueid - - [ ] debug.upvaluejoin - [ ] Run [Lua test suite](https://github.com/lua/tests) - [ ] DOM API binding diff --git a/src/lapi.js b/src/lapi.js index b41cfbe..caafc0a 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -719,6 +719,11 @@ const lua_typename = function(L, t) { return ltm.ttypename(t); }; +const lua_iscfunction = function(L, idx) { + let o = index2addr(L, idx); + return o.ttislcf(o) || o.ttisCclosure(); +}; + const lua_isnil = function(L, n) { return lua_type(L, n) === CT.LUA_TNIL; }; @@ -933,7 +938,8 @@ const getupvalref = function(L, fidx, n, pf) { assert(1 <= n && n <= f.p.upvalues.length, "invalid upvalue index"); return { closure: f, - upval: f.upvals[n - 1] + upval: f.upvals[n - 1], + upvalOff: n - 1 }; }; @@ -955,6 +961,17 @@ const lua_upvalueid = function(L, fidx, n) { } }; +const lua_upvaluejoin = function(L, fidx1, n1, fidx2, n2) { + let ref1 = getupvalref(L, fidx1, n1); + let ref2 = getupvalref(L, fidx2, n2); + let up1 = ref1.upvalOff; + let up2 = ref2.upval; + let f1 = ref1.closure; + + f1.upvals[up1] = up2; + up2.u.open.touched = true; // TODO: useful +}; + // This functions are only there for compatibility purposes const lua_gc = function () {}; @@ -993,6 +1010,7 @@ module.exports.lua_gettop = lua_gettop; module.exports.lua_getupvalue = lua_getupvalue; module.exports.lua_getuservalue = lua_getuservalue; module.exports.lua_insert = lua_insert; +module.exports.lua_iscfunction = lua_iscfunction; module.exports.lua_isfunction = lua_isfunction; module.exports.lua_isinteger = lua_isinteger; module.exports.lua_isnil = lua_isnil; @@ -1061,5 +1079,6 @@ module.exports.lua_touserdata = lua_touserdata; module.exports.lua_type = lua_type; module.exports.lua_typename = lua_typename; module.exports.lua_upvalueid = lua_upvalueid; +module.exports.lua_upvaluejoin = lua_upvaluejoin; module.exports.lua_version = lua_version; module.exports.lua_xmove = lua_xmove; diff --git a/src/ldblib.js b/src/ldblib.js index 9fc6ce6..27aca96 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -248,13 +248,21 @@ const checkupval = function(L, argf, argnup) { return nup; }; - const db_upvalueid = function(L) { let n = checkupval(L, 1, 2); lapi.lua_pushlightuserdata(L, lapi.lua_upvalueid(L, 1, n)); return 1; }; +const db_upvaluejoin = function(L) { + let n1 = checkupval(L, 1, 2); + let n2 = checkupval(L, 3, 4); + lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 1), 1, lua.to_luastring("Lua function expected")); + lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 3), 3, lua.to_luastring("Lua function expected")); + lapi.lua_upvaluejoin(L, 1, n1, 3, n2); + return 0; +}; + const db_traceback = function(L) { let thread = getthread(L); let L1 = thread.thread; @@ -281,7 +289,8 @@ const dblib = { "setupvalue": db_setupvalue, "setuservalue": db_setuservalue, "traceback": db_traceback, - "upvalueid": db_upvalueid + "upvalueid": db_upvalueid, + "upvaluejoin": db_upvaluejoin }; // Only with Node diff --git a/tests/ldblib.js b/tests/ldblib.js index 30a3eb6..7dd35c3 100644 --- a/tests/ldblib.js +++ b/tests/ldblib.js @@ -157,6 +157,51 @@ test('debug.upvalueid', function (t) { }); +test('debug.upvaluejoin', function (t) { + let luaCode = ` + local upvalue1 = "upvalue1" + local upvalue2 = "upvalue2" + + local l1 = function() + return upvalue1 + end + + local l2 = function() + return upvalue2 + end + + debug.upvaluejoin(l1, 1, l2, 1) + + return l1() + `, 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), + "upvalue2", + "Correct element(s) on the stack" + ); + +}); + + test('debug.traceback (with a global)', function (t) { let luaCode = ` local trace -- cgit v1.2.3-54-g00ecf From 43c97cbc2904d2bac87c61515bbc16c38a091548 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 14 Apr 2017 08:59:00 +0200 Subject: hooks --- README.md | 5 +--- src/lapi.js | 22 +++++++++++++++- src/ldblib.js | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ldebug.js | 49 ++++++++++++++++++++++++++++++++++ src/ldo.js | 38 +++++++++++++++++++++++++- src/lua.js | 27 +++++++++++++++++++ src/lvm.js | 9 ++++++- tests/ldblib.js | 44 +++++++++++++++++++++++++++++++ 8 files changed, 269 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/README.md b/README.md index e32f8f5..01fcbde 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,9 @@ - [ ] lua_pushfstring - [ ] lua_pushlightuserdata - [ ] lua_pushvfstring - - [ ] lua_rawgetp - [ ] lua_rawseti - - [ ] lua_rawsetp - [ ] lua_register - [ ] lua_setallocf - - [ ] lua_sethook - [ ] lua_tocfunction - [ ] Auxiliary library - [x] ... @@ -87,6 +84,7 @@ - [x] debug.getregistry - [x] debug.getupvalue - [x] debug.getuservalue + - [x] debug.sethook - [x] debug.setlocal - [x] debug.setmetatable - [x] debug.setupvalue @@ -95,7 +93,6 @@ - [x] debug.upvalueid - [x] debug.upvaluejoin - [ ] debug.gethook - - [ ] debug.sethook - [ ] Run [Lua test suite](https://github.com/lua/tests) - [ ] DOM API binding diff --git a/src/lapi.js b/src/lapi.js index caafc0a..bf9b381 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -403,6 +403,15 @@ const lua_rawset = function(L, idx) { L.top -= 2; }; +const lua_rawsetp = function(L, idx, p) { + assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack"); + let o = index2addr(L, idx); + assert(L, o.ttistable(), "table expected"); + let k = p; + o.__newindex(o, k, L.stack[L.top - 1]); + L.top--; +}; + /* ** get functions (Lua -> stack) */ @@ -436,6 +445,15 @@ const lua_rawgeti = function(L, idx, n) { return L.stack[L.top - 1].ttnov(); }; +const lua_rawgetp = function(L, idx, p) { + let t = index2addr(L, idx); + assert(t.ttistable(), "table expected"); + let k = p; + L.stack[L.top++] = t.__index(t, k); + assert(L.top <= L.ci.top, "stack overflow"); + return L.stack[L.top - 1].ttnov(); +}; + const lua_rawget = function(L, idx) { let t = index2addr(L, idx); @@ -875,7 +893,7 @@ const lua_pcallk = function(L, nargs, nresults, errfunc, ctx, k) { ci.extra = c.funcOff; ci.u.c.old_errfunc = L.errfunc; L.errfunc = func; - // TODO: setoah(ci->callstatus, L->allowhook); + ci.callstatus &= ~lstate.CIST_OAH | L.allowhook; ci.callstatus |= lstate.CIST_YPCALL; /* function can do error recovery */ ldo.luaD_call(L, c.funcOff, nresults); /* do the call */ ci.callstatus &= ~lstate.CIST_YPCALL; @@ -1049,8 +1067,10 @@ module.exports.lua_pushvalue = lua_pushvalue; module.exports.lua_rawequal = lua_rawequal; module.exports.lua_rawget = lua_rawget; module.exports.lua_rawgeti = lua_rawgeti; +module.exports.lua_rawgetp = lua_rawgetp; module.exports.lua_rawlen = lua_rawlen; module.exports.lua_rawset = lua_rawset; +module.exports.lua_rawsetp = lua_rawsetp; module.exports.lua_remove = lua_remove; module.exports.lua_replace = lua_replace; module.exports.lua_rotate = lua_rotate; diff --git a/src/ldblib.js b/src/ldblib.js index 27aca96..81f27fb 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -263,6 +263,87 @@ const db_upvaluejoin = function(L) { return 0; }; +/* +** The hook table at registry[HOOKKEY] maps threads to their current +** hook function. (We only need the unique address of 'HOOKKEY'.) +*/ +const HOOKKEY = lua.to_luastring("__hooks__"); + +const hooknames = ["call", "return", "line", "count", "tail call"].map(e => lua.to_luastring(e)); + +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ +const hookf = function(L, ar) { + lapi.lua_rawgetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY); + lapi.lua_pushthread(L); + if (lapi.lua_rawget(L, -2) === lua.CT.LUA_TFUNCTION) { /* is there a hook function? */ + lapi.lua_pushstring(L, hooknames[ar.event]); /* push event name */ + if (ar.currentline >= 0) + lapi.lua_pushinteger(L, ar.currentline); /* push current line */ + else lapi.lua_pushnil(L); + assert(ldebug.lua_getinfo(L, [char["l"], char["S"]], ar)); + lapi.lua_call(L, 2, 0); /* call hook function */ + } +}; + +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ +const makemask = function(smask, count) { + let mask = 0; + if (smask.indexOf(char["c"]) > -1) mask |= lua.LUA_MASKCALL; + if (smask.indexOf(char["r"]) > -1) mask |= lua.LUA_MASKRET; + if (smask.indexOf(char["l"]) > -1) mask |= lua.LUA_MASKLINE; + if (count > 0) mask |= lua.LUA_MASKCOUNT; + return mask; +}; + +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ +const unmakemask = function(mask, smask) { + let i = 0; + if (mask & lua.LUA_MASKCALL) smask[i++] = char["c"]; + if (mask & lua.LUA_MASKRET) smask[i++] = char["r"]; + if (mask & lua.LUA_MASKLINE) smask[i++] = char["l"]; + smask[i] = 0; + return smask; +}; + +const db_sethook = function(L) { + let mask, count, func; + let thread = getthread(L); + let L1 = thread.thread; + let arg = thread.arg; + if (lapi.lua_isnoneornil(L, arg+1)) { /* no hook? */ + lapi.lua_settop(L, arg+1); + func = null; mask = 0; count = 0; /* turn off hooks */ + } + else { + const smask = lauxlib.luaL_checkstring(L, arg + 2); + lauxlib.luaL_checktype(L, arg+1, lua.CT.LUA_TFUNCTION); + count = lauxlib.luaL_optinteger(L, arg + 3, 0); + func = hookf; mask = makemask(smask, count); + } + if (lapi.lua_rawgetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY) === lua.CT.LUA_TNIL) { + lapi.lua_createtable(L, 0, 2); /* create a hook table */ + lapi.lua_pushvalue(L, -1); + lapi.lua_rawsetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY); /* set it in position */ + lapi.lua_pushstring(L, [char["k"]]); + lapi.lua_setfield(L, -2, lua.to_luastring("__mode")); /** hooktable.__mode = "k" */ + lapi.lua_pushvalue(L, -1); + lapi.lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */ + } + checkstack(L, L1, 1); + lapi.lua_pushthread(L1); lapi.lua_xmove(L1, L, 1); /* key (thread) */ + lapi.lua_pushvalue(L, arg + 1); /* value (hook function) */ + lapi.lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + ldebug.lua_sethook(L1, func, mask, count); + return 0; +}; + const db_traceback = function(L) { let thread = getthread(L); let L1 = thread.thread; @@ -284,6 +365,7 @@ const dblib = { "getregistry": db_getregistry, "getupvalue": db_getupvalue, "getuservalue": db_getuservalue, + "sethook": db_sethook, "setlocal": db_setlocal, "setmetatable": db_setmetatable, "setupvalue": db_setupvalue, diff --git a/src/ldebug.js b/src/ldebug.js index 44410a6..a73b7f7 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -38,6 +38,19 @@ const swapextra = function(L) { } }; +const lua_sethook = function(L, func, mask, count) { + if (func === null || mask === 0) { /* turn off hooks? */ + mask = 0; + func = null; + } + if (L.ci.callstatus & lstate.CIST_LUA) + L.oldpc = L.ci.pcOff; + L.hook = func; + L.basehookcount = count; + L.hookcount = L.basehookcount; + L.hookmask = mask; +}; + const lua_getstack = function(L, level, ar) { let ci; let status; @@ -577,6 +590,40 @@ const luaG_tointerror = function(L, p1, p2) { luaG_runerror(L, lua.to_luastring(`number${lobject.jsstring(varinfo(L, p2))} has no integer representation`)); }; +const luaG_traceexec = function(L) { + let ci = L.ci; + let mask = L.hookmask; + let counthook = (--L.hookcount === 0 && (mask & lua.LUA_MASKCOUNT)); + if (counthook) + L.hookcount = L.basehookcount; /* reset count */ + else if (!(mask & lua.LUA_MASKLINE)) + return; /* no line hook and count != 0; nothing to be done */ + if (ci.callstatus & lstate.CIST_HOOKYIELD) { /* called hook last time? */ + ci.callstatus &= ~lstate.CIST_HOOKYIELD; /* erase mark */ + return; /* do not call hook again (VM yielded, so it did not move) */ + } + if (counthook) + ldo.luaD_hook(L, lua.LUA_HOOKCOUNT, -1); /* call count hook */ + if (mask & lua.LUA_MASKLINE) { + let p = ci.func.p; + let npc = ci.pcOff; // pcRel(ci.u.l.savedpc, p); + let newline = p.lineinfo ? p.lineinfo[npc] : -1; + if (npc === 0 || /* call linehook when enter a new function, */ + ci.pcOff <= L.oldpc || /* when jump back (loop), or when */ + newline !== p.lineinfo ? p.lineinfo[L.oldpc] : -1) /* enter a new line */ + ldo.luaD_hook(L, lua.LUA_HOOKLINE, newline); /* call line hook */ + } + L.oldpc = ci.pcOff; + if (L.status === TS.LUA_YIELD) { /* did hook yield? */ + if (counthook) + L.hookcount = 1; /* undo decrement to zero */ + ci.u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci.callstatus |= lstate.CIST_HOOKYIELD; /* mark that it yielded */ + ci.func = L.top - 1; /* protect stack below results */ + ldo.luaD_throw(L, TS.LUA_YIELD); + } +}; + module.exports.luaG_addinfo = luaG_addinfo; module.exports.luaG_concaterror = luaG_concaterror; module.exports.luaG_errormsg = luaG_errormsg; @@ -584,8 +631,10 @@ 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_traceexec = luaG_traceexec; module.exports.luaG_typeerror = luaG_typeerror; module.exports.lua_getinfo = lua_getinfo; module.exports.lua_getlocal = lua_getlocal; module.exports.lua_getstack = lua_getstack; +module.exports.lua_sethook = lua_sethook; module.exports.lua_setlocal = lua_setlocal; diff --git a/src/ldo.js b/src/ldo.js index 6a74cad..0fcbf0e 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -73,7 +73,8 @@ const luaD_precall = function(L, off, nresults) { ci.funcOff = off; ci.top = L.top + lua.LUA_MINSTACK; ci.callstatus = 0; - // TODO: hook + if (L.hookmask & lua.LUA_MASKCALL) + luaD_hook(L, lua.LUA_HOOKCALL, -1); let n = f(L); /* do the actual call */ assert(n < L.top - L.ci.funcOff, "not enough elements in the stack"); @@ -129,6 +130,13 @@ const luaD_precall = function(L, off, nresults) { const luaD_poscall = function(L, ci, firstResult, nres) { let wanted = ci.nresults; + + if (L.hookmask & (lua.LUA_MASKRET | lua.LUA_MASKLINE)) { + if (L.hookmask & lua.LUA_MASKRET) + luaD_hook(L, lua.LUA_HOOKRET, -1); + L.oldpc = ci.previous.pcOff; /* 'oldpc' for caller function */ + } + let res = ci.funcOff; L.ci = ci.previous; L.ciOff--; @@ -171,6 +179,33 @@ const moveresults = function(L, firstResult, res, nres, wanted) { return true; }; +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which triggers this +** function, can be changed asynchronously by signals.) +*/ +const luaD_hook = function(L, event, line) { + let hook = L.hook; + if (hook && L.allowhook) { /* make sure there is a hook */ + let ci = L.ci; + let top = L.top; + let ci_top = ci.top; + let ar = new lua.lua_Debug(); + ar.event = event; + ar.currentline = line; + ar.i_ci = ci; + ci.top = L.top + lua.LUA_MINSTACK; + L.allowhook = 0; /* cannot call hooks inside a hook */ + ci.callstatus |= lstate.CIST_HOOKED; + hook(L, ar); + assert(!L.allowhook); + L.allowhook = 1; + ci.top = ci_top; + L.top = top; + ci.callstatus &= ~lstate.CIST_HOOKED; + } +}; + const adjust_varargs = function(L, p, actual) { let nfixargs = p.numparams; /* move fixed parameters to final position */ @@ -566,6 +601,7 @@ module.exports.SParser = SParser; module.exports.adjust_varargs = adjust_varargs; module.exports.luaD_call = luaD_call; module.exports.luaD_callnoyield = luaD_callnoyield; +module.exports.luaD_hook = luaD_hook; module.exports.luaD_pcall = luaD_pcall; module.exports.luaD_poscall = luaD_poscall; module.exports.luaD_precall = luaD_precall; diff --git a/src/lua.js b/src/lua.js index d4f620b..1423750 100644 --- a/src/lua.js +++ b/src/lua.js @@ -198,6 +198,24 @@ const to_luastring = function(str, maxBytesToWrite) { return outU8Array; }; +/* +** Event codes +*/ +const LUA_HOOKCALL = 0; +const LUA_HOOKRET = 1; +const LUA_HOOKLINE = 2; +const LUA_HOOKCOUNT = 3; +const LUA_HOOKTAILCALL = 4; + + +/* +** Event masks +*/ +const LUA_MASKCALL = (1 << LUA_HOOKCALL); +const LUA_MASKRET = (1 << LUA_HOOKRET); +const LUA_MASKLINE = (1 << LUA_HOOKLINE); +const LUA_MASKCOUNT = (1 << LUA_HOOKCOUNT); + module.exports.CT = CT; module.exports.FENGARI_AUTHORS = FENGARI_AUTHORS; module.exports.FENGARI_COPYRIGHT = FENGARI_COPYRIGHT; @@ -209,8 +227,17 @@ module.exports.FENGARI_VERSION_NUM = FENGARI_VERSION_NUM; module.exports.FENGARI_VERSION_RELEASE = FENGARI_VERSION_RELEASE; module.exports.LUA_AUTHORS = LUA_AUTHORS; module.exports.LUA_COPYRIGHT = LUA_COPYRIGHT; +module.exports.LUA_HOOKCALL = LUA_HOOKCALL; +module.exports.LUA_HOOKCOUNT = LUA_HOOKCOUNT; +module.exports.LUA_HOOKLINE = LUA_HOOKLINE; +module.exports.LUA_HOOKRET = LUA_HOOKRET; +module.exports.LUA_HOOKTAILCALL = LUA_HOOKTAILCALL; module.exports.LUA_INITVARVERSION = LUA_INITVARVERSION; module.exports.LUA_INIT_VAR = LUA_INIT_VAR; +module.exports.LUA_MASKCALL = LUA_MASKCALL; +module.exports.LUA_MASKCOUNT = LUA_MASKCOUNT; +module.exports.LUA_MASKLINE = LUA_MASKLINE; +module.exports.LUA_MASKRET = LUA_MASKRET; module.exports.LUA_MINSTACK = LUA_MINSTACK; module.exports.LUA_MULTRET = -1; module.exports.LUA_NUMTAGS = LUA_NUMTAGS; diff --git a/src/lvm.js b/src/lvm.js index ad1e807..6febe4b 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -123,8 +123,15 @@ const luaV_execute = function(L) { base = ci.u.l.base; i = ci.u.l.savedpc[ci.pcOff++]; + + if (L.hookmask & (lua.LUA_MASKLINE | lua.LUA_MASKCOUNT)) { + ldebug.luaG_traceexec(L); + base = ci.u.l.base; + } + + ra = RA(L, base, i); - opcode = i.opcode + opcode = i.opcode; } if (i.breakpoint) // TODO: remove, used until lapi diff --git a/tests/ldblib.js b/tests/ldblib.js index 7dd35c3..8d00952 100644 --- a/tests/ldblib.js +++ b/tests/ldblib.js @@ -7,6 +7,50 @@ const lauxlib = require("../src/lauxlib.js"); const lua = require('../src/lua.js'); const linit = require('../src/linit.js'); +test('debug.sethook', function (t) { + let luaCode = ` + result = "" + + debug.sethook(function (event) + result = result .. event .. " " + end, "crl", 1) + + local l = function() end + + l() + l() + 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), + "return count line count line count line count return count line count line count return count line count line count return count line count line ", + "Correct element(s) on the stack" + ); + +}); + + test('debug.getlocal', function (t) { let luaCode = ` local alocal = "alocal" -- cgit v1.2.3-54-g00ecf From d8b080f555dbb9e90dd6d4908b6263910f80528f Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 14 Apr 2017 11:26:24 +0200 Subject: Fixed bad UpVal.setVal We were making the upval point to the slot of the stack from which the value to set was --- src/lfunc.js | 3 +-- tests/ldblib.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/src/lfunc.js b/src/lfunc.js index 032f653..bc88702 100644 --- a/src/lfunc.js +++ b/src/lfunc.js @@ -45,7 +45,6 @@ class UpVal { setval(L, ra) { if (this.v !== null) { this.L.stack[this.v] = L.stack[ra]; - this.v = ra; } else this.u.value = L.stack[ra]; } @@ -138,4 +137,4 @@ module.exports.findupval = findupval; module.exports.luaF_close = luaF_close; module.exports.luaF_getlocalname = luaF_getlocalname; module.exports.luaF_initupvals = luaF_initupvals; -module.exports.luaF_newLclosure = luaF_newLclosure; \ No newline at end of file +module.exports.luaF_newLclosure = luaF_newLclosure; diff --git a/tests/ldblib.js b/tests/ldblib.js index 8d00952..8c3ad7f 100644 --- a/tests/ldblib.js +++ b/tests/ldblib.js @@ -9,7 +9,7 @@ const linit = require('../src/linit.js'); test('debug.sethook', function (t) { let luaCode = ` - result = "" + local result = "" debug.sethook(function (event) result = result .. event .. " " @@ -44,7 +44,7 @@ test('debug.sethook', function (t) { t.strictEqual( lapi.lua_tojsstring(L, -1), - "return count line count line count line count return count line count line count return count line count line count return count line count line ", + "return count line count line count line count return count line count line count return count line count line count return count line return ", "Correct element(s) on the stack" ); -- cgit v1.2.3-54-g00ecf From 3d667b50989e31d18af0c235e28b68055adf62ff Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 14 Apr 2017 11:48:23 +0200 Subject: debug.gethook --- README.md | 91 +++++++++++++++++++++++++++------------------------------ src/ldblib.js | 25 +++++++++++++++- src/ldebug.js | 17 +++++++++++ tests/ldblib.js | 56 +++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 49 deletions(-) (limited to 'tests') diff --git a/README.md b/README.md index 01fcbde..73708f7 100644 --- a/README.md +++ b/README.md @@ -5,40 +5,33 @@

# fengari -🐺 φεγγάρι - A Lua VM written in JS ES6 targeting the browser +🐺 φεγγάρι - The Lua VM written in JS ES6 targeting the browser ## So far - [x] Lexing/Parsing - [x] Parse bytecode - [x] Opcodes -- [ ] Basic types representation: - - [x] nil - - [x] boolean - - [x] table - - [ ] weak table - - [x] function - - [x] string (8-bit clean) - - [x] number (32-bit) - - [x] userdata +- [x] Basic types representation: - [x] Tag Methods +- [ ] Standard library + - [x] Base lib + - [x] Coroutine + - [x] Debug + - [x] Math + - [x] String + - [x] Table + - [x] utf8 + - [ ] Package + - [ ] os + - [ ] io - [ ] C API - [x] ... - [ ] lua_arith - [ ] lua_close - - [ ] lua_gethook - - [ ] lua_gethookcount - - [ ] lua_gethookmask - - [ ] lua_geti - - [ ] lua_getinfo - - [ ] lua_getstack - - [ ] lua_getupvalue - [ ] lua_isboolean - [ ] lua_islightuserdata - - [ ] lua_isuserdata - - [ ] lua_pcallk - [ ] lua_pushfstring - - [ ] lua_pushlightuserdata - [ ] lua_pushvfstring - [ ] lua_rawseti - [ ] lua_register @@ -59,41 +52,43 @@ - [ ] luaL_newlibtable - [ ] luaL_newmetatable - [ ] luaL_optnumber - - [ ] luaL_optstring - [ ] luaL_prepbuffer - [ ] luaL_pushresultsize - [ ] luaL_ref - [ ] luaL_setmetatable - [ ] luaL_testudata - [ ] luaL_unref -- [ ] Standard library - - [x] Base lib - - [x] Coroutine - - [x] Table - - [x] Math - - [x] utf8 - - [x] String - - [ ] Package - - [ ] os - - [ ] io - - [ ] Debug - - [x] debug.debug - - [x] debug.getinfo - - [x] debug.getlocal - - [x] debug.getmetatable - - [x] debug.getregistry - - [x] debug.getupvalue - - [x] debug.getuservalue - - [x] debug.sethook - - [x] debug.setlocal - - [x] debug.setmetatable - - [x] debug.setupvalue - - [x] debug.setuservalue - - [x] debug.traceback - - [x] debug.upvalueid - - [x] debug.upvaluejoin - - [ ] debug.gethook - [ ] Run [Lua test suite](https://github.com/lua/tests) + - [x] strings.lua + - [ ] all.lua + - [ ] big.lua + - [ ] checktable.lua + - [ ] constructs.lua + - [ ] errors.lua + - [ ] gc.lua + - [ ] literals.lua + - [ ] math.lua + - [ ] sort.lua + - [ ] utf8.lua + - [ ] api.lua + - [ ] bitwise.lua + - [ ] closure.lua + - [ ] coroutine.lua + - [ ] events.lua + - [ ] goto.lua + - [ ] locals.lua + - [ ] nextvar.lua + - [ ] vararg.lua + - [ ] attrib.lua + - [ ] calls.lua + - [ ] code.lua + - [ ] db.lua + - [ ] files.lua + - [ ] heavy.lua + - [ ] main.lua + - [ ] pm.lua + - [ ] tpack.lua + - [ ] verybig.lua - [ ] DOM API binding ## References diff --git a/src/ldblib.js b/src/ldblib.js index 81f27fb..5c78724 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -308,7 +308,6 @@ const unmakemask = function(mask, smask) { if (mask & lua.LUA_MASKCALL) smask[i++] = char["c"]; if (mask & lua.LUA_MASKRET) smask[i++] = char["r"]; if (mask & lua.LUA_MASKLINE) smask[i++] = char["l"]; - smask[i] = 0; return smask; }; @@ -344,6 +343,29 @@ const db_sethook = function(L) { return 0; }; +const db_gethook = function(L) { + let thread = getthread(L); + let L1 = thread.thread; + let arg = thread.arg; + let buff = []; + let mask = ldebug.lua_gethookmask(L1); + let hook = ldebug.lua_gethook(L1); + if (hook === null) /* no hook? */ + lapi.lua_pushnil(L); + else if (hook !== hookf) /* external hook? */ + lapi.lua_pushliteral(L, "external hook"); + else { /* hook table must exist */ + lapi.lua_rawgetp(L, lua.LUA_REGISTRYINDEX, HOOKKEY); + checkstack(L, L1, 1); + lapi.lua_pushthread(L1); lapi.lua_xmove(L1, L, 1); + lapi.lua_rawget(L, -2); /* 1st result = hooktable[L1] */ + lapi.lua_remove(L, -2); /* remove hook table */ + } + lapi.lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lapi.lua_pushinteger(L, ldebug.lua_gethookcount(L1)); /* 3rd result = count */ + return 3; +}; + const db_traceback = function(L) { let thread = getthread(L); let L1 = thread.thread; @@ -359,6 +381,7 @@ const db_traceback = function(L) { }; const dblib = { + "gethook": db_gethook, "getinfo": db_getinfo, "getlocal": db_getlocal, "getmetatable": db_getmetatable, diff --git a/src/ldebug.js b/src/ldebug.js index a73b7f7..0a0e22c 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -51,6 +51,20 @@ const lua_sethook = function(L, func, mask, count) { L.hookmask = mask; }; +const lua_gethook = function(L) { + return L.hook; +}; + + +const lua_gethookmask = function(L) { + return L.hookmask; +}; + + +const lua_gethookcount = function(L) { + return L.basehookcount; +}; + const lua_getstack = function(L, level, ar) { let ci; let status; @@ -633,6 +647,9 @@ module.exports.luaG_runerror = luaG_runerror; module.exports.luaG_tointerror = luaG_tointerror; module.exports.luaG_traceexec = luaG_traceexec; module.exports.luaG_typeerror = luaG_typeerror; +module.exports.lua_gethook = lua_gethook; +module.exports.lua_gethookcount = lua_gethookcount; +module.exports.lua_gethookmask = lua_gethookmask; 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 8c3ad7f..6587228 100644 --- a/tests/ldblib.js +++ b/tests/ldblib.js @@ -51,6 +51,62 @@ test('debug.sethook', function (t) { }); +test('debug.gethook', function (t) { + let luaCode = ` + local result = "" + + debug.sethook(function (event) + result = result .. event .. " " + end, "crl", 1) + + local l = function() end + + l() + l() + l() + + return debug.gethook() + `, L; + + t.plan(5); + + 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.deepEqual( + lapi.lua_typename(L, lapi.lua_type(L, -3)), + lua.to_luastring("function"), + "Correct element(s) on the stack" + ); + + t.deepEqual( + lapi.lua_tojsstring(L, -2), + "crl", + "Correct element(s) on the stack" + ); + + t.deepEqual( + lapi.lua_tointeger(L, -1), + 1, + "Correct element(s) on the stack" + ); + +}); + + test('debug.getlocal', function (t) { let luaCode = ` local alocal = "alocal" -- cgit v1.2.3-54-g00ecf From 8e05003853773ef4d38913858ee4fc636bed21ac Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 14 Apr 2017 14:54:21 +0200 Subject: constructs.lua tests --- tests/lua.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'tests') diff --git a/tests/lua.js b/tests/lua.js index 43e82bc..7a0fb9b 100644 --- a/tests/lua.js +++ b/tests/lua.js @@ -7,6 +7,33 @@ const lauxlib = require("../src/lauxlib.js"); const lua = require('../src/lua.js'); const linit = require('../src/linit.js'); + +test('constructs.lua', function (t) { + let luaCode = ` + return dofile("tests/lua-tests/constructs.lua") + `, L; + + t.plan(1); + + 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"); + +}); + + test('strings.lua', function (t) { let luaCode = ` return dofile("tests/lua-tests/strings.lua") -- cgit v1.2.3-54-g00ecf From c709857c7d6d1af740a5763ac0c78e010dd1757e Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 18 Apr 2017 10:03:36 +0200 Subject: Temp require to run constructs.lua --- tests/lua.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests') diff --git a/tests/lua.js b/tests/lua.js index 7a0fb9b..c230dea 100644 --- a/tests/lua.js +++ b/tests/lua.js @@ -10,6 +10,8 @@ const linit = require('../src/linit.js'); test('constructs.lua', function (t) { let luaCode = ` + _soft = true + require = function(lib) return _G[lib] end -- NYI return dofile("tests/lua-tests/constructs.lua") `, L; -- cgit v1.2.3-54-g00ecf From 5f6d2e34f69d14c57e7f2964aa951c8dab4efc99 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 18 Apr 2017 10:37:02 +0200 Subject: os.time --- src/linit.js | 4 ++- src/loslib.js | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lualib.js | 2 +- tests/loslib.js | 38 ++++++++++++++++++++++++++++ tests/lua-tests | 2 +- 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/loslib.js create mode 100644 tests/loslib.js (limited to 'tests') diff --git a/src/linit.js b/src/linit.js index 9425ae7..fa827cb 100644 --- a/src/linit.js +++ b/src/linit.js @@ -12,15 +12,17 @@ const lstrlib = require('./lstrlib.js'); const ltablib = require('./ltablib.js'); const lutf8lib = require('./lutf8lib.js'); const ldblib = require('./ldblib.js'); +const loslib = require('./loslib.js'); const lualib = require('./lualib.js'); const loadedlibs = { [lualib.LUA_COLIBNAME]: lcorolib.luaopen_coroutine, + [lualib.LUA_DBLIBNAME]: ldblib.luaopen_debug, [lualib.LUA_MATHLIBNAME]: lmathlib.luaopen_math, + [lualib.LUA_OSLIBNAME]: loslib.luaopen_os, [lualib.LUA_STRLIBNAME]: lstrlib.luaopen_string, [lualib.LUA_TABLIBNAME]: ltablib.luaopen_table, [lualib.LUA_UTF8LIBNAME]: lutf8lib.luaopen_utf8, - [lualib.LUA_DBLIBNAME]: ldblib.luaopen_debug, "_G": lbaselib.luaopen_base }; diff --git a/src/loslib.js b/src/loslib.js new file mode 100644 index 0000000..20675a1 --- /dev/null +++ b/src/loslib.js @@ -0,0 +1,78 @@ +"use strict"; + +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'); + +const setfield = function(L, key, value) { + lapi.lua_pushinteger(L, value); + lapi.lua_setfield(L, -2, key); +}; + +const setallfields = function(L, time) { + setfield(L, "sec", time.getSeconds()); + setfield(L, "min", time.getMinutes()); + setfield(L, "hour", time.getHours()); + setfield(L, "day", time.getDate()); + setfield(L, "month", time.getMonth()); + setfield(L, "year", time.getYear()); + setfield(L, "wday", time.getDay()); + let now = new Date(); + setfield(L, "yday", Math.floor((now - (new Date(now.getFullYear(), 0, 0))) / (1000 * 60 * 60 * 24))); + // setboolfield(L, "isdst", time.get); +}; + +const L_MAXDATEFIELD = (Number.MAX_SAFE_INTEGER / 2); + +const getfield = function(L, key, d, delta) { + let t = lapi.lua_getfield(L, -1, lua.to_luastring(key)); /* get field and its type */ + let res = lapi.lua_tointegerx(L, -1); + if (res !== false) { /* field is not an integer? */ + if (t != lua.CT.LUA_TNIL) /* some other value? */ + return lauxlib.luaL_error(L, `field '${key}' is not an integer`); + else if (d < 0) /* absent field; no default? */ + return lauxlib.luaL_error(L, `field '${key}' missing in date table`); + res = d; + } + else { + if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) + return lauxlib.luaL_error(L, `field '${key}' is out-of-bound`); + res -= delta; + } + lapi.lua_pop(L, 1); + return res; +}; + +const os_time = function(L) { + let t = new Date(); + if (!lapi.lua_isnoneornil(L, 1)) /* called with arg */{ + lauxlib.luaL_checktype(L, 1, lua.CT.LUA_TTABLE); /* make sure table is at the top */ + lapi.lua_settop(L, 1); + t.setSeconds(getfield(L, "sec", 0, 0)); + t.setSeconds(getfield(L, "min", 0, 0)); + t.setSeconds(getfield(L, "hour", 12, 0)); + t.setSeconds(getfield(L, "day", -1, 0)); + t.setSeconds(getfield(L, "month", -1, 1)); + t.setSeconds(getfield(L, "year", -1, 1900)); + setallfields(L, t); + } + + lapi.lua_pushinteger(L, Math.floor(t / 1000)); + return 1; +}; + + +const syslib = { + "time": os_time +}; + +const luaopen_os = function(L) { + lauxlib.luaL_newlib(L, syslib); + return 1; +}; + +module.exports.luaopen_os = luaopen_os; diff --git a/src/lualib.js b/src/lualib.js index 9751302..327673e 100644 --- a/src/lualib.js +++ b/src/lualib.js @@ -20,7 +20,7 @@ module.exports.LUA_IOLIBNAME = LUA_IOLIBNAME; const LUA_OSLIBNAME = "os"; module.exports.LUA_OSLIBNAME = LUA_OSLIBNAME; -// module.exports[LUA_OSLIBNAME] = require("./loslib.js").luaopen_os; +module.exports[LUA_OSLIBNAME] = require("./loslib.js").luaopen_os; const LUA_STRLIBNAME = "string"; module.exports.LUA_STRLIBNAME = LUA_STRLIBNAME; diff --git a/tests/loslib.js b/tests/loslib.js new file mode 100644 index 0000000..1bb217f --- /dev/null +++ b/tests/loslib.js @@ -0,0 +1,38 @@ +"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('os.time', function (t) { + let luaCode = ` + return os.time() + `, 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.ok( + lapi.lua_isinteger(L, -1), + "Correct element(s) on the stack" + ); + +}); diff --git a/tests/lua-tests b/tests/lua-tests index 01eded4..21fb72d 160000 --- a/tests/lua-tests +++ b/tests/lua-tests @@ -1 +1 @@ -Subproject commit 01eded4b34159a77f61b56c5567c74f169d3b1ea +Subproject commit 21fb72de668a3307958d6a6d71770ae2118ba1d3 -- cgit v1.2.3-54-g00ecf From 4f415e5ca594c5b60e6fa6315b69acb41273ee7e Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 18 Apr 2017 11:19:22 +0200 Subject: Fixed debug.getinfo bad name lookup --- src/ldblib.js | 2 +- src/ldebug.js | 14 ++++++++------ tests/lua-tests | 2 +- tests/lua.js | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/src/ldblib.js b/src/ldblib.js index 5c78724..dfc7819 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -153,7 +153,7 @@ const db_getinfo = function(L) { 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("name"), ar.name ? ar.name : null); settabss(L, lua.to_luastring("namewhat"), ar.namewhat ? ar.namewhat : null); } if (options.indexOf(char['t']) > - 1) diff --git a/src/ldebug.js b/src/ldebug.js index 0a0e22c..d769500 100644 --- a/src/ldebug.js +++ b/src/ldebug.js @@ -79,10 +79,11 @@ const lua_getstack = function(L, level, ar) { return status; }; +// TODO: returns TValue or String array ? const upvalname = function(p, uv) { assert(uv < p.upvalues.length); let s = p.upvalues[uv].name; - if (s === null) return "?".charCodeAt(0); + if (s === null) return ["?".charCodeAt(0)]; return s; }; @@ -301,8 +302,8 @@ const kname = function(p, pc, c) { /* else no reasonable name found */ } else { /* 'c' is a register */ let what = getobjname(p, pc, c); /* search for 'c' */ - if (what && what.name[0] === 'c'.charCodeAt(0)) { - return what; + if (what && what.funcname[0] === 'c'.charCodeAt(0)) { /* found a constant name? */ + return what; /* 'name' already filled */ } /* else no reasonable name found */ } @@ -393,8 +394,9 @@ const getobjname = function(p, lastpc, reg) { let k = i.C; /* key index */ let t = i.B; /* table index */ let vn = op === 'OP_GETTABLE' ? lfunc.luaF_getlocalname(p, t + 1, pc) : upvalname(p, t); - r.name = kname(p, pc, k); - r.funcname = vn && vn === lua.to_luastring("_ENV") ? lua.to_luastring("global") : lua.to_luastring("field"); + vn = vn ? vn.jsstring() : null; + r.name = kname(p, pc, k).name; + r.funcname = vn && vn === "_ENV" ? lua.to_luastring("global") : lua.to_luastring("field"); return r; } case 'OP_GETUPVAL': { @@ -414,7 +416,7 @@ const getobjname = function(p, lastpc, reg) { } case 'OP_SELF': { let k = i.C; - r.name = kname(p, pc, k); + r.name = kname(p, pc, k).name; r.funcname = lua.to_luastring("method"); return r; } diff --git a/tests/lua-tests b/tests/lua-tests index 21fb72d..9e0d0bf 160000 --- a/tests/lua-tests +++ b/tests/lua-tests @@ -1 +1 @@ -Subproject commit 21fb72de668a3307958d6a6d71770ae2118ba1d3 +Subproject commit 9e0d0bfb5de8cb0534a13c19f23377db62b53d57 diff --git a/tests/lua.js b/tests/lua.js index c230dea..30a9c98 100644 --- a/tests/lua.js +++ b/tests/lua.js @@ -15,7 +15,7 @@ test('constructs.lua', function (t) { return dofile("tests/lua-tests/constructs.lua") `, L; - t.plan(1); + t.plan(2); t.doesNotThrow(function () { @@ -27,11 +27,11 @@ test('constructs.lua', function (t) { }, "Lua program loaded without error"); - // t.doesNotThrow(function () { + t.doesNotThrow(function () { lapi.lua_call(L, 0, -1); - // }, "Lua program ran without error"); + }, "Lua program ran without error"); }); -- cgit v1.2.3-54-g00ecf