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/07 17:32:37 UTC

[commons-jexl] branch master updated: JEXL-307: clean up lexical frames after usage, fixed for-loops lexical handling (take 2), added method to change default options in builder, updated tests to resist default option switch 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 40fd213  JEXL-307: clean up lexical frames after usage, fixed for-loops lexical handling (take 2), added method to change default options in builder, updated tests to resist default option switch Task #JEXL-307 - Variable redeclaration option
40fd213 is described below

commit 40fd2139424f5e07cf1be149e36d2dbbc1c85436
Author: henrib <he...@apache.org>
AuthorDate: Thu Nov 7 18:31:50 2019 +0100

    JEXL-307: clean up lexical frames after usage, fixed for-loops lexical handling (take 2), added method to change default options in builder, updated tests to resist default option switch
    Task #JEXL-307 - Variable redeclaration option
---
 .../java/org/apache/commons/jexl3/JexlBuilder.java | 41 ++++++++++---------
 .../org/apache/commons/jexl3/internal/Engine.java  | 13 +++---
 .../apache/commons/jexl3/internal/Interpreter.java | 33 +++++++++++++--
 .../commons/jexl3/internal/LexicalFrame.java       |  3 ++
 .../org/apache/commons/jexl3/internal/Options.java | 47 +++++++++++++++++++++-
 .../org/apache/commons/jexl3/AnnotationTest.java   | 28 +++++--------
 .../org/apache/commons/jexl3/AntishCallTest.java   | 24 +++++------
 .../commons/jexl3/ArithmeticOperatorTest.java      |  3 +-
 .../org/apache/commons/jexl3/Issues200Test.java    | 20 +++++----
 .../org/apache/commons/jexl3/JexlTestCase.java     | 10 ++++-
 .../java/org/apache/commons/jexl3/LambdaTest.java  |  3 +-
 .../java/org/apache/commons/jexl3/LexicalTest.java |  4 +-
 .../java/org/apache/commons/jexl3/VarTest.java     |  1 +
 .../org/apache/commons/jexl3/internal/Util.java    |  2 +-
 .../commons/jexl3/introspection/SandboxTest.java   | 28 ++++++-------
 15 files changed, 171 insertions(+), 89 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index aa2677e..35c8d5f 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -74,21 +74,9 @@ public class JexlBuilder {
     /** The Log to which all JexlEngine messages will be logged. */
     private Log logger = null;
 
-    /**
-     * Whether expressions evaluated by this engine will throw exceptions (false) or
-     * return null (true) on errors. Default is false.
-     */
-    private Boolean silent = null;
-
-    /** Whether this engine is in lenient or strict mode; if unspecified, use the arithmetic lenient property. */
-    private Boolean strict = null;
-
-    /** Whether this engine is in tolerant mode. */
-    private Boolean safe = false;
-
     /** Whether error messages will carry debugging information. */
     private Boolean debug = null;
-
+    
     /** Whether interrupt throws JexlException.Cancel. */
     private Boolean cancellable = null;
     
@@ -121,7 +109,23 @@ 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.
      *
@@ -328,14 +332,13 @@ public class JexlBuilder {
      * @return this builder
      */
     public JexlBuilder silent(boolean flag) {
-        this.silent = flag;
         options.setSilent(flag);
         return this;
     }
 
     /** @return the silent error handling flag */
     public Boolean silent() {
-        return this.silent;
+        return options.isSilent();
     }
 
     /**
@@ -346,14 +349,13 @@ public class JexlBuilder {
      * @return this builder
      */
     public JexlBuilder strict(boolean flag) {
-        this.strict = flag;
         options.setStrict(flag);
         return this;
     }
 
     /** @return true if strict, false otherwise */
     public Boolean strict() {
-        return this.strict;
+        return options.isStrict();
     }
 
     /**
@@ -366,14 +368,13 @@ public class JexlBuilder {
      * @return this builder
      */
     public JexlBuilder safe(boolean flag) {
-        this.safe = flag;
         options.setSafe(flag);
         return this;
     }
 
     /** @return true if safe, false otherwise */
     public Boolean safe() {
-        return this.safe;
+        return options.isSafe();
     }
 
     /**
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 23d14ed..4f19bb5 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -171,9 +171,10 @@ public class Engine extends JexlEngine {
      */
     public Engine(JexlBuilder conf) {
         // options:
-        this.strict = option(conf.strict(), true);
-        this.safe = option(conf.safe(), false);
-        this.silent = option(conf.silent(), false);
+        JexlOptions opts = conf.options();
+        this.strict = opts.isStrict();
+        this.safe = opts.isSafe();
+        this.silent = opts.isSilent();
         this.cancellable = option(conf.cancellable(), !silent && strict);
         this.debug = option(conf.debug(), true);
         this.collectAll = option(conf.collectAll(), true);
@@ -205,7 +206,7 @@ public class Engine extends JexlEngine {
             throw new IllegalArgumentException("uberspect can not be null");
         }
         // capture options
-        this.options = conf.options().copy().set(this);
+        this.options = opts.copy().set(this);
     }
 
 
@@ -403,7 +404,7 @@ public class Engine extends JexlEngine {
             final JexlNode node = script.jjtGetChild(0);
             final Frame frame = script.createFrame(bean);
             final Interpreter interpreter = createInterpreter(context, frame);
-            return node.jjtAccept(interpreter, null);
+            return interpreter.visitLexicalNode(node, null);
         } catch (JexlException xjexl) {
             if (silent) {
                 logger.warn(xjexl.getMessage(), xjexl.getCause());
@@ -432,7 +433,7 @@ public class Engine extends JexlEngine {
             final JexlNode node = script.jjtGetChild(0);
             final Frame frame = script.createFrame(bean, value);
             final Interpreter interpreter = createInterpreter(context, frame);
-            node.jjtAccept(interpreter, null);
+            interpreter.visitLexicalNode(node, null);
         } catch (JexlException xjexl) {
             if (silent) {
                 logger.warn(xjexl.getMessage(), xjexl.getCause());
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 0dbbcfa..ed7efa7 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -671,11 +671,13 @@ public class Interpreter extends InterpreterBase {
         ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
         ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0);
         final int symbol = loopVariable.getSymbol();
-        final boolean lexical = options.isLexical() && symbol >= 0;
+        final boolean lexical = options.isLexical();// && node.getSymbolCount() > 0;
+        final boolean loopSymbol = symbol >= 0 && loopVariable instanceof ASTVar;
         if (lexical) {
             // create lexical frame
             LexicalFrame locals = new LexicalFrame(frame, block);
-            if (!locals.declareSymbol(symbol)) {
+            // it may be a local previously declared
+            if (loopSymbol && !locals.declareSymbol(symbol)) {
                 return redefinedVariable(node, loopVariable.getName());
             }
             block = locals;
@@ -694,8 +696,18 @@ public class Interpreter extends InterpreterBase {
                         ? (Iterator<?>) forEach
                         : uberspect.getIterator(iterableValue);
                 if (itemsIterator != null) {
+                    int cnt = 0;
                     while (itemsIterator.hasNext()) {
                         cancelCheck(node);
+                        // reset loop varaible
+                        if (lexical && cnt++ > 0) {
+                            // clean up but remain current
+                            block.pop();
+                            // unlikely to fail 
+                            if (loopSymbol && !block.declareSymbol(symbol)) {
+                                return redefinedVariable(node, loopVariable.getName());
+                            }
+                        }
                         // set loopVariable to value of iterator
                         Object value = itemsIterator.next();
                         if (symbol < 0) {
@@ -972,7 +984,22 @@ public class Interpreter extends InterpreterBase {
             return true;
         }
     }
-
+    
+    /**
+     * Runs a node.
+     * @param node the node
+     * @param data the usual data
+     * @return the return value
+     */
+    protected Object visitLexicalNode(JexlNode node, Object data) {
+        block = new LexicalFrame(frame, null);
+        try {
+            return node.jjtAccept(this, data);
+        } finally {
+            block = block.pop();
+        }
+    }
+    
     /**
      * Runs a closure.
      * @param closure the closure
diff --git a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
index d2b341e..4af5bff 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
@@ -81,10 +81,12 @@ public class LexicalFrame extends LexicalScope {
             clean &= ~(1L << s);
             frame.set(s, Scope.UNDEFINED);
         }
+        symbols = 0L;
         if (moreSymbols != null) {
             for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + 1)) {
                 frame.set(s, Scope.UNDEFINED);
             }
+            moreSymbols.clear();
         }
         // restore values of hoisted symbols that were overwritten
         if (stack != null) {
@@ -101,4 +103,5 @@ public class LexicalFrame extends LexicalScope {
         }
         return (LexicalFrame) previous;
     }
+
 }
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Options.java b/src/main/java/org/apache/commons/jexl3/internal/Options.java
index e2c7529..535a4a0 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Options.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Options.java
@@ -43,8 +43,12 @@ public class Options implements JexlOptions {
     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 final int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH_VAR;
+    protected static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << ANTISH_VAR | 1 << SAFE;
     /** The arithmetic math context. */
     private MathContext mathContext = null;
     /** The arithmetic math scale. */
@@ -79,6 +83,47 @@ public class Options implements JexlOptions {
      * 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;
+    }
 
     /**
      * Set options from engine.
diff --git a/src/test/java/org/apache/commons/jexl3/AnnotationTest.java b/src/test/java/org/apache/commons/jexl3/AnnotationTest.java
index b41eda5..a4aebb5 100644
--- a/src/test/java/org/apache/commons/jexl3/AnnotationTest.java
+++ b/src/test/java/org/apache/commons/jexl3/AnnotationTest.java
@@ -37,8 +37,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void test197a() throws Exception {
         JexlContext jc = new MapContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("@synchronized { return 42; }");
+        JexlScript e = JEXL.createScript("@synchronized { return 42; }");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
     }
@@ -121,11 +120,10 @@ public class AnnotationTest extends JexlTestCase {
     public void testVarStmt() throws Exception {
         OptAnnotationContext jc = new OptAnnotationContext();
         JexlOptions options = jc.getEngineOptions();
-        JexlEngine jexl = new JexlBuilder().strict(true).silent(false).create();
-        jc.getEngineOptions().set(jexl);
+        jc.getEngineOptions().set(JEXL);
         JexlScript e;
         Object r;
-        e = jexl.createScript("(s, v)->{ @strict(s) @silent(v) var x = y ; 42; }");
+        e = JEXL.createScript("(s, v)->{ @strict(s) @silent(v) var x = y ; 42; }");
 
         // wont make an error
         try {
@@ -163,7 +161,7 @@ public class AnnotationTest extends JexlTestCase {
         }
         //Assert.assertEquals(42, r);
         Assert.assertTrue(options.isStrict());
-        e = jexl.createScript("@scale(5) 42;");
+        e = JEXL.createScript("@scale(5) 42;");
         r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertTrue(options.isStrict());
@@ -173,8 +171,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void testNoArg() throws Exception {
         AnnotationContext jc = new AnnotationContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("@synchronized { return 42; }");
+        JexlScript e = JEXL.createScript("@synchronized { return 42; }");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertEquals(1, jc.getCount());
@@ -184,8 +181,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void testNoArgExpression() throws Exception {
         AnnotationContext jc = new AnnotationContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("@synchronized 42");
+        JexlScript e = JEXL.createScript("@synchronized 42");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertEquals(1, jc.getCount());
@@ -195,8 +191,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void testNoArgStatement() throws Exception {
         AnnotationContext jc = new AnnotationContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("@synchronized if (true) 2 * 3 * 7; else -42;");
+        JexlScript e = JEXL.createScript("@synchronized if (true) 2 * 3 * 7; else -42;");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertEquals(1, jc.getCount());
@@ -206,8 +201,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void testHoistingStatement() throws Exception {
         AnnotationContext jc = new AnnotationContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("var t = 1; @synchronized for(var x : [2,3,7]) t *= x; t");
+        JexlScript e = JEXL.createScript("var t = 1; @synchronized for(var x : [2,3,7]) t *= x; t");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertEquals(1, jc.getCount());
@@ -217,8 +211,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void testOneArg() throws Exception {
         AnnotationContext jc = new AnnotationContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("@one(1) { return 42; }");
+        JexlScript e = JEXL.createScript("@one(1) { return 42; }");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertEquals(1, jc.getCount());
@@ -229,8 +222,7 @@ public class AnnotationTest extends JexlTestCase {
     @Test
     public void testMultiple() throws Exception {
         AnnotationContext jc = new AnnotationContext();
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlScript e = jexl.createScript("@one(1) @synchronized { return 42; }");
+        JexlScript e = JEXL.createScript("@one(1) @synchronized { return 42; }");
         Object r = e.execute(jc);
         Assert.assertEquals(42, r);
         Assert.assertEquals(2, jc.getCount());
diff --git a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
index 058abb4..5f23b11 100644
--- a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
+++ b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
@@ -131,20 +131,19 @@ public class AntishCallTest extends JexlTestCase {
 
     @Test
     public void testAntishContextVar() throws Exception {
-        JexlEngine jexl = new JexlBuilder().cache(512).strict(true).silent(false).create();
         Map<String,Object> lmap = new TreeMap<String,Object>();
-        JexlContext jc = new CallSupportContext(lmap).engine(jexl);
-        runTestCall(jexl, jc);
+        JexlContext jc = new CallSupportContext(lmap).engine(JEXL);
+        runTestCall(JEXL, jc);
         lmap.put("java.math.BigInteger", new ClassReference(java.math.BigInteger.class));
-        runTestCall(jexl, jc);
+        runTestCall(JEXL, jc);
         lmap.remove("java.math.BigInteger");
-        runTestCall(jexl, jc);
+        runTestCall(JEXL, jc);
     }
 
     @Test
     public void testAntishArithmetic() throws Exception {
         CallSupportArithmetic ja = new CallSupportArithmetic(true);
-        JexlEngine jexl = new JexlBuilder().cache(512).strict(true).silent(false).arithmetic(ja).create();
+        JexlEngine jexl = new JexlBuilder().cache(512).arithmetic(ja).create();
         Map<String,Object> lmap = new TreeMap<String,Object>();
         JexlContext jc = new MapContext(lmap);
         lmap.put("java.math.BigInteger", java.math.BigInteger.class);
@@ -174,24 +173,23 @@ public class AntishCallTest extends JexlTestCase {
     // JEXL-300
     @Test
     public void testSafeAnt() throws Exception {
-        JexlEngine jexl = new JexlBuilder().strict(true).safe(false).create();
         JexlContext ctxt = new MapContext();
         ctxt.set("x.y.z", 42);
         JexlScript script;
         Object result;
         
-        script = jexl.createScript("x.y.z");
+        script = JEXL.createScript("x.y.z");
         result = script.execute(ctxt);
         Assert.assertEquals(42, result);
         Assert.assertEquals(42, ctxt.get("x.y.z"));
                 
         result = null;
-        script = jexl.createScript("x?.y?.z");
+        script = JEXL.createScript("x?.y?.z");
         result = script.execute(ctxt);
         Assert.assertNull(result); // safe navigation, null
         
         result = null;
-        script = jexl.createScript("x?.y?.z = 3");
+        script = JEXL.createScript("x?.y?.z = 3");
         try {
              result = script.execute(ctxt);
              Assert.fail("not antish assign");
@@ -200,7 +198,7 @@ public class AntishCallTest extends JexlTestCase {
         }
         
         result = null;
-        script = jexl.createScript("x.y?.z");
+        script = JEXL.createScript("x.y?.z");
         try {
              result = script.execute(ctxt);
              Assert.fail("x not defined");
@@ -209,7 +207,7 @@ public class AntishCallTest extends JexlTestCase {
         }
         
         result = null;
-        script = jexl.createScript("x.y?.z = 3");
+        script = JEXL.createScript("x.y?.z = 3");
         try {
              result = script.execute(ctxt);
              Assert.fail("not antish assign");
@@ -218,7 +216,7 @@ public class AntishCallTest extends JexlTestCase {
         } 
         
         result = null;
-        script = jexl.createScript("x.`'y'`.z = 3");
+        script = JEXL.createScript("x.`'y'`.z = 3");
         try {
              result = script.execute(ctxt);
              Assert.fail("not antish assign");
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
index c7c051b..342e6d0 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
@@ -33,6 +33,7 @@ import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
+import java.util.TimeZone;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -373,7 +374,7 @@ public class ArithmeticOperatorTest extends JexlTestCase {
         }
 
         protected Object setDateValue(Date date, String key, Object value) throws Exception {
-            Calendar cal = Calendar.getInstance();
+            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
             cal.setTime(date);
             if ("yyyy".equals(key)) {
                 cal.set(Calendar.YEAR, toInteger(value));
diff --git a/src/test/java/org/apache/commons/jexl3/Issues200Test.java b/src/test/java/org/apache/commons/jexl3/Issues200Test.java
index 71c0324..80114e4 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues200Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues200Test.java
@@ -754,13 +754,17 @@ public class Issues200Test extends JexlTestCase {
                 + "  $out.add(c);\n"
                 + "}\n"
                 + " \n"
-                + "for(c: ['j','k','l']) {\n"
-                + "  $out.add(c);\n"
+                + "for(var dc: ['j','k','l']) {\n"
+                + "  $out.add(dc);\n"
                 + "}"
                 + " \n"
                 + "$out.size()";
 
-        JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+        JexlFeatures features = new JexlFeatures();
+        features.lexical(true);
+        JexlEngine jexl = new JexlBuilder()
+                //.features(features)
+                .safe(false).strict(true).lexical(true).create();
         JexlScript script = jexl.createScript(src);
         Object result = script.execute(ctxt, (Object) null);
         Assert.assertEquals(6, result);
@@ -778,7 +782,7 @@ public class Issues200Test extends JexlTestCase {
                 + "for(b: ['j','k','l']) { $out.add(b);}\n"
                 + "$out.size()";
 
-        JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+        JexlEngine jexl = new JexlBuilder().safe(false).strict(true).lexical(false).create();
         JexlScript script = jexl.createScript(src);
         Object result = script.execute(ctxt, (Object) null);
         Assert.assertEquals(6, result);
@@ -813,7 +817,8 @@ public class Issues200Test extends JexlTestCase {
 
     @Test
     public void test287() {
-        JexlContext ctxt = new MapContext();
+        JexlEvalContext ctxt = new JexlEvalContext();
+        JexlOptions options = ctxt.getEngineOptions();
         JexlEngine jexl = new JexlBuilder().strict(true).create();
         String src;
         JexlScript script;
@@ -834,20 +839,21 @@ public class Issues200Test extends JexlTestCase {
         result = script.execute(ctxt);
         Assert.assertEquals(42, result);
         // definition using shadowed global
+        options.setLexical(false);
         src = "(x)->{ if (x==1) { var y = 2; } else if (x==2) { var y = 3; }; y }";
         script = jexl.createScript(src);
         result = script.execute(ctxt, 1);
         Assert.assertEquals(2, result);
         result = script.execute(ctxt, 2);
         Assert.assertEquals(3, result);
+        options.setSafe(false);
         try {
             result = script.execute(ctxt, 0);
             Assert.fail("should have failed!");
         } catch (JexlException.Variable xvar) {
             Assert.assertTrue(xvar.getMessage().contains("y"));
         }
-        jexl = new JexlBuilder().strict(true).safe(true).create();
-        script = jexl.createScript(src);
+        options.setSafe(true);
         try {
             result = script.execute(ctxt, 0);
         } catch (JexlException xvar) {
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index 2662fc7..f8d00d3 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -31,6 +31,12 @@ import org.junit.Assert;
  * Eases the implementation of main methods to debug.
  */
 public class JexlTestCase {
+    // The default options: all tests where engine must lexicality is
+    // important can be identified by the builder  calling lexical(...).
+    static {
+        //JexlBuilder.setDefaultOptions("+safe", "-lexical"); 
+        JexlBuilder.setDefaultOptions("-safe", "+lexical");
+    }
     /** No parameters signature for test run. */
     private static final Class<?>[] NO_PARMS = {};
     /** String parameter signature for test run. */
@@ -40,7 +46,7 @@ public class JexlTestCase {
     protected final JexlEngine JEXL;
 
     public JexlTestCase(String name) {
-        this(name, new JexlBuilder().strict(true).silent(false).cache(32).create());
+        this(name, new JexlBuilder().cache(128).create());
     }
 
     protected JexlTestCase(String name, JexlEngine jexl) {
@@ -62,7 +68,7 @@ public class JexlTestCase {
     }
     
     public static JexlEngine createEngine(boolean lenient) {
-        return new JexlBuilder().arithmetic(new JexlArithmetic(!lenient)).cache(512).create();
+        return new JexlBuilder().arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/jexl3/LambdaTest.java b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
index 03d9339..eb8bf7e 100644
--- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
@@ -148,7 +148,8 @@ public class LambdaTest extends JexlTestCase {
     @Test
     public void testHoistLambda() throws Exception {
         JexlEngine jexl = createEngine();
-        JexlContext ctx = null;
+        JexlEvalContext ctx = new JexlEvalContext();
+        ctx.getEngineOptions().setLexical(false);
         JexlScript s42;
         Object result;
         JexlScript s15;
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index 43c59bd..cf8943e 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -324,7 +324,7 @@ public class LexicalTest {
     @Test
     public void testLexical6c() throws Exception {
         String str = "i = 0; for (var i : [42]) i; i";
-        JexlEngine jexl = new JexlBuilder().strict(true).lexical(true).create();
+        JexlEngine jexl = new JexlBuilder().strict(true).lexical(true).lexicalShade(false).create();
         JexlScript e = jexl.createScript(str);
         JexlContext ctxt = new MapContext();
         Object o = e.execute(ctxt);
@@ -333,7 +333,7 @@ public class LexicalTest {
 
     @Test
     public void testLexical6d() throws Exception {
-        String str = "i = 0; for (var i : [42]) i;; i";
+        String str = "i = 0; for (var i : [42]) i; i";
         JexlEngine jexl = new JexlBuilder().strict(true).lexical(true).lexicalShade(true).create();
         JexlScript e = jexl.createScript(str);
         JexlContext ctxt = new MapContext();
diff --git a/src/test/java/org/apache/commons/jexl3/VarTest.java b/src/test/java/org/apache/commons/jexl3/VarTest.java
index 23982b8..7586e77 100644
--- a/src/test/java/org/apache/commons/jexl3/VarTest.java
+++ b/src/test/java/org/apache/commons/jexl3/VarTest.java
@@ -45,6 +45,7 @@ public class VarTest extends JexlTestCase {
         JexlContext ctxt = new ReadonlyContext(env, options);
         options.setStrict(true);
         options.setSilent(false);
+        options.setSafe(false);
         JexlScript e;
 
         e = JEXL.createScript("x");
diff --git a/src/test/java/org/apache/commons/jexl3/internal/Util.java b/src/test/java/org/apache/commons/jexl3/internal/Util.java
index 378567b..b7b45a2 100644
--- a/src/test/java/org/apache/commons/jexl3/internal/Util.java
+++ b/src/test/java/org/apache/commons/jexl3/internal/Util.java
@@ -82,7 +82,7 @@ public class Util {
 
     /**
      * Creates a list of all descendants of a script including itself.
-     * @param script the script to flatten
+     * @param node the script to flatten
      * @return the descendants-and-self list
      */
     protected static ArrayList<JexlNode> flatten(JexlNode node) {
diff --git a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
index b6e3116..e50722b 100644
--- a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
+++ b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
@@ -131,7 +131,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.black(Foo.class.getName()).execute("");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         script = sjexl.createScript(expr);
         try {
@@ -154,7 +154,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.black(Foo.class.getName()).execute("Quux");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         script = sjexl.createScript(expr, "foo");
         try {
@@ -177,7 +177,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.black(Foo.class.getName()).read("alias");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         script = sjexl.createScript(expr, "foo");
         try {
@@ -200,7 +200,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.black(Foo.class.getName()).write("alias");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         script = sjexl.createScript(expr, "foo", "$0");
         try {
@@ -221,7 +221,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox(false);
         sandbox.white(Foo.class.getName());
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         jc.set("foo", new CantSeeMe());
         script = sjexl.createScript(expr);
@@ -244,7 +244,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.white(Foo.class.getName()).execute("");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         script = sjexl.createScript(expr);
         result = script.execute(null);
@@ -260,7 +260,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.white(Foo.class.getName()).execute("Quux");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
 
         script = sjexl.createScript(expr, "foo");
         result = script.execute(null, foo);
@@ -281,7 +281,7 @@ public class SandboxTest extends JexlTestCase {
         JexlScript script;
         Object result;
 
-        JexlEngine sjexl = new JexlBuilder().strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().strict(true).safe(false).create();
         for (String expr : exprs) {
             script = sjexl.createScript(expr, "foo");
             try {
@@ -307,7 +307,7 @@ public class SandboxTest extends JexlTestCase {
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.white(Foo.class.getName()).read("alias");
         sandbox.get(Foo.class.getName()).read().alias("alias", "ALIAS");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
 
         script = sjexl.createScript(expr, "foo");
         result = script.execute(null, foo);
@@ -327,7 +327,7 @@ public class SandboxTest extends JexlTestCase {
 
         JexlSandbox sandbox = new JexlSandbox();
         sandbox.white(Foo.class.getName()).write("alias");
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
 
         script = sjexl.createScript(expr, "foo", "$0");
         result = script.execute(null, foo, "43");
@@ -345,7 +345,7 @@ public class SandboxTest extends JexlTestCase {
         // can not create a new file
         sandbox.black(java.io.File.class.getName()).execute("");
 
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
 
         String expr;
         JexlScript script;
@@ -389,7 +389,7 @@ public class SandboxTest extends JexlTestCase {
         JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.white(java.util.List.class.getName());
         
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
         JexlScript method = sjexl.createScript("foo.add(y)", "foo", "y");
         JexlScript set = sjexl.createScript("foo[x] = y", "foo", "x", "y");
         JexlScript get = sjexl.createScript("foo[x]", "foo", "x");
@@ -443,7 +443,7 @@ public class SandboxTest extends JexlTestCase {
         sandbox.white(Operation.class.getName());
         sandbox.black(Operation.class.getName()).execute("nonCallable");
         //sandbox.black(Foo.class.getName()).execute();
-        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
         JexlScript someOp = sjexl.createScript("foo.someOp(y)", "foo", "y");
         result = someOp.execute(ctxt, foo, 30);
         Assert.assertEquals(42, result);
@@ -482,7 +482,7 @@ public class SandboxTest extends JexlTestCase {
     public void testNoJexl312() throws Exception {
         JexlContext ctxt = new MapContext();
         
-        JexlEngine sjexl = new JexlBuilder().strict(true).create();
+        JexlEngine sjexl = new JexlBuilder().safe(false).strict(true).create();
         JexlScript foo = sjexl.createScript("x.getFoo()", "x");
         try {
             foo.execute(ctxt, new Foo44());