I do not think your changes to JexlOptions are binary compatible so -1 :-( Gary
On Tue, Nov 12, 2019, 17:01 <hen...@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 <hen...@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)); > + } > + } > + > } > >