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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2
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-70-g09d2