From 98994b4140e9df0b7795c74f6debf0a4161aae5b Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 23 May 2017 11:06:36 +0200 Subject: ltests.js: sethook, runJS --- tests/test-suite/ltests.js | 382 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 381 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test-suite/ltests.js b/tests/test-suite/ltests.js index b4b575b..fc81131 100644 --- a/tests/test-suite/ltests.js +++ b/tests/test-suite/ltests.js @@ -4,7 +4,324 @@ global.WEB = false; const lua = require('../../src/lua.js'); const lauxlib = require('../../src/lauxlib.js'); +const ljstype = require('../../src/ljstype.js'); +const delimits = [" ", "\t", "\n", ",", ";"].map(e => e.charCodeAt(0)); + +const skip = function(pc) { + for (;;) { + if (pc.script[pc.offset] !== 0 && pc.offset < pc.script.length && delimits.indexOf(pc.script[pc.offset]) >= 0) + pc.offset++; + else if (pc.script[pc.offset] === '#'.charCodeAt(0)) { + while (pc.script[pc.offset] !== '\n'.charCodeAt(0) && pc.script[pc.offset] !== 0 && pc.offset < pc.script.length) + pc.offset++; + } else break; + } +}; + +const getnum = function(L, L1, pc) { + let res = 0; + let sig = 1; + skip(pc); + if (pc.script[pc.offset] === '.'.charCodeAt(0)) { + res = lua.lua_tointeger(L1, -1); + lua.lua_pop(L1, 1); + pc.offset++; + return res; + } else if (pc.script[pc.offset] === '*'.charCodeAt(0)) { + res = lua.lua_gettop(L1); + pc.offset++; + return res; + } + else if (pc.script[pc.offset] === '-'.charCodeAt(0)) { + sig = -1; + pc.offset++; + } + if (!ljstype.lisdigit(pc.script[pc.offset])) + lauxlib.luaL_error(L, lua.to_luastring("number expected (%s)"), pc.script); + while (ljstype.lisdigit(pc.script[pc.offset])) res = res*10 + pc.script[pc.offset++] - '0'.charCodeAt(0); + return sig*res; +}; + +const getstring = function(L, buff, pc) { + let i = 0; + skip(pc); + if (pc.script[pc.offset] === '"'.charCodeAt(0) || pc.script[pc.offset] === '\''.charCodeAt(0)) { /* quoted string? */ + let quote = pc.script[pc.offset++]; + while (pc.script[pc.offset] !== quote) { + if (pc.script[pc.offset] === 0 || pc.offset >= pc.script.length) + lauxlib.luaL_error(L, lua.to_luastring("unfinished string in JS script", true)); + buff[i++] = pc.script[pc.offset++]; + } + pc.offset++; + } else { + while (pc.script[pc.offset] !== 0 && pc.offset < pc.script.length && delimits.indexOf(pc.script[pc.offset]) < 0) + buff[i++] = pc.script[pc.offset++]; + } + buff[i] = 0; + return buff; +}; + +const getindex = function(L, L1, pc) { + skip(pc); + switch (pc.script[pc.offset++]) { + case 'R'.charCodeAt(0): return lua.LUA_REGISTRYINDEX; + case 'G'.charCodeAt(0): return lauxlib.luaL_error(L, lua.to_luastring("deprecated index 'G'", true)); + case 'U'.charCodeAt(0): return lua.lua_upvalueindex(getnum(L, L1, pc)); + default: pc.offset--; return getnum(L, L1, pc); + } +}; + +const codes = ["OK", "YIELD", "ERRRUN", "ERRSYNTAX", "ERRMEM", "ERRGCMM", "ERRERR"].map(e => lua.to_luastring(e)); + +const pushcode = function(L, code) { + lua.lua_pushstring(L, codes[code]); +}; + +const printstack = function(L) { + let n = lua.lua_gettop(L); + for (let i = 1; i <= n; i++) { + console.log("${i}: %{lua.to_jsstring(lauxlib.luaL_tolstring(L, i, null))}\n"); + lua.lua_pop(L, 1); + } + console.log(""); +}; + +/* +** arithmetic operation encoding for 'arith' instruction +** LUA_OPIDIV -> \ +** LUA_OPSHL -> < +** LUA_OPSHR -> > +** LUA_OPUNM -> _ +** LUA_OPBNOT -> ! +*/ +const ops = "+-*%^/\\&|~<>_!".split('').map(e => e.charCodeAt(0)); + +const runJS = function(L, L1, pc) { + let buff = []; + let status = 0; + if (!pc || pc.length === 0) return lauxlib.luaL_error(L, "attempt to runJS empty script"); + for (;;) { + let inst = getstring(L, buff, pc); + if (inst.length === 0) return 0; + else if (inst === "absindex") { + lua.lua_pushnumber(1, lua.lua_absindex(1, getindex(L, L1, pc))); + } else if (inst === "append") { + let t = getindex(L, L1, pc); + let i = lua.lua_rawlen(1, t); + lua.lua_rawseti(1, t, i + 1); + } else if (inst === "arith") { + let op; + skip(pc); + op = ops.indexOf(pc[0]); + lua.lua_arith(1, op); + } else if (inst === "call") { + let narg = getnum(L, L1, pc); + let nres = getnum(L, L1, pc); + lua.lua_call(1, narg, nres); + } else if (inst === "callk") { + let narg = getnum(L, L1, pc); + let nres = getnum(L, L1, pc); + let i = getindex(L, L1, pc); + lua.lua_callk(1, narg, nres, i, Cfunck); + } else if (inst === "checkstack") { + let sz = getnum(L, L1, pc); + let msg = getstring(L, buff, pc); + if (msg.length === 0) + msg = null; /* to test 'luaL_checkstack' with no message */ + lauxlib.luaL_checkstack(1, sz, msg); + } else if (inst === "compare") { + let opt = getstring(L, buff, pc); /* EQ, LT, or LE */ + let op = (opt[0] === 'E'.charCodeAt(0)) ? lua.LUA_OPEQ + : (opt[1] === 'T'.charCodeAt(0)) ? lua.LUA_OPLT : lua.LUA_OPLE; + let a = getindex(L, L1, pc); + let b = getindex(L, L1, pc); + lua.lua_pushboolean(1, lua.lua_compare(1, a, b, op)); + } else if (inst === "concat") { + lua.lua_concat(1, getnum(L, L1, pc)); + } else if (inst === "copy") { + let f = getindex(L, L1, pc); + lua.lua_copy(1, f, getindex(L, L1, pc)); + } else if (inst === "func2num") { + let func = lua.lua_tocfunction(1, getindex(L, L1, pc)); + lua.lua_pushnumber(1, NaN); // TOODO + } else if (inst === "getfield") { + let t = getindex(L, L1, pc); + lua.lua_getfield(1, t, getstring(L, buff, pc)); + } else if (inst === "getglobal") { + lua.lua_getglobal(1, getstring(L, buff, pc)); + } else if (inst === "getmetatable") { + if (lua.lua_getmetatable(1, getindex(L, L1, pc)) === 0) + lua.lua_pushnil(1); + } else if (inst === "gettable") { + lua.lua_gettable(1, getindex(L, L1, pc)); + } else if (inst === "gettop") { + lua.lua_pushinteger(1, lua.lua_gettop(1)); + } else if (inst === "gsub") { + let a = getnum(L, L1, pc); + let b = getnum(L, L1, pc); + let c = getnum(L, L1, pc); + lauxlib.luaL_gsub(1, lua.lua_tostring(1, a), lua.lua_tostring(1, b), lua.lua_tostring(1, c)); + } else if (inst === "insert") { + lua.lua_insert(1, getnum(L, L1, pc)); + } else if (inst === "iscfunction") { + lua.lua_pushboolean(1, lua.lua_iscfunction(1, getindex(L, L1, pc))); + } else if (inst === "isfunction") { + lua.lua_pushboolean(1, lua.lua_isfunction(1, getindex(L, L1, pc))); + } else if (inst === "isnil") { + lua.lua_pushboolean(1, lua.lua_isnil(1, getindex(L, L1, pc))); + } else if (inst === "isnull") { + lua.lua_pushboolean(1, lua.lua_isnone(1, getindex(L, L1, pc))); + } else if (inst === "isnumber") { + lua.lua_pushboolean(1, lua.lua_isnumber(1, getindex(L, L1, pc))); + } else if (inst === "isstring") { + lua.lua_pushboolean(1, lua.lua_isstring(1, getindex(L, L1, pc))); + } else if (inst === "istable") { + lua.lua_pushboolean(1, lua.lua_istable(1, getindex(L, L1, pc))); + } else if (inst === "isudataval") { + lua.lua_pushboolean(1, lua.lua_islightuserdata(1, getindex(L, L1, pc))); + } else if (inst === "isuserdata") { + lua.lua_pushboolean(1, lua.lua_isuserdata(1, getindex(L, L1, pc))); + } else if (inst === "len") { + lua.lua_len(1, getindex(L, L1, pc)); + } else if (inst === "Llen") { + lua.lua_pushinteger(1, lauxlib.luaL_len(1, getindex(L, L1, pc))); + } else if (inst === "loadfile") { + lauxlib.luaL_loadfile(1, lauxlib.luaL_checkstring(1, getnum(L, L1, pc))); + } else if (inst === "loadstring") { + let s = lauxlib.luaL_checkstring(1, getnum(L, L1, pc)); + lauxlib.luaL_loadstring(1, s); + } else if (inst === "newmetatable") { + lua.lua_pushboolean(1, lauxlib.luaL_newmetatable(1, getstring(L, buff, pc))); + } else if (inst === "newtable") { + lua.lua_newtable(1); + } else if (inst === "newthread") { + lua.lua_newthread(1); + } else if (inst === "newuserdata") { + lua.lua_newuserdata(L1, getnum(L, L1, pc)); + } else if (inst === "next") { + lua.lua_next(L1, -2); + } else if (inst === "objsize") { + lua.lua_pushinteger(L1, lua.lua_rawlen(L1, getindex(L, L1, pc))); + } else if (inst === "pcall") { + let narg = getnum(L, L1, pc); + let nres = getnum(L, L1, pc); + status = lua.lua_pcall(L1, narg, nres, getnum(L, L1, pc)); + } else if (inst === "pcallk") { + let narg = getnum(L, L1, pc); + let nres = getnum(L, L1, pc); + let i = getindex(L, L1, pc); + status = lua.lua_pcallk(L1, narg, nres, 0, i, Cfunck); + } else if (inst === "pop") { + lua.lua_pop(L1, getnum(L, L1, pc)); + } else if (inst === "print") { + let n = getnum(L, L1, pc); + if (n !== 0) { + console.log(`${lauxlib.luaL_tojsstring(L1, n, null)}\n`); + lua.lua_pop(L1, 1); + } + else printstack(L1); + } else if (inst === "pushbool") { + lua.lua_pushboolean(L1, getnum(L, L1, pc)); + } else if (inst === "pushcclosure") { + lua.lua_pushcclosure(L1, testC, getnum(L, L1, pc)); + } else if (inst === "pushint") { + lua.lua_pushinteger(L1, getnum(L, L1, pc)); + } else if (inst === "pushnil") { + lua.lua_pushnil(L1); + } else if (inst === "pushnum") { + lua.lua_pushnumber(L1, getnum(L, L1, pc)); + } else if (inst === "pushstatus") { + pushcode(L1, status); + } else if (inst === "pushstring") { + lua.lua_pushstring(L1, getstring(L, buff, pc)); + } else if (inst === "pushupvalueindex") { + lua.lua_pushinteger(L1, lua.lua_upvalueindex(getnum(L, L1, pc))); + } else if (inst === "pushvalue") { + lua.lua_pushvalue(L1, getindex(L, L1, pc)); + } else if (inst === "rawgeti") { + let t = getindex(L, L1, pc); + lua.lua_rawgeti(L1, t, getnum(L, L1, pc)); + } else if (inst === "rawgetp") { + let t = getindex(L, L1, pc); + lua.lua_rawgetp(L1, t, getnum(L, L1, pc)); + } else if (inst === "rawsetp") { + let t = getindex(L, L1, pc); + lua.lua_rawsetp(L1, t, getnum(L, L1, pc)); + } else if (inst === "remove") { + lua.lua_remove(L1, getnum(L, L1, pc)); + } else if (inst === "replace") { + lua.lua_replace(L1, getindex(L, L1, pc)); + } else if (inst === "resume") { + let i = getindex(L, L1, pc); + status = lua.lua_resume(lua.lua_tothread(L1, i), L, getnum(L, L1, pc)); + } else if (inst === "return") { + let n = getnum(L, L1, pc); + if (L1 != L) { + let i; + for (i = 0; i < n; i++) + lua.lua_pushstring(L, lua.lua_tostring(L1, -(n - i))); + } + return n; + } else if (inst === "rotate") { + let i = getindex(L, L1, pc); + lua.lua_rotate(L1, i, getnum(L, L1, pc)); + } else if (inst === "setfield") { + let t = getindex(L, L1, pc); + lua.lua_setfield(L1, t, getstring(L, buff, pc)); + } else if (inst === "setglobal") { + lua.lua_setglobal(L1, getstring(L, buff, pc)); + } else if (inst === "sethook") { + let mask = getnum(L, L1, pc); + let count = getnum(L, L1, pc); + sethookaux(L1, mask, count, getstring(L, buff, pc)); + } else if (inst === "setmetatable") { + lua.lua_setmetatable(L1, getindex(L, L1, pc)); + } else if (inst === "settable") { + lua.lua_settable(L1, getindex(L, L1, pc)); + } else if (inst === "settop") { + lua.lua_settop(L1, getnum(L, L1, pc)); + } else if (inst === "testudata") { + let i = getindex(L, L1, pc); + lua.lua_pushboolean(L1, lauxlib.luaL_testudata(L1, i, getstring(L, buff, pc)) !== null); + } else if (inst === "error") { + lua.lua_error(L1); + } else if (inst === "throw") { + throw new Error(); + } else if (inst === "tobool") { + lua.lua_pushboolean(L1, lua.lua_toboolean(L1, getindex(L, L1, pc))); + } else if (inst === "tocfunction") { + lua.lua_pushcfunction(L1, lua.lua_tocfunction(L1, getindex(L, L1, pc))); + } else if (inst === "tointeger") { + lua.lua_pushinteger(L1, lua.lua_tointeger(L1, getindex(L, L1, pc))); + } else if (inst === "tonumber") { + lua.lua_pushnumber(L1, lua.lua_tonumber(L1, getindex(L, L1, pc))); + } else if (inst === "topointer") { + lua.lua_pushnumber(L1, lua.lua_topointer(L1, getindex(L, L1, pc))); + } else if (inst === "tostring") { + let s = lua.lua_tostring(L1, getindex(L, L1, pc)); + let s1 = lua.lua_pushstring(L1, s); + lua.lua_longassert((s === null && s1 === null) || s.join('|') === s1.join('|')); + } else if (inst === "type") { + lua.lua_pushstring(L1, lauxlib.luaL_typename(L1, getnum(L, L1, pc))); + } else if (inst === "xmove") { + let f = getindex(L, L1, pc); + let t = getindex(L, L1, pc); + let fs = (f === 0) ? L1 : lua.lua_tothread(L1, f); + let ts = (t === 0) ? L1 : lua.lua_tothread(L1, t); + let n = getnum(L, L1, pc); + if (n === 0) n = lua.lua_gettop(fs); + lua.lua_xmove(fs, ts, n); + } else if (inst === "yield") { + return lua.lua_yield(L1, getnum(L, L1, pc)); + } else if (inst === "yieldk") { + let nres = getnum(L, L1, pc); + let i = getindex(L, L1, pc); + return lua.lua_yieldk(L1, nres, i, Cfunck); + } else lauxlib.luaL_error(L, lua.to_luastring("unknown instruction %s"), buff); + } + return 0; +}; const tpanic = function(L) { console.error(`PANIC: unprotected error in call to Lua API (${lua.lua_tojsstring(L, -1)})\n`); @@ -16,8 +333,71 @@ const newuserdata = function(L) { return 1; }; +/* +** C hook that runs the C script stored in registry.C_HOOK[L] +*/ +const Chook = function(L, ar) { + let scpt; + let events = ["call", "ret", "line", "count", "tailcall"].map(e => lua.to_luastring(e)); + lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring("JS_HOOK", true)); + lua.lua_pushlightuserdata(L, L); + lua.lua_gettable(L, -2); /* get C_HOOK[L] (script saved by sethookaux) */ + scpt = lua.lua_tostring(L, -1); /* not very religious (string will be popped) */ + lua.lua_pop(L, 2); /* remove C_HOOK and script */ + lua.lua_pushstring(L, events[ar.event]); /* may be used by script */ + lua.lua_pushinteger(L, ar.currentline); /* may be used by script */ + runJS(L, L, { script: scpt, offset: 0 }); /* run script from C_HOOK[L] */ +}; + + +/* +** sets 'registry.C_HOOK[L] = scpt' and sets 'Chook' as a hook +*/ +const sethookaux = function(L, mask, count, scpt) { + if (scpt.length <= 0) { /* no script? */ + lua.lua_sethook(L, null, 0, 0); /* turn off hooks */ + return; + } + lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring("JS_HOOK", true)); /* get C_HOOK table */ + if (!lua.lua_istable(L, -1)) { /* no hook table? */ + lua.lua_pop(L, 1); /* remove previous value */ + lua.lua_newtable(L); /* create new C_HOOK table */ + lua.lua_pushvalue(L, -1); + lua.lua_setfield(L, lua.LUA_REGISTRYINDEX, lua.to_luastring("JS_HOOK", true)); /* register it */ + } + lua.lua_pushlightuserdata(L, L); + lua.lua_pushstring(L, scpt); + lua.lua_settable(L, -3); /* C_HOOK[L] = script */ + lua.lua_sethook(L, Chook, mask, count); +}; + +const sethook = function(L) { + if (lua.lua_isnoneornil(L, 1)) + lua.lua_sethook(L, null, 0, 0); /* turn off hooks */ + else { + const scpt = lauxlib.luaL_checkstring(L, 1); + const smask = lauxlib.luaL_checkstring(L, 2); + let count = lauxlib.luaL_optinteger(L, 3, 0); + let mask = 0; + if (smask.indexOf('c'.charCodeAt(0))) mask |= lua.LUA_MASKCALL; + if (smask.indexOf('r'.charCodeAt(0))) mask |= lua.LUA_MASKRET; + if (smask.indexOf('l'.charCodeAt(0))) mask |= lua.LUA_MASKLINE; + if (count > 0) mask |= lua.LUA_MASKCOUNT; + sethookaux(L, mask, count, scpt); + } +}; + +const Cfunck = function(L, status, ctx) { + pushcode(L, status); + lua.lua_setglobal(L, "status"); + lua.lua_pushinteger(L, ctx); + lua.lua_setglobal(L, "ctx"); + return runJS(L, L, lua.lua_tostring(L, ctx)); +}; + const tests_funcs = { - "newuserdata": newuserdata + "newuserdata": newuserdata, + "sethook": sethook }; const luaB_opentests = function(L) { -- cgit v1.2.3-54-g00ecf