You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2018/09/10 08:47:41 UTC

[commons-jexl] branch master updated: JEXL-270, JEXL-271: fix hoisting resolution and use thread-local current interpreter to capture env while currying, fix script re-stringification, added tests

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 cb4f54a  JEXL-270, JEXL-271: fix hoisting resolution and use thread-local current interpreter to capture env while currying, fix script re-stringification, added tests
cb4f54a is described below

commit cb4f54a0e3468e5b1f5336a47ea98308d163d605
Author: henrib <he...@apache.org>
AuthorDate: Mon Sep 10 10:46:19 2018 +0200

    JEXL-270, JEXL-271: fix hoisting resolution and use thread-local current interpreter to capture env while currying, fix script re-stringification, added tests
---
 .../org/apache/commons/jexl3/JexlOperator.java     | 13 ++++++---
 .../apache/commons/jexl3/internal/Interpreter.java | 30 +++++++++++++++++--
 .../org/apache/commons/jexl3/internal/Scope.java   |  2 +-
 .../org/apache/commons/jexl3/internal/Script.java  | 31 ++++++++++----------
 .../apache/commons/jexl3/parser/ASTJexlLambda.java | 18 ------------
 .../apache/commons/jexl3/parser/ASTJexlScript.java | 16 ++++++++--
 .../org/apache/commons/jexl3/Issues200Test.java    | 34 ++++++++++++++++++++++
 7 files changed, 101 insertions(+), 43 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlOperator.java b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
index 1332d1b..5bc448e 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOperator.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
@@ -26,10 +26,15 @@ package org.apache.commons.jexl3;
  * For instance, '+' is associated to 'T add(L x, R y)'.</p>
  *
  * <p>The default JexlArithmetic implements generic versions of these methods using Object as arguments.
- * You can use your own derived JexlArithmetic that override and/or overload those operator methods; these methods
- * must be public,
- * must respect the return type when primitive
- * and may be overloaded multiple times with different signatures.</p>
+ * You can use your own derived JexlArithmetic that override and/or overload those operator methods.
+ * Note that these are overloads by convention, not actual Java overloads.
+ * The following rules apply to operator methods:</p>
+ * <ul>
+ * <li>Operator methods should be public</li>
+ * <li>Operators return type should be respected when primitive (int, boolean,...)</li>
+ * <li>Operators may be overloaded multiple times with different signatures</li>
+ * <li>Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL implementation</li>
+ * </ul>
  *
  * @since 3.0
  */
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 46d692b..89db12a 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -24,12 +24,10 @@ import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlException;
 import org.apache.commons.jexl3.JexlOperator;
 import org.apache.commons.jexl3.JexlScript;
-
 import org.apache.commons.jexl3.introspection.JexlMethod;
 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
 import org.apache.commons.jexl3.introspection.JexlPropertySet;
 import org.apache.commons.jexl3.introspection.JexlUberspect.PropertyResolver;
-
 import org.apache.commons.jexl3.parser.ASTAddNode;
 import org.apache.commons.jexl3.parser.ASTAndNode;
 import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
@@ -135,6 +133,12 @@ public class Interpreter extends InterpreterBase {
     protected Map<String, Object> functors;
 
     /**
+     * The thread local interpreter.
+     */
+    protected static final java.lang.ThreadLocal<Interpreter> INTER =
+                       new java.lang.ThreadLocal<Interpreter>();
+
+    /**
      * Creates an interpreter.
      * @param engine   the engine creating this interpreter
      * @param aContext the context to evaluate expression
@@ -168,6 +172,25 @@ public class Interpreter extends InterpreterBase {
         functions = ii.functions;
         functors = ii.functors;
     }
+    
+    /**
+     * @return the current interpreter frame
+     */
+    static Scope.Frame getCurrentFrame() {
+        Interpreter inter = INTER.get();
+        return inter != null? inter.frame : null;
+    }
+        
+    /**
+     * Swaps the current thread local interpreter.
+     * @param inter the interpreter or null
+     * @return the previous thread local interpreter
+     */
+    protected Interpreter putThreadInterpreter(Interpreter inter) {
+        Interpreter pinter = INTER.get();
+        INTER.set(inter);
+        return pinter;
+    }
 
     /**
      * Interpret the given script/expression.
@@ -181,8 +204,10 @@ public class Interpreter extends InterpreterBase {
     public Object interpret(JexlNode node) {
         JexlContext.ThreadLocal tcontext = null;
         JexlEngine tjexl = null;
+        Interpreter tinter = null;
         try {
             cancelCheck(node);
+            tinter = putThreadInterpreter(this);
             if (context instanceof JexlContext.ThreadLocal) {
                 tcontext = jexl.putThreadLocal((JexlContext.ThreadLocal) context);
             }
@@ -218,6 +243,7 @@ public class Interpreter extends InterpreterBase {
             if (context instanceof JexlContext.ThreadLocal) {
                 jexl.putThreadLocal(tcontext);
             }
+            putThreadInterpreter(tinter);
         }
         return null;
     }
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 f5bef11..0625daf 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Scope.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
@@ -109,7 +109,7 @@ public final class Scope {
     private Integer getSymbol(String name, boolean hoist) {
         Integer register = namedVariables != null ? namedVariables.get(name) : null;
         if (register == null && hoist && parent != null) {
-            Integer pr = parent.getSymbol(name, false);
+            Integer pr = parent.getSymbol(name, true);
             if (pr != null) {
                 if (hoistedVariables == null) {
                     hoistedVariables = new LinkedHashMap<Integer, Integer>();
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Script.java b/src/main/java/org/apache/commons/jexl3/internal/Script.java
index eeb727f..9cf4343 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
@@ -124,16 +124,27 @@ public class Script implements JexlScript, JexlExpression {
     public String getParsedText() {
         return getParsedText(2);
     }
-
+    
     @Override
     public String getParsedText(int indent) {
         Debugger debug = new Debugger();
         debug.setIndentation(indent);
-        debug.debug(script);
+        debug.debug(script, false);
         return debug.toString();
     }
 
     @Override
+    public String toString() {
+        CharSequence src = source;
+        if (src == null) {
+            Debugger debug = new Debugger();
+            debug.debug(script, false);
+            src = debug.toString();
+        }
+        return src.toString();
+    }
+
+    @Override
     public int hashCode() {
         // CSOFF: Magic number
         int hash = 17;
@@ -162,17 +173,6 @@ public class Script implements JexlScript, JexlExpression {
     }
 
     @Override
-    public String toString() {
-        CharSequence src = source;
-        if (src == null) {
-            Debugger debug = new Debugger();
-            debug.debug(script);
-            src = debug.toString();
-        }
-        return src.toString();
-    }
-
-    @Override
     public Object evaluate(JexlContext context) {
         return execute(context);
     }
@@ -211,10 +211,11 @@ public class Script implements JexlScript, JexlExpression {
             if (sf != null) {
                 frame = sf.assign(args);
             } else {
-                frame = script.createFrame(args);
+                sf = Interpreter.getCurrentFrame();
+                frame = script.createFrame(sf, args);
             }
         }
-
+        
         @Override
         protected Scope.Frame createFrame(Object[] args) {
             return frame != null? frame.assign(args) : super.createFrame(args);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java b/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java
index c668853..e57f586 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.internal.Scope;
-
 /**
  * Lambda (function).
  */
@@ -36,20 +34,4 @@ public final class ASTJexlLambda extends ASTJexlScript {
     public boolean isTopLevel() {
         return jjtGetParent() == null;
     }
-
-    /**
-     * Creates an array of arguments by copying values up to the number of parameters.
-     * @param frame the calling frame
-     * @param values the argument values
-     * @return the arguments array
-     */
-    public Scope.Frame createFrame(Scope.Frame frame, Object... values) {
-        if (getScope() != null) {
-            Scope.Frame cframe = getScope().createFrame(frame);
-            if (cframe != null) {
-                return cframe.assign(values);
-            }
-        }
-        return null;
-    }
 }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java b/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
index 8dd8947..74c7bbc 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
@@ -59,7 +59,7 @@ public class ASTJexlScript extends JexlNode {
     }
       /**
      * Sets this script pragmas.
-     * @param pragmas the pragmas
+     * @param thePragmas the pragmas
      */
     public void setPragmas(Map<String, Object> thePragmas) {
         this.pragmas = thePragmas;
@@ -104,18 +104,28 @@ public class ASTJexlScript extends JexlNode {
 
     /**
      * Creates an array of arguments by copying values up to the number of parameters.
+     * @param caller the calling frame
      * @param values the argument values
      * @return the arguments array
      */
-    public Scope.Frame createFrame(Object... values) {
+    public Scope.Frame createFrame(Scope.Frame caller, Object... values) {
         if (scope != null) {
-            Scope.Frame frame = scope.createFrame(null);
+            Scope.Frame frame = scope.createFrame(caller);
             if (frame != null) {
                 return frame.assign(values);
             }
         }
         return null;
     }
+    
+    /**
+     * Creates an array of arguments by copying values up to the number of parameters.
+     * @param values the argument values
+     * @return the arguments array
+     */
+    public Scope.Frame createFrame(Object... values) {
+        return createFrame(null, values);
+    }
 
     /**
      * Gets the (maximum) number of arguments this script expects.
diff --git a/src/test/java/org/apache/commons/jexl3/Issues200Test.java b/src/test/java/org/apache/commons/jexl3/Issues200Test.java
index 31fb8f8..7d4d47f 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues200Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues200Test.java
@@ -652,5 +652,39 @@ public class Issues200Test extends JexlTestCase {
         result = script.execute(ctxt);
         Assert.assertTrue(result instanceof JexlScript);
     }
+         
+    @Test
+    public void test270() throws Exception {
+        JexlEngine jexl = new JexlBuilder().create();
+        JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
+        String text = base.toString();
+        JexlScript script = base.curry(5, 15);
+        Assert.assertEquals(text, script.toString());
+
+        JexlEvalContext ctxt = new JexlEvalContext();
+        ctxt.set("s", base);
+        script = jexl.createScript("return s");
+        Object result = script.execute(ctxt);
+        Assert.assertEquals(text, result.toString());
+
+        script = jexl.createScript("return s.curry(1)");
+        result = script.execute(ctxt);
+        Assert.assertEquals(text, result.toString());
+    }
         
+    @Test
+    public void test271a() throws Exception {
+        JexlEngine jexl = new JexlBuilder().strict(false).create();
+        JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
+        Object result = base.execute(null);
+        Assert.assertEquals(42, result);
+    }
+
+    @Test
+    public void test271b() throws Exception {
+        JexlEngine jexl = new JexlBuilder().strict(false).create();
+        JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
+        Object result = base.execute(null);
+        Assert.assertEquals(8, result);
+    }
 }