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 0a9ddb9  JEXL-307: clean up lexical frames after usage, fixed template 
& for-loops lexical handling Task #JEXL-307 - Variable redeclaration option
0a9ddb9 is described below

commit 0a9ddb9065a1c25a80b99c05bbad126845c4d16f
Author: henrib <hen...@apache.org>
AuthorDate: Tue Nov 5 22:41:57 2019 +0100

    JEXL-307: clean up lexical frames after usage, fixed template & for-loops 
lexical handling
    Task #JEXL-307 - Variable redeclaration option
---
 .../apache/commons/jexl3/internal/Interpreter.java | 23 +++----
 .../commons/jexl3/internal/InterpreterBase.java    |  5 +-
 .../commons/jexl3/internal/LexicalFrame.java       | 25 +++++--
 .../commons/jexl3/internal/LexicalScope.java       | 10 ++-
 .../jexl3/internal/TemplateInterpreter.java        | 22 +++----
 .../java/org/apache/commons/jexl3/JXLTTest.java    | 77 ++++++++++++++++++++--
 .../java/org/apache/commons/jexl3/LexicalTest.java | 48 ++++++++++++++
 7 files changed, 172 insertions(+), 38 deletions(-)

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 a673f56..0dbbcfa 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -169,9 +169,6 @@ public class Interpreter extends InterpreterBase {
      * @throws JexlException if any error occurs during interpretation.
      */
     public Object interpret(JexlNode node) {
-        return interpret(node, false);
-    }
-    public Object interpret(JexlNode node, boolean rethrow) {
         JexlContext.ThreadLocal tcontext = null;
         JexlEngine tjexl = null;
         Interpreter tinter = null;
@@ -205,7 +202,7 @@ public class Interpreter extends InterpreterBase {
                 throw xcancel.clean();
             }
         } catch (JexlException xjexl) {
-            if (rethrow || !isSilent()) {
+            if (!isSilent()) {
                 throw xjexl.clean();
             }
             if (logger.isWarnEnabled()) {
@@ -674,14 +671,14 @@ public class Interpreter extends InterpreterBase {
         ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
         ASTIdentifier loopVariable = (ASTIdentifier) 
loopReference.jjtGetChild(0);
         final int symbol = loopVariable.getSymbol();
-        final LexicalFrame lexical = block;
-        if (options.isLexical()) {
-            // the iteration variable can not be declared in parent block
-            if (symbol >= 0 && block.hasSymbol(symbol)) {
+        final boolean lexical = options.isLexical() && symbol >= 0;
+        if (lexical) {
+            // create lexical frame
+            LexicalFrame locals = new LexicalFrame(frame, block);
+            if (!locals.declareSymbol(symbol)) {
                 return redefinedVariable(node, loopVariable.getName());
             }
-            // create lexical frame
-            block = new LexicalFrame(frame, lexical);
+            block = locals;
         }
         Object forEach = null;
         try {
@@ -723,8 +720,8 @@ public class Interpreter extends InterpreterBase {
             //  closeable iterator handling
             closeIfSupported(forEach);
             // restore lexical frame
-            if (lexical != null && block != null) {
-                block = lexical;
+            if (lexical) {
+                block = block.pop();
             }
         }
         return result;
@@ -987,7 +984,7 @@ public class Interpreter extends InterpreterBase {
         block = new LexicalFrame(frame, block).declareArgs();
         try {
             JexlNode body = script.jjtGetChild(script.jjtGetNumChildren() - 1);
-            return interpret(body, true);
+            return interpret(body);
         } finally {
             block = block.pop();
         }
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java 
b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
index 7727d55..a41192f 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -252,7 +252,10 @@ public abstract class InterpreterBase extends 
ParserVisitor {
                         return undefinedVariable(identifier, 
identifier.getName());
                     }
                 }
-                return frame.get(symbol);
+                Object value = frame.get(symbol);
+                if (value != Scope.UNDEFINED) {
+                    return value;
+                }
             }
         }
         String name = identifier.getName();
diff --git a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java 
b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
index 1deb14a..d2b341e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
@@ -30,19 +30,23 @@ public class LexicalFrame extends LexicalScope {
     private Deque<Object> stack = null;
     /**
      * Lexical frame ctor.
-     * @param frame the script frame
+     * @param scriptf the script frame
      * @param previous the previous lexical frame
      */
-    public LexicalFrame(Frame frame, LexicalFrame previous) {
+    public LexicalFrame(Frame scriptf, LexicalFrame previous) {
         super(previous);
-        this.frame = frame;
+        this.frame = scriptf;
     }
 
+    /**
+     * Declare the arguments.
+     * @return the number of arguments
+     */
     public LexicalFrame declareArgs() {
         if (frame != null) {
             int argc = frame.getScope().getArgCount();
             for(int a  = 0; a < argc; ++a) {
-                super.registerSymbol(a);
+                super.addSymbol(a);
             }
         }
         return this;
@@ -70,6 +74,19 @@ public class LexicalFrame extends LexicalScope {
      * @return the previous frame
      */
     public LexicalFrame pop() {
+        long clean = symbols;
+        // undefine symbols getting out of scope
+        while (clean != 0L) {
+            int s = Long.numberOfTrailingZeros(clean);
+            clean &= ~(1L << s);
+            frame.set(s, Scope.UNDEFINED);
+        }
+        if (moreSymbols != null) {
+            for (int s = moreSymbols.nextSetBit(0); s != -1; s = 
moreSymbols.nextSetBit(s + 1)) {
+                frame.set(s, Scope.UNDEFINED);
+            }
+        }
+        // restore values of hoisted symbols that were overwritten
         if (stack != null) {
             while(!stack.isEmpty()) {
                 Object value = stack.pop();
diff --git a/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java 
b/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java
index a6e6fff..3ab83ad 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/LexicalScope.java
@@ -35,7 +35,6 @@ public class LexicalScope {
 
     /**
      * Create a scope.
-     * @param frame the current execution frame
      * @param scope the previous scope
      */
     public LexicalScope(LexicalScope scope) {
@@ -80,10 +79,15 @@ public class LexicalScope {
             }
             walk = walk.previous;
         }
-        return registerSymbol(symbol);
+        return addSymbol(symbol);
     }
 
-    protected final boolean registerSymbol(int symbol) {
+    /**
+     * Adds a symbol in this scope.
+     * @param symbol the symbol
+     * @return true if registered, false if symbol was already registered
+     */
+    protected final boolean addSymbol(int symbol) {
         if (symbol < LONGBITS) {
             if ((symbols & (1L << symbol)) != 0L) {
                 return false;
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java 
b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
index 3ae8017..17b0ff9 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
@@ -51,6 +51,7 @@ public class TemplateInterpreter extends Interpreter {
         super(jexl, jcontext, jframe);
         exprs = expressions;
         writer = out;
+        block = new LexicalFrame(frame, null);
     }
 
     /**
@@ -153,20 +154,15 @@ public class TemplateInterpreter extends Interpreter {
                 }
             };
         }
-        block = new LexicalFrame(frame, block).declareArgs();
-        try {
-            // otherwise...
-            final int numChildren = node.jjtGetNumChildren();
-            Object result = null;
-            for (int i = 0; i < numChildren; i++) {
-                JexlNode child = node.jjtGetChild(i);
-                result = child.jjtAccept(this, data);
-                cancelCheck(child);
-            }
-            return result;
-        } finally {
-            block = block.pop();
+        // otherwise...
+        final int numChildren = node.jjtGetNumChildren();
+        Object result = null;
+        for (int i = 0; i < numChildren; i++) {
+            JexlNode child = node.jjtGetChild(i);
+            result = child.jjtAccept(this, data);
+            cancelCheck(child);
         }
+        return result;    
     }
 
 }
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java 
b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index ca085fc..c59193d 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.jexl3;
 import org.apache.commons.jexl3.internal.TemplateDebugger;
 import org.apache.commons.jexl3.internal.TemplateScript;
 import org.apache.commons.jexl3.internal.Debugger;
+import org.apache.commons.jexl3.internal.Options;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -787,10 +788,27 @@ public class JXLTTest extends JexlTestCase {
         }
     }
     
-    public static class Context311 extends MapContext {
+    public static class Context311 extends MapContext 
+      implements JexlContext.OptionsHandle, JexlContext.ThreadLocal {
+        private JexlOptions options = null;
+        
+        public void setOptions(JexlOptions o) {
+            options = o;
+        }
+        
         public Executor311 exec(String name) {
             return new Executor311(name);
         }
+
+        @Override
+        public JexlOptions getEngineOptions() {
+            return options;
+        }
+        
+        JexlOptions newOptions() {
+            options = new Options();
+            return options;
+        }
     }
     
     @Test
@@ -823,7 +841,8 @@ public class JXLTTest extends JexlTestCase {
     
     @Test
     public void test311c() throws Exception {
-        JexlContext ctx311 = new Context311();
+        Context311 ctx311 = new Context311();
+        ctx311.newOptions().setLexical(true);
         String rpt
                 = "$$ exec('42').execute((a)->{"
                 + "\n<p>Universe ${a}</p>"
@@ -837,7 +856,8 @@ public class JXLTTest extends JexlTestCase {
        
     @Test
     public void test311d() throws Exception {
-        JexlContext ctx311 = new Context311();
+        Context311 ctx311 = new Context311();
+        ctx311.newOptions().setLexical(true);
         String rpt
                 = "$$ exec('4').execute((a, b)->{"
                 + "\n<p>Universe ${a}${b}</p>"
@@ -848,9 +868,58 @@ public class JXLTTest extends JexlTestCase {
         String output = strw.toString();
         Assert.assertEquals("<p>Universe 42</p>\n", output);
     }
-           
+    
     @Test
     public void test311e() throws Exception {
+        Context311 ctx311 = new Context311();
+        ctx311.newOptions().setLexical(true);
+        String rpt
+                = "exec('4').execute((a, b)->{"
+                + " '<p>Universe ' + a + b + '</p>'"
+                + "}, '2')";
+        JexlScript script = JEXL.createScript(rpt);
+        String output = script.execute(ctx311, 42).toString();
+        Assert.assertEquals("<p>Universe 42</p>", output);
+    } 
+    
+    @Test
+    public void test311f() throws Exception {
+        Context311 ctx311 = new Context311();
+        ctx311.newOptions().setLexical(true);
+        String rpt
+                = "exec('4').execute((a, b)->{"
+                + " `<p>Universe ${a}${b}</p>`"
+                + "}, '2')";
+        JexlScript script = JEXL.createScript(rpt);
+        String output = script.execute(ctx311, 42).toString();
+        Assert.assertEquals("<p>Universe 42</p>", output);
+    }
+           
+    @Test
+    public void test311g() throws Exception {
+        Context311 ctx311 = new Context311();
+        ctx311.newOptions().setLexical(true);
+        String rpt
+                = "(a, b)->{"
+                + " `<p>Universe ${a}${b}</p>`"
+                + "}";
+        JexlScript script = JEXL.createScript(rpt);
+        String output = script.execute(ctx311, "4", "2").toString();
+        Assert.assertEquals("<p>Universe 42</p>", output);
+    }  
+               
+    @Test
+    public void test311h() throws Exception {
+        Context311 ctx311 = new Context311();
+        ctx311.newOptions().setLexical(true);
+        String rpt= " `<p>Universe ${a}${b}</p>`";
+        JexlScript script = JEXL.createScript(rpt, "a", "b");
+        String output = script.execute(ctx311, "4", "2").toString();
+        Assert.assertEquals("<p>Universe 42</p>", output);
+    }   
+    
+    @Test
+    public void test311i() throws Exception {
         JexlContext ctx311 = new Context311();
         String rpt
                 = "$$var u = 'Universe'; exec('4').execute((a, b)->{"
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java 
b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index fff27bd..43c59bd 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -296,4 +296,52 @@ public class LexicalTest {
             Assert.fail(ww);
         }
     }
+        
+    @Test
+    public void testLexical6a() throws Exception {
+        String str = "i = 0; { var i = 32; }; i";
+        JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        Object o = e.execute(ctxt);
+        Assert.assertEquals(0, o);
+    }   
+
+    @Test
+    public void testLexical6b() throws Exception {
+        String str = "i = 0; { var i = 32; }; i";
+        JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).lexicalShade(true).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        try {
+            Object o = e.execute(ctxt);
+            Assert.fail("i should be shaded");
+        } catch (JexlException xany) {
+            Assert.assertNotNull(xany);
+        }
+    }
+
+    @Test
+    public void testLexical6c() throws Exception {
+        String str = "i = 0; for (var i : [42]) i; i";
+        JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        Object o = e.execute(ctxt);
+        Assert.assertEquals(0, o);
+    }
+
+    @Test
+    public void testLexical6d() throws Exception {
+        String str = "i = 0; for (var i : [42]) i;; i";
+        JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).lexicalShade(true).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        try {
+            Object o = e.execute(ctxt);
+            Assert.fail("i should be shaded");
+        } catch (JexlException xany) {
+            Assert.assertNotNull(xany);
+        }
+    }
 }

Reply via email to