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/11/12 22:01:32 UTC
[commons-jexl] branch master updated: JEXL-307: tidy API (made
JexlOptions a concrete non derivable class),
try to ensure lexical interpretation on lexical feature,
allow runtime options to be controlled through pragma (jexl.options) 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
The following commit(s) were added to refs/heads/master by this push:
new 753d6e6 JEXL-307: tidy API (made JexlOptions a concrete non derivable class), try to ensure lexical interpretation on lexical feature, allow runtime options to be controlled through pragma (jexl.options) Task #JEXL-307 - Variable redeclaration option
753d6e6 is described below
commit 753d6e634d94022871b481896db78e323d7ccfed
Author: henrib <he...@apache.org>
AuthorDate: Tue Nov 12 23:00:39 2019 +0100
JEXL-307: tidy API (made JexlOptions a concrete non derivable class), try to ensure lexical interpretation on lexical feature, allow runtime options to be controlled through pragma (jexl.options)
Task #JEXL-307 - Variable redeclaration option
---
.../java/org/apache/commons/jexl3/JexlBuilder.java | 21 +-
.../java/org/apache/commons/jexl3/JexlContext.java | 16 ++
.../java/org/apache/commons/jexl3/JexlEngine.java | 10 +-
.../java/org/apache/commons/jexl3/JexlInfo.java | 11 +
.../java/org/apache/commons/jexl3/JexlOptions.java | 223 +++++++++++++++--
.../org/apache/commons/jexl3/internal/Engine.java | 68 ++++-
.../apache/commons/jexl3/internal/Interpreter.java | 33 ++-
.../commons/jexl3/internal/InterpreterBase.java | 7 +-
.../org/apache/commons/jexl3/internal/Options.java | 274 ---------------------
.../org/apache/commons/jexl3/internal/Script.java | 45 +++-
.../jexl3/internal/TemplateInterpreter.java | 2 +-
.../org/apache/commons/jexl3/AntishCallTest.java | 15 +-
.../org/apache/commons/jexl3/Issues300Test.java | 25 +-
.../java/org/apache/commons/jexl3/JXLTTest.java | 3 +-
.../org/apache/commons/jexl3/JexlEvalContext.java | 3 +-
.../org/apache/commons/jexl3/JexlTestCase.java | 2 +-
.../java/org/apache/commons/jexl3/LexicalTest.java | 43 ++++
17 files changed, 431 insertions(+), 370 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index 35c8d5f..744dfe1 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -18,7 +18,6 @@
package org.apache.commons.jexl3;
import org.apache.commons.jexl3.internal.Engine;
-import org.apache.commons.jexl3.internal.Options;
import org.apache.commons.jexl3.introspection.JexlSandbox;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.logging.Log;
@@ -81,7 +80,7 @@ public class JexlBuilder {
private Boolean cancellable = null;
/** The options. */
- private final Options options = new Options();
+ private final JexlOptions options = new JexlOptions();
/** Whether getVariables considers all potential equivalent syntactic forms. */
private Boolean collectAll = null;
@@ -109,23 +108,7 @@ public class JexlBuilder {
/** The features. */
private JexlFeatures features = null;
-
- /**
- * Sets the default (static, shared) option flags.
- * <p>
- * Whenever possible, we recommend using JexlBuilder methods to unambiguously instantiate a JEXL
- * engine; this method should only be used for testing / validation.
- * <p>A '+flag' or 'flag' will set the option named 'flag' as true, '-flag' set as false.
- * The possible flag names are:
- * cancellable, strict, silent, safe, lexical, antish, lexicalShade
- * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL engine creation
- * may ease validating JEXL3.2 in your environment.
- * @param flags the flags to set
- */
- public static void setDefaultOptions(String...flags) {
- Options.setDefaultFlags(flags);
- }
-
+
/**
* Sets the JexlUberspect instance the engine will use.
*
diff --git a/src/main/java/org/apache/commons/jexl3/JexlContext.java b/src/main/java/org/apache/commons/jexl3/JexlContext.java
index fdc85a3..ce730f7 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlContext.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlContext.java
@@ -159,4 +159,20 @@ public interface JexlContext {
*/
JexlOptions getEngineOptions();
}
+
+ /**
+ * A marker interface of the JexlContext that processes pragmas.
+ * It is called by the engine before interpreter creation; as a marker of
+ * JexlContext, it is expected to have access and interact with the context
+ * instance.
+ * @since 3.2
+ */
+ interface PragmaProcessor {
+ /**
+ * Process one pragma.
+ * @param key the key
+ * @param value the value
+ */
+ void processPragma(String key, Object value);
+ }
}
diff --git a/src/main/java/org/apache/commons/jexl3/JexlEngine.java b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
index bc41a82..7b9d198 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
@@ -352,7 +352,7 @@ public abstract class JexlEngine {
* @return A {@link JexlScript} which can be executed using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
- public abstract JexlScript createScript(JexlFeatures features, JexlInfo info, String source, String[] names);
+ public abstract JexlScript createScript(JexlFeatures features, JexlInfo info, String source, String... names);
/**
* Creates a JexlScript from a String containing valid JEXL syntax.
@@ -365,7 +365,7 @@ public abstract class JexlEngine {
* @return A {@link JexlScript} which can be executed using a {@link JexlContext}
* @throws JexlException if there is a problem parsing the script
*/
- public final JexlScript createScript(JexlInfo info, String source, String[] names) {
+ public final JexlScript createScript(JexlInfo info, String source, String... names) {
return createScript(null, info, source, names);
}
@@ -378,7 +378,7 @@ public abstract class JexlEngine {
* @throws JexlException if there is a problem parsing the script.
*/
public final JexlScript createScript(String scriptText) {
- return createScript(null, null, scriptText, null);
+ return createScript(null, null, scriptText, (String[]) null);
}
/**
@@ -404,7 +404,7 @@ public abstract class JexlEngine {
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(File scriptFile) {
- return createScript(null, null, readSource(scriptFile), null);
+ return createScript(null, null, readSource(scriptFile), (String[]) null);
}
/**
@@ -445,7 +445,7 @@ public abstract class JexlEngine {
* @throws JexlException if there is a problem reading or parsing the script.
*/
public final JexlScript createScript(URL scriptUrl) {
- return createScript(null, readSource(scriptUrl), null);
+ return createScript(null, readSource(scriptUrl), (String[]) null);
}
/**
diff --git a/src/main/java/org/apache/commons/jexl3/JexlInfo.java b/src/main/java/org/apache/commons/jexl3/JexlInfo.java
index 19a4a72..296138b 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlInfo.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlInfo.java
@@ -17,6 +17,8 @@
package org.apache.commons.jexl3;
+import org.apache.commons.jexl3.internal.Script;
+
/**
* Helper class to carry information such as a url/file name, line and column for
* debugging information reporting.
@@ -182,5 +184,14 @@ public class JexlInfo {
public final int getColumn() {
return column;
}
+
+ /**
+ * Gets the info from a script.
+ * @param script the script
+ * @return the info
+ */
+ public static JexlInfo from(JexlScript script) {
+ return script instanceof Script? ((Script) script).getInfo() : null;
+ }
}
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
index dc35074..c650e9b 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
@@ -18,6 +18,7 @@
package org.apache.commons.jexl3;
import java.math.MathContext;
+import org.apache.commons.jexl3.internal.Engine;
/**
* Flags and properties that can alter the evaluation behavior.
@@ -35,31 +36,150 @@ import java.math.MathContext;
* <p>This interface replaces the now deprecated JexlEngine.Options.
* @since 3.2
*/
-public interface JexlOptions {
+public final class JexlOptions {
+ /** The local shade bit. */
+ private static final int SHADE = 6;
+ /** The antish var bit. */
+ private static final int ANTISH = 5;
+ /** The lexical scope bit. */
+ private static final int LEXICAL = 4;
+ /** The safe bit. */
+ private static final int SAFE = 3;
+ /** The silent bit. */
+ private static final int SILENT = 2;
+ /** The strict bit. */
+ private static final int STRICT = 1;
+ /** The cancellable bit. */
+ private static final int CANCELLABLE = 0;
+ /** The flags names ordered. */
+ private static final String[] NAMES = {
+ "cancellable", "strict", "silent", "safe", "lexical", "antish", "lexicalShade"
+ };
+ /** Default mask .*/
+ private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH | 1 << SAFE;
+ /** The arithmetic math context. */
+ private MathContext mathContext = null;
+ /** The arithmetic math scale. */
+ private int mathScale = Integer.MIN_VALUE;
+ /** The arithmetic strict math flag. */
+ private boolean strictArithmetic = true;
+ /** The default flags, all but safe. */
+ private int flags = DEFAULT;
+
+ /**
+ * Sets the value of a flag in a mask.
+ * @param ordinal the flag ordinal
+ * @param mask the flags mask
+ * @param value true or false
+ * @return the new flags mask value
+ */
+ private static int set(int ordinal, int mask, boolean value) {
+ return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
+ }
+
+ /**
+ * Checks the value of a flag in the mask.
+ * @param ordinal the flag ordinal
+ * @param mask the flags mask
+ * @return the mask value with this flag or-ed in
+ */
+ private static boolean isSet(int ordinal, int mask) {
+ return (mask & 1 << ordinal) != 0;
+ }
+
+ /**
+ * Default ctor.
+ */
+ public JexlOptions() {}
+
+ /**
+ * Sets the default (static, shared) option flags.
+ * <p>
+ * Whenever possible, we recommend using JexlBuilder methods to unambiguously instantiate a JEXL
+ * engine; this method should only be used for testing / validation.
+ * <p>A '+flag' or 'flag' will set the option named 'flag' as true, '-flag' set as false.
+ * The possible flag names are:
+ * cancellable, strict, silent, safe, lexical, antish, lexicalShade
+ * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL engine creation
+ * may ease validating JEXL3.2 in your environment.
+ * @param flags the flags to set
+ */
+ public static void setDefaultFlags(String...flags) {
+ DEFAULT = parseFlags(DEFAULT, flags);
+ }
+
+ /**
+ * Parses flags by name.
+ * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
+ * The possible flag names are:
+ * cancellable, strict, silent, safe, lexical, antish, lexicalShade
+ * @param mask the initial mask state
+ * @param flags the flags to set
+ * @return the flag mask updated
+ */
+ public static int parseFlags(int mask, String...flags) {
+ for(String name : flags) {
+ boolean b = true;
+ if (name.charAt(0) == '+') {
+ name = name.substring(1);
+ } else if (name.charAt(0) == '-') {
+ name = name.substring(1);
+ b = false;
+ }
+ for(int flag = 0; flag < NAMES.length; ++flag) {
+ if (NAMES[flag].equals(name)) {
+ if (b) {
+ mask |= (1 << flag);
+ } else {
+ mask &= ~(1 << flag);
+ }
+ break;
+ }
+ }
+ }
+ return mask;
+ }
+
+ /**
+ * Sets this option flags using the +/- syntax.
+ * @param opts the option flags
+ */
+ public void setFlags(String[] opts) {
+ flags = parseFlags(flags, opts);
+ }
+
/**
* The MathContext instance used for +,-,/,*,% operations on big decimals.
* @return the math context
*/
- MathContext getMathContext();
+ public MathContext getMathContext() {
+ return mathContext;
+ }
/**
* The BigDecimal scale used for comparison and coercion operations.
* @return the scale
*/
- int getMathScale();
+ public int getMathScale() {
+ return mathScale;
+ }
/**
* Checks whether evaluation will attempt resolving antish variable names.
* @return true if antish variables are solved, false otherwise
*/
- boolean isAntish();
+ public boolean isAntish() {
+ return isSet(ANTISH, flags);
+ }
/**
* Checks whether evaluation will throw JexlException.Cancel (true) or
* return null (false) if interrupted.
* @return true when cancellable, false otherwise
*/
- boolean isCancellable();
+ public boolean isCancellable() {
+ return isSet(CANCELLABLE, flags);
+ }
/**
* Checks whether runtime variable scope is lexical.
@@ -67,7 +187,9 @@ public interface JexlOptions {
* Redefining a variable in the same lexical unit will generate errors.
* @return true if scope is lexical, false otherwise
*/
- boolean isLexical();
+ public boolean isLexical() {
+ return isSet(LEXICAL, flags);
+ }
/**
* Checks whether local variables shade global ones.
@@ -79,122 +201,169 @@ public interface JexlOptions {
* raise an error.
* @return true if lexical shading is applied, false otherwise
*/
- boolean isLexicalShade();
+ public boolean isLexicalShade() {
+ return isSet(SHADE, flags);
+ }
/**
* Checks whether the engine considers null in navigation expression as
* errors during evaluation..
* @return true if safe, false otherwise
*/
- boolean isSafe();
+ public boolean isSafe() {
+ return isSet(SAFE, flags);
+ }
/**
* Checks whether the engine will throw a {@link JexlException} when an
* error is encountered during evaluation.
* @return true if silent, false otherwise
*/
- boolean isSilent();
+ public boolean isSilent() {
+ return isSet(SILENT, flags);
+ }
/**
* Checks whether the engine considers unknown variables, methods and
* constructors as errors during evaluation.
* @return true if strict, false otherwise
*/
- boolean isStrict();
+ public boolean isStrict() {
+ return isSet(STRICT, flags);
+ }
/**
* Checks whether the arithmetic triggers errors during evaluation when null
* is used as an operand.
* @return true if strict, false otherwise
*/
- boolean isStrictArithmetic();
+ public boolean isStrictArithmetic() {
+ return strictArithmetic;
+ }
/**
* Sets whether the engine will attempt solving antish variable names from
* context.
* @param flag true if antish variables are solved, false otherwise
*/
- void setAntish(boolean flag);
+ public void setAntish(boolean flag) {
+ flags = set(ANTISH, flags, flag);
+ }
/**
* Sets whether the engine will throw JexlException.Cancel (true) or return
* null (false) when interrupted during evaluation.
* @param flag true when cancellable, false otherwise
*/
- void setCancellable(boolean flag);
+ public void setCancellable(boolean flag) {
+ flags = set(CANCELLABLE, flags, flag);
+ }
/**
* Sets whether the engine uses a strict block lexical scope during
* evaluation.
* @param flag true if lexical scope is used, false otherwise
*/
- void setLexical(boolean flag);
+ public void setLexical(boolean flag) {
+ flags = set(LEXICAL, flags, flag);
+ }
/**
* 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.
* @param flag true if creation is allowed, false otherwise
*/
- void setLexicalShade(boolean flag);
+ public void setLexicalShade(boolean flag) {
+ flags = set(SHADE, flags, flag);
+ if (flag) {
+ flags = set(LEXICAL, flags, true);
+ }
+ }
/**
* Sets the arithmetic math context.
* @param mcontext the context
*/
- void setMathContext(MathContext mcontext);
+ public void setMathContext(MathContext mcontext) {
+ this.mathContext = mcontext;
+ }
/**
* Sets the arithmetic math scale.
* @param mscale the scale
*/
- void setMathScale(int mscale);
+ public void setMathScale(int mscale) {
+ this.mathScale = mscale;
+ }
/**
* Sets whether the engine considers null in navigation expression as errors
* during evaluation.
* @param flag true if safe, false otherwise
*/
- void setSafe(boolean flag);
+ public void setSafe(boolean flag) {
+ flags = set(SAFE, flags, flag);
+ }
/**
* Sets whether the engine will throw a {@link JexlException} when an error
* is encountered during evaluation.
* @param flag true if silent, false otherwise
*/
- void setSilent(boolean flag);
+ public void setSilent(boolean flag) {
+ flags = set(SILENT, flags, flag);
+ }
/**
* Sets whether the engine considers unknown variables, methods and
* constructors as errors during evaluation.
* @param flag true if strict, false otherwise
*/
- void setStrict(boolean flag);
+ public void setStrict(boolean flag) {
+ flags = set(STRICT, flags, flag);
+ }
/**
* Sets the strict arithmetic flag.
* @param stricta true or false
*/
- void setStrictArithmetic(boolean stricta);
+ public void setStrictArithmetic(boolean stricta) {
+ this.strictArithmetic = stricta;
+ }
/**
* Set options from engine.
* @param jexl the engine
* @return this instance
- */
- JexlOptions set(JexlEngine jexl);
+ */
+ public JexlOptions set(JexlEngine jexl) {
+ if (jexl instanceof Engine) {
+ ((Engine) jexl).optionsSet(this);
+ }
+ return this;
+ }
/**
* Set options from options.
- * @param options the options
+ * @param src the options
* @return this instance
*/
- JexlOptions set(JexlOptions options);
-
+ public JexlOptions set(JexlOptions src) {
+ mathContext = src.mathContext;
+ mathScale = src.mathScale;
+ strictArithmetic = src.strictArithmetic;
+ flags = src.flags;
+ return this;
+ }
+
/**
* Creates a copy of this instance.
* @return a copy
*/
- JexlOptions copy();
+ public JexlOptions copy() {
+ return new JexlOptions().set(this);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index d752cca..4895578 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -74,6 +74,10 @@ public class Engine extends JexlEngine {
private UberspectHolder() {}
}
/**
+ * The name of the options pragma.
+ */
+ protected static final String PRAGMA_OPTIONS = "jexl.options";
+ /**
* The Log to which all JexlEngine messages will be logged.
*/
protected final Log logger;
@@ -347,18 +351,62 @@ public class Engine extends JexlEngine {
cache.clear();
}
}
-
+
+ /**
+ * Sets options from this engine options.
+ * @param opts the options to set
+ * @return the options
+ */
+ public JexlOptions optionsSet(JexlOptions opts) {
+ if (opts != null) {
+ opts.set(options);
+ }
+ return opts;
+ }
+
+ /**
+ * Creates a script evaluation options.
+ * <p>This also calls the pragma processor if any
+ * @param script the script
+ * @param context the context
+ * @return the options
+ */
+ protected JexlOptions createOptions(Script script, JexlContext context) {
+ JexlOptions opts = options(context);
+ Map<String, Object> pragmas = script.getPragmas();
+ if (pragmas != null) {
+ JexlContext.PragmaProcessor processor =
+ context instanceof JexlContext.PragmaProcessor
+ ? (JexlContext.PragmaProcessor) context
+ : null;
+ for(Map.Entry<String, Object> pragma : pragmas.entrySet()) {
+ String key = pragma.getKey();
+ Object value = pragma.getValue();
+ if (PRAGMA_OPTIONS.equals(key) && opts != null) {
+ if (value instanceof String) {
+ String[] vs = ((String) value).split(" ");
+ opts.setFlags(vs);
+ }
+ }
+ if (processor != null) {
+ processor.processPragma(key, value);
+ }
+ }
+ }
+ return opts;
+ }
+
/**
* Creates an interpreter.
* @param context a JexlContext; if null, the empty context is used instead.
* @param frame the interpreter frame
+ * @param opts the evaluation options
* @return an Interpreter
*/
- protected Interpreter createInterpreter(JexlContext context, Frame frame) {
- return new Interpreter(this, context, frame);
+ protected Interpreter createInterpreter(JexlContext context, Frame frame, JexlOptions opts) {
+ return new Interpreter(this, opts, context, frame);
}
-
@Override
public Script createExpression(JexlInfo info, String expression) {
return createScript(expressionFeatures, info, expression, null);
@@ -371,8 +419,12 @@ public class Engine extends JexlEngine {
}
String source = trimSource(scriptText);
Scope scope = names == null ? null : new Scope(null, names);
- ASTJexlScript tree = parse(info, features == null? scriptFeatures : features, source, scope);
- return new Script(this, source, tree);
+ JexlFeatures ftrs = features == null? scriptFeatures : features;
+ ASTJexlScript tree = parse(info, ftrs, source, scope);
+ // when parsing lexical, try hard to run lexical
+ return ftrs.isLexical()
+ ? new Script.Lexical(this, source, tree)
+ : new Script(this, source, tree);
}
/**
@@ -405,7 +457,7 @@ public class Engine extends JexlEngine {
final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
final JexlNode node = script.jjtGetChild(0);
final Frame frame = script.createFrame(bean);
- final Interpreter interpreter = createInterpreter(context, frame);
+ final Interpreter interpreter = createInterpreter(context, frame, null);
return interpreter.visitLexicalNode(node, null);
} catch (JexlException xjexl) {
if (silent) {
@@ -434,7 +486,7 @@ public class Engine extends JexlEngine {
final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
final JexlNode node = script.jjtGetChild(0);
final Frame frame = script.createFrame(bean, value);
- final Interpreter interpreter = createInterpreter(context, frame);
+ final Interpreter interpreter = createInterpreter(context, frame, null);
interpreter.visitLexicalNode(node, null);
} catch (JexlException xjexl) {
if (silent) {
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 ed7efa7..9c27f61 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -17,13 +17,18 @@
//CSOFF: FileLength
package org.apache.commons.jexl3.internal;
+import java.util.Iterator;
+import java.util.concurrent.Callable;
+
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.JexlOperator;
+import org.apache.commons.jexl3.JexlOptions;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.JxltEngine;
+
import org.apache.commons.jexl3.introspection.JexlMethod;
import org.apache.commons.jexl3.introspection.JexlPropertyGet;
@@ -105,9 +110,6 @@ import org.apache.commons.jexl3.parser.ASTWhileStatement;
import org.apache.commons.jexl3.parser.JexlNode;
import org.apache.commons.jexl3.parser.Node;
-import java.util.Iterator;
-import java.util.concurrent.Callable;
-
/**
* An interpreter of JEXL syntax.
*
@@ -130,11 +132,12 @@ public class Interpreter extends InterpreterBase {
/**
* Creates an interpreter.
* @param engine the engine creating this interpreter
- * @param aContext the context to evaluate expression
- * @param eFrame the interpreter evaluation frame
+ * @param aContext the evaluation context, global variables, methods and functions
+ * @param opts the evaluation options, flags modifying evaluation behavior
+ * @param eFrame the evaluation frame, arguments and local variables
*/
- protected Interpreter(Engine engine, JexlContext aContext, Frame eFrame) {
- super(engine, aContext);
+ protected Interpreter(Engine engine, JexlOptions opts, JexlContext aContext, Frame eFrame) {
+ super(engine, opts, aContext);
this.frame = eFrame;
}
@@ -1122,7 +1125,7 @@ public class Interpreter extends InterpreterBase {
JexlNode objectNode = null;
JexlNode ptyNode = null;
StringBuilder ant = null;
- boolean antish = !(parent instanceof ASTReference) && options.isAntish();
+ boolean antish = !(parent instanceof ASTReference);
int v = 1;
main:
for (int c = 0; c < numChildren; c++) {
@@ -1172,15 +1175,21 @@ public class Interpreter extends InterpreterBase {
if (first instanceof ASTIdentifier) {
ASTIdentifier afirst = (ASTIdentifier) first;
ant = new StringBuilder(afirst.getName());
- // skip the first node case since it was trialed in jjtAccept above and returned null
- if (c == 0) {
- continue;
- }
+ // skip the else...*
} else {
// not an identifier, not antish
ptyNode = objectNode;
break main;
}
+ // *... and continue
+ if (!options.isAntish()) {
+ antish = false;
+ continue;
+ }
+ // skip the first node case since it was trialed in jjtAccept above and returned null
+ if (c == 0) {
+ continue;
+ }
}
// catch up to current node
for (; v <= c; ++v) {
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 74c8f8f..504c1c1 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -78,16 +78,17 @@ public abstract class InterpreterBase extends ParserVisitor {
/**
* Creates an interpreter base.
* @param engine the engine creating this interpreter
- * @param aContext the context to evaluate expression
+ * @param opts the evaluation options
+ * @param aContext the evaluation context
*/
- protected InterpreterBase(Engine engine, JexlContext aContext) {
+ protected InterpreterBase(Engine engine, JexlOptions opts, JexlContext aContext) {
this.jexl = engine;
this.logger = jexl.logger;
this.uberspect = jexl.uberspect;
this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT;
this.cache = engine.cache != null;
JexlArithmetic jexla = jexl.arithmetic;
- this.options = jexl.options(context);
+ this.options = opts == null? engine.options(aContext) : opts;
this.arithmetic = jexla.options(options);
if (arithmetic != jexla && !arithmetic.getClass().equals(jexla.getClass())) {
logger.warn("expected arithmetic to be " + jexla.getClass().getSimpleName()
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Options.java b/src/main/java/org/apache/commons/jexl3/internal/Options.java
deleted file mode 100644
index 04dd182..0000000
--- a/src/main/java/org/apache/commons/jexl3/internal/Options.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.jexl3.internal;
-
-import org.apache.commons.jexl3.JexlOptions;
-import org.apache.commons.jexl3.JexlEngine;
-
-import java.math.MathContext;
-
-/**
- * A basic implementation of JexlOptions.
- * <p>Thread safety is only guaranteed if no modifications (call set* method)
- * occurs in different threads; note however that using the 'callable()' method to
- * allow a script to run as such will use a copy.
- */
-public class Options implements JexlOptions {
- /** The local shade bit. */
- protected static final int SHADE = 6;
- /** The antish var bit. */
- protected static final int ANTISH = 5;
- /** The lexical scope bit. */
- protected static final int LEXICAL = 4;
- /** The safe bit. */
- protected static final int SAFE = 3;
- /** The silent bit. */
- protected static final int SILENT = 2;
- /** The strict bit. */
- protected static final int STRICT = 1;
- /** The cancellable bit. */
- protected static final int CANCELLABLE = 0;
- /** The flags names ordered. */
- private static final String[] NAMES = {
- "cancellable", "strict", "silent", "safe", "lexical", "antish", "lexicalShade"
- };
- /** Default mask .*/
- protected static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH | 1 << SAFE;
- /** The arithmetic math context. */
- private MathContext mathContext = null;
- /** The arithmetic math scale. */
- private int mathScale = Integer.MIN_VALUE;
- /** The arithmetic strict math flag. */
- private boolean strictArithmetic = true;
- /** The default flags, all but safe. */
- private int flags = DEFAULT;
-
- /**
- * Sets the value of a flag in a mask.
- * @param ordinal the flag ordinal
- * @param mask the flags mask
- * @param value true or false
- * @return the new flags mask value
- */
- protected static int set(int ordinal, int mask, boolean value) {
- return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
- }
-
- /**
- * Checks the value of a flag in the mask.
- * @param ordinal the flag ordinal
- * @param mask the flags mask
- * @return the mask value with this flag or-ed in
- */
- protected static boolean isSet(int ordinal, int mask) {
- return (mask & 1 << ordinal) != 0;
- }
-
- /**
- * Default ctor.
- */
- public Options() {}
-
- /**
- * Sets the default flags.
- * <p>Used by tests to force default options.
- * @param flags the flags to set
- */
- public static void setDefaultFlags(String...flags) {
- DEFAULT = parseFlags(DEFAULT, flags);
- }
-
- /**
- * Parses flags by name.
- * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
- * The possible flag names are:
- * cancellable, strict, silent, safe, lexical, antish, lexicalShade
- * @param mask the initial mask state
- * @param flags the flags to set
- * @return the flag mask updated
- */
- public static int parseFlags(int mask, String...flags) {
- for(String name : flags) {
- boolean b = true;
- if (name.charAt(0) == '+') {
- name = name.substring(1);
- } else if (name.charAt(0) == '-') {
- name = name.substring(1);
- b = false;
- }
- for(int flag = 0; flag < NAMES.length; ++flag) {
- if (NAMES[flag].equals(name)) {
- if (b) {
- mask |= (1 << flag);
- } else {
- mask &= ~(1 << flag);
- }
- break;
- }
- }
- }
- return mask;
- }
-
- @Override
- public Options set(JexlEngine jexl) {
- if (jexl instanceof Engine) {
- set(((Engine) jexl).options);
- } else {
- mathContext = jexl.getArithmetic().getMathContext();
- mathScale = jexl.getArithmetic().getMathScale();
- strictArithmetic = jexl.getArithmetic().isStrict();
- set(STRICT, flags, jexl.isStrict());
- set(SILENT, flags, jexl.isSilent());
- set(SAFE, flags, jexl.isSafe());
- set(CANCELLABLE, flags, jexl.isCancellable());
- }
- return this;
- }
-
- @Override
- public Options set(JexlOptions opts) {
- if (opts instanceof Options) {
- Options src = (Options) opts;
- mathContext = src.mathContext;
- mathScale = src.mathScale;
- strictArithmetic = src.strictArithmetic;
- flags = src.flags;
- } else {
- mathContext = opts.getMathContext();
- mathScale = opts.getMathScale();
- strictArithmetic = opts.isStrict();
- int mask = DEFAULT;
- mask = set(STRICT, mask, opts.isStrict());
- mask = set(SILENT, mask, opts.isSilent());
- mask = set(SAFE, mask, opts.isSafe());
- mask = set(CANCELLABLE, mask, opts.isCancellable());
- mask = set(LEXICAL, mask, opts.isLexical());
- mask = set(SHADE, mask, opts.isLexicalShade());
- mask = set(ANTISH, mask, opts.isAntish());
- flags = mask;
- }
- return this;
- }
-
- @Override
- public Options copy() {
- return new Options().set(this);
- }
-
- @Override
- public void setAntish(boolean flag) {
- flags = set(ANTISH, flags, flag);
- }
-
- @Override
- public void setMathContext(MathContext mcontext) {
- this.mathContext = mcontext;
- }
-
- @Override
- public void setMathScale(int mscale) {
- this.mathScale = mscale;
- }
-
- @Override
- public void setStrictArithmetic(boolean stricta) {
- this.strictArithmetic = stricta;
- }
-
- @Override
- public void setStrict(boolean flag) {
- flags = set(STRICT, flags, flag);
- }
-
- @Override
- public void setSafe(boolean flag) {
- flags = set(SAFE, flags, flag);
- }
-
- @Override
- public void setSilent(boolean flag) {
- flags = set(SILENT, flags, flag);
- }
-
- @Override
- public void setCancellable(boolean flag) {
- flags = set(CANCELLABLE, flags, flag);
- }
-
- @Override
- public void setLexical(boolean flag) {
- flags = set(LEXICAL, flags, flag);
- }
-
- @Override
- public void setLexicalShade(boolean flag) {
- flags = set(SHADE, flags, flag);
- }
-
- @Override
- public boolean isAntish() {
- return isSet(ANTISH, flags);
- }
-
- @Override
- public boolean isSilent() {
- return isSet(SILENT, flags);
- }
-
- @Override
- public boolean isStrict() {
- return isSet(STRICT, flags);
- }
-
- @Override
- public boolean isSafe() {
- return isSet(SAFE, flags);
- }
-
- @Override
- public boolean isCancellable() {
- return isSet(CANCELLABLE, flags);
- }
-
- @Override
- public boolean isLexical() {
- return isSet(LEXICAL, flags);
- }
-
- @Override
- public boolean isLexicalShade() {
- return isSet(SHADE, flags);
- }
-
- @Override
- public boolean isStrictArithmetic() {
- return strictArithmetic;
- }
-
- @Override
- public MathContext getMathContext() {
- return mathContext;
- }
-
- @Override
- public int getMathScale() {
- return mathScale;
- }
-
-}
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 62ecaca..593ad08 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
@@ -17,6 +17,8 @@
package org.apache.commons.jexl3.internal;
import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.JexlOptions;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.JexlExpression;
@@ -96,7 +98,39 @@ public class Script implements JexlScript, JexlExpression {
protected Frame createFrame(Object[] args) {
return script.createFrame(args);
}
+
+ /**
+ * Creates this script options for evaluation.
+ * <p>This also calls the pragma processor
+ * @param context the context
+ * @return the options
+ */
+ protected JexlOptions createOptions(JexlContext context) {
+ return jexl.createOptions(this, context);
+ }
+
+ /**
+ * A lexical script, ensures options are lexical.
+ */
+ public static class Lexical extends Script {
+ /**
+ * Sole ctor.
+ * @param engine the engine
+ * @param expr the source.
+ * @param ref the ast
+ */
+ protected Lexical(Engine engine, String expr, ASTJexlScript ref) {
+ super(engine, expr, ref);
+ }
+ @Override
+ public JexlOptions createOptions(JexlContext ctxt) {
+ JexlOptions opts = super.createOptions(ctxt);
+ opts.setLexical(true);
+ return opts;
+ }
+ }
+
/**
* Creates this script interpreter.
* @param context the context
@@ -104,7 +138,7 @@ public class Script implements JexlScript, JexlExpression {
* @return the interpreter
*/
protected Interpreter createInterpreter(JexlContext context, Frame frame) {
- return jexl.createInterpreter(context, frame);
+ return jexl.createInterpreter(context, frame, createOptions(context));
}
/**
@@ -215,6 +249,13 @@ public class Script implements JexlScript, JexlExpression {
public String[] getLocalVariables() {
return script.getLocalVariables();
}
+
+ /**
+ * @return the info
+ */
+ public JexlInfo getInfo() {
+ return script.jexlInfo();
+ }
/**
* Gets this script variables.
@@ -259,7 +300,7 @@ public class Script implements JexlScript, JexlExpression {
*/
@Override
public Callable callable(JexlContext context, Object... args) {
- return new Callable(jexl.createInterpreter(context, script.createFrame(args)));
+ return new Callable(createInterpreter(context, script.createFrame(args)));
}
/**
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 17b0ff9..7babe6f 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
@@ -48,7 +48,7 @@ public class TemplateInterpreter extends Interpreter {
*/
TemplateInterpreter(Engine jexl,
JexlContext jcontext, Frame jframe, TemplateExpression[] expressions, Writer out) {
- super(jexl, jcontext, jframe);
+ super(jexl, null, jcontext, jframe);
exprs = expressions;
writer = out;
block = new LexicalFrame(frame, null);
diff --git a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
index 5f23b11..8397ce0 100644
--- a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
+++ b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
@@ -173,7 +173,8 @@ public class AntishCallTest extends JexlTestCase {
// JEXL-300
@Test
public void testSafeAnt() throws Exception {
- JexlContext ctxt = new MapContext();
+ JexlEvalContext ctxt = new JexlEvalContext();
+ JexlOptions options = ctxt.getEngineOptions();
ctxt.set("x.y.z", 42);
JexlScript script;
Object result;
@@ -182,6 +183,18 @@ public class AntishCallTest extends JexlTestCase {
result = script.execute(ctxt);
Assert.assertEquals(42, result);
Assert.assertEquals(42, ctxt.get("x.y.z"));
+
+ options.setAntish(false);
+ try {
+ result = script.execute(ctxt);
+ Assert.fail("antish var shall not be resolved");
+ } catch(JexlException.Variable xvar) {
+ Assert.assertTrue("x".equals(xvar.getVariable()));
+ } catch(JexlException xother) {
+ Assert.assertTrue(xother != null);
+ } finally {
+ options.setAntish(true);
+ }
result = null;
script = JEXL.createScript("x?.y?.z");
diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
index d2b45ec..913133b 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
@@ -324,25 +324,24 @@ public class Issues300Test {
Assert.assertEquals(52, result);
}
- public static class ClazzB {
- public int methodB() {
- return 42;
- }
- }
- public static class ClazzA {
- public ClazzB methodA() {
- return new ClazzB();
- }
- }
@Test
- public void test317Tentative() throws Exception {
+ public void test317() throws Exception {
JexlEngine jexl = new JexlBuilder().strict(true).create();
JexlContext ctxt = new MapContext();
JexlScript script;
Object result;
- script = jexl.createScript("x.methodA().methodB()", "x");
- result = script.execute(ctxt, new ClazzA());
+ JexlInfo info = new JexlInfo("test317", 1, 1);
+ script = jexl.createScript(info, "var f = "
+ + "()-> {x + x }; f",
+ "x");
+ result = script.execute(ctxt, 21);
+ Assert.assertTrue(result instanceof JexlScript);
+ script = (JexlScript) result;
+ info = JexlInfo.from(script);
+ Assert.assertNotNull(info);
+ Assert.assertEquals("test317", info.getName());
+ result = script.execute(ctxt, 21);
Assert.assertEquals(42, result);
}
}
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index c59193d..3986c2e 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -19,7 +19,6 @@ 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;
@@ -806,7 +805,7 @@ public class JXLTTest extends JexlTestCase {
}
JexlOptions newOptions() {
- options = new Options();
+ options = new JexlOptions();
return options;
}
}
diff --git a/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java b/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
index 55fdcad..f0c233c 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
@@ -19,7 +19,6 @@ package org.apache.commons.jexl3;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.jexl3.annotations.NoJexl;
-import org.apache.commons.jexl3.internal.Options;
/**
* A JEXL evaluation environment wrapping variables, namespace and options.
@@ -35,7 +34,7 @@ public class JexlEvalContext implements
/** The namespace. */
private final JexlContext.NamespaceResolver ns;
/** The options. */
- private final JexlOptions options = new Options();
+ private final JexlOptions options = new JexlOptions();
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index f8d00d3..294ea4a 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -35,7 +35,7 @@ public class JexlTestCase {
// important can be identified by the builder calling lexical(...).
static {
//JexlBuilder.setDefaultOptions("+safe", "-lexical");
- JexlBuilder.setDefaultOptions("-safe", "+lexical");
+ JexlOptions.setDefaultFlags("-safe", "+lexical");
}
/** No parameters signature for test run. */
private static final Class<?>[] NO_PARMS = {};
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index cf8943e..891393a 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.jexl3;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Set;
+import org.apache.commons.jexl3.internal.LexicalScope;
import org.junit.Assert;
import org.junit.Test;
@@ -344,4 +345,46 @@ public class LexicalTest {
Assert.assertNotNull(xany);
}
}
+
+ @Test
+ public void testPragmaOptions() throws Exception {
+ // same as 6d but using a pragma
+ String str = "#pragma jexl.options '+strict +lexical +lexicalShade -safe'\n"
+ + "i = 0; for (var i : [42]) i; i";
+ JexlEngine jexl = new JexlBuilder().strict(false).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 testPragmaNoop() throws Exception {
+ // unknow pragma
+ String str = "#pragma jexl.options 'no effect'\ni = -42; for (var i : [42]) i; i";
+ JexlEngine jexl = new JexlBuilder().lexical(false).strict(true).create();
+ JexlScript e = jexl.createScript(str);
+ JexlContext ctxt = new MapContext();
+ Object result = e.execute(ctxt);
+ Assert.assertEquals(42, result);
+ }
+
+
+ @Test
+ public void testScopeFrame() throws Exception {
+ LexicalScope scope = new LexicalScope(null);
+ for(int i = 0; i < 128; i += 2) {
+ Assert.assertTrue(scope.declareSymbol(i));
+ Assert.assertFalse(scope.declareSymbol(i));
+ }
+ for(int i = 0; i < 128; i += 2) {
+ Assert.assertTrue(scope.hasSymbol(i));
+ Assert.assertFalse(scope.hasSymbol(i + 1));
+ }
+ }
+
}
Re: [commons-jexl] branch master updated: JEXL-307: tidy API (made
JexlOptions a concrete non derivable class), try to ensure lexical
interpretation on lexical feature, allow runtime options to be controlled
through pragma (jexl.options) Task #JEXL-307 - Variable redeclaration
option
Posted by henrib <he...@apache.org>.
JexlOptions is introduced in 3.2, this class has not been released yet.
I understand your reaction but I believe it is not warranted in this case.
I also believe 'internal' classes are clearly out of the API contract - they
are documented as such.
If you believe there are already showstoppers that warrant a package change,
aka jexl4, I should not even try releasing 3.2; please let me know.
Cheers
--
Sent from: http://apache-commons.680414.n4.nabble.com/Commons-Dev-f680415.html
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org
Re: [commons-jexl] branch master updated: JEXL-307: tidy API (made
JexlOptions a concrete non derivable class), try to ensure lexical
interpretation on lexical feature, allow runtime options to be controlled
through pragma (jexl.options) Task #JEXL-307 - Variable redeclaration option
Posted by sebb <se...@gmail.com>.
If you are referring to the class
org.apache.commons.jexl3.internal.Options
Is binary compat required for an internal class?
On Wed, 13 Nov 2019 at 10:20, Gary Gregory <ga...@gmail.com> wrote:
> I do not think your changes to JexlOptions are binary compatible so -1 :-(
>
> Gary
>
> On Tue, Nov 12, 2019, 17:01 <he...@apache.org> wrote:
>
> > 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 753d6e6 JEXL-307: tidy API (made JexlOptions a concrete non
> > derivable class), try to ensure lexical interpretation on lexical
> feature,
> > allow runtime options to be controlled through pragma (jexl.options) Task
> > #JEXL-307 - Variable redeclaration option
> > 753d6e6 is described below
> >
> > commit 753d6e634d94022871b481896db78e323d7ccfed
> > Author: henrib <he...@apache.org>
> > AuthorDate: Tue Nov 12 23:00:39 2019 +0100
> >
> > JEXL-307: tidy API (made JexlOptions a concrete non derivable class),
> > try to ensure lexical interpretation on lexical feature, allow runtime
> > options to be controlled through pragma (jexl.options)
> > Task #JEXL-307 - Variable redeclaration option
>
> <snip>
Re: [commons-jexl] branch master updated: JEXL-307: tidy API (made
JexlOptions a concrete non derivable class), try to ensure lexical
interpretation on lexical feature, allow runtime options to be controlled
through pragma (jexl.options) Task #JEXL-307 - Variable redeclaration option
Posted by Gary Gregory <ga...@gmail.com>.
I do not think your changes to JexlOptions are binary compatible so -1 :-(
Gary
On Tue, Nov 12, 2019, 17:01 <he...@apache.org> wrote:
> 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 753d6e6 JEXL-307: tidy API (made JexlOptions a concrete non
> derivable class), try to ensure lexical interpretation on lexical feature,
> allow runtime options to be controlled through pragma (jexl.options) Task
> #JEXL-307 - Variable redeclaration option
> 753d6e6 is described below
>
> commit 753d6e634d94022871b481896db78e323d7ccfed
> Author: henrib <he...@apache.org>
> AuthorDate: Tue Nov 12 23:00:39 2019 +0100
>
> JEXL-307: tidy API (made JexlOptions a concrete non derivable class),
> try to ensure lexical interpretation on lexical feature, allow runtime
> options to be controlled through pragma (jexl.options)
> Task #JEXL-307 - Variable redeclaration option
> ---
> .../java/org/apache/commons/jexl3/JexlBuilder.java | 21 +-
> .../java/org/apache/commons/jexl3/JexlContext.java | 16 ++
> .../java/org/apache/commons/jexl3/JexlEngine.java | 10 +-
> .../java/org/apache/commons/jexl3/JexlInfo.java | 11 +
> .../java/org/apache/commons/jexl3/JexlOptions.java | 223 +++++++++++++++--
> .../org/apache/commons/jexl3/internal/Engine.java | 68 ++++-
> .../apache/commons/jexl3/internal/Interpreter.java | 33 ++-
> .../commons/jexl3/internal/InterpreterBase.java | 7 +-
> .../org/apache/commons/jexl3/internal/Options.java | 274
> ---------------------
> .../org/apache/commons/jexl3/internal/Script.java | 45 +++-
> .../jexl3/internal/TemplateInterpreter.java | 2 +-
> .../org/apache/commons/jexl3/AntishCallTest.java | 15 +-
> .../org/apache/commons/jexl3/Issues300Test.java | 25 +-
> .../java/org/apache/commons/jexl3/JXLTTest.java | 3 +-
> .../org/apache/commons/jexl3/JexlEvalContext.java | 3 +-
> .../org/apache/commons/jexl3/JexlTestCase.java | 2 +-
> .../java/org/apache/commons/jexl3/LexicalTest.java | 43 ++++
> 17 files changed, 431 insertions(+), 370 deletions(-)
>
> diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
> b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
> index 35c8d5f..744dfe1 100644
> --- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
> +++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
> @@ -18,7 +18,6 @@
> package org.apache.commons.jexl3;
>
> import org.apache.commons.jexl3.internal.Engine;
> -import org.apache.commons.jexl3.internal.Options;
> import org.apache.commons.jexl3.introspection.JexlSandbox;
> import org.apache.commons.jexl3.introspection.JexlUberspect;
> import org.apache.commons.logging.Log;
> @@ -81,7 +80,7 @@ public class JexlBuilder {
> private Boolean cancellable = null;
>
> /** The options. */
> - private final Options options = new Options();
> + private final JexlOptions options = new JexlOptions();
>
> /** Whether getVariables considers all potential equivalent syntactic
> forms. */
> private Boolean collectAll = null;
> @@ -109,23 +108,7 @@ public class JexlBuilder {
>
> /** The features. */
> private JexlFeatures features = null;
> -
> - /**
> - * Sets the default (static, shared) option flags.
> - * <p>
> - * Whenever possible, we recommend using JexlBuilder methods to
> unambiguously instantiate a JEXL
> - * engine; this method should only be used for testing / validation.
> - * <p>A '+flag' or 'flag' will set the option named 'flag' as true,
> '-flag' set as false.
> - * The possible flag names are:
> - * cancellable, strict, silent, safe, lexical, antish, lexicalShade
> - * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL
> engine creation
> - * may ease validating JEXL3.2 in your environment.
> - * @param flags the flags to set
> - */
> - public static void setDefaultOptions(String...flags) {
> - Options.setDefaultFlags(flags);
> - }
> -
> +
> /**
> * Sets the JexlUberspect instance the engine will use.
> *
> diff --git a/src/main/java/org/apache/commons/jexl3/JexlContext.java
> b/src/main/java/org/apache/commons/jexl3/JexlContext.java
> index fdc85a3..ce730f7 100644
> --- a/src/main/java/org/apache/commons/jexl3/JexlContext.java
> +++ b/src/main/java/org/apache/commons/jexl3/JexlContext.java
> @@ -159,4 +159,20 @@ public interface JexlContext {
> */
> JexlOptions getEngineOptions();
> }
> +
> + /**
> + * A marker interface of the JexlContext that processes pragmas.
> + * It is called by the engine before interpreter creation; as a
> marker of
> + * JexlContext, it is expected to have access and interact with the
> context
> + * instance.
> + * @since 3.2
> + */
> + interface PragmaProcessor {
> + /**
> + * Process one pragma.
> + * @param key the key
> + * @param value the value
> + */
> + void processPragma(String key, Object value);
> + }
> }
> diff --git a/src/main/java/org/apache/commons/jexl3/JexlEngine.java
> b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
> index bc41a82..7b9d198 100644
> --- a/src/main/java/org/apache/commons/jexl3/JexlEngine.java
> +++ b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
> @@ -352,7 +352,7 @@ public abstract class JexlEngine {
> * @return A {@link JexlScript} which can be executed using a {@link
> JexlContext}
> * @throws JexlException if there is a problem parsing the script
> */
> - public abstract JexlScript createScript(JexlFeatures features,
> JexlInfo info, String source, String[] names);
> + public abstract JexlScript createScript(JexlFeatures features,
> JexlInfo info, String source, String... names);
>
> /**
> * Creates a JexlScript from a String containing valid JEXL syntax.
> @@ -365,7 +365,7 @@ public abstract class JexlEngine {
> * @return A {@link JexlScript} which can be executed using a {@link
> JexlContext}
> * @throws JexlException if there is a problem parsing the script
> */
> - public final JexlScript createScript(JexlInfo info, String source,
> String[] names) {
> + public final JexlScript createScript(JexlInfo info, String source,
> String... names) {
> return createScript(null, info, source, names);
> }
>
> @@ -378,7 +378,7 @@ public abstract class JexlEngine {
> * @throws JexlException if there is a problem parsing the script.
> */
> public final JexlScript createScript(String scriptText) {
> - return createScript(null, null, scriptText, null);
> + return createScript(null, null, scriptText, (String[]) null);
> }
>
> /**
> @@ -404,7 +404,7 @@ public abstract class JexlEngine {
> * @throws JexlException if there is a problem reading or parsing the
> script.
> */
> public final JexlScript createScript(File scriptFile) {
> - return createScript(null, null, readSource(scriptFile), null);
> + return createScript(null, null, readSource(scriptFile),
> (String[]) null);
> }
>
> /**
> @@ -445,7 +445,7 @@ public abstract class JexlEngine {
> * @throws JexlException if there is a problem reading or parsing the
> script.
> */
> public final JexlScript createScript(URL scriptUrl) {
> - return createScript(null, readSource(scriptUrl), null);
> + return createScript(null, readSource(scriptUrl), (String[]) null);
> }
>
> /**
> diff --git a/src/main/java/org/apache/commons/jexl3/JexlInfo.java
> b/src/main/java/org/apache/commons/jexl3/JexlInfo.java
> index 19a4a72..296138b 100644
> --- a/src/main/java/org/apache/commons/jexl3/JexlInfo.java
> +++ b/src/main/java/org/apache/commons/jexl3/JexlInfo.java
> @@ -17,6 +17,8 @@
>
> package org.apache.commons.jexl3;
>
> +import org.apache.commons.jexl3.internal.Script;
> +
> /**
> * Helper class to carry information such as a url/file name, line and
> column for
> * debugging information reporting.
> @@ -182,5 +184,14 @@ public class JexlInfo {
> public final int getColumn() {
> return column;
> }
> +
> + /**
> + * Gets the info from a script.
> + * @param script the script
> + * @return the info
> + */
> + public static JexlInfo from(JexlScript script) {
> + return script instanceof Script? ((Script) script).getInfo() :
> null;
> + }
> }
>
> diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
> b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
> index dc35074..c650e9b 100644
> --- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
> +++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
> @@ -18,6 +18,7 @@
> package org.apache.commons.jexl3;
>
> import java.math.MathContext;
> +import org.apache.commons.jexl3.internal.Engine;
>
> /**
> * Flags and properties that can alter the evaluation behavior.
> @@ -35,31 +36,150 @@ import java.math.MathContext;
> * <p>This interface replaces the now deprecated JexlEngine.Options.
> * @since 3.2
> */
> -public interface JexlOptions {
> +public final class JexlOptions {
> + /** The local shade bit. */
> + private static final int SHADE = 6;
> + /** The antish var bit. */
> + private static final int ANTISH = 5;
> + /** The lexical scope bit. */
> + private static final int LEXICAL = 4;
> + /** The safe bit. */
> + private static final int SAFE = 3;
> + /** The silent bit. */
> + private static final int SILENT = 2;
> + /** The strict bit. */
> + private static final int STRICT = 1;
> + /** The cancellable bit. */
> + private static final int CANCELLABLE = 0;
> + /** The flags names ordered. */
> + private static final String[] NAMES = {
> + "cancellable", "strict", "silent", "safe", "lexical", "antish",
> "lexicalShade"
> + };
> + /** Default mask .*/
> + private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1
> << ANTISH | 1 << SAFE;
> + /** The arithmetic math context. */
> + private MathContext mathContext = null;
> + /** The arithmetic math scale. */
> + private int mathScale = Integer.MIN_VALUE;
> + /** The arithmetic strict math flag. */
> + private boolean strictArithmetic = true;
> + /** The default flags, all but safe. */
> + private int flags = DEFAULT;
> +
> + /**
> + * Sets the value of a flag in a mask.
> + * @param ordinal the flag ordinal
> + * @param mask the flags mask
> + * @param value true or false
> + * @return the new flags mask value
> + */
> + private static int set(int ordinal, int mask, boolean value) {
> + return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
> + }
> +
> + /**
> + * Checks the value of a flag in the mask.
> + * @param ordinal the flag ordinal
> + * @param mask the flags mask
> + * @return the mask value with this flag or-ed in
> + */
> + private static boolean isSet(int ordinal, int mask) {
> + return (mask & 1 << ordinal) != 0;
> + }
> +
> + /**
> + * Default ctor.
> + */
> + public JexlOptions() {}
> +
> + /**
> + * Sets the default (static, shared) option flags.
> + * <p>
> + * Whenever possible, we recommend using JexlBuilder methods to
> unambiguously instantiate a JEXL
> + * engine; this method should only be used for testing / validation.
> + * <p>A '+flag' or 'flag' will set the option named 'flag' as true,
> '-flag' set as false.
> + * The possible flag names are:
> + * cancellable, strict, silent, safe, lexical, antish, lexicalShade
> + * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL
> engine creation
> + * may ease validating JEXL3.2 in your environment.
> + * @param flags the flags to set
> + */
> + public static void setDefaultFlags(String...flags) {
> + DEFAULT = parseFlags(DEFAULT, flags);
> + }
> +
> + /**
> + * Parses flags by name.
> + * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
> + * The possible flag names are:
> + * cancellable, strict, silent, safe, lexical, antish, lexicalShade
> + * @param mask the initial mask state
> + * @param flags the flags to set
> + * @return the flag mask updated
> + */
> + public static int parseFlags(int mask, String...flags) {
> + for(String name : flags) {
> + boolean b = true;
> + if (name.charAt(0) == '+') {
> + name = name.substring(1);
> + } else if (name.charAt(0) == '-') {
> + name = name.substring(1);
> + b = false;
> + }
> + for(int flag = 0; flag < NAMES.length; ++flag) {
> + if (NAMES[flag].equals(name)) {
> + if (b) {
> + mask |= (1 << flag);
> + } else {
> + mask &= ~(1 << flag);
> + }
> + break;
> + }
> + }
> + }
> + return mask;
> + }
> +
> + /**
> + * Sets this option flags using the +/- syntax.
> + * @param opts the option flags
> + */
> + public void setFlags(String[] opts) {
> + flags = parseFlags(flags, opts);
> + }
> +
> /**
> * The MathContext instance used for +,-,/,*,% operations on big
> decimals.
> * @return the math context
> */
> - MathContext getMathContext();
> + public MathContext getMathContext() {
> + return mathContext;
> + }
>
> /**
> * The BigDecimal scale used for comparison and coercion operations.
> * @return the scale
> */
> - int getMathScale();
> + public int getMathScale() {
> + return mathScale;
> + }
>
> /**
> * Checks whether evaluation will attempt resolving antish variable
> names.
> * @return true if antish variables are solved, false otherwise
> */
> - boolean isAntish();
> + public boolean isAntish() {
> + return isSet(ANTISH, flags);
> + }
>
> /**
> * Checks whether evaluation will throw JexlException.Cancel (true) or
> * return null (false) if interrupted.
> * @return true when cancellable, false otherwise
> */
> - boolean isCancellable();
> + public boolean isCancellable() {
> + return isSet(CANCELLABLE, flags);
> + }
>
> /**
> * Checks whether runtime variable scope is lexical.
> @@ -67,7 +187,9 @@ public interface JexlOptions {
> * Redefining a variable in the same lexical unit will generate
> errors.
> * @return true if scope is lexical, false otherwise
> */
> - boolean isLexical();
> + public boolean isLexical() {
> + return isSet(LEXICAL, flags);
> + }
>
> /**
> * Checks whether local variables shade global ones.
> @@ -79,122 +201,169 @@ public interface JexlOptions {
> * raise an error.
> * @return true if lexical shading is applied, false otherwise
> */
> - boolean isLexicalShade();
> + public boolean isLexicalShade() {
> + return isSet(SHADE, flags);
> + }
>
> /**
> * Checks whether the engine considers null in navigation expression
> as
> * errors during evaluation..
> * @return true if safe, false otherwise
> */
> - boolean isSafe();
> + public boolean isSafe() {
> + return isSet(SAFE, flags);
> + }
>
> /**
> * Checks whether the engine will throw a {@link JexlException} when
> an
> * error is encountered during evaluation.
> * @return true if silent, false otherwise
> */
> - boolean isSilent();
> + public boolean isSilent() {
> + return isSet(SILENT, flags);
> + }
>
> /**
> * Checks whether the engine considers unknown variables, methods and
> * constructors as errors during evaluation.
> * @return true if strict, false otherwise
> */
> - boolean isStrict();
> + public boolean isStrict() {
> + return isSet(STRICT, flags);
> + }
>
> /**
> * Checks whether the arithmetic triggers errors during evaluation
> when null
> * is used as an operand.
> * @return true if strict, false otherwise
> */
> - boolean isStrictArithmetic();
> + public boolean isStrictArithmetic() {
> + return strictArithmetic;
> + }
>
> /**
> * Sets whether the engine will attempt solving antish variable names
> from
> * context.
> * @param flag true if antish variables are solved, false otherwise
> */
> - void setAntish(boolean flag);
> + public void setAntish(boolean flag) {
> + flags = set(ANTISH, flags, flag);
> + }
>
> /**
> * Sets whether the engine will throw JexlException.Cancel (true) or
> return
> * null (false) when interrupted during evaluation.
> * @param flag true when cancellable, false otherwise
> */
> - void setCancellable(boolean flag);
> + public void setCancellable(boolean flag) {
> + flags = set(CANCELLABLE, flags, flag);
> + }
>
> /**
> * Sets whether the engine uses a strict block lexical scope during
> * evaluation.
> * @param flag true if lexical scope is used, false otherwise
> */
> - void setLexical(boolean flag);
> + public void setLexical(boolean flag) {
> + flags = set(LEXICAL, flags, flag);
> + }
>
> /**
> * 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.
> * @param flag true if creation is allowed, false otherwise
> */
> - void setLexicalShade(boolean flag);
> + public void setLexicalShade(boolean flag) {
> + flags = set(SHADE, flags, flag);
> + if (flag) {
> + flags = set(LEXICAL, flags, true);
> + }
> + }
>
> /**
> * Sets the arithmetic math context.
> * @param mcontext the context
> */
> - void setMathContext(MathContext mcontext);
> + public void setMathContext(MathContext mcontext) {
> + this.mathContext = mcontext;
> + }
>
> /**
> * Sets the arithmetic math scale.
> * @param mscale the scale
> */
> - void setMathScale(int mscale);
> + public void setMathScale(int mscale) {
> + this.mathScale = mscale;
> + }
>
> /**
> * Sets whether the engine considers null in navigation expression as
> errors
> * during evaluation.
> * @param flag true if safe, false otherwise
> */
> - void setSafe(boolean flag);
> + public void setSafe(boolean flag) {
> + flags = set(SAFE, flags, flag);
> + }
>
> /**
> * Sets whether the engine will throw a {@link JexlException} when an
> error
> * is encountered during evaluation.
> * @param flag true if silent, false otherwise
> */
> - void setSilent(boolean flag);
> + public void setSilent(boolean flag) {
> + flags = set(SILENT, flags, flag);
> + }
>
> /**
> * Sets whether the engine considers unknown variables, methods and
> * constructors as errors during evaluation.
> * @param flag true if strict, false otherwise
> */
> - void setStrict(boolean flag);
> + public void setStrict(boolean flag) {
> + flags = set(STRICT, flags, flag);
> + }
>
> /**
> * Sets the strict arithmetic flag.
> * @param stricta true or false
> */
> - void setStrictArithmetic(boolean stricta);
> + public void setStrictArithmetic(boolean stricta) {
> + this.strictArithmetic = stricta;
> + }
>
> /**
> * Set options from engine.
> * @param jexl the engine
> * @return this instance
> - */
> - JexlOptions set(JexlEngine jexl);
> + */
> + public JexlOptions set(JexlEngine jexl) {
> + if (jexl instanceof Engine) {
> + ((Engine) jexl).optionsSet(this);
> + }
> + return this;
> + }
>
> /**
> * Set options from options.
> - * @param options the options
> + * @param src the options
> * @return this instance
> */
> - JexlOptions set(JexlOptions options);
> -
> + public JexlOptions set(JexlOptions src) {
> + mathContext = src.mathContext;
> + mathScale = src.mathScale;
> + strictArithmetic = src.strictArithmetic;
> + flags = src.flags;
> + return this;
> + }
> +
> /**
> * Creates a copy of this instance.
> * @return a copy
> */
> - JexlOptions copy();
> + public JexlOptions copy() {
> + return new JexlOptions().set(this);
> + }
>
> }
> \ No newline at end of file
> diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
> b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
> index d752cca..4895578 100644
> --- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
> +++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
> @@ -74,6 +74,10 @@ public class Engine extends JexlEngine {
> private UberspectHolder() {}
> }
> /**
> + * The name of the options pragma.
> + */
> + protected static final String PRAGMA_OPTIONS = "jexl.options";
> + /**
> * The Log to which all JexlEngine messages will be logged.
> */
> protected final Log logger;
> @@ -347,18 +351,62 @@ public class Engine extends JexlEngine {
> cache.clear();
> }
> }
> -
> +
> + /**
> + * Sets options from this engine options.
> + * @param opts the options to set
> + * @return the options
> + */
> + public JexlOptions optionsSet(JexlOptions opts) {
> + if (opts != null) {
> + opts.set(options);
> + }
> + return opts;
> + }
> +
> + /**
> + * Creates a script evaluation options.
> + * <p>This also calls the pragma processor if any
> + * @param script the script
> + * @param context the context
> + * @return the options
> + */
> + protected JexlOptions createOptions(Script script, JexlContext
> context) {
> + JexlOptions opts = options(context);
> + Map<String, Object> pragmas = script.getPragmas();
> + if (pragmas != null) {
> + JexlContext.PragmaProcessor processor =
> + context instanceof JexlContext.PragmaProcessor
> + ? (JexlContext.PragmaProcessor) context
> + : null;
> + for(Map.Entry<String, Object> pragma : pragmas.entrySet()) {
> + String key = pragma.getKey();
> + Object value = pragma.getValue();
> + if (PRAGMA_OPTIONS.equals(key) && opts != null) {
> + if (value instanceof String) {
> + String[] vs = ((String) value).split(" ");
> + opts.setFlags(vs);
> + }
> + }
> + if (processor != null) {
> + processor.processPragma(key, value);
> + }
> + }
> + }
> + return opts;
> + }
> +
> /**
> * Creates an interpreter.
> * @param context a JexlContext; if null, the empty context is used
> instead.
> * @param frame the interpreter frame
> + * @param opts the evaluation options
> * @return an Interpreter
> */
> - protected Interpreter createInterpreter(JexlContext context, Frame
> frame) {
> - return new Interpreter(this, context, frame);
> + protected Interpreter createInterpreter(JexlContext context, Frame
> frame, JexlOptions opts) {
> + return new Interpreter(this, opts, context, frame);
> }
>
> -
> @Override
> public Script createExpression(JexlInfo info, String expression) {
> return createScript(expressionFeatures, info, expression, null);
> @@ -371,8 +419,12 @@ public class Engine extends JexlEngine {
> }
> String source = trimSource(scriptText);
> Scope scope = names == null ? null : new Scope(null, names);
> - ASTJexlScript tree = parse(info, features == null? scriptFeatures
> : features, source, scope);
> - return new Script(this, source, tree);
> + JexlFeatures ftrs = features == null? scriptFeatures : features;
> + ASTJexlScript tree = parse(info, ftrs, source, scope);
> + // when parsing lexical, try hard to run lexical
> + return ftrs.isLexical()
> + ? new Script.Lexical(this, source, tree)
> + : new Script(this, source, tree);
> }
>
> /**
> @@ -405,7 +457,7 @@ public class Engine extends JexlEngine {
> final ASTJexlScript script = parse(null, PROPERTY_FEATURES,
> src, scope);
> final JexlNode node = script.jjtGetChild(0);
> final Frame frame = script.createFrame(bean);
> - final Interpreter interpreter = createInterpreter(context,
> frame);
> + final Interpreter interpreter = createInterpreter(context,
> frame, null);
> return interpreter.visitLexicalNode(node, null);
> } catch (JexlException xjexl) {
> if (silent) {
> @@ -434,7 +486,7 @@ public class Engine extends JexlEngine {
> final ASTJexlScript script = parse(null, PROPERTY_FEATURES,
> src, scope);
> final JexlNode node = script.jjtGetChild(0);
> final Frame frame = script.createFrame(bean, value);
> - final Interpreter interpreter = createInterpreter(context,
> frame);
> + final Interpreter interpreter = createInterpreter(context,
> frame, null);
> interpreter.visitLexicalNode(node, null);
> } catch (JexlException xjexl) {
> if (silent) {
> 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 ed7efa7..9c27f61 100644
> --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
> +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
> @@ -17,13 +17,18 @@
> //CSOFF: FileLength
> package org.apache.commons.jexl3.internal;
>
> +import java.util.Iterator;
> +import java.util.concurrent.Callable;
> +
> import org.apache.commons.jexl3.JexlArithmetic;
> import org.apache.commons.jexl3.JexlContext;
> import org.apache.commons.jexl3.JexlEngine;
> import org.apache.commons.jexl3.JexlException;
> import org.apache.commons.jexl3.JexlOperator;
> +import org.apache.commons.jexl3.JexlOptions;
> import org.apache.commons.jexl3.JexlScript;
> import org.apache.commons.jexl3.JxltEngine;
> +
> import org.apache.commons.jexl3.introspection.JexlMethod;
> import org.apache.commons.jexl3.introspection.JexlPropertyGet;
>
> @@ -105,9 +110,6 @@ import
> org.apache.commons.jexl3.parser.ASTWhileStatement;
> import org.apache.commons.jexl3.parser.JexlNode;
> import org.apache.commons.jexl3.parser.Node;
>
> -import java.util.Iterator;
> -import java.util.concurrent.Callable;
> -
> /**
> * An interpreter of JEXL syntax.
> *
> @@ -130,11 +132,12 @@ public class Interpreter extends InterpreterBase {
> /**
> * Creates an interpreter.
> * @param engine the engine creating this interpreter
> - * @param aContext the context to evaluate expression
> - * @param eFrame the interpreter evaluation frame
> + * @param aContext the evaluation context, global variables, methods
> and functions
> + * @param opts the evaluation options, flags modifying evaluation
> behavior
> + * @param eFrame the evaluation frame, arguments and local variables
> */
> - protected Interpreter(Engine engine, JexlContext aContext, Frame
> eFrame) {
> - super(engine, aContext);
> + protected Interpreter(Engine engine, JexlOptions opts, JexlContext
> aContext, Frame eFrame) {
> + super(engine, opts, aContext);
> this.frame = eFrame;
> }
>
> @@ -1122,7 +1125,7 @@ public class Interpreter extends InterpreterBase {
> JexlNode objectNode = null;
> JexlNode ptyNode = null;
> StringBuilder ant = null;
> - boolean antish = !(parent instanceof ASTReference) &&
> options.isAntish();
> + boolean antish = !(parent instanceof ASTReference);
> int v = 1;
> main:
> for (int c = 0; c < numChildren; c++) {
> @@ -1172,15 +1175,21 @@ public class Interpreter extends InterpreterBase {
> if (first instanceof ASTIdentifier) {
> ASTIdentifier afirst = (ASTIdentifier) first;
> ant = new StringBuilder(afirst.getName());
> - // skip the first node case since it was trialed
> in jjtAccept above and returned null
> - if (c == 0) {
> - continue;
> - }
> + // skip the else...*
> } else {
> // not an identifier, not antish
> ptyNode = objectNode;
> break main;
> }
> + // *... and continue
> + if (!options.isAntish()) {
> + antish = false;
> + continue;
> + }
> + // skip the first node case since it was trialed in
> jjtAccept above and returned null
> + if (c == 0) {
> + continue;
> + }
> }
> // catch up to current node
> for (; v <= c; ++v) {
> 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 74c8f8f..504c1c1 100644
> --- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
> +++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
> @@ -78,16 +78,17 @@ public abstract class InterpreterBase extends
> ParserVisitor {
> /**
> * Creates an interpreter base.
> * @param engine the engine creating this interpreter
> - * @param aContext the context to evaluate expression
> + * @param opts the evaluation options
> + * @param aContext the evaluation context
> */
> - protected InterpreterBase(Engine engine, JexlContext aContext) {
> + protected InterpreterBase(Engine engine, JexlOptions opts,
> JexlContext aContext) {
> this.jexl = engine;
> this.logger = jexl.logger;
> this.uberspect = jexl.uberspect;
> this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT;
> this.cache = engine.cache != null;
> JexlArithmetic jexla = jexl.arithmetic;
> - this.options = jexl.options(context);
> + this.options = opts == null? engine.options(aContext) : opts;
> this.arithmetic = jexla.options(options);
> if (arithmetic != jexla &&
> !arithmetic.getClass().equals(jexla.getClass())) {
> logger.warn("expected arithmetic to be " +
> jexla.getClass().getSimpleName()
> diff --git a/src/main/java/org/apache/commons/jexl3/internal/Options.java
> b/src/main/java/org/apache/commons/jexl3/internal/Options.java
> deleted file mode 100644
> index 04dd182..0000000
> --- a/src/main/java/org/apache/commons/jexl3/internal/Options.java
> +++ /dev/null
> @@ -1,274 +0,0 @@
> -/*
> - * Licensed to the Apache Software Foundation (ASF) under one or more
> - * contributor license agreements. See the NOTICE file distributed with
> - * this work for additional information regarding copyright ownership.
> - * The ASF licenses this file to You under the Apache License, Version 2.0
> - * (the "License"); you may not use this file except in compliance with
> - * the License. You may obtain a copy of the License at
> - *
> - * http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -package org.apache.commons.jexl3.internal;
> -
> -import org.apache.commons.jexl3.JexlOptions;
> -import org.apache.commons.jexl3.JexlEngine;
> -
> -import java.math.MathContext;
> -
> -/**
> - * A basic implementation of JexlOptions.
> - * <p>Thread safety is only guaranteed if no modifications (call set*
> method)
> - * occurs in different threads; note however that using the 'callable()'
> method to
> - * allow a script to run as such will use a copy.
> - */
> -public class Options implements JexlOptions {
> - /** The local shade bit. */
> - protected static final int SHADE = 6;
> - /** The antish var bit. */
> - protected static final int ANTISH = 5;
> - /** The lexical scope bit. */
> - protected static final int LEXICAL = 4;
> - /** The safe bit. */
> - protected static final int SAFE = 3;
> - /** The silent bit. */
> - protected static final int SILENT = 2;
> - /** The strict bit. */
> - protected static final int STRICT = 1;
> - /** The cancellable bit. */
> - protected static final int CANCELLABLE = 0;
> - /** The flags names ordered. */
> - private static final String[] NAMES = {
> - "cancellable", "strict", "silent", "safe", "lexical", "antish",
> "lexicalShade"
> - };
> - /** Default mask .*/
> - protected static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1
> << ANTISH | 1 << SAFE;
> - /** The arithmetic math context. */
> - private MathContext mathContext = null;
> - /** The arithmetic math scale. */
> - private int mathScale = Integer.MIN_VALUE;
> - /** The arithmetic strict math flag. */
> - private boolean strictArithmetic = true;
> - /** The default flags, all but safe. */
> - private int flags = DEFAULT;
> -
> - /**
> - * Sets the value of a flag in a mask.
> - * @param ordinal the flag ordinal
> - * @param mask the flags mask
> - * @param value true or false
> - * @return the new flags mask value
> - */
> - protected static int set(int ordinal, int mask, boolean value) {
> - return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
> - }
> -
> - /**
> - * Checks the value of a flag in the mask.
> - * @param ordinal the flag ordinal
> - * @param mask the flags mask
> - * @return the mask value with this flag or-ed in
> - */
> - protected static boolean isSet(int ordinal, int mask) {
> - return (mask & 1 << ordinal) != 0;
> - }
> -
> - /**
> - * Default ctor.
> - */
> - public Options() {}
> -
> - /**
> - * Sets the default flags.
> - * <p>Used by tests to force default options.
> - * @param flags the flags to set
> - */
> - public static void setDefaultFlags(String...flags) {
> - DEFAULT = parseFlags(DEFAULT, flags);
> - }
> -
> - /**
> - * Parses flags by name.
> - * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
> - * The possible flag names are:
> - * cancellable, strict, silent, safe, lexical, antish, lexicalShade
> - * @param mask the initial mask state
> - * @param flags the flags to set
> - * @return the flag mask updated
> - */
> - public static int parseFlags(int mask, String...flags) {
> - for(String name : flags) {
> - boolean b = true;
> - if (name.charAt(0) == '+') {
> - name = name.substring(1);
> - } else if (name.charAt(0) == '-') {
> - name = name.substring(1);
> - b = false;
> - }
> - for(int flag = 0; flag < NAMES.length; ++flag) {
> - if (NAMES[flag].equals(name)) {
> - if (b) {
> - mask |= (1 << flag);
> - } else {
> - mask &= ~(1 << flag);
> - }
> - break;
> - }
> - }
> - }
> - return mask;
> - }
> -
> - @Override
> - public Options set(JexlEngine jexl) {
> - if (jexl instanceof Engine) {
> - set(((Engine) jexl).options);
> - } else {
> - mathContext = jexl.getArithmetic().getMathContext();
> - mathScale = jexl.getArithmetic().getMathScale();
> - strictArithmetic = jexl.getArithmetic().isStrict();
> - set(STRICT, flags, jexl.isStrict());
> - set(SILENT, flags, jexl.isSilent());
> - set(SAFE, flags, jexl.isSafe());
> - set(CANCELLABLE, flags, jexl.isCancellable());
> - }
> - return this;
> - }
> -
> - @Override
> - public Options set(JexlOptions opts) {
> - if (opts instanceof Options) {
> - Options src = (Options) opts;
> - mathContext = src.mathContext;
> - mathScale = src.mathScale;
> - strictArithmetic = src.strictArithmetic;
> - flags = src.flags;
> - } else {
> - mathContext = opts.getMathContext();
> - mathScale = opts.getMathScale();
> - strictArithmetic = opts.isStrict();
> - int mask = DEFAULT;
> - mask = set(STRICT, mask, opts.isStrict());
> - mask = set(SILENT, mask, opts.isSilent());
> - mask = set(SAFE, mask, opts.isSafe());
> - mask = set(CANCELLABLE, mask, opts.isCancellable());
> - mask = set(LEXICAL, mask, opts.isLexical());
> - mask = set(SHADE, mask, opts.isLexicalShade());
> - mask = set(ANTISH, mask, opts.isAntish());
> - flags = mask;
> - }
> - return this;
> - }
> -
> - @Override
> - public Options copy() {
> - return new Options().set(this);
> - }
> -
> - @Override
> - public void setAntish(boolean flag) {
> - flags = set(ANTISH, flags, flag);
> - }
> -
> - @Override
> - public void setMathContext(MathContext mcontext) {
> - this.mathContext = mcontext;
> - }
> -
> - @Override
> - public void setMathScale(int mscale) {
> - this.mathScale = mscale;
> - }
> -
> - @Override
> - public void setStrictArithmetic(boolean stricta) {
> - this.strictArithmetic = stricta;
> - }
> -
> - @Override
> - public void setStrict(boolean flag) {
> - flags = set(STRICT, flags, flag);
> - }
> -
> - @Override
> - public void setSafe(boolean flag) {
> - flags = set(SAFE, flags, flag);
> - }
> -
> - @Override
> - public void setSilent(boolean flag) {
> - flags = set(SILENT, flags, flag);
> - }
> -
> - @Override
> - public void setCancellable(boolean flag) {
> - flags = set(CANCELLABLE, flags, flag);
> - }
> -
> - @Override
> - public void setLexical(boolean flag) {
> - flags = set(LEXICAL, flags, flag);
> - }
> -
> - @Override
> - public void setLexicalShade(boolean flag) {
> - flags = set(SHADE, flags, flag);
> - }
> -
> - @Override
> - public boolean isAntish() {
> - return isSet(ANTISH, flags);
> - }
> -
> - @Override
> - public boolean isSilent() {
> - return isSet(SILENT, flags);
> - }
> -
> - @Override
> - public boolean isStrict() {
> - return isSet(STRICT, flags);
> - }
> -
> - @Override
> - public boolean isSafe() {
> - return isSet(SAFE, flags);
> - }
> -
> - @Override
> - public boolean isCancellable() {
> - return isSet(CANCELLABLE, flags);
> - }
> -
> - @Override
> - public boolean isLexical() {
> - return isSet(LEXICAL, flags);
> - }
> -
> - @Override
> - public boolean isLexicalShade() {
> - return isSet(SHADE, flags);
> - }
> -
> - @Override
> - public boolean isStrictArithmetic() {
> - return strictArithmetic;
> - }
> -
> - @Override
> - public MathContext getMathContext() {
> - return mathContext;
> - }
> -
> - @Override
> - public int getMathScale() {
> - return mathScale;
> - }
> -
> -}
> 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 62ecaca..593ad08 100644
> --- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
> +++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
> @@ -17,6 +17,8 @@
> package org.apache.commons.jexl3.internal;
>
> import org.apache.commons.jexl3.JexlContext;
> +import org.apache.commons.jexl3.JexlInfo;
> +import org.apache.commons.jexl3.JexlOptions;
> import org.apache.commons.jexl3.JexlEngine;
> import org.apache.commons.jexl3.JexlScript;
> import org.apache.commons.jexl3.JexlExpression;
> @@ -96,7 +98,39 @@ public class Script implements JexlScript,
> JexlExpression {
> protected Frame createFrame(Object[] args) {
> return script.createFrame(args);
> }
> +
> + /**
> + * Creates this script options for evaluation.
> + * <p>This also calls the pragma processor
> + * @param context the context
> + * @return the options
> + */
> + protected JexlOptions createOptions(JexlContext context) {
> + return jexl.createOptions(this, context);
> + }
> +
> + /**
> + * A lexical script, ensures options are lexical.
> + */
> + public static class Lexical extends Script {
> + /**
> + * Sole ctor.
> + * @param engine the engine
> + * @param expr the source.
> + * @param ref the ast
> + */
> + protected Lexical(Engine engine, String expr, ASTJexlScript ref) {
> + super(engine, expr, ref);
> + }
>
> + @Override
> + public JexlOptions createOptions(JexlContext ctxt) {
> + JexlOptions opts = super.createOptions(ctxt);
> + opts.setLexical(true);
> + return opts;
> + }
> + }
> +
> /**
> * Creates this script interpreter.
> * @param context the context
> @@ -104,7 +138,7 @@ public class Script implements JexlScript,
> JexlExpression {
> * @return the interpreter
> */
> protected Interpreter createInterpreter(JexlContext context, Frame
> frame) {
> - return jexl.createInterpreter(context, frame);
> + return jexl.createInterpreter(context, frame,
> createOptions(context));
> }
>
> /**
> @@ -215,6 +249,13 @@ public class Script implements JexlScript,
> JexlExpression {
> public String[] getLocalVariables() {
> return script.getLocalVariables();
> }
> +
> + /**
> + * @return the info
> + */
> + public JexlInfo getInfo() {
> + return script.jexlInfo();
> + }
>
> /**
> * Gets this script variables.
> @@ -259,7 +300,7 @@ public class Script implements JexlScript,
> JexlExpression {
> */
> @Override
> public Callable callable(JexlContext context, Object... args) {
> - return new Callable(jexl.createInterpreter(context,
> script.createFrame(args)));
> + return new Callable(createInterpreter(context,
> script.createFrame(args)));
> }
>
> /**
> 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 17b0ff9..7babe6f 100644
> ---
> a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
> +++
> b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
> @@ -48,7 +48,7 @@ public class TemplateInterpreter extends Interpreter {
> */
> TemplateInterpreter(Engine jexl,
> JexlContext jcontext, Frame jframe, TemplateExpression[]
> expressions, Writer out) {
> - super(jexl, jcontext, jframe);
> + super(jexl, null, jcontext, jframe);
> exprs = expressions;
> writer = out;
> block = new LexicalFrame(frame, null);
> diff --git a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
> b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
> index 5f23b11..8397ce0 100644
> --- a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
> +++ b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
> @@ -173,7 +173,8 @@ public class AntishCallTest extends JexlTestCase {
> // JEXL-300
> @Test
> public void testSafeAnt() throws Exception {
> - JexlContext ctxt = new MapContext();
> + JexlEvalContext ctxt = new JexlEvalContext();
> + JexlOptions options = ctxt.getEngineOptions();
> ctxt.set("x.y.z", 42);
> JexlScript script;
> Object result;
> @@ -182,6 +183,18 @@ public class AntishCallTest extends JexlTestCase {
> result = script.execute(ctxt);
> Assert.assertEquals(42, result);
> Assert.assertEquals(42, ctxt.get("x.y.z"));
> +
> + options.setAntish(false);
> + try {
> + result = script.execute(ctxt);
> + Assert.fail("antish var shall not be resolved");
> + } catch(JexlException.Variable xvar) {
> + Assert.assertTrue("x".equals(xvar.getVariable()));
> + } catch(JexlException xother) {
> + Assert.assertTrue(xother != null);
> + } finally {
> + options.setAntish(true);
> + }
>
> result = null;
> script = JEXL.createScript("x?.y?.z");
> diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
> b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
> index d2b45ec..913133b 100644
> --- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
> +++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
> @@ -324,25 +324,24 @@ public class Issues300Test {
> Assert.assertEquals(52, result);
> }
>
> - public static class ClazzB {
> - public int methodB() {
> - return 42;
> - }
> - }
> - public static class ClazzA {
> - public ClazzB methodA() {
> - return new ClazzB();
> - }
> - }
>
> @Test
> - public void test317Tentative() throws Exception {
> + public void test317() throws Exception {
> JexlEngine jexl = new JexlBuilder().strict(true).create();
> JexlContext ctxt = new MapContext();
> JexlScript script;
> Object result;
> - script = jexl.createScript("x.methodA().methodB()", "x");
> - result = script.execute(ctxt, new ClazzA());
> + JexlInfo info = new JexlInfo("test317", 1, 1);
> + script = jexl.createScript(info, "var f = "
> + + "()-> {x + x }; f",
> + "x");
> + result = script.execute(ctxt, 21);
> + Assert.assertTrue(result instanceof JexlScript);
> + script = (JexlScript) result;
> + info = JexlInfo.from(script);
> + Assert.assertNotNull(info);
> + Assert.assertEquals("test317", info.getName());
> + result = script.execute(ctxt, 21);
> Assert.assertEquals(42, result);
> }
> }
> diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
> b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
> index c59193d..3986c2e 100644
> --- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
> +++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
> @@ -19,7 +19,6 @@ 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;
> @@ -806,7 +805,7 @@ public class JXLTTest extends JexlTestCase {
> }
>
> JexlOptions newOptions() {
> - options = new Options();
> + options = new JexlOptions();
> return options;
> }
> }
> diff --git a/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
> b/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
> index 55fdcad..f0c233c 100644
> --- a/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
> +++ b/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
> @@ -19,7 +19,6 @@ package org.apache.commons.jexl3;
> import java.util.Collections;
> import java.util.Map;
> import org.apache.commons.jexl3.annotations.NoJexl;
> -import org.apache.commons.jexl3.internal.Options;
>
> /**
> * A JEXL evaluation environment wrapping variables, namespace and
> options.
> @@ -35,7 +34,7 @@ public class JexlEvalContext implements
> /** The namespace. */
> private final JexlContext.NamespaceResolver ns;
> /** The options. */
> - private final JexlOptions options = new Options();
> + private final JexlOptions options = new JexlOptions();
>
>
>
> diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
> b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
> index f8d00d3..294ea4a 100644
> --- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
> +++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
> @@ -35,7 +35,7 @@ public class JexlTestCase {
> // important can be identified by the builder calling lexical(...).
> static {
> //JexlBuilder.setDefaultOptions("+safe", "-lexical");
> - JexlBuilder.setDefaultOptions("-safe", "+lexical");
> + JexlOptions.setDefaultFlags("-safe", "+lexical");
> }
> /** No parameters signature for test run. */
> private static final Class<?>[] NO_PARMS = {};
> diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
> b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
> index cf8943e..891393a 100644
> --- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
> +++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
> @@ -19,6 +19,7 @@ package org.apache.commons.jexl3;
> import java.io.StringReader;
> import java.io.StringWriter;
> import java.util.Set;
> +import org.apache.commons.jexl3.internal.LexicalScope;
> import org.junit.Assert;
> import org.junit.Test;
>
> @@ -344,4 +345,46 @@ public class LexicalTest {
> Assert.assertNotNull(xany);
> }
> }
> +
> + @Test
> + public void testPragmaOptions() throws Exception {
> + // same as 6d but using a pragma
> + String str = "#pragma jexl.options '+strict +lexical
> +lexicalShade -safe'\n"
> + + "i = 0; for (var i : [42]) i; i";
> + JexlEngine jexl = new JexlBuilder().strict(false).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 testPragmaNoop() throws Exception {
> + // unknow pragma
> + String str = "#pragma jexl.options 'no effect'\ni = -42; for (var
> i : [42]) i; i";
> + JexlEngine jexl = new
> JexlBuilder().lexical(false).strict(true).create();
> + JexlScript e = jexl.createScript(str);
> + JexlContext ctxt = new MapContext();
> + Object result = e.execute(ctxt);
> + Assert.assertEquals(42, result);
> + }
> +
> +
> + @Test
> + public void testScopeFrame() throws Exception {
> + LexicalScope scope = new LexicalScope(null);
> + for(int i = 0; i < 128; i += 2) {
> + Assert.assertTrue(scope.declareSymbol(i));
> + Assert.assertFalse(scope.declareSymbol(i));
> + }
> + for(int i = 0; i < 128; i += 2) {
> + Assert.assertTrue(scope.hasSymbol(i));
> + Assert.assertFalse(scope.hasSymbol(i + 1));
> + }
> + }
> +
> }
>
>