diff options
author | Benoit Giannangeli <benoit.giannangeli@boursorama.fr> | 2017-02-06 11:30:50 +0100 |
---|---|---|
committer | Benoit Giannangeli <giann008@gmail.com> | 2017-02-06 22:12:39 +0100 |
commit | dfc839625011d6bbc51c9415be4e55a6d4ffe45a (patch) | |
tree | 477a786adb8d0a6521b73d50b9e6abff689f8f44 | |
parent | 0926e6a6d00ee13e594e87ec8fc06f8e754eb145 (diff) | |
download | fengari-dfc839625011d6bbc51c9415be4e55a6d4ffe45a.tar.gz fengari-dfc839625011d6bbc51c9415be4e55a6d4ffe45a.tar.bz2 fengari-dfc839625011d6bbc51c9415be4e55a6d4ffe45a.zip |
TAILCALL
-rw-r--r-- | src/lvm.js | 45 | ||||
-rw-r--r-- | tests/lvm.js | 26 |
2 files changed, 67 insertions, 4 deletions
@@ -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 |