summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lvm.js26
-rw-r--r--tests/test-suite/closure.js3
2 files changed, 26 insertions, 3 deletions
diff --git a/src/lvm.js b/src/lvm.js
index 31ad74d..5f32129 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -687,7 +687,11 @@ const luaV_execute = function(L) {
}
case OCi.OP_CLOSURE: {
let p = cl.p.p[i.Bx];
- pushclosure(L, p, cl.upvals, base, ra); /* create a new one */
+ let ncl = getcached(p, cl.upvals, L.stack, base); /* cached closure */
+ if (ncl === null) /* no match? */
+ pushclosure(L, p, cl.upvals, base, ra); /* create a new one */
+ else
+ L.stack[ra].setclLvalue(ncl);
break;
}
case OCi.OP_VARARG: {
@@ -1024,6 +1028,25 @@ const luaV_shiftl = function(x, y) {
};
/*
+** check whether cached closure in prototype 'p' may be reused, that is,
+** whether there is a cached closure with the same upvalues needed by
+** new closure to be created.
+*/
+const getcached = function(p, encup, stack, base) {
+ let c = p.cache;
+ if (c !== null) { /* is there a cached closure? */
+ let uv = p.upvalues;
+ let nup = uv.length;
+ for (let i = 0; i < nup; i++) { /* check whether it has right upvalues */
+ let v = uv[i].instack ? stack[base + uv[i].idx] : encup[uv[i].idx].v;
+ if (c.upvals[i].v !== v)
+ return null; /* wrong upvalue; cannot reuse closure */
+ }
+ }
+ return c; /* return cached closure (or NULL if no cached closure) */
+};
+
+/*
** create a new Lua closure, push it in the stack, and initialize
** its upvalues.
*/
@@ -1040,6 +1063,7 @@ const pushclosure = function(L, p, encup, base, ra) {
ncl.upvals[i] = encup[uv[i].idx];
ncl.upvals[i].refcount++;
}
+ p.cache = ncl; /* save it on cache for reuse */
};
const cvt2str = function(o) {
diff --git a/tests/test-suite/closure.js b/tests/test-suite/closure.js
index 3f63f17..8e9e4b1 100644
--- a/tests/test-suite/closure.js
+++ b/tests/test-suite/closure.js
@@ -8,8 +8,7 @@ const lua = require('../../src/lua.js');
const lauxlib = require('../../src/lauxlib.js');
const lualib = require('../../src/lualib.js');
-// TODO: fengari doesn't cache closures yet/ever
-test("[test-suite] closure: testing equality", { skip: true }, function (t) {
+test("[test-suite] closure: testing equality", function (t) {
let luaCode = `
a = {}
for i = 1, 5 do a[i] = function (x) return x + a + _ENV end end