fengari
Version:
A Lua VM written in JS ES6 targeting the browser
874 lines (825 loc) • 29.9 kB
JavaScript
"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;