683 lines
22 KiB
JavaScript
683 lines
22 KiB
JavaScript
"use strict";
|
|||
|
|
|
||
|
|
const {
|
||
|
|
LUA_HOOKCOUNT,
|
||
|
|
LUA_HOOKLINE,
|
||
|
|
LUA_MASKCOUNT,
|
||
|
|
LUA_MASKLINE,
|
||
|
|
constant_types: {
|
||
|
|
LUA_TBOOLEAN,
|
||
|
|
LUA_TNIL,
|
||
|
|
LUA_TTABLE
|
||
|
|
},
|
||
|
|
thread_status: {
|
||
|
|
LUA_ERRRUN,
|
||
|
|
LUA_YIELD
|
||
|
|
},
|
||
|
|
from_userstring,
|
||
|
|
luastring_eq,
|
||
|
|
luastring_indexOf,
|
||
|
|
to_luastring
|
||
|
|
} = require('./defs.js');
|
||
|
|
const {
|
||
|
|
api_check,
|
||
|
|
lua_assert
|
||
|
|
} = require('./llimits.js');
|
||
|
|
const { LUA_IDSIZE } = require('./luaconf.js');
|
||
|
|
const lapi = require('./lapi.js');
|
||
|
|
const ldo = require('./ldo.js');
|
||
|
|
const lfunc = require('./lfunc.js');
|
||
|
|
const llex = require('./llex.js');
|
||
|
|
const lobject = require('./lobject.js');
|
||
|
|
const lopcodes = require('./lopcodes.js');
|
||
|
|
const lstate = require('./lstate.js');
|
||
|
|
const ltable = require('./ltable.js');
|
||
|
|
const ltm = require('./ltm.js');
|
||
|
|
const lvm = require('./lvm.js');
|
||
|
|
|
||
|
|
const currentpc = function(ci) {
|
||
|
|
lua_assert(ci.callstatus & lstate.CIST_LUA);
|
||
|
|
return ci.l_savedpc - 1;
|
||
|
|
};
|
||
|
|
|
||
|
|
const currentline = function(ci) {
|
||
|
|
return ci.func.value.p.lineinfo.length !== 0 ? ci.func.value.p.lineinfo[currentpc(ci)] : -1;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
** If function yielded, its 'func' can be in the 'extra' field. The
|
||
|
|
** next function restores 'func' to its correct value for debugging
|
||
|
|
** purposes. (It exchanges 'func' and 'extra'; so, when called again,
|
||
|
|
** after debugging, it also "re-restores" ** 'func' to its altered value.
|
||
|
|
*/
|
||
|
|
const swapextra = function(L) {
|
||
|
|
if (L.status === LUA_YIELD) {
|
||
|
|
let ci = L.ci; /* get function that yielded */
|
||
|
|
let temp = ci.funcOff; /* exchange its 'func' and 'extra' values */
|
||
|
|
ci.func = L.stack[ci.extra];
|
||
|
|
ci.funcOff = ci.extra;
|
||
|
|
ci.extra = temp;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
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.l_savedpc;
|
||
|
|
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;
|
||
|
|
if (level < 0) return 0; /* invalid (negative) level */
|
||
|
|
for (ci = L.ci; level > 0 && ci !== L.base_ci; ci = ci.previous)
|
||
|
|
level--;
|
||
|
|
if (level === 0 && ci !== L.base_ci) { /* level found? */
|
||
|
|
status = 1;
|
||
|
|
ar.i_ci = ci;
|
||
|
|
} else
|
||
|
|
status = 0; /* no such level */
|
||
|
|
return status;
|
||
|
|
};
|
||
|
|
|
||
|
|
const upvalname = function(p, uv) {
|
||
|
|
lua_assert(uv < p.upvalues.length);
|
||
|
|
let s = p.upvalues[uv].name;
|
||
|
|
if (s === null) return to_luastring("?", true);
|
||
|
|
return s.getstr();
|
||
|
|
};
|
||
|
|
|
||
|
|
const findvararg = function(ci, n) {
|
||
|
|
let nparams = ci.func.value.p.numparams;
|
||
|
|
if (n >= ci.l_base - ci.funcOff - nparams)
|
||
|
|
return null; /* no such vararg */
|
||
|
|
else {
|
||
|
|
return {
|
||
|
|
pos: ci.funcOff + nparams + n,
|
||
|
|
name: to_luastring("(*vararg)", true) /* generic name for any vararg */
|
||
|
|
};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const findlocal = function(L, ci, n) {
|
||
|
|
let base, name = null;
|
||
|
|
|
||
|
|
if (ci.callstatus & lstate.CIST_LUA) {
|
||
|
|
if (n < 0) /* access to vararg values? */
|
||
|
|
return findvararg(ci, -n);
|
||
|
|
else {
|
||
|
|
base = ci.l_base;
|
||
|
|
name = lfunc.luaF_getlocalname(ci.func.value.p, n, currentpc(ci));
|
||
|
|
}
|
||
|
|
} else
|
||
|
|
base = ci.funcOff + 1;
|
||
|
|
|
||
|
|
if (name === null) { /* no 'standard' name? */
|
||
|
|
let limit = ci === L.ci ? L.top : ci.next.funcOff;
|
||
|
|
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
|
||
|
|
name = to_luastring("(*temporary)", true); /* generic name for any valid slot */
|
||
|
|
else
|
||
|
|
return null; /* no name */
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
pos: base + (n - 1),
|
||
|
|
name: name
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
const lua_getlocal = function(L, ar, n) {
|
||
|
|
let name;
|
||
|
|
swapextra(L);
|
||
|
|
if (ar === null) { /* information about non-active function? */
|
||
|
|
if (!L.stack[L.top - 1].ttisLclosure()) /* not a Lua function? */
|
||
|
|
name = null;
|
||
|
|
else /* consider live variables at function start (parameters) */
|
||
|
|
name = lfunc.luaF_getlocalname(L.stack[L.top - 1].value.p, n, 0);
|
||
|
|
} else { /* active function; get information through 'ar' */
|
||
|
|
let local = findlocal(L, ar.i_ci, n);
|
||
|
|
if (local) {
|
||
|
|
name = local.name;
|
||
|
|
lobject.pushobj2s(L, L.stack[local.pos]);
|
||
|
|
api_check(L, L.top <= L.ci.top, "stack overflow");
|
||
|
|
} else {
|
||
|
|
name = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
swapextra(L);
|
||
|
|
return name;
|
||
|
|
};
|
||
|
|
|
||
|
|
const lua_setlocal = function(L, ar, n) {
|
||
|
|
let name;
|
||
|
|
swapextra(L);
|
||
|
|
let local = findlocal(L, ar.i_ci, n);
|
||
|
|
if (local) {
|
||
|
|
name = local.name;
|
||
|
|
lobject.setobjs2s(L, local.pos, L.top - 1);
|
||
|
|
delete L.stack[--L.top]; /* pop value */
|
||
|
|
} else {
|
||
|
|
name = null;
|
||
|
|
}
|
||
|
|
swapextra(L);
|
||
|
|
return name;
|
||
|
|
};
|
||
|
|
|
||
|
|
const funcinfo = function(ar, cl) {
|
||
|
|
if (cl === null || cl instanceof lobject.CClosure) {
|
||
|
|
ar.source = to_luastring("=[JS]", true);
|
||
|
|
ar.linedefined = -1;
|
||
|
|
ar.lastlinedefined = -1;
|
||
|
|
ar.what = to_luastring("J", true);
|
||
|
|
} else {
|
||
|
|
let p = cl.p;
|
||
|
|
ar.source = p.source ? p.source.getstr() : to_luastring("=?", true);
|
||
|
|
ar.linedefined = p.linedefined;
|
||
|
|
ar.lastlinedefined = p.lastlinedefined;
|
||
|
|
ar.what = ar.linedefined === 0 ? to_luastring("main", true) : to_luastring("Lua", true);
|
||
|
|
}
|
||
|
|
|
||
|
|
ar.short_src = lobject.luaO_chunkid(ar.source, LUA_IDSIZE);
|
||
|
|
};
|
||
|
|
|
||
|
|
const collectvalidlines = function(L, f) {
|
||
|
|
if (f === null || f instanceof lobject.CClosure) {
|
||
|
|
L.stack[L.top] = new lobject.TValue(LUA_TNIL, null);
|
||
|
|
lapi.api_incr_top(L);
|
||
|
|
} else {
|
||
|
|
let lineinfo = f.p.lineinfo;
|
||
|
|
let t = ltable.luaH_new(L);
|
||
|
|
L.stack[L.top] = new lobject.TValue(LUA_TTABLE, t);
|
||
|
|
lapi.api_incr_top(L);
|
||
|
|
let v = new lobject.TValue(LUA_TBOOLEAN, true);
|
||
|
|
for (let i = 0; i < lineinfo.length; i++)
|
||
|
|
ltable.luaH_setint(t, lineinfo[i], v);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const getfuncname = function(L, ci) {
|
||
|
|
let r = {
|
||
|
|
name: null,
|
||
|
|
funcname: null
|
||
|
|
};
|
||
|
|
if (ci === null)
|
||
|
|
return null;
|
||
|
|
else if (ci.callstatus & lstate.CIST_FIN) { /* is this a finalizer? */
|
||
|
|
r.name = to_luastring("__gc", true);
|
||
|
|
r.funcname = to_luastring("metamethod", true); /* report it as such */
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
/* calling function is a known Lua function? */
|
||
|
|
else if (!(ci.callstatus & lstate.CIST_TAIL) && ci.previous.callstatus & lstate.CIST_LUA)
|
||
|
|
return funcnamefromcode(L, ci.previous);
|
||
|
|
else return null; /* no way to find a name */
|
||
|
|
};
|
||
|
|
|
||
|
|
const auxgetinfo = function(L, what, ar, f, ci) {
|
||
|
|
let status = 1;
|
||
|
|
for (; what.length > 0; what = what.subarray(1)) {
|
||
|
|
switch (what[0]) {
|
||
|
|
case 83 /* ('S').charCodeAt(0) */: {
|
||
|
|
funcinfo(ar, f);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 108 /* ('l').charCodeAt(0) */: {
|
||
|
|
ar.currentline = ci && ci.callstatus & lstate.CIST_LUA ? currentline(ci) : -1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 117 /* ('u').charCodeAt(0) */: {
|
||
|
|
ar.nups = f === null ? 0 : f.nupvalues;
|
||
|
|
if (f === null || f instanceof lobject.CClosure) {
|
||
|
|
ar.isvararg = true;
|
||
|
|
ar.nparams = 0;
|
||
|
|
} else {
|
||
|
|
ar.isvararg = f.p.is_vararg;
|
||
|
|
ar.nparams = f.p.numparams;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 116 /* ('t').charCodeAt(0) */: {
|
||
|
|
ar.istailcall = ci ? ci.callstatus & lstate.CIST_TAIL : 0;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 110 /* ('n').charCodeAt(0) */: {
|
||
|
|
let r = getfuncname(L, ci);
|
||
|
|
if (r === null) {
|
||
|
|
ar.namewhat = to_luastring("", true);
|
||
|
|
ar.name = null;
|
||
|
|
} else {
|
||
|
|
ar.namewhat = r.funcname;
|
||
|
|
ar.name = r.name;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 76 /* ('L').charCodeAt(0) */:
|
||
|
|
case 102 /* ('f').charCodeAt(0) */: /* handled by lua_getinfo */
|
||
|
|
break;
|
||
|
|
default: status = 0; /* invalid option */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return status;
|
||
|
|
};
|
||
|
|
|
||
|
|
const lua_getinfo = function(L, what, ar) {
|
||
|
|
what = from_userstring(what);
|
||
|
|
let status, cl, ci, func;
|
||
|
|
swapextra(L);
|
||
|
|
if (what[0] === 62 /* ('>').charCodeAt(0) */) {
|
||
|
|
ci = null;
|
||
|
|
func = L.stack[L.top - 1];
|
||
|
|
api_check(L, func.ttisfunction(), "function expected");
|
||
|
|
what = what.subarray(1); /* skip the '>' */
|
||
|
|
L.top--; /* pop function */
|
||
|
|
} else {
|
||
|
|
ci = ar.i_ci;
|
||
|
|
func = ci.func;
|
||
|
|
lua_assert(ci.func.ttisfunction());
|
||
|
|
}
|
||
|
|
|
||
|
|
cl = func.ttisclosure() ? func.value : null;
|
||
|
|
status = auxgetinfo(L, what, ar, cl, ci);
|
||
|
|
if (luastring_indexOf(what, 102 /* ('f').charCodeAt(0) */) >= 0) {
|
||
|
|
lobject.pushobj2s(L, func);
|
||
|
|
api_check(L, L.top <= L.ci.top, "stack overflow");
|
||
|
|
}
|
||
|
|
|
||
|
|
swapextra(L);
|
||
|
|
if (luastring_indexOf(what, 76 /* ('L').charCodeAt(0) */) >= 0)
|
||
|
|
collectvalidlines(L, cl);
|
||
|
|
|
||
|
|
return status;
|
||
|
|
};
|
||
|
|
|
||
|
|
const kname = function(p, pc, c) {
|
||
|
|
let r = {
|
||
|
|
name: null,
|
||
|
|
funcname: null
|
||
|
|
};
|
||
|
|
|
||
|
|
if (lopcodes.ISK(c)) { /* is 'c' a constant? */
|
||
|
|
let kvalue = p.k[lopcodes.INDEXK(c)];
|
||
|
|
if (kvalue.ttisstring()) { /* literal constant? */
|
||
|
|
r.name = kvalue.svalue(); /* it is its own name */
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
/* else no reasonable name found */
|
||
|
|
} else { /* 'c' is a register */
|
||
|
|
let what = getobjname(p, pc, c); /* search for 'c' */
|
||
|
|
if (what && what.funcname[0] === 99 /* ('c').charCodeAt(0) */) { /* found a constant name? */
|
||
|
|
return what; /* 'name' already filled */
|
||
|
|
}
|
||
|
|
/* else no reasonable name found */
|
||
|
|
}
|
||
|
|
r.name = to_luastring("?", true);
|
||
|
|
return r; /* no reasonable name found */
|
||
|
|
};
|
||
|
|
|
||
|
|
const filterpc = function(pc, jmptarget) {
|
||
|
|
if (pc < jmptarget) /* is code conditional (inside a jump)? */
|
||
|
|
return -1; /* cannot know who sets that register */
|
||
|
|
else return pc; /* current position sets that register */
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
** try to find last instruction before 'lastpc' that modified register 'reg'
|
||
|
|
*/
|
||
|
|
const findsetreg = function(p, lastpc, reg) {
|
||
|
|
let setreg = -1; /* keep last instruction that changed 'reg' */
|
||
|
|
let jmptarget = 0; /* any code before this address is conditional */
|
||
|
|
let OCi = lopcodes.OpCodesI;
|
||
|
|
for (let pc = 0; pc < lastpc; pc++) {
|
||
|
|
let i = p.code[pc];
|
||
|
|
let a = i.A;
|
||
|
|
switch (i.opcode) {
|
||
|
|
case OCi.OP_LOADNIL: {
|
||
|
|
let b = i.B;
|
||
|
|
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */
|
||
|
|
setreg = filterpc(pc, jmptarget);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case OCi.OP_TFORCALL: {
|
||
|
|
if (reg >= a + 2) /* affect all regs above its base */
|
||
|
|
setreg = filterpc(pc, jmptarget);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case OCi.OP_CALL:
|
||
|
|
case OCi.OP_TAILCALL: {
|
||
|
|
if (reg >= a) /* affect all registers above base */
|
||
|
|
setreg = filterpc(pc, jmptarget);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case OCi.OP_JMP: {
|
||
|
|
let b = i.sBx;
|
||
|
|
let dest = pc + 1 + b;
|
||
|
|
/* jump is forward and do not skip 'lastpc'? */
|
||
|
|
if (pc < dest && dest <= lastpc) {
|
||
|
|
if (dest > jmptarget)
|
||
|
|
jmptarget = dest; /* update 'jmptarget' */
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
if (lopcodes.testAMode(i.opcode) && reg === a)
|
||
|
|
setreg = filterpc(pc, jmptarget);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return setreg;
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
const getobjname = function(p, lastpc, reg) {
|
||
|
|
let r = {
|
||
|
|
name: lfunc.luaF_getlocalname(p, reg + 1, lastpc),
|
||
|
|
funcname: null
|
||
|
|
};
|
||
|
|
|
||
|
|
if (r.name) { /* is a local? */
|
||
|
|
r.funcname = to_luastring("local", true);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* else try symbolic execution */
|
||
|
|
let pc = findsetreg(p, lastpc, reg);
|
||
|
|
let OCi = lopcodes.OpCodesI;
|
||
|
|
if (pc !== -1) { /* could find instruction? */
|
||
|
|
let i = p.code[pc];
|
||
|
|
switch (i.opcode) {
|
||
|
|
case OCi.OP_MOVE: {
|
||
|
|
let b = i.B; /* move from 'b' to 'a' */
|
||
|
|
if (b < i.A)
|
||
|
|
return getobjname(p, pc, b); /* get name for 'b' */
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case OCi.OP_GETTABUP:
|
||
|
|
case OCi.OP_GETTABLE: {
|
||
|
|
let k = i.C; /* key index */
|
||
|
|
let t = i.B; /* table index */
|
||
|
|
let vn = i.opcode === OCi.OP_GETTABLE ? lfunc.luaF_getlocalname(p, t + 1, pc) : upvalname(p, t);
|
||
|
|
r.name = kname(p, pc, k).name;
|
||
|
|
r.funcname = (vn && luastring_eq(vn, llex.LUA_ENV)) ? to_luastring("global", true) : to_luastring("field", true);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
case OCi.OP_GETUPVAL: {
|
||
|
|
r.name = upvalname(p, i.B);
|
||
|
|
r.funcname = to_luastring("upvalue", true);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
case OCi.OP_LOADK:
|
||
|
|
case OCi.OP_LOADKX: {
|
||
|
|
let b = i.opcode === OCi.OP_LOADK ? i.Bx : p.code[pc + 1].Ax;
|
||
|
|
if (p.k[b].ttisstring()) {
|
||
|
|
r.name = p.k[b].svalue();
|
||
|
|
r.funcname = to_luastring("constant", true);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case OCi.OP_SELF: {
|
||
|
|
let k = i.C;
|
||
|
|
r.name = kname(p, pc, k).name;
|
||
|
|
r.funcname = to_luastring("method", true);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
default: break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
** Try to find a name for a function based on the code that called it.
|
||
|
|
** (Only works when function was called by a Lua function.)
|
||
|
|
** Returns what the name is (e.g., "for iterator", "method",
|
||
|
|
** "metamethod") and sets '*name' to point to the name.
|
||
|
|
*/
|
||
|
|
const funcnamefromcode = function(L, ci) {
|
||
|
|
let r = {
|
||
|
|
name: null,
|
||
|
|
funcname: null
|
||
|
|
};
|
||
|
|
|
||
|
|
let tm = 0; /* (initial value avoids warnings) */
|
||
|
|
let p = ci.func.value.p; /* calling function */
|
||
|
|
let pc = currentpc(ci); /* calling instruction index */
|
||
|
|
let i = p.code[pc]; /* calling instruction */
|
||
|
|
let OCi = lopcodes.OpCodesI;
|
||
|
|
|
||
|
|
if (ci.callstatus & lstate.CIST_HOOKED) {
|
||
|
|
r.name = to_luastring("?", true);
|
||
|
|
r.funcname = to_luastring("hook", true);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (i.opcode) {
|
||
|
|
case OCi.OP_CALL:
|
||
|
|
case OCi.OP_TAILCALL:
|
||
|
|
return getobjname(p, pc, i.A); /* get function name */
|
||
|
|
case OCi.OP_TFORCALL:
|
||
|
|
r.name = to_luastring("for iterator", true);
|
||
|
|
r.funcname = to_luastring("for iterator", true);
|
||
|
|
return r;
|
||
|
|
/* other instructions can do calls through metamethods */
|
||
|
|
case OCi.OP_SELF:
|
||
|
|
case OCi.OP_GETTABUP:
|
||
|
|
case OCi.OP_GETTABLE:
|
||
|
|
tm = ltm.TMS.TM_INDEX;
|
||
|
|
break;
|
||
|
|
case OCi.OP_SETTABUP:
|
||
|
|
case OCi.OP_SETTABLE:
|
||
|
|
tm = ltm.TMS.TM_NEWINDEX;
|
||
|
|
break;
|
||
|
|
case OCi.OP_ADD: tm = ltm.TMS.TM_ADD; break;
|
||
|
|
case OCi.OP_SUB: tm = ltm.TMS.TM_SUB; break;
|
||
|
|
case OCi.OP_MUL: tm = ltm.TMS.TM_MUL; break;
|
||
|
|
case OCi.OP_MOD: tm = ltm.TMS.TM_MOD; break;
|
||
|
|
case OCi.OP_POW: tm = ltm.TMS.TM_POW; break;
|
||
|
|
case OCi.OP_DIV: tm = ltm.TMS.TM_DIV; break;
|
||
|
|
case OCi.OP_IDIV: tm = ltm.TMS.TM_IDIV; break;
|
||
|
|
case OCi.OP_BAND: tm = ltm.TMS.TM_BAND; break;
|
||
|
|
case OCi.OP_BOR: tm = ltm.TMS.TM_BOR; break;
|
||
|
|
case OCi.OP_BXOR: tm = ltm.TMS.TM_BXOR; break;
|
||
|
|
case OCi.OP_SHL: tm = ltm.TMS.TM_SHL; break;
|
||
|
|
case OCi.OP_SHR: tm = ltm.TMS.TM_SHR; break;
|
||
|
|
case OCi.OP_UNM: tm = ltm.TMS.TM_UNM; break;
|
||
|
|
case OCi.OP_BNOT: tm = ltm.TMS.TM_BNOT; break;
|
||
|
|
case OCi.OP_LEN: tm = ltm.TMS.TM_LEN; break;
|
||
|
|
case OCi.OP_CONCAT: tm = ltm.TMS.TM_CONCAT; break;
|
||
|
|
case OCi.OP_EQ: tm = ltm.TMS.TM_EQ; break;
|
||
|
|
case OCi.OP_LT: tm = ltm.TMS.TM_LT; break;
|
||
|
|
case OCi.OP_LE: tm = ltm.TMS.TM_LE; break;
|
||
|
|
default:
|
||
|
|
return null; /* cannot find a reasonable name */
|
||
|
|
}
|
||
|
|
|
||
|
|
r.name = L.l_G.tmname[tm].getstr();
|
||
|
|
r.funcname = to_luastring("metamethod", true);
|
||
|
|
return r;
|
||
|
|
};
|
||
|
|
|
||
|
|
const isinstack = function(L, ci, o) {
|
||
|
|
for (let i = ci.l_base; i < ci.top; i++) {
|
||
|
|
if (L.stack[i] === o)
|
||
|
|
return i;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
** Checks whether value 'o' came from an upvalue. (That can only happen
|
||
|
|
** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
|
||
|
|
** upvalues.)
|
||
|
|
*/
|
||
|
|
const getupvalname = function(L, ci, o) {
|
||
|
|
let c = ci.func.value;
|
||
|
|
for (let i = 0; i < c.nupvalues; i++) {
|
||
|
|
if (c.upvals[i] === o) {
|
||
|
|
return {
|
||
|
|
name: upvalname(c.p, i),
|
||
|
|
funcname: to_luastring('upvalue', true)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
};
|
||
|
|
|
||
|
|
const varinfo = function(L, o) {
|
||
|
|
let ci = L.ci;
|
||
|
|
let kind = null;
|
||
|
|
if (ci.callstatus & lstate.CIST_LUA) {
|
||
|
|
kind = getupvalname(L, ci, o); /* check whether 'o' is an upvalue */
|
||
|
|
let stkid = isinstack(L, ci, o);
|
||
|
|
if (!kind && stkid) /* no? try a register */
|
||
|
|
kind = getobjname(ci.func.value.p, currentpc(ci), stkid - ci.l_base);
|
||
|
|
}
|
||
|
|
|
||
|
|
return kind ? lobject.luaO_pushfstring(L, to_luastring(" (%s '%s')", true), kind.funcname, kind.name) : to_luastring("", true);
|
||
|
|
};
|
||
|
|
|
||
|
|
const luaG_typeerror = function(L, o, op) {
|
||
|
|
let t = ltm.luaT_objtypename(L, o);
|
||
|
|
luaG_runerror(L, to_luastring("attempt to %s a %s value%s", true), op, t, varinfo(L, o));
|
||
|
|
};
|
||
|
|
|
||
|
|
const luaG_concaterror = function(L, p1, p2) {
|
||
|
|
if (p1.ttisstring() || lvm.cvt2str(p1)) p1 = p2;
|
||
|
|
luaG_typeerror(L, p1, to_luastring('concatenate', true));
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
** Error when both values are convertible to numbers, but not to integers
|
||
|
|
*/
|
||
|
|
const luaG_opinterror = function(L, p1, p2, msg) {
|
||
|
|
if (lvm.tonumber(p1) === false)
|
||
|
|
p2 = p1;
|
||
|
|
luaG_typeerror(L, p2, msg);
|
||
|
|
};
|
||
|
|
|
||
|
|
const luaG_ordererror = function(L, p1, p2) {
|
||
|
|
let t1 = ltm.luaT_objtypename(L, p1);
|
||
|
|
let t2 = ltm.luaT_objtypename(L, p2);
|
||
|
|
if (luastring_eq(t1, t2))
|
||
|
|
luaG_runerror(L, to_luastring("attempt to compare two %s values", true), t1);
|
||
|
|
else
|
||
|
|
luaG_runerror(L, to_luastring("attempt to compare %s with %s", true), t1, t2);
|
||
|
|
};
|
||
|
|
|
||
|
|
/* add src:line information to 'msg' */
|
||
|
|
const luaG_addinfo = function(L, msg, src, line) {
|
||
|
|
let buff;
|
||
|
|
if (src)
|
||
|
|
buff = lobject.luaO_chunkid(src.getstr(), LUA_IDSIZE);
|
||
|
|
else
|
||
|
|
buff = to_luastring("?", true);
|
||
|
|
|
||
|
|
return lobject.luaO_pushfstring(L, to_luastring("%s:%d: %s", true), buff, line, msg);
|
||
|
|
};
|
||
|
|
|
||
|
|
const luaG_runerror = function(L, fmt, ...argp) {
|
||
|
|
let ci = L.ci;
|
||
|
|
let msg = lobject.luaO_pushvfstring(L, fmt, argp);
|
||
|
|
if (ci.callstatus & lstate.CIST_LUA) /* if Lua function, add source:line information */
|
||
|
|
luaG_addinfo(L, msg, ci.func.value.p.source, currentline(ci));
|
||
|
|
luaG_errormsg(L);
|
||
|
|
};
|
||
|
|
|
||
|
|
const luaG_errormsg = function(L) {
|
||
|
|
if (L.errfunc !== 0) { /* is there an error handling function? */
|
||
|
|
let errfunc = L.errfunc;
|
||
|
|
lobject.pushobj2s(L, L.stack[L.top - 1]); /* move argument */
|
||
|
|
lobject.setobjs2s(L, L.top - 2, errfunc); /* push function */
|
||
|
|
ldo.luaD_callnoyield(L, L.top - 2, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
ldo.luaD_throw(L, LUA_ERRRUN);
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
** Error when both values are convertible to numbers, but not to integers
|
||
|
|
*/
|
||
|
|
const luaG_tointerror = function(L, p1, p2) {
|
||
|
|
let temp = lvm.tointeger(p1);
|
||
|
|
if (temp === false)
|
||
|
|
p2 = p1;
|
||
|
|
luaG_runerror(L, to_luastring("number%s has no integer representation", true), varinfo(L, p2));
|
||
|
|
};
|
||
|
|
|
||
|
|
const luaG_traceexec = function(L) {
|
||
|
|
let ci = L.ci;
|
||
|
|
let mask = L.hookmask;
|
||
|
|
let counthook = (--L.hookcount === 0 && (mask & LUA_MASKCOUNT));
|
||
|
|
if (counthook)
|
||
|
|
L.hookcount = L.basehookcount; /* reset count */
|
||
|
|
else if (!(mask & 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_HOOKCOUNT, -1); /* call count hook */
|
||
|
|
if (mask & LUA_MASKLINE) {
|
||
|
|
let p = ci.func.value.p;
|
||
|
|
let npc = ci.l_savedpc - 1; // pcRel(ci.u.l.savedpc, p);
|
||
|
|
let newline = p.lineinfo.length !== 0 ? p.lineinfo[npc] : -1;
|
||
|
|
if (npc === 0 || /* call linehook when enter a new function, */
|
||
|
|
ci.l_savedpc <= L.oldpc || /* when jump back (loop), or when */
|
||
|
|
newline !== (p.lineinfo.length !== 0 ? p.lineinfo[L.oldpc - 1] : -1)) /* enter a new line */
|
||
|
|
ldo.luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */
|
||
|
|
}
|
||
|
|
L.oldpc = ci.l_savedpc;
|
||
|
|
if (L.status === LUA_YIELD) { /* did hook yield? */
|
||
|
|
if (counthook)
|
||
|
|
L.hookcount = 1; /* undo decrement to zero */
|
||
|
|
ci.l_savedpc--; /* undo increment (resume will increment it again) */
|
||
|
|
ci.callstatus |= lstate.CIST_HOOKYIELD; /* mark that it yielded */
|
||
|
|
ci.funcOff = L.top - 1; /* protect stack below results */
|
||
|
|
ci.func = L.stack[ci.funcOff];
|
||
|
|
ldo.luaD_throw(L, LUA_YIELD);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
module.exports.luaG_addinfo = luaG_addinfo;
|
||
|
|
module.exports.luaG_concaterror = luaG_concaterror;
|
||
|
|
module.exports.luaG_errormsg = luaG_errormsg;
|
||
|
|
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;
|