"use strict"; const assert = require("assert"); const lua = require('../../src/lua.js'); const lauxlib = require('../../src/lauxlib.js'); const { luastring_eq, luastring_indexOf, to_jsstring, to_luastring } = require("../../src/fengaricore.js"); const ljstype = require('../../src/ljstype.js'); const lopcodes = require('../../src/lopcodes.js'); const { pushobj2s } = require('../../src/lobject.js'); const sprintf = require('sprintf-js').sprintf; 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, 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, 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++]; } return buff.subarray(0, i); }; 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, 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 => 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}: %{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 = new Uint8Array(300); let status = 0; if (!pc || !pc.script) return lauxlib.luaL_error(L, to_luastring("attempt to runJS null script")); for (;;) { let inst = to_jsstring(getstring(L, buff, pc)); if (inst.length === 0) return 0; switch (inst) { case "absindex": { lua.lua_pushnumber(L1, lua.lua_absindex(L1, getindex(L, L1, pc))); break; } case "append": { let t = getindex(L, L1, pc); let i = lua.lua_rawlen(L1, t); lua.lua_rawseti(L1, t, i + 1); break; } case "arith": { let op; skip(pc); op = ops.indexOf(pc.script[pc.offset++]); lua.lua_arith(L1, op); break; } case "call": { let narg = getnum(L, L1, pc); let nres = getnum(L, L1, pc); lua.lua_call(L1, narg, nres); break; } case "callk": { let narg = getnum(L, L1, pc); let nres = getnum(L, L1, pc); let i = getindex(L, L1, pc); lua.lua_callk(L1, narg, nres, i, Cfunck); break; } case "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(L1, sz, msg); break; } case "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(L1, lua.lua_compare(L1, a, b, op)); break; } case "concat": { lua.lua_concat(L1, getnum(L, L1, pc)); break; } case "copy": { let f = getindex(L, L1, pc); lua.lua_copy(L1, f, getindex(L, L1, pc)); break; } case "func2num": { let func = lua.lua_tocfunction(L1, getindex(L, L1, pc)); if (func === null) func = 0; else if (func.id) func = func.id; lua.lua_pushnumber(L1, func); break; } case "getfield": { let t = getindex(L, L1, pc); lua.lua_getfield(L1, t, getstring(L, buff, pc)); break; } case "getglobal": { lua.lua_getglobal(L1, getstring(L, buff, pc)); break; } case "getmetatable": { if (lua.lua_getmetatable(L1, getindex(L, L1, pc)) === 0) lua.lua_pushnil(L1); break; } case "gettable": { lua.lua_gettable(L1, getindex(L, L1, pc)); break; } case "gettop": { lua.lua_pushinteger(L1, lua.lua_gettop(L1)); break; } case "gsub": { let a = getnum(L, L1, pc); let b = getnum(L, L1, pc); let c = getnum(L, L1, pc); lauxlib.luaL_gsub(L1, lua.lua_tostring(L1, a), lua.lua_tostring(L1, b), lua.lua_tostring(L1, c)); break; } case "insert": { lua.lua_insert(L1, getnum(L, L1, pc)); break; } case "iscfunction": { lua.lua_pushboolean(L1, lua.lua_iscfunction(L1, getindex(L, L1, pc))); break; } case "isfunction": { lua.lua_pushboolean(L1, lua.lua_isfunction(L1, getindex(L, L1, pc))); break; } case "isnil": { lua.lua_pushboolean(L1, lua.lua_isnil(L1, getindex(L, L1, pc))); break; } case "isnull": { lua.lua_pushboolean(L1, lua.lua_isnone(L1, getindex(L, L1, pc))); break; } case "isnumber": { lua.lua_pushboolean(L1, lua.lua_isnumber(L1, getindex(L, L1, pc))); break; } case "isstring": { lua.lua_pushboolean(L1, lua.lua_isstring(L1, getindex(L, L1, pc))); break; } case "istable": { lua.lua_pushboolean(L1, lua.lua_istable(L1, getindex(L, L1, pc))); break; } case "isudataval": { lua.lua_pushboolean(L1, lua.lua_islightuserdata(L1, getindex(L, L1, pc))); break; } case "isuserdata": { lua.lua_pushboolean(L1, lua.lua_isuserdata(L1, getindex(L, L1, pc))); break; } case "len": { lua.lua_len(L1, getindex(L, L1, pc)); break; } case "Llen": { lua.lua_pushinteger(L1, lauxlib.luaL_len(L1, getindex(L, L1, pc))); break; } case "loadfile": { lauxlib.luaL_loadfile(L1, lauxlib.luaL_checkstring(L1, getnum(L, L1, pc))); break; } case "loadstring": { let s = lauxlib.luaL_checkstring(L1, getnum(L, L1, pc)); lauxlib.luaL_loadstring(L1, s); break; } case "newmetatable": { lua.lua_pushboolean(L1, lauxlib.luaL_newmetatable(L1, getstring(L, buff, pc))); break; } case "newtable": { lua.lua_newtable(L1); break; } case "newthread": { lua.lua_newthread(L1); break; } case "newuserdata": { lua.lua_newuserdata(L1, getnum(L, L1, pc)); break; } case "next": { lua.lua_next(L1, -2); break; } case "objsize": { lua.lua_pushinteger(L1, lua.lua_rawlen(L1, getindex(L, L1, pc))); break; } case "pcall": { let narg = getnum(L, L1, pc); let nres = getnum(L, L1, pc); status = lua.lua_pcall(L1, narg, nres, getnum(L, L1, pc)); break; } case "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); break; } case "pop": { lua.lua_pop(L1, getnum(L, L1, pc)); break; } case "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); break; } case "pushbool": { lua.lua_pushboolean(L1, getnum(L, L1, pc)); break; } case "pushcclosure": { lua.lua_pushcclosure(L1, testJS, getnum(L, L1, pc)); break; } case "pushint": { lua.lua_pushinteger(L1, getnum(L, L1, pc)); break; } case "pushnil": { lua.lua_pushnil(L1); break; } case "pushnum": { lua.lua_pushnumber(L1, getnum(L, L1, pc)); break; } case "pushstatus": { pushcode(L1, status); break; } case "pushstring": { lua.lua_pushstring(L1, getstring(L, buff, pc)); break; } case "pushupvalueindex": { lua.lua_pushinteger(L1, lua.lua_upvalueindex(getnum(L, L1, pc))); break; } case "pushvalue": { lua.lua_pushvalue(L1, getindex(L, L1, pc)); break; } case "rawgeti": { let t = getindex(L, L1, pc); lua.lua_rawgeti(L1, t, getnum(L, L1, pc)); break; } case "rawgetp": { let t = getindex(L, L1, pc); lua.lua_rawgetp(L1, t, getnum(L, L1, pc)); break; } case "rawsetp": { let t = getindex(L, L1, pc); lua.lua_rawsetp(L1, t, getnum(L, L1, pc)); break; } case "remove": { lua.lua_remove(L1, getnum(L, L1, pc)); break; } case "replace": { lua.lua_replace(L1, getindex(L, L1, pc)); break; } case "resume": { let i = getindex(L, L1, pc); status = lua.lua_resume(lua.lua_tothread(L1, i), L, getnum(L, L1, pc)); break; } case "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; } case "rotate": { let i = getindex(L, L1, pc); lua.lua_rotate(L1, i, getnum(L, L1, pc)); break; } case "setfield": { let t = getindex(L, L1, pc); lua.lua_setfield(L1, t, getstring(L, buff, pc)); break; } case "setglobal": { lua.lua_setglobal(L1, getstring(L, buff, pc)); break; } case "sethook": { let mask = getnum(L, L1, pc); let count = getnum(L, L1, pc); sethookaux(L1, mask, count, getstring(L, buff, pc)); break; } case "setmetatable": { lua.lua_setmetatable(L1, getindex(L, L1, pc)); break; } case "settable": { lua.lua_settable(L1, getindex(L, L1, pc)); break; } case "settop": { lua.lua_settop(L1, getnum(L, L1, pc)); break; } case "testudata": { let i = getindex(L, L1, pc); lua.lua_pushboolean(L1, lauxlib.luaL_testudata(L1, i, getstring(L, buff, pc)) !== null); break; } case "error": { lua.lua_error(L1); break; } case "throw": { throw new Error(); } case "tobool": { lua.lua_pushboolean(L1, lua.lua_toboolean(L1, getindex(L, L1, pc))); break; } case "tocfunction": { lua.lua_pushcfunction(L1, lua.lua_tocfunction(L1, getindex(L, L1, pc))); break; } case "tointeger": { lua.lua_pushinteger(L1, lua.lua_tointeger(L1, getindex(L, L1, pc))); break; } case "tonumber": { lua.lua_pushnumber(L1, lua.lua_tonumber(L1, getindex(L, L1, pc))); break; } case "topointer": { let p = lua.lua_topointer(L1, getindex(L, L1, pc)); if (p === null) p = 0; else if (p.id) p = p.id; lua.lua_pushnumber(L1, p); /* in ltests.c, p is casted to a size_t so NULL gives 0 */ break; } case "tostring": { let s = lua.lua_tostring(L1, getindex(L, L1, pc)); let s1 = lua.lua_pushstring(L1, s); assert(luastring_eq(s, s1)); break; } case "type": { lua.lua_pushstring(L1, lauxlib.luaL_typename(L1, getnum(L, L1, pc))); break; } case "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); break; } case "yield": { return lua.lua_yield(L1, getnum(L, L1, pc)); } case "yieldk": { let nres = getnum(L, L1, pc); let i = getindex(L, L1, pc); return lua.lua_yieldk(L1, nres, i, Cfunck); } default: lauxlib.luaL_error(L, to_luastring("unknown instruction %s"), buff); } } }; const testJS = function(L) { let L1; let pc; if (lua.lua_isuserdata(L, 1)) { L1 = getstate(L); pc = lauxlib.luaL_checkstring(L, 2); } else if (lua.lua_isthread(L, 1)) { L1 = lua.lua_tothread(L, 1); pc = lauxlib.luaL_checkstring(L, 2); } else { L1 = L; pc = lauxlib.luaL_checkstring(L, 1); } return runJS(L, L1, { script: pc, offset: 0 }); }; const upvalue = function(L) { let n = lauxlib.luaL_checkinteger(L, 2); lauxlib.luaL_checktype(L, 1, lua.LUA_TFUNCTION); if (lua.lua_isnone(L, 3)) { let name = lua.lua_getupvalue(L, 1, n); if (name === null) return 0; lua.lua_pushstring(L, name); return 2; } else { let name = lua.lua_setupvalue(L, 1, n); lua.lua_pushstring(L, name); return 1; } }; const pushuserdata = function(L) { let u = lauxlib.luaL_checkinteger(L, 1); lua.lua_pushlightuserdata(L, u); return 1; }; const udataval = function(L) { lua.lua_pushinteger(L, lua.lua_touserdata(L, 1)); return 1; }; const d2s = function(L) { let d = lauxlib.luaL_checknumber(L, 1); let b = new ArrayBuffer(8); new DataView(b).setFloat64(0, d, true); lua.lua_pushlstring(L, new Uint8Array(b), 8); return 1; }; const s2d = function(L) { let b = lauxlib.luaL_checkstring(L, 1); let dv = new DataView(b.buffer); lua.lua_pushnumber(L, dv.getFloat64(0, true)); return 1; }; const newstate = function(L) { let L1 = lua.lua_newstate(); if (L1) { lua.lua_atpanic(L1, tpanic); lua.lua_pushlightuserdata(L, L1); } else lua.lua_pushnil(L); return 1; }; const getstate = function(L) { let L1 = lua.lua_touserdata(L, 1); lauxlib.luaL_argcheck(L, L1 !== null, 1, "state expected"); return L1; }; const luaopen_base = require("../../src/lbaselib.js").luaopen_base; const luaopen_coroutine = require("../../src/lcorolib.js").luaopen_coroutine; const luaopen_debug = require("../../src/ldblib.js").luaopen_debug; const luaopen_io = require("../../src/liolib.js").luaopen_io; const luaopen_os = require("../../src/loslib.js").luaopen_os; const luaopen_math = require("../../src/lmathlib.js").luaopen_math; const luaopen_string = require("../../src/lstrlib.js").luaopen_string; const luaopen_table = require("../../src/ltablib.js").luaopen_table; const luaopen_package = require("../../src/loadlib.js").luaopen_package; const loadlib = function(L) { let libs = { "_G": luaopen_base, "coroutine": luaopen_coroutine, "debug": luaopen_debug, "io": luaopen_io, "os": luaopen_os, "math": luaopen_math, "string": luaopen_string, "table": luaopen_table }; let L1 = getstate(L); lauxlib.luaL_requiref(L1, to_luastring("package", true), luaopen_package, 0); assert(lua.lua_type(L1, -1) == lua.LUA_TTABLE); /* 'requiref' should not reload module already loaded... */ lauxlib.luaL_requiref(L1, to_luastring("package", true), null, 1); /* seg. fault if it reloads */ /* ...but should return the same module */ assert(lua.lua_compare(L1, -1, -2, lua.LUA_OPEQ)); lauxlib.luaL_getsubtable(L1, lua.LUA_REGISTRYINDEX, lauxlib.LUA_PRELOAD_TABLE); for (let name in libs) { lua.lua_pushcfunction(L1, libs[name]); lua.lua_setfield(L1, -2, to_luastring(name, true)); } return 0; }; const closestate = function(L) { let L1 = getstate(L); lua.lua_close(L1); return 0; }; const doremote = function(L) { let L1 = getstate(L); let lcode; let code = lauxlib.luaL_checklstring(L, 2, lcode); let status; lua.lua_settop(L1, 0); status = lauxlib.luaL_loadbuffer(L1, code, lcode, code); if (status === lua.LUA_OK) status = lua.lua_pcall(L1, 0, lua.LUA_MULTRET, 0); if (status !== lua.LUA_OK) { lua.lua_pushnil(L); lua.lua_pushstring(L, lua.lua_tostring(L1, -1)); lua.lua_pushinteger(L, status); return 3; } else { let i = 0; while (!lua.lua_isnone(L1, ++i)) lua.lua_pushstring(L, lua.lua_tostring(L1, i)); lua.lua_pop(L1, i-1); return i-1; } }; const tpanic = function(L) { console.error(`PANIC: unprotected error in call to Lua API (${lua.lua_tojsstring(L, -1)})\n`); return process.exit(1); /* do not return to Lua */ }; const newuserdata = function(L) { lua.lua_newuserdata(L, lauxlib.luaL_checkinteger(L, 1)); 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 => to_luastring(e)); lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, 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] */ }; class Aux { constructor() { this.paniccode = null; this.L = null; } } /* ** does a long-jump back to "main program". */ const panicback = function(L) { let b = new Aux(); lua.lua_checkstack(L, 1); /* open space for 'Aux' struct */ lua.lua_getfield(L, lua.LUA_REGISTRYINDEX, to_luastring("_jmpbuf", true)); /* get 'Aux' struct */ b = lua.lua_touserdata(L, -1); lua.lua_pop(L, 1); /* remove 'Aux' struct */ runJS(b.L, L, { script: b.paniccode, offset: 0 }); /* run optional panic code */ throw 1; }; const checkpanic = function(L) { let b = new Aux(); let code = lauxlib.luaL_checkstring(L, 1); b.paniccode = lauxlib.luaL_optstring(L, 2, ""); b.L = L; let L1 = lua.lua_newstate(); /* create new state */ if (L1 === null) { /* error? */ lua.lua_pushnil(L); return 1; } lua.lua_atpanic(L1, panicback); /* set its panic function */ lua.lua_pushlightuserdata(L1, b); lua.lua_setfield(L1, lua.LUA_REGISTRYINDEX, to_luastring("_jmpbuf", true)); /* store 'Aux' struct */ try { /* set jump buffer */ runJS(L, L1, { script: code, offset: 0 }); /* run code unprotected */ lua.lua_pushliteral(L, "no errors"); } catch (e) { /* error handling */ /* move error message to original state */ lua.lua_pushstring(L, lua.lua_tostring(L1, -1)); } lua.lua_close(L1); return 1; }; /* ** 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, 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, 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 (luastring_indexOf(smask, 'c'.charCodeAt(0)) >= 0) mask |= lua.LUA_MASKCALL; if (luastring_indexOf(smask, 'r'.charCodeAt(0)) >= 0) mask |= lua.LUA_MASKRET; if (luastring_indexOf(smask, 'l'.charCodeAt(0)) >= 0) mask |= lua.LUA_MASKLINE; if (count > 0) mask |= lua.LUA_MASKCOUNT; sethookaux(L, mask, count, scpt); } return 0; }; const Cfunc = function(L) { return runJS(L, L, { script: lua.lua_tostring(L, lua.lua_upvalueindex(1)), offset: 0 }); }; const Cfunck = function(L, status, ctx) { pushcode(L, status); lua.lua_setglobal(L, to_luastring("status", true)); lua.lua_pushinteger(L, ctx); lua.lua_setglobal(L, to_luastring("ctx", true)); return runJS(L, L, { script: lua.lua_tostring(L, ctx), offset: 0 }); }; const makeCfunc = function(L) { lauxlib.luaL_checkstring(L, 1); lua.lua_pushcclosure(L, Cfunc, lua.lua_gettop(L)); return 1; }; const coresume = function(L) { let status; let co = lua.lua_tothread(L, 1); lauxlib.luaL_argcheck(L, co, 1, "coroutine expected"); status = lua.lua_resume(co, L, 0); if (status != lua.LUA_OK && status !== lua.LUA_YIELD) { lua.lua_pushboolean(L, 0); lua.lua_insert(L, -2); return 2; /* return false + error message */ } else { lua.lua_pushboolean(L, 1); return 1; } }; const obj_at = function(L, k) { return L.stack[L.ci.funcOff + k].value.p; }; const setnameval = function(L, name, val) { lua.lua_pushstring(L, name); lua.lua_pushinteger(L, val); lua.lua_settable(L, -3); }; const pushobject = function(L, o){ pushobj2s(L, o); assert(L.top <= L.ci.top, "stack overflow"); }; const buildop = function(p, pc) { let i = p.code[pc]; let o = lopcodes.GET_OPCODE(i); let name = lopcodes.OpCodes[o]; let line = p.lineinfo.length !== 0 ? p.lineinfo[pc] : -1; let result = sprintf("(%4d) %4d - ", line, pc); //`(${line}) ${pc} - `; switch (lopcodes.getOpMode(o)) { case lopcodes.iABC: result += sprintf("%-12s%4d %4d %4d", name, lopcodes.GETARG_A(i), lopcodes.GETARG_B(i), lopcodes.GETARG_C(i)); // `${name} ${lopcodes.GETARG_A(i)} ${lopcodes.GETARG_B(i)} ${lopcodes.GETARG_C(i)}`; break; case lopcodes.iABx: result += sprintf("%-12s%4d %4d", name, lopcodes.GETARG_A(i), lopcodes.GETARG_Bx(i)); // `${name} ${lopcodes.GETARG_A(i)} ${lopcodes.GETARG_Bx(i)}`; break; case lopcodes.iAsBx: result += sprintf("%-12s%4d %4d", name, lopcodes.GETARG_A(i), lopcodes.GETARG_sBx(i)); // `${name} ${lopcodes.GETARG_A(i)} ${lopcodes.GETARG_sBx(i)}`; break; case lopcodes.iAx: result += sprintf("%-12s%4d", name, lopcodes.GETARG_Ax(i)); // `${name} ${lopcodes.GETARG_Ax(i)}`; break; } return to_luastring(result); }; const listcode = function(L) { lauxlib.luaL_argcheck(L, lua.lua_isfunction(L, 1) && !lua.lua_iscfunction(L, 1), 1, "Lua function expected"); let p = obj_at(L, 1); lua.lua_newtable(L); setnameval(L, to_luastring("maxstack", true), p.maxstacksize); setnameval(L, to_luastring("numparams", true), p.numparams); for (let pc = 0; pc < p.code.length; pc++) { lua.lua_pushinteger(L, pc+1); lua.lua_pushstring(L, buildop(p, pc)); lua.lua_settable(L, -3); } return 1; }; const listk = function(L) { lauxlib.luaL_argcheck(L, lua.lua_isfunction(L, 1) && !lua.lua_iscfunction(L, 1), 1, "Lua function expected"); let p = obj_at(L, 1); lua.lua_createtable(L, p.k.length, 0); for (let i = 0; i < p.k.length; i++) { pushobject(L, p.k[i]); lua.lua_rawseti(L, -2, i + 1); } return 1; }; const tests_funcs = { "checkpanic": checkpanic, "closestate": closestate, "d2s": d2s, "doremote": doremote, "listcode": listcode, "listk": listk, "loadlib": loadlib, "makeCfunc": makeCfunc, "newstate": newstate, "newuserdata": newuserdata, "pushuserdata": pushuserdata, "resume": coresume, "s2d": s2d, "sethook": sethook, "testC": testJS, "testJS": testJS, "udataval": udataval, "upvalue": upvalue }; const luaB_opentests = function(L) { lua.lua_atpanic(L, tpanic); lauxlib.luaL_newlib(L, tests_funcs); return 1; }; const luaopen_tests = function(L) { lauxlib.luaL_requiref(L, to_luastring("T"), luaB_opentests, 1); lua.lua_pop(L, 1); /* remove lib */ }; module.exports.luaopen_tests = luaopen_tests;