From ed0518f417a61b32e531f85c434b8024859202cd Mon Sep 17 00:00:00 2001
From: daurnimator <quae@daurnimator.com>
Date: Wed, 24 May 2017 20:44:34 +1000
Subject: Modify stack values up to L.top instead of replacing

---
 src/lapi.js    |  98 +++++++++++-----------
 src/ldebug.js  |  21 ++---
 src/ldo.js     |  79 +++++++++++++-----
 src/lobject.js |  28 ++++---
 src/lparser.js |   4 +-
 src/lstate.js  |   2 +-
 src/ltm.js     |  18 ++--
 src/lundump.js |   2 +-
 src/lvm.js     | 257 +++++++++++++++++++++++++++++++++++++++------------------
 9 files changed, 320 insertions(+), 189 deletions(-)

(limited to 'src')

diff --git a/src/lapi.js b/src/lapi.js
index 9b10d94..a8e0c82 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -112,7 +112,9 @@ const lua_xmove = function(from, to, n) {
 
     from.top -= n;
     for (let i = 0; i < n; i++) {
+        to.stack[to.top] = new lobject.TValue();
         lobject.setobj2s(to, to.top, from.stack[from.top + i]);
+        delete from.stack[from.top + i];
         to.top++;
     }
 };
@@ -135,21 +137,24 @@ const lua_gettop = function(L) {
 };
 
 const lua_pushvalue = function(L, idx) {
-    lobject.setobj2s(L, L.top, index2addr(L, idx));
-    L.top++;
+    lobject.pushobj2s(L, index2addr(L, idx));
     assert(L.top <= L.ci.top, "stack overflow");
 };
 
 const lua_settop = function(L, idx) {
     let func = L.ci.funcOff;
+    let newtop;
     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;
+        newtop = func + 1 + idx;
     } else {
         assert(-(idx + 1) <= L.top - (func + 1), "invalid new top");
-        let newtop = L.top + idx + 1; /* 'subtract' index (index is negative) */
+        newtop = L.top + idx + 1; /* 'subtract' index (index is negative) */
+    }
+    if (L.top < newtop) {
+        while (L.top < newtop)
+            L.stack[L.top++] = new TValue(CT.LUA_TNIL, null);
+    } else {
         while (L.top > newtop)
             delete L.stack[--L.top];
     }
@@ -161,7 +166,8 @@ const lua_pop = function(L, n) {
 
 const reverse = function(L, from, to) {
     for (; from < to; from++, to--) {
-        let temp = L.stack[from];
+        let fromtv = L.stack[from];
+        let temp = new TValue(fromtv.type, fromtv.value);
         lobject.setobjs2s(L, from, to);
         lobject.setobj2s(L, to, temp);
     }
@@ -188,7 +194,7 @@ const lua_rotate = function(L, idx, n) {
 
 const lua_copy = function(L, fromidx, toidx) {
     let from = index2addr(L, fromidx);
-    L.stack[index2addr_(L, toidx)] = new TValue(from.type, from.value);
+    L.stack[index2addr_(L, toidx)].setfrom(from);
 };
 
 const lua_remove = function(L, idx) {
@@ -239,8 +245,7 @@ const lua_pushlstring = function(L, s, len) {
         assert(Array.isArray(s), "lua_pushlstring expects array of byte");
         ts = lstring.luaS_bless(L, s.slice(0, len));
     }
-    lobject.setsvalue2s(L, L.top, ts);
-    L.top++;
+    lobject.pushsvalue2s(L, ts);
     assert(L.top <= L.ci.top, "stack overflow");
 
     return ts.value;
@@ -249,14 +254,14 @@ const lua_pushlstring = function(L, s, len) {
 const lua_pushstring = function (L, s) {
     assert(Array.isArray(s) || s === undefined || s === null, "lua_pushstring expects array of byte");
 
-    if (s === undefined || s === null)
-        L.stack[L.top] = new TValue(CT.LUA_TNIL, null);
-    else {
+    if (s === undefined || s === null) {
+        L.stack[L.top] = new TValue(CT.LUA_TNIL, null)
+        L.top++;
+    } else {
         let ts = lstring.luaS_new(L, s);
-        lobject.setsvalue2s(L, L.top, ts);
+        lobject.pushsvalue2s(L, ts);
         s = ts.getstr(); /* internal copy */
     }
-    L.top++;
     assert(L.top <= L.ci.top, "stack overflow");
 
     return s;
@@ -276,14 +281,14 @@ const lua_pushfstring = function (L, fmt, ...argp) {
 const lua_pushliteral = function (L, s) {
     assert(typeof s === "string" || s === undefined || s === null, "lua_pushliteral expects a JS string");
 
-    if (s === undefined || s === null)
+    if (s === undefined || s === null) {
         L.stack[L.top] = new TValue(CT.LUA_TNIL, null);
-    else {
+        L.top++;
+    } else {
         let ts = lstring.luaS_newliteral(L, s);
-        lobject.setsvalue2s(L, L.top, ts);
+        lobject.pushsvalue2s(L, ts);
         s = ts.getstr(); /* internal copy */
     }
-    L.top++;
     assert(L.top <= L.ci.top, "stack overflow");
 
     return s;
@@ -300,16 +305,14 @@ const lua_pushcclosure = function(L, fn, n) {
         assert(n <= MAXUPVAL, "upvalue index too large");
 
         let cl = new CClosure(L, fn, n);
-
-        L.top -= n;
-        while (n--) {
-            cl.upvalue[n].setfrom(L.stack[L.top + n])
-            delete L.stack[L.top + n];
-        }
-
-        L.stack[L.top] = new TValue(CT.LUA_TCCL, cl);
+        for (let i=0; i<n; i++)
+            cl.upvalue[i].setfrom(L.stack[L.top - n + i]);
+        for (let i=1; i<n; i++)
+            delete L.stack[--L.top];
+        if (n>0)
+            --L.top;
+        L.stack[L.top].setclCvalue(cl);
     }
-
     L.top++;
     assert(L.top <= L.ci.top, "stack overflow");
 };
@@ -357,8 +360,7 @@ const auxsetstr = function(L, t, k) {
 
     let str = lstring.luaS_new(L, k);
     assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack");
-    lobject.setsvalue2s(L, L.top, str); /* push 'str' (to make it a TValue) */
-    L.top++;
+    lobject.pushsvalue2s(L, str); /* push 'str' (to make it a TValue) */
     assert(L.top <= L.ci.top, "stack overflow");
     lvm.settable(L, t, L.stack[L.top - 1], L.stack[L.top - 2]);
     /* pop value and key */
@@ -435,7 +437,8 @@ const lua_rawset = function(L, idx) {
         slot.setfrom(v);
     }
     ltable.invalidateTMcache(o.value);
-    L.top -= 2;
+    delete L.stack[--L.top];
+    delete L.stack[--L.top];
 };
 
 const lua_rawseti = function(L, idx, n) {
@@ -458,7 +461,7 @@ const lua_rawsetp = function(L, idx, p) {
         let slot = ltable.luaH_set(L, o.value, k);
         slot.setfrom(v);
     }
-    L.top--;
+    delete L.stack[--L.top];
 };
 
 /*
@@ -468,8 +471,7 @@ const lua_rawsetp = function(L, idx, p) {
 const auxgetstr = function(L, t, k) {
     assert(Array.isArray(k), "key must be an array of bytes");
     let str = lstring.luaS_new(L, k);
-    lobject.setsvalue2s(L, L.top, str);
-    L.top++;
+    lobject.pushsvalue2s(L, str);
     assert(L.top <= L.ci.top, "stack overflow");
     lvm.gettable(L, t, L.stack[L.top - 1], L.top - 1);
     return L.stack[L.top - 1].ttnov();
@@ -478,8 +480,7 @@ const auxgetstr = function(L, t, k) {
 const lua_rawgeti = function(L, idx, n) {
     let t = index2addr(L, idx);
     assert(t.ttistable(), "table expected");
-    lobject.setobj2s(L, L.top, ltable.luaH_getint(t.value, n));
-    L.top++;
+    lobject.pushobj2s(L, ltable.luaH_getint(t.value, n));
     assert(L.top <= L.ci.top, "stack overflow");
     return L.stack[L.top - 1].ttnov();
 };
@@ -488,8 +489,7 @@ const lua_rawgetp = function(L, idx, p) {
     let t = index2addr(L, idx);
     assert(t.ttistable(), "table expected");
     let k = new TValue(CT.LUA_TLIGHTUSERDATA, p);
-    lobject.setobj2s(L, L.top, ltable.luaH_get(L, t.value, k));
-    L.top++;
+    lobject.pushobj2s(L, ltable.luaH_get(L, t.value, k));
     assert(L.top <= L.ci.top, "stack overflow");
     return L.stack[L.top - 1].ttnov();
 };
@@ -550,8 +550,7 @@ const lua_getupvalue = function(L, funcindex, n) {
     if (up) {
         let name = up.name;
         let val = up.val;
-        lobject.setobj2s(L, L.top, val);
-        L.top++;
+        lobject.pushobj2s(L, val);
         assert(L.top <= L.ci.top, "stack overflow");
         return name;
     }
@@ -565,8 +564,8 @@ const lua_setupvalue = function(L, funcindex, n) {
     if (aux) {
         let name = aux.name;
         let val = aux.val;
-        L.top--;
-        val.setfrom(L.stack[L.top]);
+        val.setfrom(L.stack[L.top-1]);
+        delete L.stack[--L.top];
         return name;
     }
     return null;
@@ -898,13 +897,12 @@ const lua_arith = function(L, op) {
         assert(2 < L.top - L.ci.funcOff, "not enough elements in the stack");  /* all other operations expect two operands */
     else {  /* for unary operations, add fake 2nd operand */
         assert(1 < L.top - L.ci.funcOff, "not enough elements in the stack");
-        lobject.setobjs2s(L, L.top, L.top - 1);
-        L.top++;
+        lobject.pushobj2s(L, L.stack[L.top-1]);
         assert(L.top <= L.ci.top, "stack overflow");
     }
     /* first operand at top - 2, second at top - 1; result go to top - 2 */
     lobject.luaO_arith(L, op, L.stack[L.top - 2], L.stack[L.top - 1], L.stack[L.top - 2]);
-    L.top--;  /* remove second operand */
+    delete L.stack[--L.top];  /* remove second operand */
 };
 
 /*
@@ -1037,13 +1035,15 @@ const lua_error = function(L) {
 const lua_next = function(L, idx) {
     let t = index2addr(L, idx);
     assert(t.ttistable(), "table expected");
+    L.stack[L.top] = new TValue();
     let more = ltable.luaH_next(L, t.value, L.top - 1);
     if (more) {
         L.top++;
         assert(L.top <= L.ci.top, "stack overflow");
         return 1;
     } else {
-        L.top--;
+        delete L.stack[L.top];
+        delete L.stack[--L.top];
         return 0;
     }
 };
@@ -1053,15 +1053,15 @@ const lua_concat = function(L, n) {
     if (n >= 2)
         lvm.luaV_concat(L, n);
     else if (n === 0) {
-        lobject.setsvalue2s(L, L.top, lstring.luaS_bless(L, []));
-        L.top++;
+        lobject.pushsvalue2s(L, lstring.luaS_bless(L, []));
         assert(L.top <= L.ci.top, "stack overflow");
     }
 };
 
 const lua_len = function(L, idx) {
     let t = index2addr(L, idx);
-    lvm.luaV_objlen(L, L.top, t);
+    L.stack[L.top] = new TValue();
+    lvm.luaV_objlen(L, L.stack[L.top], t);
     L.top++;
     assert(L.top <= L.ci.top, "stack overflow");
 };
diff --git a/src/ldebug.js b/src/ldebug.js
index 2a9ada6..3636a06 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -140,8 +140,7 @@ const lua_getlocal = function(L, ar, n) {
         let local = findlocal(L, ar.i_ci, n);
         if (local) {
             name = local.name;
-            lobject.setobj2s(L, L.top, L.stack[local.pos]);
-            L.top++;
+            lobject.pushobj2s(L, L.stack[local.pos]);
             assert(L.top <= L.ci.top, "stack overflow");
         } else {
             name = null;
@@ -265,27 +264,24 @@ const auxgetinfo = function(L, what, ar, f, ci) {
 };
 
 const lua_getinfo = function(L, what, ar) {
-    let status, cl, ci, func, funcOff;
+    let status, cl, ci, func;
     swapextra(L);
     if (what[0] === '>'.charCodeAt(0)) {
         ci = null;
-        funcOff = L.top - 1;
-        func = L.stack[funcOff];
+        func = L.stack[L.top - 1];
         assert(L, func.ttisfunction(), "function expected");
         what = what.slice(1);  /* skip the '>' */
         L.top--;  /* pop function */
     } else {
         ci = ar.i_ci;
         func = ci.func;
-        funcOff = ci.funcOff;
         assert(ci.func.ttisfunction());
     }
 
     cl = func.ttisclosure() ? func.value : null;
     status = auxgetinfo(L, what, ar, cl, ci);
     if (what.indexOf('f'.charCodeAt(0)) >= 0) {
-        lobject.setobjs2s(L, L.top, funcOff);
-        L.top++;
+        lobject.pushobj2s(L, func);
         assert(L.top <= L.ci.top, "stack overflow");
     }
 
@@ -595,9 +591,8 @@ const luaG_runerror = function(L, fmt, ...argp) {
 const luaG_errormsg = function(L) {
     if (L.errfunc !== 0) {  /* is there an error handling function? */
         let errfunc = L.errfunc;
-        lobject.setobjs2s(L, L.top, L.top - 1); /* move argument */
-        lobject.setobjs2s(L, L.top - 1, errfunc); /* push function */
-        L.top++;
+        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);
     }
 
@@ -643,8 +638,8 @@ const luaG_traceexec = function(L) {
             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.func = L.stack[L.top - 1];  /* protect stack below results */
-        ci.funcOff = L.top - 1;
+        ci.funcOff = L.top - 1;  /* protect stack below results */
+        ci.func = L.stack[ci.funcOff];
         ldo.luaD_throw(L, TS.LUA_YIELD);
     }
 };
diff --git a/src/ldo.js b/src/ldo.js
index b32e404..464d373 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -36,7 +36,13 @@ const seterrorobj = function(L, errcode, oldtop) {
         }
     }
 
-    L.top = oldtop + 1;
+    if (L.top < oldtop + 1) {
+        while (L.top < oldtop + 1)
+            L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+    } else {
+        while (L.top > oldtop + 1)
+            delete L.stack[--L.top];
+    }
 };
 
 const ERRORSTACKSIZE = luaconf.LUAI_MAXSTACK + 200;
@@ -94,8 +100,8 @@ const luaD_shrinkstack = function(L) {
 };
 
 const luaD_inctop = function(L) {
-  luaD_checkstack(L, 1);
-  L.top++;
+    luaD_checkstack(L, 1);
+    L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
 };
 
 /*
@@ -151,7 +157,14 @@ const luaD_precall = function(L, off, nresults) {
             ci.nresults = nresults;
             ci.func = func;
             ci.l_base = base;
-            L.top = ci.top = base + fsize;
+            ci.top = base + fsize;
+            if (L.top < ci.top) {
+                while (L.top < ci.top)
+                    L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+            } else {
+                while (L.top > ci.top)
+                    delete L.stack[--L.top];
+            }
             ci.l_code = p.code;
             ci.l_savedpc = 0;
             ci.callstatus = lstate.CIST_LUA;
@@ -159,6 +172,7 @@ const luaD_precall = function(L, off, nresults) {
             return false;
         }
         default:
+            luaD_checkstack(L, 1);
             tryfuncTM(L, off, func);
             return luaD_precall(L, off, nresults);
     }
@@ -185,33 +199,42 @@ const moveresults = function(L, firstResult, res, nres, wanted) {
             break;
         case 1: {
             if (nres === 0)
-                L.stack[firstResult] = lobject.luaO_nilobject;
-            lobject.setobjs2s(L, res, firstResult); /* move it to proper place */
+                L.stack[res].setnilvalue();
+            else {
+                lobject.setobjs2s(L, res, firstResult); /* move it to proper place */
+            }
             break;
         }
         case defs.LUA_MULTRET: {
             for (let i = 0; i < nres; i++)
                 lobject.setobjs2s(L, res + i, firstResult + i);
+            for (let i=L.top; i>=(res + nres); i--)
+                delete L.stack[i];
             L.top = res + nres;
             return false;
         }
         default: {
             let i;
             if (wanted <= nres) {
-                for (i = 0; i < wanted; i++) {
+                for (i = 0; i < wanted; i++)
                     lobject.setobjs2s(L, res + i, firstResult + i);
-                }
             } else {
                 for (i = 0; i < nres; i++)
                     lobject.setobjs2s(L, res + i, firstResult + i);
-                for (; i < wanted; i++)
-                    L.stack[res + i] = new lobject.TValue(CT.LUA_TNIL, null);
+                for (; i < wanted; i++) {
+                    if (res+i >= L.top)
+                        L.stack[res + i] = new lobject.TValue(CT.LUAT_NIL, null);
+                    else
+                        L.stack[res + i].setnilvalue();
+                }
             }
             break;
         }
     }
-
-    L.top = res + wanted; /* top points after the last result */
+    let newtop = res + wanted; /* top points after the last result */
+    for (let i=L.top; i>=newtop; i--)
+        delete L.stack[i];
+    L.top = newtop;
     return true;
 };
 
@@ -239,7 +262,13 @@ const luaD_hook = function(L, event, line) {
         assert(!L.allowhook);
         L.allowhook = 1;
         ci.top = ci_top;
-        L.top = top;
+        if (L.top < top) {
+            while (L.top < top)
+                L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+        } else {
+            while (L.top > top)
+                delete L.stack[--L.top];
+        }
         ci.callstatus &= ~lstate.CIST_HOOKED;
     }
 };
@@ -252,8 +281,8 @@ const adjust_varargs = function(L, p, actual) {
 
     let i;
     for (i = 0; i < nfixargs && i < actual; i++) {
-        lobject.setobjs2s(L, L.top++, fixed + i);
-        L.stack[fixed + i] = new lobject.TValue(CT.LUA_TNIL, null);
+        lobject.pushobj2s(L, L.stack[fixed + i]);
+        L.stack[fixed + i].setnilvalue();
     }
 
     for (; i < nfixargs; i++)
@@ -267,9 +296,9 @@ const tryfuncTM = function(L, off, func) {
     if (!tm.ttisfunction(tm))
         ldebug.luaG_typeerror(L, func, defs.to_luastring("call", true));
     /* Open a hole inside the stack at 'func' */
-    for (let p = L.top; p > off; p--)
-        lobject.setobjs2s(L, p, p-1);
-    L.top++; /* slot ensured by caller */
+    lobject.pushobj2s(L, L.stack[L.top-1]); /* push top of stack again */
+    for (let p = L.top-2; p > off; p--)
+        lobject.setobjs2s(L, p, p-1); /* move other items up one */
     lobject.setobj2s(L, off, tm); /* tag method is the new function to be called */
 };
 
@@ -464,10 +493,16 @@ const recover = function(L, status) {
 ** coroutine error handler and should not kill the coroutine.)
 */
 const resume_error = function(L, msg, narg) {
-    L.top -= narg;  /* remove args from the stack */
-    lobject.setsvalue2s(L, L.top, lstring.luaS_newliteral(L, msg));  /* push error message */
-    L.top++;
-    assert(L.top <= L.ci.top, "stack overflow");
+    let ts = lstring.luaS_newliteral(L, msg);
+    if (narg === 0) {
+        lobject.pushsvalue2s(L, ts);
+        assert(L.top <= L.ci.top, "stack overflow");
+    } else {
+        /* remove args from the stack */
+        for (let i=1; i<narg; i++)
+            delete L.stack[--L.top];
+        lobject.setsvalue2s(L, L.top-1, ts);  /* push error message */
+    }
     return TS.LUA_ERRRUN;
 };
 
diff --git a/src/lobject.js b/src/lobject.js
index aec7109..f395036 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -220,16 +220,22 @@ class TValue {
 
 }
 
+const pushobj2s = function(L, tv) {
+    L.stack[L.top++] = new TValue(tv.type, tv.value);
+};
+const pushsvalue2s = function(L, ts) {
+    L.stack[L.top++] = new TValue(CT.LUA_TLNGSTR, ts);
+};
 /* from stack to (same) stack */
 const setobjs2s = function(L, newidx, oldidx) {
-    L.stack[newidx] = L.stack[oldidx];
+    L.stack[newidx].setfrom(L.stack[oldidx]);
 };
 /* to stack (not from same stack) */
 const setobj2s = function(L, newidx, oldtv) {
-    L.stack[newidx] = new TValue(oldtv.type, oldtv.value);
+    L.stack[newidx].setfrom(oldtv);
 };
 const setsvalue2s = function(L, newidx, ts) {
-    L.stack[newidx] = new TValue(CT.LUA_TLNGSTR, ts);
+    L.stack[newidx].setsvalue(ts);
 };
 
 const luaO_nilobject = new TValue(CT.LUA_TNIL, null);
@@ -528,8 +534,8 @@ const luaO_tostring = function(L, obj) {
 };
 
 const pushstr = function(L, str) {
-    setsvalue2s(L, L.top, lstring.luaS_new(L, str));
     ldo.luaD_inctop(L);
+    setsvalue2s(L, L.top-1, lstring.luaS_new(L, str));
 };
 
 const luaO_pushvfstring = function(L, fmt, argp) {
@@ -659,8 +665,7 @@ const luaO_arith = function(L, op, p1, p2, res) {
         case defs.LUA_OPBNOT: {  /* operate only on integers */
             let i1, i2;
             if ((i1 = lvm.tointeger(p1)) !== false && (i2 = lvm.tointeger(p2)) !== false) {
-                res.type  = CT.LUA_TNUMINT;
-                res.value = intarith(L, op, i1, i2);
+                res.setivalue(intarith(L, op, i1, i2));
                 return;
             }
             else break;  /* go to the end */
@@ -668,8 +673,7 @@ const luaO_arith = function(L, op, p1, p2, res) {
         case defs.LUA_OPDIV: case defs.LUA_OPPOW: {  /* operate only on floats */
             let n1, n2;
             if ((n1 = lvm.tonumber(p1)) !== false && (n2 = lvm.tonumber(p2)) !== false) {
-                res.type  = CT.LUA_TNUMFLT;
-                res.value = numarith(L, op, n1, n2);
+                res.setfltvalue(numarith(L, op, n1, n2));
                 return;
             }
             else break;  /* go to the end */
@@ -677,13 +681,11 @@ const luaO_arith = function(L, op, p1, p2, res) {
         default: {  /* other operations */
             let n1, n2;
             if (p1.ttisinteger() && p2.ttisinteger()) {
-                res.type  = CT.LUA_TNUMINT;
-                res.value = intarith(L, op, p1.value, p2.value);
+                res.setivalue(intarith(L, op, p1.value, p2.value));
                 return;
             }
             else if ((n1 = lvm.tonumber(p1)) !== false && (n2 = lvm.tonumber(p2)) !== false) {
-                res.type  = CT.LUA_TNUMFLT;
-                res.value = numarith(L, op, n1, n2);
+                res.setfltvalue(numarith(L, op, n1, n2));
                 return;
             }
             else break;  /* go to the end */
@@ -714,6 +716,8 @@ module.exports.luaO_tostring     = luaO_tostring;
 module.exports.luaO_utf8desc     = luaO_utf8desc;
 module.exports.luaO_utf8esc      = luaO_utf8esc;
 module.exports.numarith          = numarith;
+module.exports.pushobj2s         = pushobj2s;
+module.exports.pushsvalue2s      = pushsvalue2s;
 module.exports.setobjs2s         = setobjs2s;
 module.exports.setobj2s          = setobj2s;
 module.exports.setsvalue2s       = setsvalue2s;
diff --git a/src/lparser.js b/src/lparser.js
index 96bad67..0079141 100644
--- a/src/lparser.js
+++ b/src/lparser.js
@@ -1568,7 +1568,7 @@ const luaY_parser = function(L, z, buff, dyd, name, firstchar) {
     L.stack[L.top-1].setclLvalue(cl);
     lexstate.h = ltable.luaH_new(L);  /* create table for scanner */
     ldo.luaD_inctop(L);
-    L.stack[L.top-1] = new TValue(defs.CT.LUA_TTABLE, lexstate.h);
+    L.stack[L.top-1].sethvalue(lexstate.h);
     funcstate.f = cl.p = new Proto(L);
     funcstate.f.source = lstring.luaS_new(L, name);
     lexstate.buff = buff;
@@ -1579,7 +1579,7 @@ const luaY_parser = function(L, z, buff, dyd, name, firstchar) {
     assert(!funcstate.prev && funcstate.nups === 1 && !lexstate.fs);
     /* all scopes should be correctly finished */
     assert(dyd.actvar.n === 0 && dyd.gt.n === 0 && dyd.label.n === 0);
-    L.top--;  /* remove scanner's table */
+    delete L.stack[--L.top];  /* remove scanner's table */
     return cl;  /* closure is on the stack, too */
 };
 
diff --git a/src/lstate.js b/src/lstate.js
index b7a11bc..9046adf 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -109,8 +109,8 @@ const stack_init = function(L1, L) {
     let ci = L1.base_ci;
     ci.next = ci.previous = null;
     ci.callstatus = 0;
-    ci.func = L1.stack[L1.top];
     ci.funcOff = L1.top;
+    ci.func = L1.stack[L1.top];
     L1.stack[L1.top++] = new lobject.TValue(CT.LUA_TNIL, null);
     ci.top = L1.top + defs.LUA_MINSTACK;
     L1.ci = ci;
diff --git a/src/ltm.js b/src/ltm.js
index c3efc1e..8f4908c 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -110,13 +110,12 @@ const luaT_objtypename = function(L, o) {
 const luaT_callTM = function(L, f, p1, p2, p3, hasres) {
     let func = L.top;
 
-    lobject.setobj2s(L, L.top, f); /* push function (assume EXTRA_STACK) */
-    lobject.setobj2s(L, L.top + 1, p1); /* 1st argument */
-    lobject.setobj2s(L, L.top + 2, p2); /* 2nd argument */
-    L.top += 3;
+    lobject.pushobj2s(L, f); /* push function (assume EXTRA_STACK) */
+    lobject.pushobj2s(L, p1); /* 1st argument */
+    lobject.pushobj2s(L, p2); /* 2nd argument */
 
     if (!hasres)  /* no result? 'p3' is third argument */
-        lobject.setobj2s(L, L.top++, p3);  /* 3rd argument */
+        lobject.pushobj2s(L, p3); /* 3rd argument */
 
     if (L.ci.callstatus & lstate.CIST_LUA)
         ldo.luaD_call(L, func, hasres);
@@ -124,7 +123,9 @@ const luaT_callTM = function(L, f, p1, p2, p3, hasres) {
         ldo.luaD_callnoyield(L, func, hasres);
 
     if (hasres) {  /* if has result, move it to its place */
-        lobject.setobjs2s(L, p3, --L.top);
+        let tv = L.stack[L.top-1];
+        delete L.stack[--L.top];
+        p3.setfrom(tv);
     }
 };
 
@@ -158,10 +159,11 @@ const luaT_trybinTM = function(L, p1, p2, res, event) {
 };
 
 const luaT_callorderTM = function(L, p1, p2, event) {
-    if (!luaT_callbinTM(L, p1, p2, L.top, event))
+    let res = new lobject.TValue(CT.LUA_TNIL, null);
+    if (!luaT_callbinTM(L, p1, p2, res, event))
         return -1;
     else
-        return !L.stack[L.top].l_isfalse() ? 1 : 0;
+        return res.l_isfalse() ? 0 : 1;
 };
 
 const fasttm = function(l, et, e) {
diff --git a/src/lundump.js b/src/lundump.js
index 5686430..3f61cb6 100644
--- a/src/lundump.js
+++ b/src/lundump.js
@@ -265,8 +265,8 @@ const luaU_undump = function(L, Z, name) {
     let S = new BytecodeParser(L, Z, name);
     S.checkHeader();
     let cl = lfunc.luaF_newLclosure(L, S.readByte());
-    L.stack[L.top].setclLvalue(cl);
     ldo.luaD_inctop(L);
+    L.stack[L.top-1].setclLvalue(cl);
     cl.p = new lfunc.Proto(L);
     S.readFunction(cl.p, null);
     assert(cl.nupvalues === cl.p.upvalues.length);
diff --git a/src/lvm.js b/src/lvm.js
index e308807..724f689 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -34,12 +34,13 @@ const luaV_finishOp = function(L) {
         case OCi.OP_MOD: case OCi.OP_POW:
         case OCi.OP_UNM: case OCi.OP_BNOT: case OCi.OP_LEN:
         case OCi.OP_GETTABUP: case OCi.OP_GETTABLE: case OCi.OP_SELF: {
-            lobject.setobjs2s(L, base + inst.A, --L.top);
+            lobject.setobjs2s(L, base + inst.A, L.top-1);
+            delete L.stack[--L.top];
             break;
         }
         case OCi.OP_LE: case OCi.OP_LT: case OCi.OP_EQ: {
             let res = !L.stack[L.top - 1].l_isfalse();
-            L.top--;
+            delete L.stack[--L.top];
             if (ci.callstatus & lstate.CIST_LEQ) {  /* "<=" using "<" instead? */
                 assert(op === OCi.OP_LE);
                 ci.callstatus ^= lstate.CIST_LEQ;  /* clear mark */
@@ -61,17 +62,39 @@ const luaV_finishOp = function(L) {
             }
             /* move final result to final position */
             lobject.setobjs2s(L, ci.l_base + inst.A, L.top - 1);
-            L.top = ci.top;  /* restore top */
+            /* restore top */
+            if (L.top < ci.top) {
+                while (L.top < ci.top)
+                    L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+            } else {
+                while (L.top > ci.top)
+                    delete L.stack[--L.top];
+            }
             break;
         }
         case OCi.OP_TFORCALL: {
             assert(ci.l_code[ci.l_savedpc].opcode === OCi.OP_TFORLOOP);
-            L.top = ci.top;  /* correct top */
+            /* correct top */
+            if (L.top < ci.top) {
+                while (L.top < ci.top)
+                    L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+            } else {
+                while (L.top > ci.top)
+                    delete L.stack[--L.top];
+            }
             break;
         }
         case OCi.OP_CALL: {
-            if (inst.C - 1 >= 0)  /* nresults >= 0? */
-                L.top = ci.top;  /* adjust results */
+            if (inst.C - 1 >= 0) { /* nresults >= 0? */
+                /* adjust results */
+                if (L.top < ci.top) {
+                    while (L.top < ci.top)
+                        L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                } else {
+                    while (L.top > ci.top)
+                        delete L.stack[--L.top];
+                }
+            }
             break;
         }
     }
@@ -135,7 +158,7 @@ const luaV_execute = function(L) {
                 break;
             }
             case OCi.OP_LOADBOOL: {
-                L.stack[ra] = new lobject.TValue(CT.LUA_TBOOLEAN, i.B !== 0);
+                L.stack[ra].setbvalue(i.B !== 0);
 
                 if (i.C !== 0)
                     ci.l_savedpc++; /* skip next instruction (if C) */
@@ -144,7 +167,7 @@ const luaV_execute = function(L) {
             }
             case OCi.OP_LOADNIL: {
                 for (let j = 0; j <= i.B; j++)
-                    L.stack[ra + j] = new lobject.TValue(CT.LUA_TNIL, null);
+                    L.stack[ra + j].setnilvalue();
                 break;
             }
             case OCi.OP_GETUPVAL: {
@@ -155,7 +178,7 @@ const luaV_execute = function(L) {
             case OCi.OP_SETUPVAL: {
                 let uv = cl.upvals[i.B];
                 if (uv.isopen()) {
-                    uv.L.stack[uv.v] = L.stack[ra];
+                    uv.L.stack[uv.v].setfrom(L.stack[ra]);
                 } else {
                     uv.value.setfrom(L.stack[ra]);
                 }
@@ -192,7 +215,7 @@ const luaV_execute = function(L) {
                 break;
             }
             case OCi.OP_NEWTABLE: {
-                L.stack[ra] = new lobject.TValue(CT.LUA_TTABLE, ltable.luaH_new(L));
+                L.stack[ra].sethvalue(ltable.luaH_new(L));
                 break;
             }
             case OCi.OP_SELF: {
@@ -210,11 +233,11 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (op1.ttisinteger() && op2.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, (op1.value + op2.value)|0);
+                    L.stack[ra].setivalue((op1.value + op2.value)|0);
                 } else if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, numberop1 + numberop2);
+                    L.stack[ra].setfltvalue(numberop1 + numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_ADD);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_ADD);
                 }
                 break;
             }
@@ -225,11 +248,11 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (op1.ttisinteger() && op2.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, (op1.value - op2.value)|0);
+                    L.stack[ra].setivalue((op1.value - op2.value)|0);
                 } else if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, numberop1 - numberop2);
+                    L.stack[ra].setfltvalue(numberop1 - numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SUB);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_SUB);
                 }
                 break;
             }
@@ -240,11 +263,11 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (op1.ttisinteger() && op2.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, Math.imul(op1.value, op2.value));
+                    L.stack[ra].setivalue(Math.imul(op1.value, op2.value));
                 } else if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, numberop1 * numberop2);
+                    L.stack[ra].setfltvalue(numberop1 * numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MUL);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_MUL);
                 }
                 break;
             }
@@ -255,11 +278,11 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (op1.ttisinteger() && op2.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, luaV_mod(L, op1.value, op2.value));
+                    L.stack[ra].setivalue(luaV_mod(L, op1.value, op2.value));
                 } else if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, llimit.luai_nummod(L, numberop1, numberop2));
+                    L.stack[ra].setfltvalue(llimit.luai_nummod(L, numberop1, numberop2));
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MOD);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_MOD);
                 }
                 break;
             }
@@ -270,9 +293,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, Math.pow(numberop1, numberop2));
+                    L.stack[ra].setfltvalue(Math.pow(numberop1, numberop2));
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_POW);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_POW);
                 }
                 break;
             }
@@ -283,9 +306,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, numberop1 / numberop2);
+                    L.stack[ra].setfltvalue(numberop1 / numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_DIV);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_DIV);
                 }
                 break;
             }
@@ -296,11 +319,11 @@ const luaV_execute = function(L) {
                 let numberop2 = tonumber(op2);
 
                 if (op1.ttisinteger() && op2.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, luaV_div(L, op1.value, op2.value));
+                    L.stack[ra].setivalue(luaV_div(L, op1.value, op2.value));
                 } else if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, Math.floor(numberop1 / numberop2));
+                    L.stack[ra].setfltvalue(Math.floor(numberop1 / numberop2));
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_IDIV);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_IDIV);
                 }
                 break;
             }
@@ -311,9 +334,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tointeger(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, (numberop1 & numberop2));
+                    L.stack[ra].setivalue(numberop1 & numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BAND);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_BAND);
                 }
                 break;
             }
@@ -324,9 +347,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tointeger(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, (numberop1 | numberop2));
+                    L.stack[ra].setivalue(numberop1 | numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BOR);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_BOR);
                 }
                 break;
             }
@@ -337,9 +360,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tointeger(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, (numberop1 ^ numberop2));
+                    L.stack[ra].setivalue(numberop1 ^ numberop2);
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BXOR);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_BXOR);
                 }
                 break;
             }
@@ -350,9 +373,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tointeger(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, luaV_shiftl(numberop1, numberop2));
+                    L.stack[ra].setivalue(luaV_shiftl(numberop1, numberop2));
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHL);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_SHL);
                 }
                 break;
             }
@@ -363,9 +386,9 @@ const luaV_execute = function(L) {
                 let numberop2 = tointeger(op2);
 
                 if (numberop1 !== false && numberop2 !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, luaV_shiftl(numberop1, -numberop2));
+                    L.stack[ra].setivalue(luaV_shiftl(numberop1, -numberop2));
                 } else {
-                    ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHR);
+                    ltm.luaT_trybinTM(L, op1, op2, L.stack[ra], ltm.TMS.TM_SHR);
                 }
                 break;
             }
@@ -374,11 +397,11 @@ const luaV_execute = function(L) {
                 let numberop = tonumber(op);
 
                 if (op.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, (-op.value)|0);
+                    L.stack[ra].setivalue((-op.value)|0);
                 } else if (numberop !== false) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, -numberop);
+                    L.stack[ra].setfltvalue(-numberop);
                 } else {
-                    ltm.luaT_trybinTM(L, op, op, ra, ltm.TMS.TM_UNM);
+                    ltm.luaT_trybinTM(L, op, op, L.stack[ra], ltm.TMS.TM_UNM);
                 }
                 break;
             }
@@ -386,19 +409,19 @@ const luaV_execute = function(L) {
                 let op = L.stack[RB(L, base, i)];
 
                 if (op.ttisinteger()) {
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, ~op.value);
+                    L.stack[ra].setivalue(~op.value);
                 } else {
-                    ltm.luaT_trybinTM(L, op, op, ra, ltm.TMS.TM_BNOT);
+                    ltm.luaT_trybinTM(L, op, op, L.stack[ra], ltm.TMS.TM_BNOT);
                 }
                 break;
             }
             case OCi.OP_NOT: {
                 let op = L.stack[RB(L, base, i)];
-                L.stack[ra] = new lobject.TValue(CT.LUA_TBOOLEAN, op.l_isfalse());
+                L.stack[ra].setbvalue(op.l_isfalse());
                 break;
             }
             case OCi.OP_LEN: {
-                luaV_objlen(L, ra, L.stack[RB(L, base, i)]);
+                luaV_objlen(L, L.stack[ra], L.stack[RB(L, base, i)]);
                 break;
             }
             case OCi.OP_CONCAT: {
@@ -408,7 +431,14 @@ const luaV_execute = function(L) {
                 luaV_concat(L, c - b + 1);
                 let rb = base + b;
                 lobject.setobjs2s(L, ra, rb);
-                L.top = ci.top; /* restore top */
+                /* restore top */
+                if (L.top < ci.top) {
+                    while (L.top < ci.top)
+                        L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                } else {
+                    while (L.top > ci.top)
+                        delete L.stack[--L.top];
+                }
                 break;
             }
             case OCi.OP_JMP: {
@@ -458,12 +488,26 @@ const luaV_execute = function(L) {
                 let b = i.B;
                 let nresults = i.C - 1;
 
-                if (b !== 0)
-                    L.top = ra+b;
+                if (b !== 0) {
+                    if (L.top < ra+b) {
+                        while (L.top < ra+b)
+                            L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                    } else {
+                        while (L.top > ra+b)
+                            delete L.stack[--L.top];
+                    }
+                }
 
                 if (ldo.luaD_precall(L, ra, nresults)) {
-                    if (nresults >= 0)
-                        L.top = ci.top;
+                    if (nresults >= 0) {
+                        if (L.top < ci.top) {
+                            while (L.top < ci.top)
+                                L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                        } else {
+                            while (L.top > ci.top)
+                                delete L.stack[--L.top];
+                        }
+                    }
                 } else {
                     ci = L.ci;
                     continue newframe;
@@ -472,7 +516,16 @@ const luaV_execute = function(L) {
                 break;
             }
             case OCi.OP_TAILCALL: {
-                if (i.B !== 0) L.top = ra + i.B;
+                let b = i.B;
+                if (b !== 0) {
+                    if (L.top < ra+b) {
+                        while (L.top < ra+b)
+                            L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                    } else {
+                        while (L.top > ra+b)
+                            delete L.stack[--L.top];
+                    }
+                } /* else previous instruction set top */
                 if (ldo.luaD_precall(L, ra, LUA_MULTRET)) { // JS function
                 } else {
                     /* tail call: put called frame (n) in place of caller one (o) */
@@ -485,9 +538,15 @@ const luaV_execute = function(L) {
                     if (cl.p.p.length > 0) lfunc.luaF_close(L, oci.l_base);
                     for (let aux = 0; nfuncOff + aux < lim; aux++)
                         lobject.setobjs2s(L, ofuncOff + aux, nfuncOff + aux);
-                    oci.func = nci.func;
                     oci.l_base = ofuncOff + (nci.l_base - nfuncOff);
-                    oci.top = L.top = ofuncOff + (L.top - nfuncOff);
+                    oci.top = ofuncOff + (L.top - nfuncOff);
+                    if (L.top < nci.top) {
+                        while (L.top < oci.top)
+                            L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                    } else {
+                        while (L.top > oci.top)
+                            delete L.stack[--L.top];
+                    }
                     oci.l_code = nci.l_code;
                     oci.l_savedpc = nci.l_savedpc;
                     oci.callstatus |= lstate.CIST_TAIL;
@@ -506,10 +565,19 @@ const luaV_execute = function(L) {
 
                 if (ci.callstatus & lstate.CIST_FRESH)
                     return; /* external invocation: return */
-
+                /* invocation via reentry: continue execution */
                 ci = L.ci;
-                if (b) L.top = ci.top;
-
+                if (b) {
+                    if (L.top < ci.top) {
+                        while (L.top < ci.top)
+                            L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                    } else {
+                        while (L.top > ci.top)
+                            delete L.stack[--L.top];
+                    }
+                }
+                assert(ci.callstatus & lstate.CIST_LUA);
+                assert(ci.l_code[ci.l_savedpc - 1].opcode === OCi.OP_CALL);
                 continue newframe;
             }
             case OCi.OP_FORLOOP: {
@@ -521,7 +589,7 @@ const luaV_execute = function(L) {
                     if (0 < step ? idx <= limit : limit <= idx) {
                         ci.l_savedpc += i.sBx;
                         L.stack[ra].chgivalue(idx);  /* update internal index... */
-                        L.stack[ra + 3] = new lobject.TValue(CT.LUA_TNUMINT, idx);
+                        L.stack[ra + 3].setivalue(idx);
                     }
                 } else { /* floating loop */
                     let step = L.stack[ra + 2].value;
@@ -531,7 +599,7 @@ const luaV_execute = function(L) {
                     if (0 < step ? idx <= limit : limit <= idx) {
                         ci.l_savedpc += i.sBx;
                         L.stack[ra].chgfltvalue(idx);  /* update internal index... */
-                        L.stack[ra + 3] = new lobject.TValue(CT.LUA_TNUMFLT, idx);
+                        L.stack[ra + 3].setfltvalue(idx);
                     }
                 }
                 break;
@@ -550,13 +618,13 @@ const luaV_execute = function(L) {
                     let nlimit, nstep, ninit;
                     if ((nlimit = tonumber(plimit)) === false)
                         ldebug.luaG_runerror(L, defs.to_luastring("'for' limit must be a number", true));
-                    L.stack[ra + 1] = new lobject.TValue(CT.LUA_TNUMFLT, nlimit);
+                    L.stack[ra + 1].setfltvalue(nlimit);
                     if ((nstep = tonumber(pstep)) === false)
                         ldebug.luaG_runerror(L, defs.to_luastring("'for' step must be a number", true));
-                    L.stack[ra + 2] = new lobject.TValue(CT.LUA_TNUMFLT, nstep);
+                    L.stack[ra + 2].setfltvalue(nstep);
                     if ((ninit = tonumber(init)) === false)
                         ldebug.luaG_runerror(L, defs.to_luastring("'for' initial value must be a number", true));
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNUMFLT, ninit - nstep);
+                    L.stack[ra].setfltvalue(ninit - nstep);
                 }
 
                 ci.l_savedpc += i.sBx;
@@ -567,15 +635,28 @@ const luaV_execute = function(L) {
                 lobject.setobjs2s(L, cb+2, ra+2);
                 lobject.setobjs2s(L, cb+1, ra+1);
                 lobject.setobjs2s(L, cb, ra);
-                L.top = cb + 3; /* func. + 2 args (state and index) */
+                /* func. + 2 args (state and index) */
+                if (L.top < cb + 3) {
+                    while (L.top < cb + 3)
+                        L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                } else {
+                    while (L.top > cb + 3)
+                        delete L.stack[--L.top];
+                }
                 ldo.luaD_call(L, cb, i.C);
+                if (L.top < ci.top) {
+                    while (L.top < ci.top)
+                        L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                } else {
+                    while (L.top > ci.top)
+                        delete L.stack[--L.top];
+                }
                 /* go straight to OP_TFORLOOP */
-                L.top = ci.top;
                 i = ci.l_code[ci.l_savedpc++];
                 ra = RA(L, base, i);
                 assert(i.opcode === OCi.OP_TFORLOOP);
-                /* fall through */
             }
+            /* fall through */
             case OCi.OP_TFORLOOP: {
                 if (!L.stack[ra + 1].ttisnil()) { /* continue loop? */
                     lobject.setobjs2s(L, ra, ra + 1); /* save control variable */
@@ -601,7 +682,14 @@ const luaV_execute = function(L) {
                     ltable.luaH_setint(h, last--, L.stack[ra + n]);
                 }
 
-                L.top = ci.top; /* correct top (in case of previous open call) */
+                /* correct top (in case of previous open call) */
+                if (L.top < ci.top) {
+                    while (L.top < ci.top)
+                        L.stack[L.top++] = new lobject.TValue(CT.LUA_TNIL, null);
+                } else {
+                    while (L.top > ci.top)
+                        delete L.stack[--L.top];
+                }
                 break;
             }
             case OCi.OP_CLOSURE: {
@@ -620,14 +708,23 @@ const luaV_execute = function(L) {
                 if (b < 0) {
                     b = n;  /* get all var. arguments */
                     ldo.luaD_checkstack(L, n);
-                    L.top = ra + n;
+                    if (L.top >= ra+n) {
+                        while (L.top > ra+n)
+                                delete L.stack[--L.top];
+                    } else {
+                        while (L.top < ra+n) {
+                                L.stack[L.top] = new lobject.TValue();
+                                L.top++;
+                        }
+                    }
+                    assert(L.top == ra + n);
                 }
 
                 for (j = 0; j < b && j < n; j++)
                     lobject.setobjs2s(L, ra + j, base - n + j);
 
                 for (; j < b; j++) /* complete required results with nil */
-                    L.stack[ra + j] = new lobject.TValue(CT.LUA_TNIL, null);
+                    L.stack[ra + j].setnilvalue();
                 break;
             }
             case OCi.OP_EXTRAARG: {
@@ -725,8 +822,9 @@ const luaV_equalobj = function(L, t1, t2) {
     if (tm === null) /* no TM? */
         return 0;
 
-    ltm.luaT_callTM(L, tm, t1, t2, L.top, 1);
-    return L.stack[L.top].l_isfalse() ? 0 : 1;
+    let tv = new lobject.TValue(); /* doesn't use the stack */
+    ltm.luaT_callTM(L, tm, t1, t2, tv, 1);
+    return tv.l_isfalse() ? 0 : 1;
 };
 
 const luaV_rawequalobj = function(t1, t2) {
@@ -890,12 +988,12 @@ const luaV_objlen = function(L, ra, rb) {
             let h = rb.value;
             tm = ltm.fasttm(L, h.metatable, ltm.TMS.TM_LEN);
             if (tm !== null) break; /* metamethod? break switch to call it */
-            L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, ltable.luaH_getn(h)); /* else primitive len */
+            ra.setivalue(ltable.luaH_getn(h)); /* else primitive len */
             return;
         }
         case CT.LUA_TSHRSTR:
         case CT.LUA_TLNGSTR:
-            L.stack[ra] = new lobject.TValue(CT.LUA_TNUMINT, rb.vslen());
+            ra.setivalue(rb.vslen());
             return;
         default: {
             tm = ltm.luaT_gettmbyobj(L, rb, ltm.TMS.TM_LEN);
@@ -989,32 +1087,29 @@ const luaV_concat = function(L, total) {
         let n = 2; /* number of elements handled in this pass (at least 2) */
 
         if (!(L.stack[top-2].ttisstring() || cvt2str(L.stack[top-2])) || !tostring(L, top - 1)) {
-            ltm.luaT_trybinTM(L, L.stack[top-2], L.stack[top-1], top-2, ltm.TMS.TM_CONCAT);
-            delete L.stack[top - 1];
+            ltm.luaT_trybinTM(L, L.stack[top-2], L.stack[top-1], L.stack[top-2], ltm.TMS.TM_CONCAT);
         } else if (isemptystr(L.stack[top-1])) {
             tostring(L, top - 2);
-            delete L.stack[top - 1];
         } else if (isemptystr(L.stack[top-2])) {
             lobject.setobjs2s(L, top - 2, top - 1);
-            delete L.stack[top - 1];
         } else {
             /* at least two non-empty string values; get as many as possible */
             let toconcat = new Array(total);
             toconcat[total-1] = L.stack[top-1].svalue();
-            delete L.stack[top - 1];
             for (n = 1; n < total; n++) {
                 if (!tostring(L, top - n - 1)) {
                         toconcat = toconcat.slice(total-n);
                         break;
                 }
                 toconcat[total-n-1] = L.stack[top - n - 1].svalue();
-                delete L.stack[top - n - 1];
             }
             let ts = lstring.luaS_bless(L, Array.prototype.concat.apply([], toconcat));
             lobject.setsvalue2s(L, top - n, ts);
         }
         total -= n - 1; /* got 'n' strings to create 1 new */
-        L.top -= n - 1; /* popped 'n' strings and pushed one */
+        /* popped 'n' strings and pushed one */
+        for (; L.top > top-(n-1);)
+            delete L.stack[--L.top];
     } while (total > 1); /* repeat until only 1 result left */
 };
 
@@ -1037,14 +1132,14 @@ const gettable = function(L, t, key, ra) {
             } else { /* 't' is a table */
                 tm = ltm.fasttm(L, t.value.metatable, ltm.TMS.TM_INDEX);  /* table's metamethod */
                 if (tm === null) { /* no metamethod? */
-                    L.stack[ra] = new lobject.TValue(CT.LUA_TNIL, null); /* result is nil */
+                    L.stack[ra].setnilvalue(); /* result is nil */
                     return;
                 }
             }
             /* else will try the metamethod */
         }
         if (tm.ttisfunction()) { /* is metamethod a function? */
-            ltm.luaT_callTM(L, tm, t, key, ra, 1); /* call it */
+            ltm.luaT_callTM(L, tm, t, key, L.stack[ra], 1); /* call it */
             return;
         }
         t = tm;  /* else try to access 'tm[key]' */
-- 
cgit v1.2.3-70-g09d2