summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lvm.js45
-rw-r--r--tests/lvm.js26
2 files changed, 67 insertions, 4 deletions
diff --git a/src/lvm.js b/src/lvm.js
index 95382b3..e0901c5 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -1,7 +1,9 @@
/*jshint esversion: 6 */
"use strict";
-const BytecodeParser = require("./lundump.js");
+const assert = require('assert');
+
+const BytecodeParser = require('./lundump.js');
const OC = require('./lopcodes.js');
const lua = require('./lua.js');
const CT = lua.constant_types;
@@ -358,13 +360,48 @@ class LuaVM {
if (b !== 0)
L.top = ra+b;
- this.precall(ra, L.stack[ra], nresults);
- ci = L.ci;
- continue newframe;
+ if (this.precall(ra, L.stack[ra], nresults)) {
+ if (nresults >= 0)
+ L.top = ci.top;
+ base = ci.u.l.base;
+ } else {
+ ci = L.ci;
+ continue newframe;
+ }
break;
}
case "OP_TAILCALL": {
+ if (i.B !== 0) L.top = ra + i.B;
+ if (this.precall(ra, L.stack[ra], LUA_MULTRET)) {
+ base = ci.u.l.base;
+ } else {
+ /* tail call: put called frame (n) in place of caller one (o) */
+ let nci = L.ci;
+ let oci = nci.previous;
+ let nfunc = nci.func;
+ let nfuncOff = nci.funcOff;
+ let ofunc = oci.func;
+ let ofuncOff = oci.funcOff;
+ let lim = nci.u.l.base + nfunc.p.numparams;
+ // TODO close upvalues ?
+ for (let aux = 0; nfuncOff + aux < lim; aux++)
+ L.stack[ofuncOff + aux] = L.stack[nfuncOff + aux];
+
+ oci.u.l.base = ofuncOff + (nci.u.l.base - nfuncOff);
+ L.top = ofuncOff + (L.top - nfuncOff);
+ oci.top = L.top;
+ oci.u.l.savedpc = nci.u.l.savedpc;
+ oci.pcOff = nci.pcOff;
+ //TODO callstatus
+ L.ci = oci;
+ ci = L.ci;
+ L.ciOff--;
+
+ assert(L.top === oci.u.l.base + L.stack[ofuncOff].p.maxstacksize);
+
+ continue newframe;
+ }
break;
}
case "OP_RETURN": {
diff --git a/tests/lvm.js b/tests/lvm.js
index 0a71bf8..c454bcf 100644
--- a/tests/lvm.js
+++ b/tests/lvm.js
@@ -219,3 +219,29 @@ test('Multiple return', function (t) {
"Program output is correct"
);
});
+
+
+test('TAILCALL', function (t) {
+ let luaCode = `
+ local f = function (a, b)
+ return a + b
+ end
+
+ return f(1,2)
+ `, vm;
+
+ t.plan(2);
+
+ t.comment("Running following code: \n" + luaCode);
+
+ t.doesNotThrow(function () {
+ vm = getVM(luaCode);
+ vm.execute();
+ }, "Program executed without errors");
+
+ t.strictEqual(
+ vm.L.stack[vm.L.top - 1].value,
+ 3,
+ "Program output is correct"
+ );
+}); \ No newline at end of file