aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lapi.js15
-rw-r--r--src/lcode.js14
-rw-r--r--src/ldo.js63
-rw-r--r--src/lobject.js2
-rw-r--r--src/lstate.js17
-rw-r--r--src/lvm.js1
6 files changed, 95 insertions, 17 deletions
diff --git a/src/lapi.js b/src/lapi.js
index cea0568..ca5a8a0 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -91,8 +91,20 @@ const index2addr_ = function(L, idx) {
};
const lua_checkstack = function(L, n) {
+ let res;
let ci = L.ci;
- let res = L.stack.length < luaconf.LUAI_MAXSTACK;
+ assert(n >= 0, "negative 'n'");
+ if (L.stack_last - L.top > n) /* stack large enough? */
+ res = true;
+ else { /* no; need to grow stack */
+ let inuse = L.top + lstate.EXTRA_STACK;
+ if (inuse > luaconf.LUAI_MAXSTACK - n) /* can grow without overflow? */
+ res = false; /* no */
+ else { /* try to grow stack */
+ ldo.luaD_growstack(L, n);
+ res = true;
+ }
+ }
if (res && ci.top < L.top + n)
ci.top = L.top + n; /* adjust frame top */
@@ -140,6 +152,7 @@ const lua_pushvalue = function(L, idx) {
const lua_settop = function(L, idx) {
let func = L.ci.funcOff;
if (idx >= 0) {
+ assert(idx <= L.stack_last - (func + 1), "new top too large");
while (L.top < func + 1 + idx)
L.stack[L.top++] = new TValue(CT.LUA_TNIL, null);
L.top = func + 1 + idx;
diff --git a/src/lcode.js b/src/lcode.js
index 6515d93..ea010aa 100644
--- a/src/lcode.js
+++ b/src/lcode.js
@@ -211,11 +211,14 @@ const luaK_getlabel = function(fs) {
** jump (that is, its condition), or the jump itself if it is
** unconditional.
*/
-const getjumpcontrol = function(fs, pc) {
+const getjumpcontroloffset = function(fs, pc) {
if (pc >= 1 && lopcodes.testTMode(fs.f.code[pc - 1].opcode))
- return fs.f.code[pc - 1];
+ return pc - 1;
else
- return fs.f.code[pc];
+ return pc;
+};
+const getjumpcontrol = function(fs, pc) {
+ return fs.f.code[getjumpcontroloffset(fs, pc)];
};
/*
@@ -226,7 +229,8 @@ const getjumpcontrol = function(fs, pc) {
** no register value)
*/
const patchtestreg = function(fs, node, reg) {
- let i = getjumpcontrol(fs, node);
+ let pc = getjumpcontroloffset(fs, node);
+ let i = fs.f.code[pc];
if (i.opcode !== OpCodesI.OP_TESTSET)
return false; /* cannot patch other instructions */
if (reg !== lopcodes.NO_REG && reg !== i.B)
@@ -234,7 +238,7 @@ const patchtestreg = function(fs, node, reg) {
else {
/* no register to put value or register already has the value;
change instruction to simple test */
- i = lopcodes.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C);
+ fs.f.code[pc] = lopcodes.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C);
}
return true;
};
diff --git a/src/ldo.js b/src/ldo.js
index eb8df06..83f26cf 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -13,6 +13,7 @@ const lparser = require('./lparser.js');
const lstate = require('./lstate.js');
const lstring = require('./lstring.js');
const ltm = require('./ltm.js');
+const luaconf = require('./luaconf.js');
const lundump = require('./lundump.js');
const lvm = require('./lvm.js');
const lzio = require('./lzio.js');
@@ -38,6 +39,56 @@ const seterrorobj = function(L, errcode, oldtop) {
L.top = oldtop + 1;
};
+const ERRORSTACKSIZE = luaconf.LUAI_MAXSTACK + 200;
+
+const luaD_reallocstack = function(L, newsize) {
+ assert(newsize <= luaconf.LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
+ assert(L.stack_last == L.stack.length - lstate.EXTRA_STACK);
+ L.stack.length = newsize;
+ L.stack_last = newsize - lstate.EXTRA_STACK;
+};
+
+const luaD_growstack = function(L, n) {
+ let size = L.stack.length;
+ if (size > luaconf.LUAI_MAXSTACK)
+ luaD_throw(L, TS.LUA_ERRERR);
+ else {
+ let needed = L.top + n + lstate.EXTRA_STACK;
+ let newsize = 2 * size;
+ if (newsize > luaconf.LUAI_MAXSTACK) newsize = luaconf.LUAI_MAXSTACK;
+ if (newsize < needed) newsize = needed;
+ if (newsize > luaconf.LUAI_MAXSTACK) { /* stack overflow? */
+ luaD_reallocstack(L, ERRORSTACKSIZE);
+ ldebug.luaG_runerror(L, "stack overflow");
+ }
+ else
+ luaD_reallocstack(L, newsize);
+ }
+};
+
+const luaD_checkstack = function(L, n) {
+ if (L.stack_last - L.top <= n)
+ luaD_growstack(L, n);
+};
+
+const stackinuse = function(L) {
+ let lim = L.top;
+ for (let ci = L.ci; ci !== null; ci = ci.previous) {
+ if (lim < ci.top) lim = ci.top;
+ }
+ assert(lim <= L.stack_last);
+ return lim + 1; /* part of stack in use */
+};
+
+const luaD_shrinkstack = function(L) {
+ let inuse = stackinuse(L);
+ let goodsize = inuse + Math.floor(inuse / 8) + 2*lstate.EXTRA_STACK;
+ if (goodsize > luaconf.LUAI_MAXSTACK)
+ goodsize = luaconf.LUAI_MAXSTACK; /* respect stack limit */
+ if (inuse <= (luaconf.LUAI_MAXSTACK - lstate.EXTRA_STACK) && goodsize < L.stack.length)
+ luaD_reallocstack(L, goodsize);
+};
+
/*
** Prepares a function call: checks the stack, creates a new CallInfo
** entry, fills in the relevant information, calls hook if needed.
@@ -53,11 +104,13 @@ const luaD_precall = function(L, off, nresults) {
case CT.LUA_TLCF: {
let f = func.type === CT.LUA_TCCL ? func.value.f : func.value;
+ luaD_checkstack(L, defs.LUA_MINSTACK);
let ci = lstate.luaE_extendCI(L);
ci.funcOff = off;
ci.nresults = nresults;
ci.func = func;
ci.top = L.top + defs.LUA_MINSTACK;
+ assert(ci.top <= L.stack_last);
ci.callstatus = 0;
if (L.hookmask & defs.LUA_MASKCALL)
luaD_hook(L, defs.LUA_HOOKCALL, -1);
@@ -71,11 +124,11 @@ const luaD_precall = function(L, off, nresults) {
return true;
}
case CT.LUA_TLCL: {
+ let base;
let p = func.value.p;
let n = L.top - off - 1;
let fsize = p.maxstacksize;
- let base;
-
+ luaD_checkstack(L, fsize);
if (p.is_vararg) {
base = adjust_varargs(L, p, n);
} else {
@@ -169,6 +222,7 @@ const luaD_hook = function(L, event, line) {
ar.currentline = line;
ar.i_ci = ci;
ci.top = L.top + defs.LUA_MINSTACK;
+ assert(ci.top <= L.stack_last);
L.allowhook = 0; /* cannot call hooks inside a hook */
ci.callstatus |= lstate.CIST_HOOKED;
hook(L, ar);
@@ -388,6 +442,7 @@ const recover = function(L, status) {
L.ci = ci;
L.allowhook = ci.callstatus & lstate.CIST_OAH; /* restore original 'allowhook' */
L.nny = 0; /* should be zero to be yieldable */
+ luaD_shrinkstack(L);
L.errfunc = ci.c_old_errfunc;
return 1; /* continue running the coroutine */
};
@@ -531,6 +586,7 @@ const luaD_pcall = function(L, func, u, old_top, ef) {
L.ci = old_ci;
L.allowhook = old_allowhooks;
L.nny = old_nny;
+ luaD_shrinkstack(L);
}
L.errfunc = old_errfunc;
@@ -594,12 +650,15 @@ 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_checkstack = luaD_checkstack;
+module.exports.luaD_growstack = luaD_growstack;
module.exports.luaD_hook = luaD_hook;
module.exports.luaD_pcall = luaD_pcall;
module.exports.luaD_poscall = luaD_poscall;
module.exports.luaD_precall = luaD_precall;
module.exports.luaD_protectedparser = luaD_protectedparser;
module.exports.luaD_rawrunprotected = luaD_rawrunprotected;
+module.exports.luaD_reallocstack = luaD_reallocstack;
module.exports.luaD_throw = luaD_throw;
module.exports.lua_isyieldable = lua_isyieldable;
module.exports.lua_resume = lua_resume;
diff --git a/src/lobject.js b/src/lobject.js
index 79496d5..41c3a84 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -6,6 +6,7 @@ const assert = require('assert');
const defs = require('./defs.js');
const ljstype = require('./ljstype.js');
const ldebug = require('./ldebug.js');
+const ldo = require('./ldo.js');
const lstring = require('./lstring.js');
const luaconf = require('./luaconf.js');
const lvm = require('./lvm.js');
@@ -492,6 +493,7 @@ const luaO_pushvfstring = function(L, fmt, argp) {
n += 2;
i = e + 2;
}
+ ldo.luaD_checkstack(L, 1);
pushstr(L, fmt.slice(i));
if (n > 0) lvm.luaV_concat(L, n+1);
return L.stack[L.top-1].svalue();
diff --git a/src/lstate.js b/src/lstate.js
index 5a65c12..b71e011 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -14,6 +14,8 @@ const CT = defs.constant_types;
const TS = defs.thread_status;
const LUA_NUMTAGS = defs.LUA_NUMTAGS;
+const EXTRA_STACK = 5;
+
const BASIC_STACK_SIZE = 2 * defs.LUA_MINSTACK;
class CallInfo {
@@ -47,7 +49,8 @@ class lua_State {
this.base_ci = new CallInfo(); // Will be populated later
this.top = 0;
this.ci = null;
- this.stack = [];
+ this.stack = null;
+ this.stack_last = NaN;
this.openupval = null;
this.status = TS.LUA_OK;
this.next = null;
@@ -83,14 +86,11 @@ const luaE_extendCI = function(L) {
return ci;
};
-const luaE_freeCI = function(L) {
- let ci = L.ci;
- ci.next = null;
-};
-
const stack_init = function(L1, L) {
- L1.stack = new Array(BASIC_STACK_SIZE); // TODO: for now we don't care about the stack size
+ L1.stack = new Array(BASIC_STACK_SIZE);
L1.top = 0;
+ L1.stack_last = BASIC_STACK_SIZE - EXTRA_STACK;
+ /* initialize first ci */
let ci = L1.base_ci;
ci.next = ci.previous = null;
ci.callstatus = 0;
@@ -103,7 +103,6 @@ const stack_init = function(L1, L) {
const freestack = function(L) {
L.ci = L.base_ci;
- luaE_freeCI(L);
L.stack = null;
};
@@ -200,9 +199,9 @@ module.exports.CIST_TAIL = (1<<5); /* call was tail called */
module.exports.CIST_HOOKYIELD = (1<<6); /* last hook called yielded */
module.exports.CIST_LEQ = (1<<7); /* using __lt for __le */
module.exports.CIST_FIN = (1<<8); /* call is running a finalizer */
+module.exports.EXTRA_STACK = EXTRA_STACK;
module.exports.lua_close = lua_close;
module.exports.lua_newstate = lua_newstate;
module.exports.lua_newthread = lua_newthread;
module.exports.luaE_extendCI = luaE_extendCI;
-module.exports.luaE_freeCI = luaE_freeCI;
module.exports.luaE_freethread = luaE_freethread;
diff --git a/src/lvm.js b/src/lvm.js
index 56be558..4804cde 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -645,6 +645,7 @@ const luaV_execute = function(L) {
if (b < 0) {
b = n; /* get all var. arguments */
+ ldo.luaD_checkstack(L, n);
L.top = ra + n;
}