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);
+ }
}