This is an automated email from the ASF dual-hosted git repository. henrib pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push: new 37fda79e JEXL-405: avoid corrupting frame by ensuring auto-capture of recursive function variable is only called at definition time; new d59714b4 Merge remote-tracking branch 'origin/master' 37fda79e is described below commit 37fda79ec11d236e745a5dbfadeacf7c61c6819f Author: henrib <hen...@apache.org> AuthorDate: Wed Aug 30 20:44:45 2023 +0200 JEXL-405: avoid corrupting frame by ensuring auto-capture of recursive function variable is only called at definition time; --- .../org/apache/commons/jexl3/internal/Closure.java | 7 +++++ .../apache/commons/jexl3/internal/Interpreter.java | 8 ++++-- .../org/apache/commons/jexl3/internal/Scope.java | 4 +++ .../org/apache/commons/jexl3/Issues400Test.java | 32 ++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/jexl3/internal/Closure.java b/src/main/java/org/apache/commons/jexl3/internal/Closure.java index aeaaac34..733ef85a 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Closure.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Closure.java @@ -150,4 +150,11 @@ public class Closure extends Script { } }; } + + boolean hasParent(Scope scope) { + Scope s = frame != null? frame.getScope() : null; + return scope != null && s != null + ? scope == s.getParent() + : false; + } } diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java index 5b6ffdf1..80397f5e 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -1069,7 +1069,7 @@ public class Interpreter extends InterpreterBase { this.visit(variable, data); final int symbol = variable.getSymbol(); frame.set(symbol, closure); - // make the closure accessible to itself, ie capture the currently set variable after frame creation + // make the closure accessible to itself, ie capture the 'function' variable after frame creation closure.setCaptured(symbol, closure); } return closure; @@ -1428,7 +1428,11 @@ public class Interpreter extends InterpreterBase { if (assignop == null) { // make the closure accessible to itself, ie capture the currently set variable after frame creation if (right instanceof Closure) { - ((Closure) right).setCaptured(symbol, right); + Closure closure = (Closure) right; + // the variable scope must be the parent of the lambdas + if (closure.hasParent(frame.getScope())) { + closure.setCaptured(symbol, right); + } } frame.set(symbol, right); } else { diff --git a/src/main/java/org/apache/commons/jexl3/internal/Scope.java b/src/main/java/org/apache/commons/jexl3/internal/Scope.java index 3f71c888..3d7028a0 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Scope.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Scope.java @@ -349,4 +349,8 @@ public final class Scope { return locals.toArray(new String[0]); } + + Scope getParent() { + return parent; + } } diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java b/src/test/java/org/apache/commons/jexl3/Issues400Test.java index f760dae7..b988ff02 100644 --- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java +++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java @@ -155,4 +155,36 @@ public class Issues400Test { result = script.execute(null, a); Assert.assertEquals(1042, result); } + + @Test + public void test405a() { + final JexlEngine jexl = new JexlBuilder() + .cache(64) + .strict(true) + .safe(false) + .create(); + String libSrc = "var tfn = pfn -> { var fn = pfn; fn() }; { 'theFunction' : tfn }"; + String src1 = "var v0 = 42; var v1 = -42; lib.theFunction(()->{ v1 + v0 }) "; + JexlScript libMap = jexl.createScript(libSrc); + Object theLib = libMap.execute(null); + JexlScript f1 = jexl.createScript(src1, "lib"); + Object result = f1.execute(null, theLib); + Assert.assertEquals(0, result); + } + + @Test + public void test405b() { + final JexlEngine jexl = new JexlBuilder() + .cache(64) + .strict(true) + .safe(false) + .create(); + String libSrc = "function tfn(pfn) { var fn = pfn; fn() }; { 'theFunction' : tfn }"; + String src1 = "var v0 = 42; var v1 = -42; lib.theFunction(()->{ v1 + v0 }) "; + JexlScript libMap = jexl.createScript(libSrc); + Object theLib = libMap.execute(null); + JexlScript f1 = jexl.createScript(src1, "lib"); + Object result = f1.execute(null, theLib); + Assert.assertEquals(0, result); + } }