aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md99
-rw-r--r--src/lapi.js74
-rw-r--r--src/lauxlib.js32
-rw-r--r--src/lbaselib.js34
-rw-r--r--src/lcode.js6
-rw-r--r--src/lcorolib.js2
-rw-r--r--src/ldblib.js251
-rw-r--r--src/ldebug.js129
-rw-r--r--src/ldo.js50
-rw-r--r--src/lfunc.js3
-rw-r--r--src/linit.js4
-rw-r--r--src/llex.js26
-rw-r--r--src/lmathlib.js20
-rw-r--r--src/lobject.js21
-rw-r--r--src/loslib.js78
-rw-r--r--src/lparser.js44
-rw-r--r--src/lstate.js4
-rw-r--r--src/lstrlib.js74
-rw-r--r--src/ltable.js16
-rw-r--r--src/ltablib.js24
-rw-r--r--src/ltm.js54
-rw-r--r--src/lua.js38
-rw-r--r--src/lualib.js2
-rw-r--r--src/lutf8lib.js20
-rw-r--r--src/lvm.js88
-rw-r--r--tests/lbaselib.js1
-rw-r--r--tests/ldblib.js211
-rw-r--r--tests/lexparse.js4
-rw-r--r--tests/loslib.js38
-rw-r--r--tests/ltablib.js2
m---------tests/lua-tests0
-rw-r--r--tests/lua.js29
-rw-r--r--tests/lvm.js4
33 files changed, 1122 insertions, 360 deletions
diff --git a/README.md b/README.md
index 112e227..73708f7 100644
--- a/README.md
+++ b/README.md
@@ -5,53 +5,38 @@
</p>
# 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_getuservalue
- [ ] lua_isboolean
- - [ ] lua_iscfunction
- [ ] lua_islightuserdata
- - [ ] lua_isuserdata
- - [ ] lua_pcallk
- [ ] lua_pushfstring
- - [ ] lua_pushlightuserdata
- [ ] lua_pushvfstring
- - [ ] lua_rawgetp
- [ ] lua_rawseti
- - [ ] lua_rawsetp
- [ ] lua_register
- [ ] lua_setallocf
- - [ ] lua_sethook
- - [ ] lua_setlocal
- - [ ] lua_setuservalue
- [ ] lua_tocfunction
- - [ ] lua_upvaluejoin
- [ ] Auxiliary library
- [x] ...
- [ ] luaL_addsize
@@ -67,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.getlocal
- - [x] debug.traceback
- - [ ] debug.gethook
- - [ ] debug.getinfo
- - [ ] debug.getmetatable
- - [ ] debug.getregistry
- - [ ] debug.getupvalue
- - [ ] debug.getuservalue
- - [ ] debug.sethook
- - [ ] debug.setlocal
- - [ ] debug.setmetatable
- - [ ] debug.setupvalue
- - [ ] debug.setuservalue
- - [ ] debug.upvalueid
- - [ ] debug.upvaluejoin
- [ ] 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/lapi.js b/src/lapi.js
index 894b5bb..72c35f0 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -221,7 +221,7 @@ const lua_pushlstring = function(L, s, len) {
assert(Array.isArray(s), "lua_pushlstring expects array of byte");
assert(typeof len === "number");
- let ts = len === 0 ? L.l_G.intern(lua.to_luastring("")) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len));
+ let ts = len === 0 ? L.l_G.intern(lua.to_luastring("", true)) : new TValue(CT.LUA_TLNGSTR, s.slice(0, len));
L.stack[L.top++] = ts;
assert(L.top <= L.ci.top, "stack overflow");
@@ -346,7 +346,7 @@ const auxsetstr = function(L, t, k) {
};
const lua_setglobal = function(L, name) {
- auxsetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS - 1), name);
+ auxsetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS), name);
};
const lua_setmetatable = function(L, objindex) {
@@ -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);
@@ -537,6 +555,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);
@@ -562,7 +589,7 @@ const lua_geti = function(L, idx, n) {
};
const lua_getglobal = function(L, name) {
- return auxgetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS - 1), name);
+ return auxgetstr(L, L.l_G.l_registry.value.get(lua.LUA_RIDX_GLOBALS), name);
};
/*
@@ -710,6 +737,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;
};
@@ -773,7 +805,7 @@ const lua_load = function(L, reader, data, chunckname, mode) {
if (f.nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
let reg = L.l_G.l_registry;
- let gt = reg.value.get(lua.LUA_RIDX_GLOBALS - 1);
+ let gt = reg.value.get(lua.LUA_RIDX_GLOBALS);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
f.upvals[0].u.value = gt;
}
@@ -793,6 +825,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");
@@ -851,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;
@@ -896,7 +938,7 @@ const lua_concat = function(L, n) {
if (n >= 2)
lvm.luaV_concat(L, n);
else if (n === 0) {
- L.stack[L.top++] = L.l_G.intern(lua.to_luastring(""));
+ L.stack[L.top++] = L.l_G.intern(lua.to_luastring("", true));
assert(L.top <= L.ci.top, "stack overflow");
}
};
@@ -914,7 +956,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
};
};
@@ -936,6 +979,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 () {};
@@ -972,7 +1026,9 @@ 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_iscfunction = lua_iscfunction;
module.exports.lua_isfunction = lua_isfunction;
module.exports.lua_isinteger = lua_isinteger;
module.exports.lua_isnil = lua_isnil;
@@ -1011,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;
@@ -1023,6 +1081,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;
@@ -1040,5 +1099,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/lauxlib.js b/src/lauxlib.js
index 8e263fe..3d75282 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -59,7 +59,7 @@ const findfield = function(L, objidx, level) {
const pushglobalfuncname = function(L, ar) {
let top = lapi.lua_gettop(L);
ldebug.lua_getinfo(L, [char['f']], ar); /* push function */
- lapi.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(LUA_LOADED_TABLE));
+ lapi.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring(LUA_LOADED_TABLE, true));
if (findfield(L, top + 1, 2)) {
let name = lapi.lua_tostring(L, -1);
if (lobject.jsstring(name).startsWith("_G.")) { /* name start with '_G.'? */
@@ -79,7 +79,7 @@ const sv = s => s ? s : [];
const pushfuncname = function(L, ar) {
if (pushglobalfuncname(L, ar)) { /* try first a global name */
- lapi.lua_pushstring(L, lua.to_luastring("function '").concat(lapi.lua_tostring(L, -1)).concat([char["'"]]));
+ lapi.lua_pushstring(L, lua.to_luastring("function '", true).concat(lapi.lua_tostring(L, -1)).concat([char["'"]]));
lapi.lua_remove(L, -2); /* remove name */
}
else if (ar.namewhat) /* is there a name from code? */
@@ -87,7 +87,7 @@ const pushfuncname = function(L, ar) {
else if (ar.what && ar.what[0] === char['m']) /* main? */
lapi.lua_pushliteral(L, "main chunk");
else if (ar.what && ar.what[0] != char['C']) /* for Lua functions, use <file:line> */
- lapi.lua_pushstring(L, lua.to_luastring("function <").concat(...sv(ar.short_src), char[':'], ...lua.to_luastring(`${ar.linedefined}>`)));
+ lapi.lua_pushstring(L, lua.to_luastring("function <", true).concat(...sv(ar.short_src), char[':'], ...lua.to_luastring(`${ar.linedefined}>`)));
else /* nothing left... */
lapi.lua_pushliteral(L, "?");
};
@@ -121,7 +121,7 @@ const luaL_traceback = function(L, L1, msg, level) {
lapi.lua_pushliteral(L, "\n\t..."); /* add a '...' */
level = last - LEVELS2 + 1; /* and skip to last ones */
} else {
- ldebug.lua_getinfo(L1, lua.to_luastring("Slnt"), ar);
+ ldebug.lua_getinfo(L1, lua.to_luastring("Slnt", true), ar);
lapi.lua_pushstring(L, [char['\n'], char['\t'], char['.'], char['.'], char['.']].concat(ar.short_src));
if (ar.currentline > 0)
lapi.lua_pushliteral(L, `${ar.currentline}:`);
@@ -147,7 +147,7 @@ const luaL_argerror = function(L, arg, extramsg) {
ldebug.lua_getinfo(L, 'n', ar);
- if (ar.namewhat === lua.to_luastring('method')) {
+ if (ar.namewhat === lua.to_luastring('method', true)) {
arg--; /* do not count 'self' */
if (arg === 0) /* error is in the self argument itself? */
return luaL_error(L, lua.to_luastring(`calling '${lobject.jsstring(ar.name)}' on bad self (${lobject.jsstring(extramsg)})`));
@@ -161,10 +161,10 @@ const luaL_argerror = function(L, arg, extramsg) {
const typeerror = function(L, arg, tname) {
let typearg;
- if (luaL_getmetafield(L, arg, lua.to_luastring("__name")) === CT.LUA_TSTRING)
+ if (luaL_getmetafield(L, arg, lua.to_luastring("__name", true)) === CT.LUA_TSTRING)
typearg = lapi.lua_tostring(L, -1);
else if (lapi.lua_type(L, arg) === CT.LUA_TLIGHTUSERDATA)
- typearg = lua.to_luastring("light userdata");
+ typearg = lua.to_luastring("light userdata", true);
else
typearg = luaL_typename(L, arg);
@@ -175,7 +175,7 @@ const typeerror = function(L, arg, tname) {
const luaL_where = function(L, level) {
let ar = new lua.lua_Debug();
if (ldebug.lua_getstack(L, level, ar)) {
- ldebug.lua_getinfo(L, lua.to_luastring("Sl"), ar);
+ ldebug.lua_getinfo(L, lua.to_luastring("Sl", true), ar);
if (ar.currentline > 0) {
lapi.lua_pushstring(L, lua.to_luastring(`${lobject.jsstring(ar.short_src)}:${ar.currentline}:`));
return;
@@ -220,7 +220,7 @@ const luaL_argcheck = function(L, cond, arg, extramsg) {
const luaL_checkany = function(L, arg) {
if (lapi.lua_type(L, arg) === CT.LUA_TNONE)
- luaL_argerror(L, arg, lua.to_luastring("value expected"));
+ luaL_argerror(L, arg, lua.to_luastring("value expected", true));
};
const luaL_checktype = function(L, arg, t) {
@@ -248,7 +248,7 @@ const luaL_optstring = luaL_optlstring;
const interror = function(L, arg) {
if (lapi.lua_isnumber(L, arg))
- luaL_argerror(L, arg, lua.to_luastring("number has no integer representation"));
+ luaL_argerror(L, arg, lua.to_luastring("number has no integer representation", true));
else
tag_error(L, arg, CT.LUA_TNUMBER);
};
@@ -357,15 +357,15 @@ const luaL_len = function(L, idx) {
lapi.lua_len(L, idx);
let l = lapi.lua_tointegerx(L, -1);
if (l === false)
- luaL_error(L, lua.to_luastring("object length is not an integer"));
+ luaL_error(L, lua.to_luastring("object length is not an integer", true));
lapi.lua_pop(L, 1); /* remove object */
return l;
};
const luaL_tolstring = function(L, idx) {
- if (luaL_callmeta(L, idx, lua.to_luastring("__tostring"))) {
+ if (luaL_callmeta(L, idx, lua.to_luastring("__tostring", true))) {
if (!lapi.lua_isstring(L, -1))
- luaL_error(L, lua.to_luastring("'__tostring' must return a string"));
+ luaL_error(L, lua.to_luastring("'__tostring' must return a string", true));
} else {
switch(lapi.lua_type(L, idx)) {
case CT.LUA_TNUMBER:
@@ -379,7 +379,7 @@ const luaL_tolstring = function(L, idx) {
lapi.lua_pushstring(L, lua.to_luastring(`nil`));
break;
default:
- let tt = luaL_getmetafield(L, idx, lua.to_luastring("__name"));
+ let tt = luaL_getmetafield(L, idx, lua.to_luastring("__name", true));
let kind = tt === CT.LUA_TSTRING ? lapi.lua_tostring(L, -1) : luaL_typename(L, idx);
lapi.lua_pushstring(L, lua.to_luastring(`${lobject.jsstring(kind)}: 0x${lapi.index2addr(L, -1).id.toString(16)}`));
if (tt !== CT.LUA_TNIL)
@@ -438,7 +438,7 @@ const luaL_getsubtable = function(L, idx, fname) {
** Returns with only the table at the stack.
*/
const luaL_setfuncs = function(L, l, nup) {
- luaL_checkstack(L, nup, lua.to_luastring("too many upvalues"));
+ luaL_checkstack(L, nup, lua.to_luastring("too many upvalues", true));
for (let lib in l) { /* fill the table with given functions */
for (let i = 0; i < nup; i++) /* copy upvalues to the top */
lapi.lua_pushvalue(L, -nup);
@@ -460,7 +460,7 @@ const luaL_checkstack = function(L, space, msg) {
if (msg)
luaL_error(L, lua.to_luastring(`stack overflow (${lobject.jsstring(msg)})`));
else
- luaL_error(L, lua.to_luastring('stack overflow'));
+ luaL_error(L, lua.to_luastring('stack overflow', true));
}
};
diff --git a/src/lbaselib.js b/src/lbaselib.js
index 931a285..af7c3cc 100644
--- a/src/lbaselib.js
+++ b/src/lbaselib.js
@@ -13,14 +13,14 @@ const luaB_print = function(L) {
let n = lapi.lua_gettop(L); /* number of arguments */
let str = [];
- lapi.lua_getglobal(L, lua.to_luastring("tostring"));
+ lapi.lua_getglobal(L, lua.to_luastring("tostring", true));
for (let i = 1; i <= n; i++) {
lapi.lua_pushvalue(L, -1); /* function to be called */
lapi.lua_pushvalue(L, i); /* value to print */
lapi.lua_call(L, 1, 1);
let s = lapi.lua_tolstring(L, -1);
if (s === null)
- return lauxlib.luaL_error(L, lua.to_luastring("'tostring' must return a string to 'print'"));
+ return lauxlib.luaL_error(L, lua.to_luastring("'tostring' must return a string to 'print'", true));
if (i > 1) s = ["\t".charCodeAt(0)].concat(s);
str = str.concat(s);
lapi.lua_pop(L, 1);
@@ -45,16 +45,16 @@ const luaB_getmetatable = function(L) {
lapi.lua_pushnil(L);
return 1; /* no metatable */
}
- lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable"));
+ lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable", true));
return 1; /* returns either __metatable field (if present) or metatable */
};
const luaB_setmetatable = function(L) {
let t = lapi.lua_type(L, 2);
lauxlib.luaL_checktype(L, 1, CT.LUA_TTABLE);
- lauxlib.luaL_argcheck(L, t === CT.LUA_TNIL || t === CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected"));
- if (lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable")) !== CT.LUA_TNIL)
- return lauxlib.luaL_error(L, lua.to_luastring("cannot change a protected metatable"));
+ lauxlib.luaL_argcheck(L, t === CT.LUA_TNIL || t === CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected", true));
+ if (lauxlib.luaL_getmetafield(L, 1, lua.to_luastring("__metatable", true)) !== CT.LUA_TNIL)
+ return lauxlib.luaL_error(L, lua.to_luastring("cannot change a protected metatable", true));
lapi.lua_settop(L, 2);
lapi.lua_setmetatable(L, 1);
return 1;
@@ -69,7 +69,7 @@ const luaB_rawequal = function(L) {
const luaB_rawlen = function(L) {
let t = lapi.lua_type(L, 1);
- lauxlib.luaL_argcheck(L, t === CT.LUA_TTABLE || t === CT.LUA_TSTRING, 1, lua.to_luastring("table or string expected"));
+ lauxlib.luaL_argcheck(L, t === CT.LUA_TTABLE || t === CT.LUA_TSTRING, 1, lua.to_luastring("table or string expected", true));
lapi.lua_pushinteger(L, lapi.lua_rawlen(L, 1));
return 1;
};
@@ -93,7 +93,7 @@ const luaB_rawset = function(L) {
const luaB_type = function(L) {
let t = lapi.lua_type(L, 1);
- lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, lua.to_luastring("value expected"));
+ lauxlib.luaL_argcheck(L, t !== CT.LUA_TNONE, 1, lua.to_luastring("value expected", true));
lapi.lua_pushstring(L, lapi.lua_typename(L, t));
return 1;
};
@@ -124,7 +124,7 @@ const luaB_next = function(L) {
};
const luaB_pairs = function(L) {
- return pairsmeta(L, lua.to_luastring("__pairs"), 0, luaB_next);
+ return pairsmeta(L, lua.to_luastring("__pairs", true), 0, luaB_next);
};
/*
@@ -166,7 +166,7 @@ const luaB_tonumber = function(L) {
let base = lauxlib.luaL_checkinteger(L, 2);
lauxlib.luaL_checktype(L, 1, CT.LUA_TSTRING); /* no numbers as strings */
let s = lapi.lua_tostring(L, 1);
- lauxlib.luaL_argcheck(L, 2 <= base && base <= 36, 2, lua.to_luastring("base out of range"));
+ lauxlib.luaL_argcheck(L, 2 <= base && base <= 36, 2, lua.to_luastring("base out of range", true));
let n = parseInt(lobject.jsstring(s), base);
if (!isNaN(n)) {
lapi.lua_pushinteger(L, n);
@@ -210,7 +210,7 @@ const luaB_select = function(L) {
let i = lauxlib.luaL_checkinteger(L, 1);
if (i < 0) i = n + i;
else if (i > n) i = n;
- lauxlib.luaL_argcheck(L, 1 <= i, 1, lua.to_luastring("index out of range"));
+ lauxlib.luaL_argcheck(L, 1 <= i, 1, lua.to_luastring("index out of range", true));
return n - i;
}
};
@@ -284,28 +284,28 @@ const RESERVEDSLOT = 5;
** reserved slot inside the stack.
*/
const generic_reader = function(L, ud) {
- lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many nested functions"));
+ lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many nested functions", true));
lapi.lua_pushvalue(L, 1); /* get function */
lapi.lua_call(L, 0, 1); /* call it */
if (lapi.lua_isnil(L, -1)) {
lapi.lua_pop(L, 1); /* pop result */
return null;
} else if (!lapi.lua_isstring(L, -1))
- lauxlib.luaL_error(L, lua.to_luastring("reader function must return a string"));
+ lauxlib.luaL_error(L, lua.to_luastring("reader function must return a string", true));
lapi.lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
return lapi.lua_tostring(L, RESERVEDSLOT);
};
const luaB_load = function(L) {
let s = lapi.lua_tostring(L, 1);
- let mode = lauxlib.luaL_optstring(L, 3, lua.to_luastring("bt"));
+ let mode = lauxlib.luaL_optstring(L, 3, lua.to_luastring("bt", true));
let env = !lapi.lua_isnone(L, 4) ? 4 : 0; /* 'env' index or 0 if no 'env' */
let status;
if (s !== null) { /* loading a string? */
let chunkname = lauxlib.luaL_optstring(L, 2, s);
status = lauxlib.luaL_loadbufferx(L, s, chunkname, mode);
} else { /* loading from a reader function */
- let chunkname = lauxlib.luaL_optstring(L, 2, lua.to_luastring("=(load)"));
+ let chunkname = lauxlib.luaL_optstring(L, 2, lua.to_luastring("=(load)", true));
lauxlib.luaL_checktype(L, 1, CT.LUA_TFUNCTION);
lapi.lua_settop(L, RESERVEDSLOT); /* create reserved slot */
status = lapi.lua_load(L, generic_reader, null, chunkname, mode);
@@ -378,10 +378,10 @@ const luaopen_base = function(L) {
lauxlib.luaL_setfuncs(L, base_funcs, 0);
/* set global _G */
lapi.lua_pushvalue(L, -1);
- lapi.lua_setfield(L, -2, lua.to_luastring("_G"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("_G", true));
/* set global _VERSION */
lapi.lua_pushliteral(L, lua.LUA_VERSION);
- lapi.lua_setfield(L, -2, lua.to_luastring("_VERSION"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("_VERSION", true));
return 1;
};
diff --git a/src/lcode.js b/src/lcode.js
index 0b3d9bd..ec233c6 100644
--- a/src/lcode.js
+++ b/src/lcode.js
@@ -183,7 +183,7 @@ const fixjump = function(fs, pc, dest) {
let offset = dest - (pc + 1);
assert(dest !== NO_JUMP);
if (Math.abs(offset) > lopcode.MAXARG_sBx)
- llex.luaX_syntaxerror(fs.ls, lua.to_luastring("control structure too long"));
+ llex.luaX_syntaxerror(fs.ls, lua.to_luastring("control structure too long", true));
lopcode.SETARG_sBx(jmp, offset);
};
@@ -424,7 +424,7 @@ const luaK_checkstack = function(fs, n) {
let newstack = fs.freereg + n;
if (newstack > fs.f.maxstacksize) {
if (newstack >= MAXREGS)
- llex.luaX_syntaxerror(fs.ls, lua.to_luastring("function or expression needs to many registers"));
+ llex.luaX_syntaxerror(fs.ls, lua.to_luastring("function or expression needs to many registers", true));
fs.f.maxstacksize = newstack;
}
};
@@ -1230,7 +1230,7 @@ const luaK_setlist = function(fs, base, nelems, tostore) {
codeextraarg(fs, c);
}
else
- llex.luaX_syntaxerror(fs.ls, lua.to_luastring("constructor too long"));
+ llex.luaX_syntaxerror(fs.ls, lua.to_luastring("constructor too long", true));
fs.freereg = base + 1; /* free registers with list values */
};
diff --git a/src/lcorolib.js b/src/lcorolib.js
index 58d061c..b7c4555 100644
--- a/src/lcorolib.js
+++ b/src/lcorolib.js
@@ -14,7 +14,7 @@ const TS = lua.thread_status;
const getco = function(L) {
let co = lapi.lua_tothread(L, 1);
- lauxlib.luaL_argcheck(L, co, 1, lua.to_luastring("thread expected"));
+ lauxlib.luaL_argcheck(L, co, 1, lua.to_luastring("thread expected", true));
return co;
};
diff --git a/src/ldblib.js b/src/ldblib.js
index ea080c0..c637af2 100644
--- a/src/ldblib.js
+++ b/src/ldblib.js
@@ -18,6 +18,44 @@ const checkstack = function(L, L1, n) {
lauxlib.luaL_error(L, "stack overflow");
};
+const db_getregistry = function(L) {
+ lapi.lua_pushvalue(L, lua.LUA_REGISTRYINDEX);
+ return 1;
+};
+
+const db_getmetatable = function(L) {
+ lauxlib.luaL_checkany(L, 1);
+ if (!lapi.lua_getmetatable(L, 1)) {
+ lapi.lua_pushnil(L); /* no metatable */
+ }
+ return 1;
+};
+
+const db_setmetatable = function(L) {
+ const t = lapi.lua_type(L, 2);
+ lauxlib.luaL_argcheck(L, t == lua.CT.LUA_TNIL || t == lua.CT.LUA_TTABLE, 2, lua.to_luastring("nil or table expected", true));
+ lapi.lua_settop(L, 2);
+ lapi.lua_setmetatable(L, 1);
+ 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
@@ -85,7 +123,7 @@ const db_getinfo = function(L) {
let thread = getthread(L);
let arg = thread.arg;
let L1 = thread.thread;
- let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu"));
+ let options = lauxlib.luaL_optstring(L, arg + 2, lua.to_luastring("flnStu", true));
checkstack(L, L1, 3);
if (lapi.lua_isfunction(L, arg + 1)) { /* info about a function? */
options = [char['>']].concat(options); /* add '>' to 'options' */
@@ -99,31 +137,31 @@ const db_getinfo = function(L) {
}
if (!ldebug.lua_getinfo(L1, options, ar))
- lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option"));
+ lauxlib.luaL_argerror(L, arg + 2, lua.to_luastring("invalid option", true));
lapi.lua_newtable(L); /* table to collect results */
if (options.indexOf(char['S']) > -1) {
- settabss(L, lua.to_luastring("source"), ar.source.value);
- settabss(L, lua.to_luastring("short_src"), ar.short_src);
- settabss(L, lua.to_luastring("linedefined"), lua.to_luastring(`${ar.linedefined}`));
- settabss(L, lua.to_luastring("lastlinedefined"), lua.to_luastring(`${ar.lastlinedefined}`));
- settabss(L, lua.to_luastring("what"), ar.what);
+ settabss(L, lua.to_luastring("source", true), ar.source.value);
+ settabss(L, lua.to_luastring("short_src", true), ar.short_src);
+ settabss(L, lua.to_luastring("linedefined", true), lua.to_luastring(`${ar.linedefined}`));
+ settabss(L, lua.to_luastring("lastlinedefined", true), lua.to_luastring(`${ar.lastlinedefined}`));
+ settabss(L, lua.to_luastring("what", true), ar.what);
}
if (options.indexOf(char['l']) > -1)
- settabsi(L, lua.to_luastring("currentline"), ar.currentline);
+ settabsi(L, lua.to_luastring("currentline", true), ar.currentline);
if (options.indexOf(char['u']) > -1)
- settabsi(L, lua.to_luastring("nups"), ar.nups);
- settabsi(L, lua.to_luastring("nparams"), ar.nparams);
- settabsb(L, lua.to_luastring("isvararg"), ar.isvararg);
+ settabsi(L, lua.to_luastring("nups", true), ar.nups);
+ settabsi(L, lua.to_luastring("nparams", true), ar.nparams);
+ settabsb(L, lua.to_luastring("isvararg", true), ar.isvararg);
if (options.indexOf(char['n']) > - 1) {
- settabss(L, lua.to_luastring("name"), ar.name ? ar.name.value : null);
- settabss(L, lua.to_luastring("namewhat"), ar.namewhat ? ar.namewhat : null);
+ settabss(L, lua.to_luastring("name", true), ar.name ? ar.name : null);
+ settabss(L, lua.to_luastring("namewhat", true), ar.namewhat ? ar.namewhat : null);
}
if (options.indexOf(char['t']) > - 1)
- settabsb(L, lua.to_luastring("istailcall"), ar.istailcall);
+ settabsb(L, lua.to_luastring("istailcall", true), ar.istailcall);
if (options.indexOf(char['L']) > - 1)
- treatstackoption(L, L1, lua.to_luastring("activelines"));
+ treatstackoption(L, L1, lua.to_luastring("activelines", true));
if (options.indexOf(char['f']) > - 1)
- treatstackoption(L, L1, lua.to_luastring("func"));
+ treatstackoption(L, L1, lua.to_luastring("func", true));
return 1; /* return table */
};
@@ -140,7 +178,7 @@ const db_getlocal = function(L) {
} else { /* stack-level argument */
let level = lauxlib.luaL_checkinteger(L, arg + 1);
if (!ldebug.lua_getstack(L1, level, ar)) /* out of range? */
- return lauxlib.luaL_argerror(L, arg+1, lapi.to_luastring("level out of range"));
+ return lauxlib.luaL_argerror(L, arg+1, lapi.to_luastring("level out of range", true));
checkstack(L, L1, 1);
let name = ldebug.lua_getlocal(L1, ar, nvar);
if (name) {
@@ -156,6 +194,49 @@ 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
+*/
+const auxupvalue = function(L, get) {
+ let n = lauxlib.luaL_checkinteger(L, 2); /* upvalue index */
+ lauxlib.luaL_checktype(L, 1, lua.CT.LUA_TFUNCTION); /* closure */
+ let name = get ? lapi.lua_getupvalue(L, 1, n) : lapi.lua_setupvalue(L, 1, n);
+ if (name === null) return 0;
+ lapi.lua_pushstring(L, name);
+ lapi.lua_insert(L, -(get+1)); /* no-op if get is false */
+ return get + 1;
+};
+
+
+const db_getupvalue = function(L) {
+ return auxupvalue(L, 1);
+};
+
+const db_setupvalue = function(L) {
+ lauxlib.luaL_checkany(L, 3);
+ return auxupvalue(L, 0);
+};
+
/*
** Check whether a given upvalue from a given closure exists and
** returns its index
@@ -163,17 +244,128 @@ const db_getlocal = function(L) {
const checkupval = function(L, argf, argnup) {
let nup = lauxlib.luaL_checkinteger(L, argnup); /* upvalue index */
lauxlib.luaL_checktype(L, argf, lua.CT.LUA_TFUNCTION); /* closure */
- lauxlib.luaL_argcheck(L, (lapi.lua_getupvalue(L, argf, nup) !== null), argnup, lua.to_luastring("invalid upvalue index"));
+ lauxlib.luaL_argcheck(L, (lapi.lua_getupvalue(L, argf, nup) !== null), argnup, lua.to_luastring("invalid upvalue index", true));
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", true));
+ lauxlib.luaL_argcheck(L, !lapi.lua_iscfunction(L, 3), 3, lua.to_luastring("Lua function expected", true));
+ lapi.lua_upvaluejoin(L, 1, n1, 3, n2);
+ 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__", true);
+
+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"];
+ 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", true)); /** 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_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;
@@ -189,10 +381,21 @@ const db_traceback = function(L) {
};
const dblib = {
- "getinfo": db_getinfo,
- "getlocal": db_getlocal,
- "traceback": db_traceback,
- "upvalueid": db_upvalueid
+ "gethook": db_gethook,
+ "getinfo": db_getinfo,
+ "getlocal": db_getlocal,
+ "getmetatable": db_getmetatable,
+ "getregistry": db_getregistry,
+ "getupvalue": db_getupvalue,
+ "getuservalue": db_getuservalue,
+ "sethook": db_sethook,
+ "setlocal": db_setlocal,
+ "setmetatable": db_setmetatable,
+ "setupvalue": db_setupvalue,
+ "setuservalue": db_setuservalue,
+ "traceback": db_traceback,
+ "upvalueid": db_upvalueid,
+ "upvaluejoin": db_upvaluejoin
};
// Only with Node
@@ -220,7 +423,7 @@ if (typeof require === "function") {
continue;
let buffer = lua.to_luastring(input);
- if (lauxlib.luaL_loadbuffer(L, buffer, buffer.length, lua.to_luastring("=(debug command)"))
+ if (lauxlib.luaL_loadbuffer(L, buffer, buffer.length, lua.to_luastring("=(debug command)", true))
|| lapi.lua_pcall(L, 0, 0, 0)) {
lauxlib.lua_writestringerror(`${lapi.lua_tojsstring(L, -1)}\n`);
}
diff --git a/src/ldebug.js b/src/ldebug.js
index 713a0fb..16449e2 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -38,6 +38,33 @@ 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_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;
@@ -52,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;
};
@@ -66,7 +94,7 @@ const findvararg = function(ci, n, pos) {
else {
return {
pos: ci.funcOff + nparams + n,
- name: lua.to_luastring("(*vararg)") /* generic name for any vararg */
+ name: lua.to_luastring("(*vararg)", true) /* generic name for any vararg */
};
}
};
@@ -87,7 +115,7 @@ const findlocal = function(L, ci, n) {
if (name === null) { /* no 'standard' name? */
let limit = ci === L.ci ? L.top : ci.next.func;
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
- name = lua.to_luastring("(*temporary)"); /* generic name for any valid slot */
+ name = lua.to_luastring("(*temporary)", true); /* generic name for any valid slot */
else
return null; /* no name */
}
@@ -116,18 +144,32 @@ 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]");
+ ar.source = lua.to_luastring("=[JS]", true);
ar.linedefined = -1;
ar.lastlinedefined = -1;
ar.what = "J";
} else {
let p = cl.p;
- ar.source = p.source ? p.source : lua.to_luastring("=?");
+ ar.source = p.source ? p.source : lua.to_luastring("=?", true);
ar.linedefined = p.linedefined;
ar.lastlinedefined = p.lastlinedefined;
- ar.what = ar.linedefined === 0 ? lua.to_luastring("main") : lua.to_luastring("Lua");
+ ar.what = ar.linedefined === 0 ? lua.to_luastring("main", true) : lua.to_luastring("Lua", true);
}
ar.short_src = lobject.luaO_chunkid(ar.source, luaconf.LUA_IDSIZE);
@@ -156,8 +198,8 @@ const getfuncname = function(L, ci) {
if (ci === null)
return null;
else if (ci.callstatus & lstate.CIST_FIN) { /* is this a finalizer? */
- r.name = lua.to_luastring("__gc");
- r.funcname = lua.to_luastring("metamethod"); /* report it as such */
+ r.name = lua.to_luastring("__gc", true);
+ r.funcname = lua.to_luastring("metamethod", true); /* report it as such */
return r;
}
/* calling function is a known Lua function? */
@@ -260,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 */
}
@@ -331,7 +373,7 @@ const getobjname = function(p, lastpc, reg) {
};
if (r.name) { /* is a local? */
- r.funcname = lua.to_luastring("local");
+ r.funcname = lua.to_luastring("local", true);
return r;
}
@@ -352,13 +394,14 @@ 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", true) : lua.to_luastring("field", true);
return r;
}
case 'OP_GETUPVAL': {
r.name = upvalname(p, i.B);
- r.funcname = lua.to_luastring("upvalue");
+ r.funcname = lua.to_luastring("upvalue", true);
return r;
}
case 'OP_LOADK':
@@ -366,15 +409,15 @@ const getobjname = function(p, lastpc, reg) {
let b = op === 'OP_LOADK' ? i.Bx : p.code[pc + 1].Ax;
if (p.k[b].ttisstring()) {
r.name = p.k[b].value;
- r.funcname = lua.to_luastring("constant");
+ r.funcname = lua.to_luastring("constant", true);
return r;
}
break;
}
case 'OP_SELF': {
let k = i.C;
- r.name = kname(p, pc, k);
- r.funcname = lua.to_luastring("method");
+ r.name = kname(p, pc, k).name;
+ r.funcname = lua.to_luastring("method", true);
return r;
}
default: break;
@@ -403,7 +446,7 @@ const funcnamefromcode = function(L, ci) {
if (ci.callstatus & lstate.CIST_HOOKED) {
r.name = [lua.char["?"]];
- r.funcname = lua.to_luastring("hook");
+ r.funcname = lua.to_luastring("hook", true);
return r;
}
@@ -412,8 +455,8 @@ const funcnamefromcode = function(L, ci) {
case 'OP_TAILCALL':
return getobjname(p, pc, i.A); /* get function name */
case 'OP_TFORCALL':
- r.name = lua.to_luastring("for iterator");
- r.funcname = lua.to_luastring("for iterator");
+ r.name = lua.to_luastring("for iterator", true);
+ r.funcname = lua.to_luastring("for iterator", true);
return r;
/* other instructions can do calls through metamethods */
case 'OP_SELF':
@@ -449,7 +492,7 @@ const funcnamefromcode = function(L, ci) {
}
r.name = L.l_G.tmname[tm];
- r.funcname = lua.to_luastring("metamethod");
+ r.funcname = lua.to_luastring("metamethod", true);
return r;
};
@@ -473,7 +516,7 @@ const getupvalname = function(L, ci, o, name) {
if (c.upvals[i].val(L) === o) {
return {
name: upvalname(c.p, i),
- funcname: lua.to_luastring('upvalue')
+ funcname: lua.to_luastring('upvalue', true)
};
}
}
@@ -501,7 +544,7 @@ const luaG_typeerror = function(L, o, op) {
const luaG_concaterror = function(L, p1, p2) {
if (p1.ttisstring() || p1.ttisnumber()) p1 = p2;
- luaG_typeerror(L, p1, lua.to_luastring('concatenate'));
+ luaG_typeerror(L, p1, lua.to_luastring('concatenate', true));
};
/*
@@ -563,6 +606,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;
@@ -570,7 +647,13 @@ 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_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;
+module.exports.lua_sethook = lua_sethook;
+module.exports.lua_setlocal = lua_setlocal;
diff --git a/src/ldo.js b/src/ldo.js
index 6a74cad..a3521c8 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -23,11 +23,11 @@ const TValue = lobject.TValue;
const seterrorobj = function(L, errcode, oldtop) {
switch (errcode) {
case TS.LUA_ERRMEM: {
- L.stack[oldtop] = L.l_G.intern(lua.to_luastring("not enough memory"));
+ L.stack[oldtop] = L.l_G.intern(lua.to_luastring("not enough memory", true));
break;
}
case TS.LUA_ERRERR: {
- L.stack[oldtop] = L.l_G.intern(lua.to_luastring("error in error handling"));
+ L.stack[oldtop] = L.l_G.intern(lua.to_luastring("error in error handling", true));
break;
}
default: {
@@ -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 */
@@ -448,9 +483,9 @@ const lua_yieldk = function(L, nresults, ctx, k) {
if (L.nny > 0) {
if (L !== L.l_G.mainthread)
- ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield across a JS-call boundary"));
+ ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield across a JS-call boundary", true));
else
- ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield from outside a coroutine"));
+ ldebug.luaG_runerror(L, lua.to_luastring("attempt to yield from outside a coroutine", true));
}
L.status = TS.LUA_YIELD;
@@ -529,10 +564,10 @@ const f_parser = function(L, p) {
let cl;
let c = p.z.getc(); /* read first character */
if (c === lua.LUA_SIGNATURE.charCodeAt(0)) {
- checkmode(L, p.mode, lua.to_luastring("binary"));
+ checkmode(L, p.mode, lua.to_luastring("binary", true));
cl = new BytecodeParser(L, p.z.buffer).luaU_undump();
} else {
- checkmode(L, p.mode, lua.to_luastring("text"));
+ checkmode(L, p.mode, lua.to_luastring("text", true));
cl = lparser.luaY_parser(L, p.z, p.buff, p.dyd, p.name, c);
}
@@ -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/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/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/llex.js b/src/llex.js
index 3ed0e56..b038619 100644
--- a/src/llex.js
+++ b/src/llex.js
@@ -152,7 +152,7 @@ const save = function(ls, c) {
let b = ls.buff;
if (b.n + 1 > b.buffer.length) {
if (b.buffer.length >= Number.MAX_SAFE_INTEGER/2)
- lexerror(ls, lua.to_luastring("lexical element too long"), 0);
+ lexerror(ls, lua.to_luastring("lexical element too long", true), 0);
}
b.buffer[b.n++] = c < 0 ? 255 + c + 1 : c;
};
@@ -193,7 +193,7 @@ const inclinenumber = function(ls) {
if (currIsNewline(ls) && ls.current !== old)
next(ls); /* skip '\n\r' or '\r\n' */
if (++ls.linenumber >= Number.MAX_SAFE_INTEGER)
- lexerror(ls, lua.to_luastring("chunk has too many lines"), 0);
+ lexerror(ls, lua.to_luastring("chunk has too many lines", true), 0);
};
const luaX_setinput = function(L, ls, z, source, firstchar) {
@@ -220,7 +220,7 @@ const luaX_setinput = function(L, ls, z, source, firstchar) {
ls.linenumber = 1;
ls.lastline = 1;
ls.source = source;
- ls.envn = L.l_G.intern(lua.to_luastring("_ENV"));
+ ls.envn = L.l_G.intern(lua.to_luastring("_ENV", true));
};
const check_next1 = function(ls, c) {
@@ -267,7 +267,7 @@ const read_numeral = function(ls, seminfo) {
let obj = lobject.luaO_str2num(ls.buff.buffer);
if (obj === false) /* format error? */
- lexerror(ls, lua.to_luastring("malformed number"), R.TK_FLT);
+ lexerror(ls, lua.to_luastring("malformed number", true), R.TK_FLT);
if (obj.ttisinteger()) {
seminfo.i = obj.value;
return R.TK_INT;
@@ -373,7 +373,7 @@ const esccheck = function(ls, c, msg) {
const gethexa = function(ls) {
save_and_next(ls);
- esccheck(ls, ljstype.lisxdigit(ls.current), lua.to_luastring("hexadecimal digit expected"));
+ esccheck(ls, ljstype.lisxdigit(ls.current), lua.to_luastring("hexadecimal digit expected", true));
return lobject.luaO_hexavalue(ls.current);
};
@@ -387,17 +387,17 @@ const readhexaesc = function(ls) {
const readutf8desc = function(ls) {
let i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
save_and_next(ls); /* skip 'u' */
- esccheck(ls, ls.current === char['{'], lua.to_luastring("missing '{'"));
+ esccheck(ls, ls.current === char['{'], lua.to_luastring("missing '{'", true));
let r = gethexa(ls); /* must have at least one digit */
save_and_next(ls);
while (ljstype.lisxdigit(ls.current)) {
i++;
r = (r << 4) + lobject.luaO_hexavalue(ls.current);
- esccheck(ls, r <= 0x10FFFF, lua.to_luastring("UTF-8 value too large"));
+ esccheck(ls, r <= 0x10FFFF, lua.to_luastring("UTF-8 value too large", true));
save_and_next(ls);
}
- esccheck(ls, ls.current === char['}'], lua.to_luastring("missing '}'"));
+ esccheck(ls, ls.current === char['}'], lua.to_luastring("missing '}'", true));
next(ls); /* skip '}' */
ls.buff.n -= i; /* remove saved chars from buffer */
return r;
@@ -417,7 +417,7 @@ const readdecesc = function(ls) {
r = 10 * r + ls.current - char['0'];
save_and_next(ls);
}
- esccheck(ls, r <= 255, lua.to_luastring("decimal escape too large"));
+ esccheck(ls, r <= 255, lua.to_luastring("decimal escape too large", true));
ls.buff.n -= i; /* remove read digits from buffer */
return r;
};
@@ -428,11 +428,11 @@ const read_string = function(ls, del, seminfo) {
while (ls.current !== del) {
switch (ls.current) {
case -1:
- lexerror(ls, lua.to_luastring("unfinished string"), R.TK_EOS);
+ lexerror(ls, lua.to_luastring("unfinished string", true), R.TK_EOS);
break;
case char['\n']:
case char['\r']:
- lexerror(ls, lua.to_luastring("unfinished string"), R.TK_STRING);
+ lexerror(ls, lua.to_luastring("unfinished string", true), R.TK_STRING);
break;
case char['\\']: { /* escape sequences */
save_and_next(ls); /* keep '\\' for error messages */
@@ -463,7 +463,7 @@ const read_string = function(ls, del, seminfo) {
will = 'no_save'; break;
}
default: {
- esccheck(ls, ljstype.lisdigit(ls.current), lua.to_luastring("invalid escape sequence"));
+ esccheck(ls, ljstype.lisdigit(ls.current), lua.to_luastring("invalid escape sequence", true));
c = readdecesc(ls); /* digital escape '\ddd' */
will = 'only_save'; break;
}
@@ -537,7 +537,7 @@ const llex = function(ls, seminfo) {
read_long_string(ls, seminfo, sep);
return R.TK_STRING;
} else if (sep !== -1) /* '[=...' missing second bracket */
- lexerror(ls, lua.to_luastring("invalid long string delimiter"), R.TK_STRING);
+ lexerror(ls, lua.to_luastring("invalid long string delimiter", true), R.TK_STRING);
return char['['];
}
case char['=']: {
diff --git a/src/lmathlib.js b/src/lmathlib.js
index 500331b..c052fcb 100644
--- a/src/lmathlib.js
+++ b/src/lmathlib.js
@@ -37,13 +37,13 @@ const math_random = function(L) {
up = lauxlib.luaL_checkinteger(L, 2);
break;
}
- default: return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments"));
+ default: return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments", true));
}
/* random integer in the interval [low, up] */
- lauxlib.luaL_argcheck(L, low <= up, 1, lua.to_luastring("interval is empty"));
+ lauxlib.luaL_argcheck(L, low <= up, 1, lua.to_luastring("interval is empty", true));
lauxlib.luaL_argcheck(L, low >= 0 || up <= Number.MAX_SAFE_INTEGER + low, 1,
- lua.to_luastring("interval too large"));
+ lua.to_luastring("interval too large", true));
r *= (up - low) + 1;
lapi.lua_pushinteger(L, r + low);
@@ -173,7 +173,7 @@ const math_rad = function(L) {
const math_min = function(L) {
let n = lapi.lua_gettop(L); /* number of arguments */
let imin = 1; /* index of current minimum value */
- lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected"));
+ lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected", true));
for (let i = 2; i <= n; i++){
if (lapi.lua_compare(L, i, imin, lua.LUA_OPLT))
imin = i;
@@ -185,7 +185,7 @@ const math_min = function(L) {
const math_max = function(L) {
let n = lapi.lua_gettop(L); /* number of arguments */
let imax = 1; /* index of current minimum value */
- lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected"));
+ lauxlib.luaL_argcheck(L, n >= 1, 1, lua.to_luastring("value expected", true));
for (let i = 2; i <= n; i++){
if (lapi.lua_compare(L, imax, i, lua.LUA_OPLT))
imax = i;
@@ -211,7 +211,7 @@ const math_fmod = function(L) {
if (lapi.lua_isinteger(L, 1) && lapi.lua_isinteger(L, 2)) {
let d = lapi.lua_tointeger(L, 2);
if (Math.abs(d) + 1 <= 1) {
- lauxlib.luaL_argcheck(L, d !== 0, 2, lua.to_luastring("zero"));
+ lauxlib.luaL_argcheck(L, d !== 0, 2, lua.to_luastring("zero", true));
lapi.lua_pushinteger(L, 0);
} else
lapi.lua_pushinteger(L, lapi.lua_tointeger(L, 1) % d);
@@ -265,13 +265,13 @@ const mathlib = {
const luaopen_math = function(L) {
lauxlib.luaL_newlib(L, mathlib);
lapi.lua_pushnumber(L, Math.PI);
- lapi.lua_setfield(L, -2, lua.to_luastring("pi"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("pi", true));
lapi.lua_pushnumber(L, Number.MAX_VALUE);
- lapi.lua_setfield(L, -2, lua.to_luastring("huge"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("huge", true));
lapi.lua_pushinteger(L, Number.MAX_SAFE_INTEGER);
- lapi.lua_setfield(L, -2, lua.to_luastring("maxinteger"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("maxinteger", true));
lapi.lua_pushinteger(L, Number.MIN_SAFE_INTEGER);
- lapi.lua_setfield(L, -2, lua.to_luastring("mininteger"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("mininteger", true));
return 1;
};
diff --git a/src/lobject.js b/src/lobject.js
index 618e801..52ca385 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -207,22 +207,13 @@ class Table extends TValue {
__newindex(table, key, value) {
key = Table.keyValue(key);
- if (typeof key === 'number' && key > 0) {
- table.value.set(key - 1, value); // Lua array starts at 1
- } else {
- table.value.set(key, value);
- }
+ table.value.set(key, value);
}
__index(table, key) {
key = Table.keyValue(key);
- let v = luaO_nilobject;
- if (typeof key === 'number' && key > 0) {
- v = table.value.get(key - 1); // Lua array starts at 1
- } else {
- v = table.value.get(key);
- }
+ let v = table.value.get(key);
return v ? v : luaO_nilobject;
}
@@ -283,7 +274,7 @@ class LocVar {
}
}
-const RETS = lua.to_luastring("...");
+const RETS = lua.to_luastring("...", true);
const PRE = lua.to_luastring("[string \"");
const POS = lua.to_luastring("\"]");
@@ -445,7 +436,7 @@ const l_str2d = function(s) {
return null;
let end = l_str2dloc(s, mode); /* try to convert */
if (end === null) { /* failed? may be a different locale */
- throw new Error("Locale not available to handle number"); // TODO
+ // throw new Error("Locale not available to handle number"); // TODO
}
return end;
};
@@ -483,7 +474,7 @@ const l_str2int = function(s) {
while (ljstype.lisspace(s[0])) s = s.slice(1); /* skip trailing spaces */
- if (empty || s[0] !== 0) return null; /* something wrong in the numeral */
+ if (empty || (s.length > 0 && s[0] !== 0)) return null; /* something wrong in the numeral */
else {
return neg ? -a : a;
}
@@ -549,7 +540,7 @@ const intarith = function(L, op, v1, v2) {
case lua.LUA_OPADD: return (v1 + v2);
case lua.LUA_OPSUB: return (v1 - v2);
case lua.LUA_OPMUL: return (v1 * v2);
- case lua.LUA_OPMOD: return (v1 % v2);
+ case lua.LUA_OPMOD: return (v1 - Math.floor(v1 / v2) * v2); // % semantic on negative numbers is different in js
case lua.LUA_OPIDIV: return (v1 / v2);
case lua.LUA_OPBAND: return (v1 & v2);
case lua.LUA_OPBOR: return (v1 | v2);
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/lparser.js b/src/lparser.js
index bdaa160..cf5f55b 100644
--- a/src/lparser.js
+++ b/src/lparser.js
@@ -242,7 +242,7 @@ const new_localvar = function(ls, name) {
let fs = ls.fs;
let dyd = ls.dyd;
let reg = registerlocalvar(ls, name);
- checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, lua.to_luastring("local variables"));
+ checklimit(fs, dyd.actvar.n + 1 - fs.firstlocal, MAXVARS, lua.to_luastring("local variables", true));
dyd.actvar.arr[dyd.actvar.n] = new Vardesc();
dyd.actvar.arr[dyd.actvar.n].idx = reg;
dyd.actvar.n++;
@@ -282,7 +282,7 @@ const searchupvalue = function(fs, name) {
const newupvalue = function(fs, name, v) {
let f = fs.f;
- checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, lua.to_luastring("upvalues"));
+ checklimit(fs, fs.nups + 1, lfunc.MAXUPVAL, lua.to_luastring("upvalues", true));
f.upvalues[fs.nups] = new UpVal(fs.ls.L);
f.upvalues[fs.nups].instack = v.k === expkind.VLOCAL;
f.upvalues[fs.nups].idx = v.u.info;
@@ -373,7 +373,7 @@ const adjust_assign = function(ls, nvars, nexps, e) {
const enterlevel = function(ls) {
let L = ls.L;
++L.nCcalls;
- checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels"));
+ checklimit(ls.fs, L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels", true));
};
const leavelevel = function(ls) {
@@ -480,7 +480,7 @@ const enterblock = function(fs, bl, isloop) {
** create a label named 'break' to resolve break statements
*/
const breaklabel = function(ls) {
- let n = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break"));
+ let n = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break", true));
let l = newlabelentry(ls, ls.dyd.label, n, 0, ls.fs.pc);
findgotos(ls, ls.dyd.label.arr[l]);
};
@@ -639,7 +639,7 @@ const recfield = function(ls, cc) {
let val = new expdesc();
if (ls.t.token === R.TK_NAME) {
- checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor"));
+ checklimit(fs, cc.nh, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor", true));
checkname(ls, key);
} else /* ls->t.token === '[' */
yindex(ls, key);
@@ -677,7 +677,7 @@ const lastlistfield = function(fs, cc) {
const listfield = function(ls, cc) {
/* listfield -> exp */
expr(ls, cc.v);
- checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor"));
+ checklimit(ls.fs, cc.na, Number.MAX_SAFE_INTEGER, lua.to_luastring("items in a constructor", true));
cc.na++;
cc.tostore++;
};
@@ -749,7 +749,7 @@ const parlist = function(ls) {
f.is_vararg = 1; /* declared vararg */
break;
}
- default: llex.luaX_syntaxerror(ls, lua.to_luastring("<name> or '...' expected"));
+ default: llex.luaX_syntaxerror(ls, lua.to_luastring("<name> or '...' expected", true));
}
} while(!f.is_vararg && testnext(ls, char[',']));
}
@@ -767,7 +767,7 @@ const body = function(ls, e, ismethod, line) {
open_func(ls, new_fs, bl);
checknext(ls, char['(']);
if (ismethod) {
- new_localvarliteral(ls, lua.to_luastring("self")); /* create 'self' parameter */
+ new_localvarliteral(ls, lua.to_luastring("self", true)); /* create 'self' parameter */
adjustlocalvars(ls, 1);
}
parlist(ls);
@@ -816,7 +816,7 @@ const funcargs = function(ls, f, line) {
break;
}
default: {
- llex.luaX_syntaxerror(ls, lua.to_luastring("function arguments expected"));
+ llex.luaX_syntaxerror(ls, lua.to_luastring("function arguments expected", true));
}
}
assert(f.k === expkind.VNONRELOC);
@@ -856,7 +856,7 @@ const primaryexp = function(ls, v) {
return;
}
default: {
- llex.luaX_syntaxerror(ls, lua.to_luastring("unexpected symbol"));
+ llex.luaX_syntaxerror(ls, lua.to_luastring("unexpected symbol", true));
}
}
};
@@ -930,7 +930,7 @@ const simpleexp = function(ls, v) {
}
case R.TK_DOTS: { /* vararg */
let fs = ls.fs;
- check_condition(ls, fs.f.is_vararg, lua.to_luastring("cannot use '...' outside a vararg function"));
+ check_condition(ls, fs.f.is_vararg, lua.to_luastring("cannot use '...' outside a vararg function", true));
init_exp(v, expkind.VVARARG, lcode.luaK_codeABC(fs, OpCodesI.OP_VARARG, 0, 1, 0));
break;
}
@@ -1102,14 +1102,14 @@ const check_conflict = function(ls, lh, v) {
const assignment = function(ls, lh, nvars) {
let e = new expdesc();
- check_condition(ls, vkisvar(lh.v.k), lua.to_luastring("syntax error"));
+ check_condition(ls, vkisvar(lh.v.k), lua.to_luastring("syntax error", true));
if (testnext(ls, char[','])) { /* assignment -> ',' suffixedexp assignment */
let nv = new LHS_assign();
nv.prev = lh;
suffixedexp(ls, nv.v);
if (nv.v.k !== expkind.VINDEXED)
check_conflict(ls, lh, nv.v);
- checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels"));
+ checklimit(ls.fs, nvars + ls.L.nCcalls, llimit.LUAI_MAXCCALLS, lua.to_luastring("JS levels", true));
assignment(ls, nv, nvars + 1);
} else { /* assignment -> '=' explist */
checknext(ls, char['=']);
@@ -1142,7 +1142,7 @@ const gotostat = function(ls, pc) {
label = str_checkname(ls);
else {
llex.luaX_next(ls); /* skip break */
- label = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break"));
+ label = new TValue(lua.CT.LUA_TLNGSTR, lua.to_luastring("break", true));
}
let g = newlabelentry(ls, ls.dyd.gt, label, line, pc);
findlabel(ls, g); /* close it if label already defined */
@@ -1253,9 +1253,9 @@ const fornum = function(ls, varname, line) {
/* fornum -> NAME = exp1,exp1[,exp1] forbody */
let fs = ls.fs;
let base = fs.freereg;
- new_localvarliteral(ls, lua.to_luastring("(for index)"));
- new_localvarliteral(ls, lua.to_luastring("(for limit)"));
- new_localvarliteral(ls, lua.to_luastring("(for step)"));
+ new_localvarliteral(ls, lua.to_luastring("(for index)", true));
+ new_localvarliteral(ls, lua.to_luastring("(for limit)", true));
+ new_localvarliteral(ls, lua.to_luastring("(for step)", true));
new_localvar(ls, varname);
checknext(ls, char['=']);
exp1(ls); /* initial value */
@@ -1277,9 +1277,9 @@ const forlist = function(ls, indexname) {
let nvars = 4; /* gen, state, control, plus at least one declared var */
let base = fs.freereg;
/* create control variables */
- new_localvarliteral(ls, lua.to_luastring("(for generator)"));
- new_localvarliteral(ls, lua.to_luastring("(for state)"));
- new_localvarliteral(ls, lua.to_luastring("(for control)"));
+ new_localvarliteral(ls, lua.to_luastring("(for generator)", true));
+ new_localvarliteral(ls, lua.to_luastring("(for state)", true));
+ new_localvarliteral(ls, lua.to_luastring("(for control)", true));
/* create declared variables */
new_localvar(ls, indexname);
while (testnext(ls, char[','])) {
@@ -1303,7 +1303,7 @@ const forstat = function(ls, line) {
switch (ls.t.token) {
case char['=']: fornum(ls, varname, line); break;
case char[',']: case R.TK_IN: forlist(ls, varname); break;
- default: llex.luaX_syntaxerror(ls, lua.to_luastring("'=' or 'in' expected"));
+ default: llex.luaX_syntaxerror(ls, lua.to_luastring("'=' or 'in' expected", true));
}
check_match(ls, R.TK_END, R.TK_FOR, line);
leaveblock(fs); /* loop scope ('break' jumps to this point) */
@@ -1421,7 +1421,7 @@ const exprstat= function(ls) {
assignment(ls, v, 1);
}
else { /* stat -> func */
- check_condition(ls, v.v.k === expkind.VCALL, lua.to_luastring("syntax error"));
+ check_condition(ls, v.v.k === expkind.VCALL, lua.to_luastring("syntax error", true));
lopcode.SETARG_C(lcode.getinstruction(fs, v.v), 1); /* call statement uses no results */
}
};
diff --git a/src/lstate.js b/src/lstate.js
index 74c908d..e18e7aa 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -109,8 +109,8 @@ const stack_init = function(L1, L) {
const init_registry = function(L, g) {
let registry = new lobject.Table();
g.l_registry = registry;
- registry.value.set(lua.LUA_RIDX_MAINTHREAD - 1, L);
- registry.value.set(lua.LUA_RIDX_GLOBALS - 1, new lobject.Table());
+ registry.value.set(lua.LUA_RIDX_MAINTHREAD, L);
+ registry.value.set(lua.LUA_RIDX_GLOBALS, new lobject.Table());
};
/*
diff --git a/src/lstrlib.js b/src/lstrlib.js
index 73fc068..c2ee817 100644
--- a/src/lstrlib.js
+++ b/src/lstrlib.js
@@ -103,11 +103,11 @@ const num2straux = function(x) {
let buff = [];
/* if 'inf' or 'NaN', format it like '%g' */
if (Object.is(x, Infinity))
- return lua.to_luastring('inf');
+ return lua.to_luastring('inf', true);
else if (Object.is(x, -Infinity))
- return lua.to_luastring('-inf');
+ return lua.to_luastring('-inf', true);
else if (Number.isNaN(x))
- return lua.to_luastring('nan');
+ return lua.to_luastring('nan', true);
else if (x === 0) { /* can be -0... */
/* create "0" or "-0" followed by exponent */
let zero = sprintf(luaconf.LUA_NUMBER_FMT + "x0p+0", x).split('').map(e => e.charCodeAt(0));
@@ -144,7 +144,7 @@ const lua_number2strx = function(L, fmt, x) {
for (let i = 0; i < buff.length; i++)
buff[i] = char[String.fromCharCode(buff[i]).toUpperCase()];
} else if (fmt[SIZELENMOD] !== char['a'])
- lauxlib.luaL_error(L, lua.to_luastring("modifiers for format '%a'/'%A' not implemented"));
+ lauxlib.luaL_error(L, lua.to_luastring("modifiers for format '%a'/'%A' not implemented", true));
return buff;
};
@@ -239,7 +239,7 @@ const addliteral = function(L, b, arg) {
break;
}
default: {
- lauxlib.luaL_argerror(L, arg, lua.to_luastring("value has no literal form"));
+ lauxlib.luaL_argerror(L, arg, lua.to_luastring("value has no literal form", true));
}
}
};
@@ -248,7 +248,7 @@ const scanformat = function(L, strfrmt, form) {
let p = strfrmt;
while (p[0] !== 0 && FLAGS.indexOf(p[0]) >= 0) p = p.slice(1); /* skip flags */
if (strfrmt.length - p.length >= FLAGS.length)
- lauxlib.luaL_error(L, lua.to_luastring("invalid format (repeated flags)"));
+ lauxlib.luaL_error(L, lua.to_luastring("invalid format (repeated flags)", true));
if (isdigit(p[0])) p = p.slice(1); /* skip width */
if (isdigit(p[0])) p = p.slice(1); /* (2 digits at most) */
if (p[0] === char['.']) {
@@ -257,7 +257,7 @@ const scanformat = function(L, strfrmt, form) {
if (isdigit(p[0])) p = p.slice(1); /* (2 digits at most) */
}
if (isdigit(p[0]))
- lauxlib.luaL_error(L, lua.to_luastring("invalid format (width or precision too long)"));
+ lauxlib.luaL_error(L, lua.to_luastring("invalid format (width or precision too long)", true));
form[0] = char["%"];
for (let i = 0; i < strfrmt.length - p.length + 1; i++)
form[i + 1] = strfrmt[i];
@@ -298,7 +298,7 @@ const str_format = function(L) {
} else { /* format item */
let form = []; /* to store the format ('%...') */
if (++arg > top)
- lauxlib.luaL_argerror(L, arg, lua.to_luastring("no value"));
+ lauxlib.luaL_argerror(L, arg, lua.to_luastring("no value", true));
let f = scanformat(L, strfrmt, form);
strfrmt = f.p;
form = f.form;
@@ -343,7 +343,7 @@ const str_format = function(L) {
concat(b, s); /* keep entire string */
lapi.lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
} else {
- lauxlib.luaL_argcheck(L, s.length === strlen(s), arg, lua.to_luastring("string contains zeros"));
+ lauxlib.luaL_argcheck(L, s.length === strlen(s), arg, lua.to_luastring("string contains zeros", true));
if (form.indexOf(char['.']) < 0 && s.length >= 100) {
/* no precision and string is too long to be formatted */
concat(b, s); /* keep entire string */
@@ -468,7 +468,7 @@ const getoption = function(h, fmt) {
case char['c']: {
r.size = getnum(fmt, -1);
if (r.size === -1)
- lauxlib.luaL_error(h.L, lua.to_luastring("missing size for format option 'c'"));
+ lauxlib.luaL_error(h.L, lua.to_luastring("missing size for format option 'c'", true));
r.opt = KOption.Kchar;
return r;
}
@@ -509,13 +509,13 @@ const getdetails = function(h, totalsize, fmt) {
let align = r.size; /* usually, alignment follows size */
if (r.opt === KOption.Kpaddalign) { /* 'X' gets alignment from following option */
if (fmt.s[0] === 0)
- lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'"));
+ lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'", true));
else {
let o = getoption(h, fmt);
align = o.size;
o = o.opt;
if (o === KOption.Kchar || align === 0)
- lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'"));
+ lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("invalid next option for option 'X'", true));
}
}
if (align <= 1 || r.opt === KOption.Kchar) /* need no alignment? */
@@ -524,7 +524,7 @@ const getdetails = function(h, totalsize, fmt) {
if (align > h.maxalign) /* enforce maximum alignment */
align = h.maxalign;
if ((align & (align -1)) !== 0) /* is 'align' not a power of 2? */
- lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("format asks for alignment not power of 2"));
+ lauxlib.luaL_argerror(h.L, 1, lua.to_luastring("format asks for alignment not power of 2", true));
r.ntoalign = (align - (totalsize & (align - 1))) & (align - 1);
}
return r;
@@ -585,7 +585,7 @@ const str_pack = function(L) {
let n = lauxlib.luaL_checkinteger(L, arg);
if (size < SZINT) { /* need overflow check? */
let lim = 1 << (size * 8) - 1;
- lauxlib.luaL_argcheck(L, -lim <= n && n < lim, arg, lua.to_luastring("integer overflow"));
+ lauxlib.luaL_argcheck(L, -lim <= n && n < lim, arg, lua.to_luastring("integer overflow", true));
}
packint(b, n, h.islittle, size, n < 0);
break;
@@ -593,7 +593,7 @@ const str_pack = function(L) {
case KOption.Kuint: { /* unsigned integers */
let n = lauxlib.luaL_checkinteger(L, arg);
if (size < SZINT)
- lauxlib.luaL_argcheck(L, n < (1 << (size * NB)), arg, lua.to_luastring("unsigned overflow"));
+ lauxlib.luaL_argcheck(L, n < (1 << (size * NB)), arg, lua.to_luastring("unsigned overflow", true));
packint(b, n, h.islittle, size, false);
break;
}
@@ -605,7 +605,7 @@ const str_pack = function(L) {
case KOption.Kchar: { /* fixed-size string */
let s = lauxlib.luaL_checkstring(L, arg);
let len = s.length;
- lauxlib.luaL_argcheck(L, len <= size, arg, lua.to_luastring("string long than given size"));
+ lauxlib.luaL_argcheck(L, len <= size, arg, lua.to_luastring("string long than given size", true));
b.push(...s); /* add string */
while (len++ < size) /* pad extra space */
b.push(LUAL_PACKPADBYTE);
@@ -614,7 +614,7 @@ const str_pack = function(L) {
case KOption.Kstring: { /* strings with length count */
let s = lauxlib.luaL_checkstring(L, arg);
let len = s.length;
- lauxlib.luaL_argcheck(L, size >= NB || len < (1 << size * NB), arg, lua.to_luastring("string length does not fit in given size"));
+ lauxlib.luaL_argcheck(L, size >= NB || len < (1 << size * NB), arg, lua.to_luastring("string length does not fit in given size", true));
packint(b, len, h.islittle, size, 0); /* pack length */
b.push(...s);
totalsize += len;
@@ -623,7 +623,7 @@ const str_pack = function(L) {
case KOption.Kzstr: { /* zero-terminated string */
let s = lauxlib.luaL_checkstring(L, arg);
let len = s.length;
- lauxlib.luaL_argcheck(L, s.length === String.fromCharCode(...s).length, arg, lua.to_luastring("strings contains zeros"));
+ lauxlib.luaL_argcheck(L, s.length === String.fromCharCode(...s).length, arg, lua.to_luastring("strings contains zeros", true));
b.push(...s);
b.push(0); /* add zero at the end */
totalsize += len + 1;
@@ -662,7 +662,7 @@ const str_rep = function(L) {
let sep = lauxlib.luaL_optstring(L, 3, []);
if (s.length + sep.length < s.length || s.length + sep.length > MAXSIZE / n) /* may overflow? */
- return lauxlib.luaL_error(L, lua.to_luastring("resulting string too large"));
+ return lauxlib.luaL_error(L, lua.to_luastring("resulting string too large", true));
let r = [];
for (let i = 0; i < n - 1; i++)
@@ -683,10 +683,10 @@ const str_byte = function(L) {
if (pose > l) pose = l;
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= Number.MAX_SAFE_INTEGER) /* arithmetic overflow? */
- return lauxlib.luaL_error(L, lua.to_luastring("string slice too long"));
+ return lauxlib.luaL_error(L, lua.to_luastring("string slice too long", true));
let n = (pose - posi) + 1;
- lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long"));
+ lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long", true));
for (let i = 0; i < n; i++)
lapi.lua_pushinteger(L, s[posi + i - 1]);
return n;
@@ -707,12 +707,12 @@ const str_packsize = function(L) {
let size = details.size;
let ntoalign = details.ntoalign;
size += ntoalign; /* total space used by option */
- lauxlib.luaL_argcheck(L, totalsize <= MAXSIZE - size - 1, lua.to_luastring("format result too large"));
+ lauxlib.luaL_argcheck(L, totalsize <= MAXSIZE - size - 1, lua.to_luastring("format result too large", true));
totalsize += size;
switch (opt) {
case KOption.Kstring: /* strings with length count */
case KOption.Kzstr: /* zero-terminated string */
- lauxlib.luaL_argerror(L, 1, lua.to_luastring("variable-length format"));
+ lauxlib.luaL_argerror(L, 1, lua.to_luastring("variable-length format", true));
default: break;
}
}
@@ -771,17 +771,17 @@ const str_unpack = function(L) {
let ld = data.length;
let pos = posrelat(lauxlib.luaL_optinteger(L, 3, 1), ld) - 1;
let n = 0; /* number of results */
- lauxlib.luaL_argcheck(L, pos <= ld, 3, lua.to_luastring("initial position out of string"));
+ lauxlib.luaL_argcheck(L, pos <= ld, 3, lua.to_luastring("initial position out of string", true));
while (fmt.s.length - 1 > 0) {
let details = getdetails(h, pos, fmt);
let opt = details.opt;
let size = details.size;
let ntoalign = details.ntoalign;
if (/*ntoalign + size > ~pos ||*/ pos + ntoalign + size > ld)
- lauxlib.luaL_argerror(L, 2, lua.to_luastring("data string too short"));
+ lauxlib.luaL_argerror(L, 2, lua.to_luastring("data string too short", true));
pos += ntoalign; /* skip alignment */
/* stack space for item + next position */
- lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many results"));
+ lauxlib.luaL_checkstack(L, 2, lua.to_luastring("too many results", true));
n++;
switch (opt) {
case KOption.Kint:
@@ -801,7 +801,7 @@ const str_unpack = function(L) {
}
case KOption.Kstring: {
let len = unpackint(L, data.slice(pos), h.islittle, size, 0);
- lauxlib.luaL_argcheck(L, pos + len + size <= ld, 2, lua.to_luastring("data string too short"));
+ lauxlib.luaL_argcheck(L, pos + len + size <= ld, 2, lua.to_luastring("data string too short", true));
lapi.lua_pushstring(L, data.slice(pos + size, pos + size + len));
pos += len; /* skip string */
break;
@@ -852,21 +852,21 @@ const capture_to_close = function(ms) {
let level = ms.level;
for (level--; level >= 0; level--)
if (ms.capture[level].len === CAP_UNFINISHED) return level;
- return lauxlib.luaL_error(ms.L, lua.to_luastring("invalid pattern capture"));
+ return lauxlib.luaL_error(ms.L, lua.to_luastring("invalid pattern capture", true));
};
const classend = function(ms, p) {
switch(ms.p[p++]) {
case L_ESC: {
if (p === ms.p_end)
- lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (ends with '%')"));
+ lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (ends with '%')", true));
return p + 1;
}
case char['[']: {
if (ms.p[p] === char['^']) p++;
do { /* look for a ']' */
if (p === ms.p_end)
- lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing ']')"));
+ lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing ']')", true));
if (ms.p[p++] === L_ESC && p < ms.p_end)
p++; /* skip escapes (e.g. '%]') */
} while (ms.p[p] !== char[']']);
@@ -933,7 +933,7 @@ const singlematch = function(ms, s, p, ep) {
const matchbalance = function(ms, s, p) {
if (p >= ms.p_end - 1)
- lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing arguments to '%b'"));
+ lauxlib.luaL_error(ms.L, lua.to_luastring("malformed pattern (missing arguments to '%b'", true));
if (ms.src[s] !== ms.p[p])
return null;
else {
@@ -976,7 +976,7 @@ const min_expand = function(ms, s, p, ep) {
const start_capture = function(ms, s, p, what) {
let level = ms.level;
- if (level >= LUA_MAXCAPTURES) lauxlib.luaL_error(ms.L, lua.to_luastring("too many captures"));
+ if (level >= LUA_MAXCAPTURES) lauxlib.luaL_error(ms.L, lua.to_luastring("too many captures", true));
ms.capture[level] = ms.capture[level] ? ms.capture[level] : {};
ms.capture[level].init = s;
ms.capture[level].len = what;
@@ -1009,7 +1009,7 @@ const match = function(ms, s, p) {
let gotoinit = true;
if (ms.matchdepth-- === 0)
- lauxlib.luaL_error(ms.L, lua.to_luastring("pattern too complex"));
+ lauxlib.luaL_error(ms.L, lua.to_luastring("pattern too complex", true));
while (gotoinit || gotodefault) {
gotoinit = false;
@@ -1118,7 +1118,7 @@ const push_onecapture = function(ms, i, s, e) {
lauxlib.luaL_error(ms.L, lua.to_luastring(`invalid capture index %${i + 1}`));
} else {
let l = ms.capture[i].len;
- if (l === CAP_UNFINISHED) lauxlib.luaL_error(ms.L, lua.to_luastring("unfinished capture"));
+ if (l === CAP_UNFINISHED) lauxlib.luaL_error(ms.L, lua.to_luastring("unfinished capture", true));
if (l === CAP_POSITION)
lapi.lua_pushinteger(ms.L, ms.src_init + 1);
else
@@ -1128,7 +1128,7 @@ const push_onecapture = function(ms, i, s, e) {
const push_captures = function(ms, s, e) {
let nlevels = ms.level === 0 && ms.src.slice(s) ? 1 : ms.level;
- lauxlib.luaL_checkstack(ms.L, nlevels, lua.to_luastring("too many catpures"));
+ lauxlib.luaL_checkstack(ms.L, nlevels, lua.to_luastring("too many catpures", true));
for (let i = 0; i < nlevels; i++)
push_onecapture(ms, i, s, e);
return nlevels; /* number of strings pushed */
@@ -1340,7 +1340,7 @@ const str_gsub = function(L) {
let ms = new MatchState(L);
let b = new lauxlib.luaL_Buffer(L);
lauxlib.luaL_argcheck(L, tr === CT.LUA_TNUMBER || tr === CT.LUA_TSTRING || tr === CT.LUA_TFUNCTION || tr === CT.LUA_TTABLE, 3,
- lua.to_luastring("string/function/table expected"));
+ lua.to_luastring("string/function/table expected", true));
lauxlib.luaL_buffinit(L, b);
if (anchor) {
p = p.slice(1); lp--; /* skip anchor character */
@@ -1392,7 +1392,7 @@ const createmetatable = function(L) {
lapi.lua_setmetatable(L, -2); /* set table as metatable for strings */
lapi.lua_pop(L, 1); /* pop dummy string */
lapi.lua_pushvalue(L, -2); /* get string library */
- lapi.lua_setfield(L, -2, lua.to_luastring("__index")); /* metatable.__index = string */
+ lapi.lua_setfield(L, -2, lua.to_luastring("__index", true)); /* metatable.__index = string */
lapi.lua_pop(L, 1); /* pop metatable */
};
diff --git a/src/ltable.js b/src/ltable.js
index 039dd6d..6ba8236 100644
--- a/src/ltable.js
+++ b/src/ltable.js
@@ -13,7 +13,7 @@ const TValue = lobject.TValue;
Table.prototype.ordered_intindexes = function() {
return [...this.value.keys()]
- .filter(e => typeof e === 'number' && e % 1 === 0) // Only integer indexes
+ .filter(e => typeof e === 'number' && e % 1 === 0 && e > 0) // Only integer indexes
.sort(function (a, b) {
return a > b ? 1 : -1;
});
@@ -22,8 +22,8 @@ Table.prototype.ordered_intindexes = function() {
Table.prototype.ordered_indexes = function() {
return [...this.value.keys()]
.sort(function(a, b) {
- if (typeof a !== "number") return 1;
- if (typeof b !== "number") return -1;
+ if (typeof a !== "number" || a <= 0) return 1;
+ if (typeof b !== "number" || b <= 0) return -1;
return a > b ? 1 : -1;
});
};
@@ -33,16 +33,18 @@ Table.prototype.ordered_indexes = function() {
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
Table.prototype.luaH_getn = function() {
- // TODO: is this costly ?
let indexes = this.ordered_intindexes();
let len = indexes.length;
+ // If first index != 1, length is 0
+ if (indexes[0] !== 1) return 0;
+
for (let i = 0; i < len; i++) {
let key = indexes[i];
- if (!this.__index(this, key).ttisnil() // t[i] is non-nil
- && (i === len - 1 || this.__index(this, indexes[i + 1]).ttisnil())) { // t[i+1] is nil or is the last integer indexed element
- return indexes[i] + 1;
+ if (!this.__index(this, key).ttisnil() // t[key] is non-nil
+ && (indexes[i + 1] - key > 1 || this.__index(this, indexes[i + 1]).ttisnil())) { // gap with next key or next value is nil
+ return indexes[i];
}
}
diff --git a/src/ltablib.js b/src/ltablib.js
index eaaf24d..0794887 100644
--- a/src/ltablib.js
+++ b/src/ltablib.js
@@ -36,9 +36,9 @@ const checktab = function(L, arg, what) {
if (lapi.lua_type(L, arg) !== CT.LUA_TTABLE) { /* is it not a table? */
let n = 1;
if (lapi.lua_getmetatable(L, arg) && /* must have metatable */
- (!(what & TAB_R) || checkfield(L, lua.to_luastring("__index"), ++n)) &&
- (!(what & TAB_W) || checkfield(L, lua.to_luastring("__newindex"), ++n)) &&
- (!(what & TAB_L) || checkfield(L, lua.to_luastring("__len"), ++n))) {
+ (!(what & TAB_R) || checkfield(L, lua.to_luastring("__index", true), ++n)) &&
+ (!(what & TAB_W) || checkfield(L, lua.to_luastring("__newindex", true), ++n)) &&
+ (!(what & TAB_L) || checkfield(L, lua.to_luastring("__len", true), ++n))) {
lapi.lua_pop(L, n); /* pop metatable and tested metamethods */
}
else
@@ -68,7 +68,7 @@ const tinsert = function(L) {
break;
case 3: {
pos = lauxlib.luaL_checkinteger(L, 2); /* 2nd argument is the position */
- lauxlib.luaL_argcheck(L, 1 <= pos && pos <= e, 2, lua.to_luastring("position out of bounds"));
+ lauxlib.luaL_argcheck(L, 1 <= pos && pos <= e, 2, lua.to_luastring("position out of bounds", true));
for (let i = e; i > pos; i--) { /* move up elements */
lapi.lua_geti(L, 1, i - 1);
lapi.lua_seti(L, 1, i); /* t[i] = t[i - 1] */
@@ -76,7 +76,7 @@ const tinsert = function(L) {
break;
}
default: {
- return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments to 'insert'"));
+ return lauxlib.luaL_error(L, lua.to_luastring("wrong number of arguments to 'insert'", true));
}
}
@@ -88,7 +88,7 @@ const tremove = function(L) {
let size = aux_getn(L, 1, TAB_RW);
let pos = lauxlib.luaL_optinteger(L, 2, size);
if (pos !== size) /* validate 'pos' if given */
- lauxlib.luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, lua.to_luastring("position out of bounds"));
+ lauxlib.luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, lua.to_luastring("position out of bounds", true));
lapi.lua_geti(L, 1, pos); /* result = t[pos] */
for (; pos < size; pos++) {
lapi.lua_geti(L, 1, pos + 1);
@@ -113,9 +113,9 @@ const tmove = function(L) {
checktab(L, 1, TAB_R);
checktab(L, tt, TAB_W);
if (e >= f) { /* otherwise, nothing to move */
- lauxlib.luaL_argcheck(L, f > 0 || e < llimit.LUA_MAXINTEGER + f, 3, lua.to_luastring("too many elements to move"));
+ lauxlib.luaL_argcheck(L, f > 0 || e < llimit.LUA_MAXINTEGER + f, 3, lua.to_luastring("too many elements to move", true));
let n = e - f + 1; /* number of elements to move */
- lauxlib.luaL_argcheck(L, t <= llimit.LUA_MAXINTEGER - n + 1, 4, lua.to_luastring("destination wrap around"));
+ lauxlib.luaL_argcheck(L, t <= llimit.LUA_MAXINTEGER - n + 1, 4, lua.to_luastring("destination wrap around", true));
if (t > e || t <= f || (tt !== 1 && lapi.lua_compare(L, 1, tt, lua.LUA_OPEQ) !== 1)) {
for (let i = 0; i < n; i++) {
@@ -173,7 +173,7 @@ const unpack = function(L) {
if (i > e) return 0; /* empty range */
let n = e - i; /* number of elements minus 1 (avoid overflows) */
if (n >= Number.MAX_SAFE_INTEGER || !lapi.lua_checkstack(L, ++n))
- return lauxlib.luaL_error(L, lua.to_luastring("too many results to unpack"));
+ return lauxlib.luaL_error(L, lua.to_luastring("too many results to unpack", true));
for (; i < e; i++) /* push arg[i..e - 1] (to avoid overflows) */
lapi.lua_geti(L, 1, i);
lapi.lua_geti(L, 1, e); /* push last element */
@@ -192,7 +192,7 @@ const auxsort = function(L) {
else if (typeof b[0] !== 'number') return -1;
return lapi.lua_compare_(L, a[1], b[1], lua.LUA_OPLT) === 1 ? -1 : 1; /* a < b */
})
- .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i, e[1]) : true);
+ .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i + 1, e[1]) : true);
} else {
[...t.value.entries()]
.sort(function (a, b) {
@@ -207,14 +207,14 @@ const auxsort = function(L) {
lapi.lua_pop(L, 1); /* pop result */
return res ? -1 : 1;
})
- .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i, e[1]) : true);
+ .forEach((e, i) => typeof e[0] === 'number' ? t.value.set(i + 1, e[1]) : true);
}
};
const sort = function(L) {
let n = aux_getn(L, 1, TAB_RW);
if (n > 1) { /* non-trivial interval? */
- lauxlib.luaL_argcheck(L, n < Number.MAX_SAFE_INTEGER, 1, lua.to_luastring("array too big"));
+ lauxlib.luaL_argcheck(L, n < Number.MAX_SAFE_INTEGER, 1, lua.to_luastring("array too big", true));
if (!lapi.lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
lauxlib.luaL_checktype(L, 2, CT.LUA_TFUNCTION); /* must be a function */
lapi.lua_settop(L, 2); /* make sure there are two arguments */
diff --git a/src/ltm.js b/src/ltm.js
index 4a90f6d..57c2e3f 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -15,30 +15,30 @@ const CT = lua.constant_types;
const TMS = {
- TM_INDEX: lua.to_luastring("__index"),
- TM_NEWINDEX: lua.to_luastring("__newindex"),
- TM_GC: lua.to_luastring("__gc"),
- TM_MODE: lua.to_luastring("__mode"),
- TM_LEN: lua.to_luastring("__len"),
- TM_EQ: lua.to_luastring("__eq"), /* last tag method with fast access */
- TM_ADD: lua.to_luastring("__add"),
- TM_SUB: lua.to_luastring("__sub"),
- TM_MUL: lua.to_luastring("__mul"),
- TM_MOD: lua.to_luastring("__mod"),
- TM_POW: lua.to_luastring("__pow"),
- TM_DIV: lua.to_luastring("__div"),
- TM_IDIV: lua.to_luastring("__idiv"),
- TM_BAND: lua.to_luastring("__band"),
- TM_BOR: lua.to_luastring("__bor"),
- TM_BXOR: lua.to_luastring("__bxor"),
- TM_SHL: lua.to_luastring("__shl"),
- TM_SHR: lua.to_luastring("__shr"),
- TM_UNM: lua.to_luastring("__unm"),
- TM_BNOT: lua.to_luastring("__bnot"),
- TM_LT: lua.to_luastring("__lt"),
- TM_LE: lua.to_luastring("__le"),
- TM_CONCAT: lua.to_luastring("__concat"),
- TM_CALL: lua.to_luastring("__call")
+ TM_INDEX: lua.to_luastring("__index", true),
+ TM_NEWINDEX: lua.to_luastring("__newindex", true),
+ TM_GC: lua.to_luastring("__gc", true),
+ TM_MODE: lua.to_luastring("__mode", true),
+ TM_LEN: lua.to_luastring("__len", true),
+ TM_EQ: lua.to_luastring("__eq", true), /* last tag method with fast access */
+ TM_ADD: lua.to_luastring("__add", true),
+ TM_SUB: lua.to_luastring("__sub", true),
+ TM_MUL: lua.to_luastring("__mul", true),
+ TM_MOD: lua.to_luastring("__mod", true),
+ TM_POW: lua.to_luastring("__pow", true),
+ TM_DIV: lua.to_luastring("__div", true),
+ TM_IDIV: lua.to_luastring("__idiv", true),
+ TM_BAND: lua.to_luastring("__band", true),
+ TM_BOR: lua.to_luastring("__bor", true),
+ TM_BXOR: lua.to_luastring("__bxor", true),
+ TM_SHL: lua.to_luastring("__shl", true),
+ TM_SHR: lua.to_luastring("__shr", true),
+ TM_UNM: lua.to_luastring("__unm", true),
+ TM_BNOT: lua.to_luastring("__bnot", true),
+ TM_LT: lua.to_luastring("__lt", true),
+ TM_LE: lua.to_luastring("__le", true),
+ TM_CONCAT: lua.to_luastring("__concat", true),
+ TM_CALL: lua.to_luastring("__call", true)
};
const luaT_typenames_ = [
@@ -71,7 +71,7 @@ const luaT_init = function(L) {
*/
const luaT_objtypename = function(L, o) {
if ((o.ttistable() && o.metatable !== null) || (o.ttisfulluserdata() && o.metatable !== null)) {
- let name = o.__index(o, lua.to_luastring('__name'));
+ let name = o.__index(o, lua.to_luastring('__name', true));
if (name.ttisstring())
return name.jsstring();
}
@@ -123,10 +123,10 @@ const luaT_trybinTM = function(L, p1, p2, res, event) {
if (n1 !== false && n2 !== false)
ldebug.luaG_tointerror(L, p1, p2);
else
- ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform bitwise operation on"));
+ ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform bitwise operation on", true));
}
default:
- ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform arithmetic on"));
+ ldebug.luaG_opinterror(L, p1, p2, lua.to_luastring("perform arithmetic on", true));
}
}
};
diff --git a/src/lua.js b/src/lua.js
index d4f620b..5175bfb 100644
--- a/src/lua.js
+++ b/src/lua.js
@@ -140,9 +140,16 @@ class lua_Debug {
}
-const to_luastring = function(str, maxBytesToWrite) {
+const to_luastring_cache = {};
+
+const to_luastring = function(str, cache, maxBytesToWrite) {
assert(typeof str === "string", "to_luastring expect a js string");
+ if (cache) {
+ let cached = to_luastring_cache[str];
+ if (Array.isArray(cached)) return cached;
+ }
+
maxBytesToWrite = maxBytesToWrite !== undefined ? maxBytesToWrite : Number.MAX_SAFE_INTEGER;
let outU8Array = [];
@@ -195,9 +202,29 @@ const to_luastring = function(str, maxBytesToWrite) {
}
// Null-terminate the pointer to the buffer.
// outU8Array[outIdx] = 0;
+
+ if (cache) to_luastring_cache[str] = outU8Array;
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 +236,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/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/src/lutf8lib.js b/src/lutf8lib.js
index ef9739f..f58223a 100644
--- a/src/lutf8lib.js
+++ b/src/lutf8lib.js
@@ -87,7 +87,7 @@ const utflen = function(L) {
const pushutfchar = function(L, arg) {
let code = lauxlib.luaL_checkinteger(L, arg);
- lauxlib.luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, lua.to_luastring("value out of range"));
+ lauxlib.luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, lua.to_luastring("value out of range", true));
lapi.lua_pushstring(L, lua.to_luastring(String.fromCharCode(code)));
};
@@ -121,14 +121,14 @@ const byteoffset = function(L) {
let posi = n >= 0 ? 1 : s.length + 1;
posi = u_posrelat(lauxlib.luaL_optinteger(L, 3, posi), s.length);
- lauxlib.luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, lua.to_luastring("position ot ouf range"));
+ lauxlib.luaL_argcheck(L, 1 <= posi && --posi <= s.length, 3, lua.to_luastring("position ot ouf range", true));
if (n === 0) {
/* find beginning of current byte sequence */
while (posi > 0 && iscont(s[posi])) posi--;
} else {
if (iscont(s[posi]))
- lauxlib.luaL_error(L, lua.to_luastring("initial position is a continuation byte"));
+ lauxlib.luaL_error(L, lua.to_luastring("initial position is a continuation byte", true));
if (n < 0) {
while (n < 0 && posi > 0) { /* move back */
@@ -166,19 +166,19 @@ const codepoint = function(L) {
let posi = u_posrelat(lauxlib.luaL_optinteger(L, 2, 1), s.length);
let pose = u_posrelat(lauxlib.luaL_optinteger(L, 3, posi), s.length);
- lauxlib.luaL_argcheck(L, posi >= 1, 2, lua.to_luastring("out of range"));
- lauxlib.luaL_argcheck(L, pose <= s.length, 3, lua.to_luastring("out of range"));
+ lauxlib.luaL_argcheck(L, posi >= 1, 2, lua.to_luastring("out of range", true));
+ lauxlib.luaL_argcheck(L, pose <= s.length, 3, lua.to_luastring("out of range", true));
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= Number.MAX_SAFE_INTEGER)
- return lauxlib.luaL_error(L, lua.to_luastring("string slice too long"));
+ return lauxlib.luaL_error(L, lua.to_luastring("string slice too long", true));
let n = (pose - posi) + 1;
- lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long"));
+ lauxlib.luaL_checkstack(L, n, lua.to_luastring("string slice too long", true));
n = 0;
for (s = s.slice(posi - 1); n < pose - posi;) {
let dec = utf8_decode(s);
if (dec === null)
- return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code"));
+ return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code", true));
s = dec.string;
let code = dec.code;
lapi.lua_pushinteger(L, code);
@@ -207,7 +207,7 @@ const iter_aux = function(L) {
let code = dec ? dec.code : null;
let next = dec ? dec.string : null;
if (next === null || iscont(next[0]))
- return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code"));
+ return lauxlib.luaL_error(L, lua.to_luastring("invalid UTF-8 code", true));
lapi.lua_pushinteger(L, n + 1);
lapi.lua_pushinteger(L, code);
return 2;
@@ -236,7 +236,7 @@ const UTF8PATT = "[\0-\x7F\xC2-\xF4][\x80-\xBF]*";
const luaopen_utf8 = function(L) {
lauxlib.luaL_newlib(L, funcs);
lapi.lua_pushstring(L, lua.to_luastring(UTF8PATT));
- lapi.lua_setfield(L, -2, lua.to_luastring("charpattern"));
+ lapi.lua_setfield(L, -2, lua.to_luastring("charpattern", true));
return 1;
};
diff --git a/src/lvm.js b/src/lvm.js
index ad1e807..1d131cf 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
@@ -228,7 +235,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value + op2.value));
} else if (numberop1 !== false && numberop2 !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value + op2.value);
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, numberop1 + numberop2);
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_ADD);
base = ci.u.l.base;
@@ -244,7 +251,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value - op2.value));
} else if (numberop1 !== false && numberop2 !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value - op2.value);
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, numberop1 - numberop2);
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SUB);
base = ci.u.l.base;
@@ -260,7 +267,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value * op2.value));
} else if (numberop1 !== false && numberop2 !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value * op2.value);
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, numberop1 * numberop2);
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MUL);
base = ci.u.l.base;
@@ -274,9 +281,9 @@ const luaV_execute = function(L) {
let numberop2 = tonumber(op2);
if (op1.ttisinteger() && op2.ttisinteger()) {
- L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value % op2.value));
+ L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value - Math.floor(op1.value / op2.value) * op2.value));
} else if (numberop1 !== false && numberop2 !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value % op2.value);
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, (numberop1 - Math.floor(numberop1 / numberop2) * numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MOD);
base = ci.u.l.base;
@@ -290,7 +297,7 @@ const luaV_execute = function(L) {
let numberop2 = tonumber(op2);
if (numberop1 !== false && numberop2 !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.pow(op1.value, op2.value));
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.pow(numberop1, numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_POW);
base = ci.u.l.base;
@@ -320,7 +327,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, Math.floor(op1.value / op2.value));
} else if (numberop1 !== false && numberop2 !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.floor(op1.value / op2.value));
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.floor(numberop1 / numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_IDIV);
base = ci.u.l.base;
@@ -330,11 +337,11 @@ const luaV_execute = function(L) {
case OCi.OP_BAND: {
let op1 = RKB(L, base, k, i);
let op2 = RKC(L, base, k, i);
- let numberop1 = tonumber(op1);
- let numberop2 = tonumber(op2);
+ let numberop1 = tointeger(op1);
+ let numberop2 = tointeger(op2);
- if (op1.ttisinteger() && op2.ttisinteger()) {
- L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value & op2.value));
+ if (numberop1 !== false && numberop2) {
+ L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 & numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BAND);
base = ci.u.l.base;
@@ -344,11 +351,11 @@ const luaV_execute = function(L) {
case OCi.OP_BOR: {
let op1 = RKB(L, base, k, i);
let op2 = RKC(L, base, k, i);
- let numberop1 = tonumber(op1);
- let numberop2 = tonumber(op2);
+ let numberop1 = tointeger(op1);
+ let numberop2 = tointeger(op2);
- if (op1.ttisinteger() && op2.ttisinteger()) {
- L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value | op2.value));
+ if (numberop1 !== false && numberop2) {
+ L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 | numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BOR);
base = ci.u.l.base;
@@ -358,11 +365,11 @@ const luaV_execute = function(L) {
case OCi.OP_BXOR: {
let op1 = RKB(L, base, k, i);
let op2 = RKC(L, base, k, i);
- let numberop1 = tonumber(op1);
- let numberop2 = tonumber(op2);
+ let numberop1 = tointeger(op1);
+ let numberop2 = tointeger(op2);
- if (op1.ttisinteger() && op2.ttisinteger()) {
- L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value ^ op2.value));
+ if (numberop1 !== false && numberop2) {
+ L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 ^ numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BXOR);
base = ci.u.l.base;
@@ -372,11 +379,11 @@ const luaV_execute = function(L) {
case OCi.OP_SHL: {
let op1 = RKB(L, base, k, i);
let op2 = RKC(L, base, k, i);
- let numberop1 = tonumber(op1);
- let numberop2 = tonumber(op2);
+ let numberop1 = tointeger(op1);
+ let numberop2 = tointeger(op2);
- if (op1.ttisinteger() && op2.ttisinteger()) {
- L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value << op2.value));
+ if (numberop1 !== false && numberop2) {
+ L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 << numberop2)); // TODO: luaV_shiftl ?
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHL);
base = ci.u.l.base;
@@ -386,11 +393,11 @@ const luaV_execute = function(L) {
case OCi.OP_SHR: {
let op1 = RKB(L, base, k, i);
let op2 = RKC(L, base, k, i);
- let numberop1 = tonumber(op1);
- let numberop2 = tonumber(op2);
+ let numberop1 = tointeger(op1);
+ let numberop2 = tointeger(op2);
- if (op1.ttisinteger() && op2.ttisinteger()) {
- L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value >> op2.value));
+ if (numberop1 !== false && numberop2) {
+ L.stack[ra] = new TValue(CT.LUA_TNUMINT, (numberop1 >> numberop2));
} else {
ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHR);
base = ci.u.l.base;
@@ -404,7 +411,7 @@ const luaV_execute = function(L) {
if (op.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, -op.value);
} else if (numberop !== false) {
- L.stack[ra] = new TValue(CT.LUA_TNUMFLT, -op.value);
+ L.stack[ra] = new TValue(CT.LUA_TNUMFLT, -numberop);
} else {
ltm.luaT_trybinTM(L, op, op, ra, ltm.TMS.TM_UNM);
base = ci.u.l.base;
@@ -595,19 +602,19 @@ const luaV_execute = function(L) {
let nstep = tonumber(pstep);
if (nlimit === false)
- ldebug.luaG_runerror(L, lua.to_luastring("'for' limit must be a number"));
+ ldebug.luaG_runerror(L, lua.to_luastring("'for' limit must be a number", true));
plimit.type = CT.LUA_TNUMFLT;
plimit.value = nlimit;
if (nstep === false)
- ldebug.luaG_runerror(L, lua.to_luastring("'for' step must be a number"));
+ ldebug.luaG_runerror(L, lua.to_luastring("'for' step must be a number", true));
pstep.type = CT.LUA_TNUMFLT;
pstep.value = nstep;
if (ninit === false)
- ldebug.luaG_runerror(L, lua.to_luastring("'for' initial value must be a number"));
+ ldebug.luaG_runerror(L, lua.to_luastring("'for' initial value must be a number", true));
init.type = CT.LUA_TNUMFLT;
init.value = ninit - nstep;
@@ -768,9 +775,10 @@ const luaV_equalobj = function(L, t1, t2) {
switch(t1.ttype()) {
case CT.LUA_TNIL:
return 1;
+ case CT.LUA_TBOOLEAN:
+ return t1.value == t2.value ? 1 : 0; // Might be 1 or true
case CT.LUA_TNUMINT:
case CT.LUA_TNUMFLT:
- case CT.LUA_TBOOLEAN:
case CT.LUA_TLCF:
return t1.value === t2.value ? 1 : 0;
case CT.LUA_TSHRSTR:
@@ -861,7 +869,7 @@ const luaV_tointeger = function(obj, mode) {
} else if (obj.ttisinteger()) {
return obj.value;
} else if (obj.ttisstring()) {
- return luaV_tointeger(new TValue(CT.LUA_TNUMFLT, parseFloat(obj.jsstring())), mode); // TODO: luaO_str2num
+ return luaV_tointeger(lobject.luaO_str2num(obj.value), mode);
}
return false;
@@ -876,7 +884,7 @@ const tonumber = function(v) {
return v.value;
if (v.ttnov() === CT.LUA_TSTRING)
- return parseFloat(v.value); // TODO: luaO_str2num
+ return lobject.luaO_str2num(v.value);
return false;
};
@@ -980,7 +988,7 @@ const luaV_objlen = function(L, ra, rb) {
default: {
tm = ltm.luaT_gettmbyobj(L, rb, ltm.TMS.TM_LEN);
if (tm.ttisnil())
- ldebug.luaG_typeerror(L, rb, lua.to_luastring("get length of"));
+ ldebug.luaG_typeerror(L, rb, lua.to_luastring("get length of", true));
break;
}
}
@@ -1045,7 +1053,7 @@ const gettable = function(L, table, key, ra, recur) {
recur = recur ? recur : 0;
if (recur >= MAXTAGRECUR)
- ldebug.luaG_runerror(L, lua.to_luastring("'__index' chain too long; possible loop"));
+ ldebug.luaG_runerror(L, lua.to_luastring("'__index' chain too long; possible loop", true));
if (table.ttistable()) {
let element = table.__index(table, key);
@@ -1066,7 +1074,7 @@ const luaV_finishget = function(L, t, key, val, slot, recur) {
assert(!t.ttistable());
tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX);
if (tm.ttisnil())
- ldebug.luaG_typeerror(L, t, lua.to_luastring('index'));
+ ldebug.luaG_typeerror(L, t, lua.to_luastring('index', true));
} else { /* 't' is a table */
assert(slot.ttisnil());
tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX); // TODO: fasttm
@@ -1088,7 +1096,7 @@ const settable = function(L, table, key, v, recur) {
recur = recur ? recur : 0;
if (recur >= MAXTAGRECUR)
- ldebug.luaG_runerror(L, lua.to_luastring("'__newindex' chain too long; possible loop"));
+ ldebug.luaG_runerror(L, lua.to_luastring("'__newindex' chain too long; possible loop", true));
if (table.ttistable()) {
let element = table.__index(table, key);
@@ -1115,7 +1123,7 @@ const luaV_finishset = function(L, t, key, val, slot, recur) {
} else { /* not a table; check metamethod */
tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_NEWINDEX);
if (tm.ttisnil())
- ldebug.luaG_typeerror(L, t, lua.to_luastring('index'));
+ ldebug.luaG_typeerror(L, t, lua.to_luastring('index', true));
}
if (tm.ttisfunction()) {
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/ldblib.js b/tests/ldblib.js
index a770c76..6587228 100644
--- a/tests/ldblib.js
+++ b/tests/ldblib.js
@@ -7,6 +7,106 @@ 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 = `
+ local 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 return ",
+ "Correct element(s) on the stack"
+ );
+
+});
+
+
+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"
@@ -54,6 +154,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"
@@ -91,6 +257,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
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/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/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/lua-tests b/tests/lua-tests
-Subproject 01eded4b34159a77f61b56c5567c74f169d3b1e
+Subproject 9e0d0bfb5de8cb0534a13c19f23377db62b53d5
diff --git a/tests/lua.js b/tests/lua.js
index 43e82bc..30a9c98 100644
--- a/tests/lua.js
+++ b/tests/lua.js
@@ -7,6 +7,35 @@ 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 = `
+ _soft = true
+ require = function(lib) return _G[lib] end -- NYI
+ return dofile("tests/lua-tests/constructs.lua")
+ `, L;
+
+ t.plan(2);
+
+ 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")
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"
);