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 2019/12/11 21:28:19 UTC

[commons-jexl] 02/02: JEXL-307: added lexical shade flag to features, aligned with options semantics Task #JEXL-307 - Variable redeclaration option

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

commit 7cbcc66519325d23bb16ee6e2da63c9030ef7923
Author: henrib <he...@apache.org>
AuthorDate: Wed Dec 11 22:27:46 2019 +0100

    JEXL-307: added lexical shade flag to features, aligned with options semantics
    Task #JEXL-307 - Variable redeclaration option
---
 .../org/apache/commons/jexl3/JexlFeatures.java     | 27 ++++++++++++++++++++--
 .../java/org/apache/commons/jexl3/JexlOptions.java |  4 ++--
 .../org/apache/commons/jexl3/internal/Script.java  |  9 ++++++--
 .../apache/commons/jexl3/parser/JexlParser.java    | 18 ++++++++-------
 .../java/org/apache/commons/jexl3/LexicalTest.java | 15 ++++++++++++
 5 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index 12d1e6e..e2ec0f7 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -30,6 +30,7 @@ import java.util.TreeSet;
  * <li>Reserved Names: a set of reserved variable names that can not be used as local variable (or parameter) names
  * <li>Global Side Effect : assigning/modifying values on global variables (=, += , -=, ...)
  * <li>Lexical: lexical scope, prevents redefining local variables 
+ * <li>Lexical Shade: local variables shade globals, prevents confusing a global variable with a local one
  * <li>Side Effect : assigning/modifying values on any variables or left-value
  * <li>Constant Array Reference: ensures array references only use constants;they should be statically solvable.
  * <li>New Instance: creating an instance using new(...)
@@ -52,7 +53,7 @@ public final class JexlFeatures {
     private static final String[] F_NAMES = {
         "register", "reserved variable", "local variable", "assign/modify",
         "global assign/modify", "array reference", "create instance", "loop", "function",
-        "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical"
+        "method call", "set/map/array literal", "pragma", "annotation", "script", "lexical", "lexicalShade"
     };
     /** Registers feature ordinal. */
     private static final int REGISTER = 0;
@@ -82,8 +83,10 @@ public final class JexlFeatures {
     public static final int ANNOTATION = 12;
     /** Script feature ordinal. */
     public static final int SCRIPT = 13;
-    /** Script feature ordinal. */
+    /** Lexical feature ordinal. */
     public static final int LEXICAL = 14;
+    /** Lexical shade feature ordinal. */
+    public static final int LEXICAL_SHADE = 15;
 
     /**
      * Creates an all-features-enabled instance.
@@ -490,4 +493,24 @@ public final class JexlFeatures {
     public boolean isLexical() {
         return getFeature(LEXICAL);
     }
+        
+    /**
+     * Sets whether syntactic lexical shade is enabled.
+     *
+     * @param flag true means syntactic lexical shade is in effect and implies lexical scope
+     * @return this features instance
+     */
+    public JexlFeatures lexicalShade(boolean flag) {
+        setFeature(LEXICAL_SHADE, flag);
+        if (flag) {
+            setFeature(LEXICAL, true);
+        }
+        return this;
+    }
+    
+    
+    /** @return whether lexical shade feature is enabled */
+    public boolean isLexicalShade() {
+        return getFeature(LEXICAL_SHADE);
+    }
 }
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
index 7d3df9e..943aa72 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
@@ -274,8 +274,8 @@ public final class JexlOptions {
     /**
      * Sets whether the engine strictly shades global variables.
      * Local symbols shade globals after definition and creating global
-     * variables is prohibited evaluation.
-     * If setting to lexical shade, lexical is also set.
+     * variables is prohibited during evaluation.
+     * If setting to lexical shade, lexical scope is also set.
      * @param flag true if creation is allowed, false otherwise
      */
     public void setLexicalShade(boolean flag) {
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 4aa692e..5b1f53d 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
@@ -110,8 +110,13 @@ public class Script implements JexlScript, JexlExpression {
         JexlOptions opts = jexl.createOptions(this, context);
         // when parsing lexical, try hard to run lexical
         JexlFeatures features = script.getFeatures();
-        if (features != null && features.isLexical()) {
-            opts.setLexical(true);
+        if (features != null) {
+            if (features.isLexical()) {
+                opts.setLexical(true);
+            }
+            if (features.isLexicalShade()) {
+                opts.setLexicalShade(true);
+            }
         }
         return opts;
     }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
index d32feb5..8a5d51b 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -276,23 +276,25 @@ public abstract class JexlParser extends StringParser {
         if (frame != null) {
             Integer symbol = frame.getSymbol(name);
             if (symbol != null) {
-                // can not reuse a local as a global
+                boolean declared = true;
                 if (getFeatures().isLexical()) {
-                    // one of the lexical blocks above must declare it
-                    if (!block.hasSymbol(symbol)) {
-                        boolean declared = false;
+                    declared = block.hasSymbol(symbol);
+                    // one of the lexical blocks above should declare it
+                    if (!declared) {
                         for (LexicalUnit u : blocks) {
                             if (u.hasSymbol(symbol)) {
                                 declared = true;
                                 break;
                             }
                         }
-                        if (!declared) {
-                            throw new JexlException(identifier, name + ": variable is not defined");
-                        }
                     }
                 }
-                identifier.setSymbol(symbol, name);
+                if (declared) {
+                    identifier.setSymbol(symbol, name);
+                } else if (getFeatures().isLexicalShade()) {
+                    // can not reuse a local as a global
+                    throw new JexlException(identifier, name + ": variable is not defined");
+                }
             }
         }
         return name;
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index df3e8ec..cbdde4f 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -503,6 +503,7 @@ public class LexicalTest {
     public void testForVariable0() throws Exception {
         JexlFeatures f = new JexlFeatures();
         f.lexical(true);
+        f.lexicalShade(true);
         JexlEngine jexl = new JexlBuilder().strict(true).features(f).create();
         try {
             JexlScript script = jexl.createScript("for(var x : 1..3) { var c = 0}; return x");
@@ -516,6 +517,7 @@ public class LexicalTest {
     public void testForVariable1() throws Exception {
         JexlFeatures f = new JexlFeatures();
         f.lexical(true);
+        f.lexicalShade(true);
         JexlEngine jexl = new JexlBuilder().strict(true).features(f).create();
         try {
             JexlScript script = jexl.createScript("for(var x : 1..3) { var c = 0} for(var x : 1..3) { var c = 0}; return x");
@@ -530,6 +532,7 @@ public class LexicalTest {
     public void testUndeclaredVariable() throws Exception {
         JexlFeatures f = new JexlFeatures();
         f.lexical(true);
+        f.lexicalShade(true);
         JexlEngine jexl = new JexlBuilder().strict(true).features(f).create();
         try {
             JexlScript script = jexl.createScript("{var x = 0}; return x");
@@ -539,4 +542,16 @@ public class LexicalTest {
            Assert.assertTrue(ex instanceof JexlException);
         }
     }
+    
+    @Test
+    public void testLexical6a1() throws Exception {
+        String str = "i = 0; { var i = 32; }; i";
+        JexlFeatures f = new JexlFeatures();
+        f.lexical(true);
+        JexlEngine jexl = new JexlBuilder().strict(true).features(f).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        Object o = e.execute(ctxt);
+        Assert.assertEquals(0, o);
+    }
 }