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 ca83d6bc JEXL-425, JEXL-426, JEXL-427 : added flag for logical 
expressions 3.4 compatibility; - javadoc, nitpicks, light code refactoring;
ca83d6bc is described below

commit ca83d6bcb403eb0dcb624509646de56a3c0e35ad
Author: Henrib <hbies...@gmail.com>
AuthorDate: Fri Aug 30 19:23:32 2024 +0200

    JEXL-425, JEXL-426, JEXL-427 : added flag for logical expressions 3.4 
compatibility;
    - javadoc, nitpicks, light code refactoring;
---
 src/changes/changes.xml                            |   9 ++
 .../java/org/apache/commons/jexl3/JexlBuilder.java |  57 +++++++---
 .../org/apache/commons/jexl3/JexlFeatures.java     |   5 +-
 .../java/org/apache/commons/jexl3/JexlOptions.java |  44 +++++++-
 .../org/apache/commons/jexl3/internal/Frame.java   |  47 ++++----
 .../apache/commons/jexl3/internal/Interpreter.java |  54 +++++-----
 .../commons/jexl3/internal/LexicalFrame.java       |   7 +-
 .../org/apache/commons/jexl3/internal/Scope.java   |   3 +-
 .../apache/commons/jexl3/parser/ASTIdentifier.java |   1 -
 .../org/apache/commons/jexl3/parser/JexlNode.java  |  10 +-
 .../apache/commons/jexl3/parser/JexlParser.java    |  32 ++++--
 .../org/apache/commons/jexl3/parser/Parser.jjt     |   8 +-
 .../org/apache/commons/jexl3/FeaturesTest.java     |  60 +++++------
 .../org/apache/commons/jexl3/Issues400Test.java    |  87 ++++++++++-----
 .../java/org/apache/commons/jexl3/LexicalTest.java | 119 +++++++++++----------
 .../java/org/apache/commons/jexl3/SpreadCache.java |   4 +-
 .../commons/jexl3/introspection/SandboxTest.java   |  62 +++++------
 .../commons/jexl3/jexl342/ReferenceUberspect.java  |   2 +-
 18 files changed, 369 insertions(+), 242 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ae72546d..61f9150f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -29,8 +29,17 @@
     <body>
         <release version="3.4.1" date="YYYY-MM-DD" description="This is a 
feature and maintenance release. Java 8 or later is required.">
           <!-- FIX -->
+            <action dev="henrib" type="fix" issue="JEXL-425" due-to="Xu 
Pengcheng">
+                Multiline format literals does not always return string
+            </action>
           <action type="fix" dev="ggregory" due-to="Gary Gregory">Replace 
NumberParser use of Locale.ENGLISH with Locale.ROOT.</action>
           <!-- ADD -->
+            <action dev="henrib" type="add" issue="JEXL-427" due-to="Xu 
Pengcheng">
+                Avoid coercing logical expressions to boolean
+            </action>
+            <action dev="henrib" type="add" issue="JEXL-426" due-to="Xu 
Pengcheng">
+                Enable pass-by-reference for Captured Variables
+            </action>
           <!-- UPDATE -->
           <action type="update" dev="ggregory">Bump 
commons-logging:commons-logging from 1.3.2 to 1.3.4 #267, #280.</action>
           <action type="update" dev="ggregory">Bump 
org.codehaus.mojo:animal-sniffer-maven-plugin from 1.23 to 1.24 #266.</action>
diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java 
b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index 03146109..a8cb6ee5 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -187,15 +187,15 @@ public class JexlBuilder {
     }
 
     /**
-         * Sets whether the engine will resolve antish variable names.
-         *
-         * @param flag true means antish resolution is enabled, false disables 
it
-         * @return this builder
-         */
-        public JexlBuilder antish(final boolean flag) {
-            options.setAntish(flag);
-            return this;
-        }
+     * Sets whether the engine will resolve antish variable names.
+     *
+     * @param flag true means antish resolution is enabled, false disables it
+     * @return this builder
+     */
+    public JexlBuilder antish(final boolean flag) {
+        options.setAntish(flag);
+        return this;
+    }
 
     /** @return the arithmetic */
     public JexlArithmetic arithmetic() {
@@ -216,6 +216,16 @@ public class JexlBuilder {
         return this;
     }
 
+    /**
+     * Sets whether logical expressions (&quot;&quot; , ||) coerce their 
result to boolean.
+     * @param flag true or false
+     * @return this builder
+     */
+    public JexlBuilder booleanLogical(final boolean flag) {
+      options.setBooleanLogical(flag);
+      return this;
+    }
+
     /**
      * @return the cache size
      */
@@ -431,7 +441,12 @@ public class JexlBuilder {
         return imports(Arrays.asList(imports));
     }
 
-    /** @return whether lexical scope is enabled */
+    /**
+     * @see JexlOptions#isLexical()
+     * @return whether lexical scope is enabled
+     * @deprecated 3.4.1
+     */
+    @Deprecated
     public boolean lexical() {
         return options.isLexical();
     }
@@ -448,7 +463,12 @@ public class JexlBuilder {
         return this;
     }
 
-    /** @return whether lexical shading is enabled */
+    /**
+     * @see JexlOptions#isLexicalShade()
+     * @return whether lexical shading is enabled
+     * @deprecated 3.4.1
+     */
+    @Deprecated
     public boolean lexicalShade() {
         return options.isLexicalShade();
     }
@@ -546,9 +566,11 @@ public class JexlBuilder {
         return this;
     }
 
-    /** @return the current set of options */
+    /**
+     * @return the current set of options
+     */
     public JexlOptions options() {
-        return options;
+      return options;
     }
 
     /** @return the permissions */
@@ -675,15 +697,16 @@ public class JexlBuilder {
         return this;
     }
 
+    /**
+     * @see JexlOptions#setStrictInterpolation(boolean)
+     * @param flag strict interpolation flag
+     * @return this builder
+     */
     public JexlBuilder strictInterpolation(final boolean flag) {
         options.setStrictInterpolation(flag);
         return this;
     }
 
-    public boolean strictInterpolation() {
-        return options.isStrictInterpolation();
-    }
-
     /** @return the uberspect */
     public JexlUberspect uberspect() {
         return this.uberspect;
diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java 
b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index 73f65068..7ec7f1a2 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -59,6 +59,7 @@ import java.util.function.Predicate;
  * <li>Pragma anywhere: whether pragma, that are <em>not</em> statements and 
handled before execution begins,
  * can appear anywhere in the source or before any statements - ie at the 
beginning of a script.</li>
  * <li>Const Capture: whether variables captured by lambdas are read-only (aka 
const, same as Java) or read-write.</li>
+ * <li>Reference Capture: whether variables captured by lambdas are 
pass-by-reference or pass-by-value.</li>
  * </ul>
  * @since 3.2
  */
@@ -341,7 +342,7 @@ public final class JexlFeatures {
     }
 
     /**
-     * Sets whether lambda captured-variables are const or not.
+     * Sets whether lambda captured-variables are constant or mutable.
      * <p>
      * When disabled, lambda-captured variables are implicitly converted to 
read-write local variable (let),
      * when enabled, those are implicitly converted to read-only local 
variables (const).
@@ -355,7 +356,7 @@ public final class JexlFeatures {
     }
 
     /**
-     * Sets whether lambda captured-variables are references or not.
+     * Sets whether lambda captured-variables are references or values.
      * <p>When variables are pass-by-reference, side-effects are visible from 
inner lexical scopes
      * to outer-scope.</p>
      * <p>
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java 
b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
index 3ca5c7ef..a20bb0f5 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
@@ -38,12 +38,15 @@ import org.apache.commons.jexl3.internal.Engine;
  * <li>sharedInstance: whether these options can be modified at runtime during 
execution (expert)</li>
  * <li>constCapture: whether captured variables will throw an error if an 
attempt is made to change their value</li>
  * <li>strictInterpolation: whether interpolation strings always return a 
string or attempt to parse and return integer</li>
+ * <li>booleanLogical: whether logical expressions (&quot;&quot; , ||) coerce 
their result to boolean</li>
  * </ul>
  * The sensible default is cancellable, strict and strictArithmetic.
  * <p>This interface replaces the now deprecated JexlEngine.Options.
  * @since 3.2
  */
 public final class JexlOptions {
+    /** The boolean logical flag. */
+    private static final int BOOLEAN_LOGICAL = 10;
     /** The interpolation string bit. */
     private static final int STRICT_INTERPOLATION= 9;
     /** The const capture bit. */
@@ -67,10 +70,12 @@ public final class JexlOptions {
     /** The flag names ordered. */
     private static final String[] NAMES = {
         "cancellable", "strict", "silent", "safe", "lexical", "antish",
-        "lexicalShade", "sharedInstance", "constCapture", "strictInterpolation"
+        "lexicalShade", "sharedInstance", "constCapture", 
"strictInterpolation",
+        "booleanShortCircuit"
     };
     /** Default mask .*/
     private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << 
ANTISH | 1 << SAFE;
+
     /**
      * Checks the value of a flag in the mask.
      * @param ordinal the flag ordinal
@@ -80,6 +85,7 @@ public final class JexlOptions {
     private static boolean isSet(final int ordinal, final int mask) {
         return (mask & 1 << ordinal) != 0;
     }
+
     /**
      * Parses flags by name.
      * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
@@ -115,6 +121,7 @@ public final class JexlOptions {
         }
         return mask;
     }
+
     /**
      * Sets the value of a flag in a mask.
      * @param ordinal the flag ordinal
@@ -125,6 +132,7 @@ public final class JexlOptions {
     private static int set(final int ordinal, final int mask, final boolean 
value) {
         return value? mask | 1 << ordinal : mask & ~(1 << ordinal);
     }
+
     /**
      * Sets the default (static, shared) option flags.
      * <p>
@@ -140,8 +148,10 @@ public final class JexlOptions {
     public static void setDefaultFlags(final String...flags) {
         DEFAULT = parseFlags(DEFAULT, flags);
     }
+
     /** The arithmetic math context. */
     private MathContext mathContext;
+
     /** The arithmetic math scale. */
     private int mathScale = Integer.MIN_VALUE;
 
@@ -212,6 +222,17 @@ public final class JexlOptions {
         return isSet(ANTISH, flags);
     }
 
+    /**
+     * Gets whether logical expressions (&quot;&quot; , ||) coerce their 
result to boolean; if set,
+     * an expression like (3 &quot;&quot; 4 &quot;&quot; 5) will evaluate to 
true. If not, it will evaluate to 5.
+     * To preserve strict compatibility with 3.4, set the flag to true.
+     * @return true if short-circuit logicals coerce their result to boolean, 
false otherwise
+     * @since 3.4.1
+     */
+    public boolean isBooleanLogical() {
+        return isSet(BOOLEAN_LOGICAL, flags);
+    }
+
     /**
      * Checks whether evaluation will throw JexlException.Cancel (true) or
      * return null (false) if interrupted.
@@ -339,6 +360,14 @@ public final class JexlOptions {
         flags = set(ANTISH, flags, flag);
     }
 
+    /**
+     * Sets whether logical expressions (&quot;&quot; , ||) coerce their 
result to boolean.
+     * @param flag true or false
+     */
+    public void setBooleanLogical(final boolean flag) {
+        flags = set(BOOLEAN_LOGICAL, flags, flag);
+    }
+
     /**
      * Sets whether the engine will throw JexlException.Cancel (true) or return
      * null (false) when interrupted during evaluation.
@@ -473,10 +502,15 @@ public final class JexlOptions {
 
     /**
      * Sets the strict interpolation flag.
-     * @param flag true or false
-     */
-    public void setStrictInterpolation(final boolean flag) {
-        flags = set(STRICT_INTERPOLATION, flags, flag);
+     * <p>When strict, interpolation strings composed only of an expression 
(ie `${...}`) are evaluated
+     * as strings; when not strict, integer results are left untouched.</p>
+     * This can affect the results of expressions like 
<code>map.`${key}`</code> when key is
+     * an integer (or a string); it is almost always possible to use 
<code>map[key]</code> to ensure
+     * that the key type is not altered.
+     * @param strict true or false
+     */
+    public void setStrictInterpolation(final boolean strict) {
+        flags = set(STRICT_INTERPOLATION, flags, strict);
     }
 
     @Override public String toString() {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Frame.java 
b/src/main/java/org/apache/commons/jexl3/internal/Frame.java
index e8387694..5602433e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Frame.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Frame.java
@@ -65,7 +65,7 @@ public class Frame {
     }
 
     /**
-     * Creates a new from of this frame&quote;s class.
+     * Creates a new from of this frame&quot;s class.
      * @param s the scope
      * @param r the arguments
      * @param c the number of curried parameters
@@ -149,6 +149,9 @@ public class Frame {
     }
 }
 
+/**
+ * Pass-by-reference frame.
+ */
 class ReferenceFrame extends Frame {
     ReferenceFrame(Scope s, Object[] r, int c) {
         super(s, r, c);
@@ -160,42 +163,40 @@ class ReferenceFrame extends Frame {
     }
 
     @Override
-    CaptureReference capture(int s) {
-        synchronized(stack) {
-            Object o = stack[s];
-            if (o instanceof CaptureReference) {
-                return (CaptureReference) o;
-            } else {
-                CaptureReference captured = new CaptureReference(o);
-                stack[s] = captured;
-                return captured;
-            }
+    CaptureReference capture(final int s) {
+        final Object o = stack[s];
+        if (o instanceof CaptureReference) {
+            return (CaptureReference) o;
+        } else {
+            // change the type of the captured register, wrap the value in a 
reference
+            CaptureReference captured = new CaptureReference(o);
+            stack[s] = captured;
+            return captured;
         }
     }
 
     @Override
     Object get(final int s) {
-        synchronized(stack) {
-            Object o = stack[s];
-            return o instanceof CaptureReference ? ((CaptureReference) 
o).get() : o;
-        }
+        final Object o = stack[s];
+        return o instanceof CaptureReference ? ((CaptureReference) o).get() : 
o;
     }
 
     @Override
     void set(final int r, final Object value) {
-        synchronized (stack) {
-            Object o = stack[r];
-            if (o instanceof CaptureReference) {
-                if (value != Scope.UNDEFINED && value != Scope.UNDECLARED) {
-                    ((CaptureReference) o).set(value);
-                }
-            } else {
-                stack[r] = value;
+        final Object o = stack[r];
+        if (o instanceof CaptureReference) {
+            if (value != Scope.UNDEFINED && value != Scope.UNDECLARED) {
+                ((CaptureReference) o).set(value);
             }
+        } else {
+            stack[r] = value;
         }
     }
 }
 
+/**
+ * Captured variable reference.
+ */
 class CaptureReference extends AtomicReference<Object> {
     CaptureReference(Object o) {
         super(o);
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 cfc2fd54..3479c92a 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -489,10 +489,11 @@ public class Interpreter extends InterpreterBase {
         if (expr instanceof TemplateEngine.TemplateExpression ) {
            Object eval = ((TemplateEngine.TemplateExpression ) 
expr).evaluate(context, frame, options);
             if (eval != null) {
+                String inter = eval.toString();
                 if (options.isStrictInterpolation()) {
-                    return eval.toString();
+                    return inter;
                 }
-                final Integer id = 
ASTIdentifierAccess.parseIdentifier(eval.toString());
+                final Integer id = ASTIdentifierAccess.parseIdentifier(inter);
                 return id != null ? id : eval;
             }
         }
@@ -1076,8 +1077,14 @@ public class Interpreter extends InterpreterBase {
         }
     }
 
-    @Override
-    protected Object visit(final ASTAndNode node, final Object data) {
+    /**
+     * Short-circuit evaluation of logical expression.
+     * @param check the fuse value that will stop evaluation, true for OR, 
false for AND
+     * @param node a ASTAndNode or a ASTOrNode
+     * @param data
+     * @return true or false if boolean logical option is true, the last 
evaluated argument otherwise
+     */
+    private Object shortCircuit(final boolean check, final JexlNode node, 
final Object data) {
         /*
          * The pattern for exception mgmt is to let the child*.jjtAccept out 
of the try/catch loop so that if one fails,
          * the ex will traverse up to the interpreter. In cases where this is 
not convenient/possible, JexlException
@@ -1085,20 +1092,33 @@ public class Interpreter extends InterpreterBase {
          */
         final int last = node.jjtGetNumChildren();
         Object argument = null;
+        boolean result = false;
         for (int c = 0; c < last; ++c) {
             argument = node.jjtGetChild(c).jjtAccept(this, data);
             try {
                 // short-circuit
-                if (!arithmetic.toBoolean(argument)) {
+                result = arithmetic.toBoolean(argument);
+                if (result == check) {
                     break;
                 }
             } catch (final ArithmeticException xrt) {
                 throw new JexlException(node.jjtGetChild(0), "boolean coercion 
error", xrt);
             }
         }
-        return argument;
+        return options.isBooleanLogical()? result : argument;
+    }
+
+    @Override
+    protected Object visit(final ASTAndNode node, final Object data) {
+        return shortCircuit(false, node, data);
     }
 
+    @Override
+    protected Object visit(final ASTOrNode node, final Object data) {
+        return shortCircuit(true, node, data);
+    }
+
+
     @Override
     protected Object visit(final ASTAnnotatedStatement node, final Object 
data) {
         return processAnnotation(node, 0, data);
@@ -1786,24 +1806,6 @@ public class Interpreter extends InterpreterBase {
         return node.getLiteral();
     }
 
-    @Override
-    protected Object visit(final ASTOrNode node, final Object data) {
-        final int last = node.jjtGetNumChildren();
-        Object argument = null;
-        for (int c = 0; c < last; ++c) {
-            argument = node.jjtGetChild(c).jjtAccept(this, data);
-            try {
-                // short-circuit
-                if (arithmetic.toBoolean(argument)) {
-                    break;
-                }
-            } catch (final ArithmeticException xrt) {
-                throw new JexlException(node.jjtGetChild(0), "boolean coercion 
error", xrt);
-            }
-        }
-        return argument;
-    }
-
     @Override
     protected Object visit(final ASTQualifiedIdentifier node, final Object 
data) {
         return resolveClassName(node.getName());
@@ -2113,13 +2115,13 @@ public class Interpreter extends InterpreterBase {
         }
         // ternary as in "x ? y : z"
         if (node.jjtGetNumChildren() == 3) {
-            if (condition != null && arithmetic.toBoolean(condition)) {
+            if (condition != null && arithmetic.testPredicate(condition)) {
                 return node.jjtGetChild(1).jjtAccept(this, data);
             }
             return node.jjtGetChild(2).jjtAccept(this, data);
         }
         // elvis as in "x ?: z"
-        if (condition != null && arithmetic.toBoolean(condition)) {
+        if (condition != null && arithmetic.testPredicate(condition)) {
             return condition;
         }
         return node.jjtGetChild(1).jjtAccept(this, data);
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 13a0715e..822c0dc2 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/LexicalFrame.java
@@ -21,7 +21,9 @@ import java.util.Deque;
 
 /**
  * The set of valued symbols defined in a lexical frame.
- * <p>The symbol identifiers are determined by the functional scope.
+ * <p>The symbol identifiers are determined by the functional scope. Since the 
frame contains values of
+ * all symbols in the functional scope, the lexical frame preserves values of 
symbols reused for local
+ * definition.
  */
 public class LexicalFrame extends LexicalScope {
     /**
@@ -34,6 +36,7 @@ public class LexicalFrame extends LexicalScope {
     protected final LexicalFrame previous;
     /**
      * The stack of values in the lexical frame.
+     * <p>[symbol identifier, value] are stacked in pairs</p>
      */
     private Deque<Object> stack;
 
@@ -105,7 +108,7 @@ public class LexicalFrame extends LexicalScope {
      */
     public LexicalFrame pop() {
         // undefine all symbols
-        clearSymbols(s ->   frame.set(s, Scope.UNDEFINED) );
+        clearSymbols(s -> frame.set(s, Scope.UNDEFINED) );
         // restore values of captured symbols that were overwritten
         if (stack != null) {
             while (!stack.isEmpty()) {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Scope.java 
b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
index 1f671e63..d407fc82 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Scope.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Scope.java
@@ -70,7 +70,6 @@ public final class Scope {
      * The map of local captured variables to parent scope variables, ie 
closure.
      */
     private Map<Integer, Integer> capturedVariables;
-
     /**
      * Let symbols.
      */
@@ -125,7 +124,7 @@ public final class Scope {
             for (final Map.Entry<Integer, Integer> capture : 
capturedVariables.entrySet()) {
                 final Integer target = capture.getKey();
                 final Integer source = capture.getValue();
-                final Object arg = frame.capture(source); //frame.get(source);
+                final Object arg = frame.capture(source);
                 arguments[target] = arg;
             }
             newFrame = frame.newFrame(this, arguments, 0);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java
index dca516a0..94fb9033 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifier.java
@@ -29,7 +29,6 @@ public class ASTIdentifier extends JexlNode {
     private static final int SHADED = 1;
     /** The captured variable flag. */
     private static final int CAPTURED = 2;
-
     /** The lexical variable flag. */
     private static final int LEXICAL = 3;
     /** The const variable flag. */
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java 
b/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
index 3171e41a..2b505913 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
@@ -42,11 +42,19 @@ public abstract class JexlNode extends SimpleNode {
     public interface Funcall {}
 
     /**
-     * Marker interface for nodes hosting a JxltExpression
+     * Marker interface for nodes hosting a JxltExpression.
      */
     public interface JxltHandle {
+        /** @return the expression source. */
         String getExpressionSource();
+
+        /**@return the expression instance, should be a 
TemplateEngine.TemplateExpression. */
         JxltEngine.Expression getExpression();
+
+        /**
+         * Sets the template expression.
+         * @param expr a TemplateEngine.TemplateExpression instance
+         */
         void setExpression(JxltEngine.Expression expr);
     }
 
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java 
b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
index 50d7b972..0db5e377 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -301,11 +301,11 @@ public abstract class JexlParser extends StringParser {
                 }
                 identifier.setSymbol(symbol, name);
                 if (!declared) {
-                    identifier.setShaded(true);
-                    if (/*identifier.isLexical() ||*/ 
getFeatures().isLexicalShade()) {
+                    if (getFeatures().isLexicalShade()) {
                         // can not reuse a local as a global
                         throw new JexlException.Parsing(info, name + ": 
variable is not declared").clean();
                     }
+                    identifier.setShaded(true);
                 }
             }
         }
@@ -480,9 +480,9 @@ public abstract class JexlParser extends StringParser {
      * </p>
      *
      * @param variable the identifier used to declare
-     * @param lexical whether the symbol is lexical
+     * @param lexical  whether the symbol is lexical
      * @param constant whether the symbol is constant
-     * @param token      the variable name toekn
+     * @param token    the variable name token
      */
     protected void declareVariable(final ASTVar variable, final Token token, 
final boolean lexical, final boolean constant) {
         final String name = token.image;
@@ -717,10 +717,26 @@ public abstract class JexlParser extends StringParser {
      * @param node the node
      */
     protected void jjtreeOpenNodeScope(final JexlNode node) {
-//        if (node instanceof ASTBlock || node instanceof ASTForeachStatement) 
{
-//            final LexicalUnit unit = (LexicalUnit) node;
-//            unit.setScope(scope);
-//        }
+        // nothing
+    }
+
+    /**
+     * Starts the definition of a lambda.
+     * @param jjtThis the script
+     */
+    protected void beginLambda(final ASTJexlScript jjtThis) {
+        jjtThis.setFeatures(this.getFeatures());
+        pushScope();
+        pushUnit(jjtThis);
+    }
+
+    /**
+     * Ends the definition of a lambda.
+     * @param jjtThis the script
+     */
+    protected void endLambda(final ASTJexlScript jjtThis) {
+        popUnit(jjtThis);
+        popScope();
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt 
b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
index a8ddf22f..4dd4fee6 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -426,7 +426,7 @@ void Block() #Block : {}
 
 void FunctionStatement() #JexlLambda : {}
 {
-<FUNCTION> DeclareFunction() { pushScope(); 
jjtThis.setFeatures(this.getFeatures()); pushUnit(jjtThis); } Parameters() ( 
LOOKAHEAD(3) Block() | Expression()) { popUnit(jjtThis); popScope(); }
+<FUNCTION> DeclareFunction() { beginLambda(jjtThis); } Parameters() ( 
LOOKAHEAD(3) Block() | Expression()) { endLambda(jjtThis); }
 }
 
 void ExpressionStatement() #void : {}
@@ -1058,11 +1058,11 @@ void Lambda() #JexlLambda :
 }
 {
   <FUNCTION> (LOOKAHEAD(<IDENTIFIER>) DeclareFunction())? {
-   pushScope(); jjtThis.setFeatures(this.getFeatures()); pushUnit(jjtThis); } 
Parameters() ( LOOKAHEAD(3) Block() | Expression()) { popUnit(jjtThis); 
popScope(); }
+   beginLambda(jjtThis); } Parameters() ( LOOKAHEAD(3) Block() | Expression()) 
{ endLambda(jjtThis); }
   |
-  { pushScope(); jjtThis.setFeatures(this.getFeatures()); pushUnit(jjtThis); } 
Parameters() (arrow=<LAMBDA> | arrow=<FATARROW>) ( LOOKAHEAD(3) Block() | 
Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); }
+  { beginLambda(jjtThis); } Parameters() (arrow=<LAMBDA> | arrow=<FATARROW>) ( 
LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); endLambda(jjtThis); }
   |
-  { pushScope(); jjtThis.setFeatures(this.getFeatures()); pushUnit(jjtThis); } 
Parameter() (arrow=<LAMBDA> | arrow=<FATARROW>)( LOOKAHEAD(3) Block() | 
Expression()) { checkLambda(arrow); popUnit(jjtThis); popScope(); }
+  { beginLambda(jjtThis); } Parameter() (arrow=<LAMBDA> | arrow=<FATARROW>)( 
LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); endLambda(jjtThis); }
 }
 
 
diff --git a/src/test/java/org/apache/commons/jexl3/FeaturesTest.java 
b/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
index dc3923ec..2b83ad60 100644
--- a/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
+++ b/src/test/java/org/apache/commons/jexl3/FeaturesTest.java
@@ -42,7 +42,7 @@ public class FeaturesTest extends JexlTestCase {
         super("FeaturesTest");
     }
 
-    private void assertOk(final JexlFeatures features, final String[] scripts) 
{
+    private void assertOk(final String[] scripts) {
         for(final String str : scripts) {
             try {
                 final JexlScript e = jexl.createScript(str);
@@ -55,8 +55,8 @@ public class FeaturesTest extends JexlTestCase {
     /**
      * Checks that the script is valid with all features on then verifies it
      * throws a feature exception with the given set (in features param).
-     * @param features
-     * @param scripts
+     * @param features the features
+     * @param scripts the scripts
      * @throws Exception
      */
     private void checkFeature(final JexlFeatures features, final String[] 
scripts) throws Exception {
@@ -69,7 +69,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void test410a() {
+    void test410a() {
         final long x = JexlFeatures.createAll().getFlags();
         assertEquals(REF_CAPTURE + 1, Long.bitCount(x));
         assertTrue((x & 1L << REF_CAPTURE) != 0);
@@ -82,7 +82,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void test410b() {
+    void test410b() {
         final JexlFeatures features = JexlFeatures.createScript();
         assertTrue(features.isLexical());
         assertTrue(features.isLexicalShade());
@@ -113,7 +113,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testAnnotations() throws Exception {
+    void testAnnotations() throws Exception {
         final JexlFeatures f = new JexlFeatures().annotation(false);
         final String[] scripts = {
             "@synchronized(2) { return 42; }",
@@ -123,7 +123,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testArrayRefs() throws Exception {
+    void testArrayRefs() throws Exception {
         final JexlFeatures f = new JexlFeatures().arrayReferenceExpr(false);
 
         final String[] scripts = {
@@ -133,7 +133,7 @@ public class FeaturesTest extends JexlTestCase {
             "x.y['a'][b]"
         };
         checkFeature(f, scripts);
-        assertOk(f, scripts);
+        assertOk(scripts);
         // same ones with constant array refs should work
         final String[] scriptsOk = {
             "x['y']",
@@ -141,11 +141,11 @@ public class FeaturesTest extends JexlTestCase {
             "x()['a']['b']",
             "x.y['a']['b']"
         };
-        assertOk(f, scriptsOk);
+        assertOk(scriptsOk);
     }
 
     @Test
-    public void testConstCapture() throws Exception {
+    void testConstCapture() throws Exception {
         final JexlFeatures f = new JexlFeatures().constCapture(true);
         final String[] scripts = {
             "let x = 0; const f = y -> x += y; f(42)",
@@ -153,11 +153,11 @@ public class FeaturesTest extends JexlTestCase {
         };
         checkFeature(f, scripts);
         final JexlFeatures nof = new JexlFeatures().constCapture(true);
-        assertOk(nof, scripts);
+        assertOk(scripts);
     }
 
     @Test
-    public void testCreate() {
+    void testCreate() {
         final JexlFeatures f = JexlFeatures.createNone();
         assertTrue(f.supportsExpression());
 
@@ -187,7 +187,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testIssue409() {
+    void testIssue409() {
         final JexlFeatures baseFeatures =  JexlFeatures.createDefault();
         assertFalse(baseFeatures.isLexical());
         assertFalse(baseFeatures.isLexicalShade());
@@ -202,7 +202,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testMethodCalls() throws Exception {
+    void testMethodCalls() throws Exception {
         final JexlFeatures f = new JexlFeatures().methodCall(false);
         final String[] scripts = {
             "x.y(z)",
@@ -217,11 +217,11 @@ public class FeaturesTest extends JexlTestCase {
             "x('a')[1]",
             "x()['a']['b']",
         };
-        assertOk(f, scriptsOk);
+        assertOk(scriptsOk);
     }
 
     @Test
-    public void testMixedFeatures() throws Exception {
+    void testMixedFeatures() throws Exception {
         // no new, no local, no lambda, no loops, no-side effects
         final JexlFeatures f = new JexlFeatures()
                 .newInstance(false)
@@ -241,7 +241,7 @@ public class FeaturesTest extends JexlTestCase {
         checkFeature(f, scripts);
     }
     @Test
-    public void testNoComparatorNames() throws Exception {
+    void testNoComparatorNames() throws Exception {
         final JexlFeatures f = new JexlFeatures().comparatorNames(false);
         final String[] scripts = {
             "1 eq 1",
@@ -255,7 +255,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoLambda() throws Exception {
+    void testNoLambda() throws Exception {
         final JexlFeatures f = new JexlFeatures().lambda(false);
         final String[] scripts = {
             "var x  = ()->{ return 0 };",
@@ -269,7 +269,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoLocals() throws Exception {
+    void testNoLocals() throws Exception {
         final JexlFeatures f = new JexlFeatures().localVar(false);
         final String[] scripts = {
             "var x = 0;",
@@ -279,7 +279,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoLoop() throws Exception {
+    void testNoLoop() throws Exception {
         final JexlFeatures f = new JexlFeatures().loops(false);
         final String[] scripts = {
             "while(true);",
@@ -288,7 +288,7 @@ public class FeaturesTest extends JexlTestCase {
         checkFeature(f, scripts);
     }
     @Test
-    public void testNoNew() throws Exception {
+    void testNoNew() throws Exception {
         final JexlFeatures f = new JexlFeatures().newInstance(false);
         final String[] scripts = {
             "return new(clazz);",
@@ -298,7 +298,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoScript() throws Exception {
+    void testNoScript() throws Exception {
         final JexlFeatures f = new JexlFeatures().script(false);
         assertTrue(f.supportsExpression());
         final String[] scripts = {
@@ -311,7 +311,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoSideEffects() throws Exception {
+    void testNoSideEffects() throws Exception {
         final JexlFeatures f = new JexlFeatures().sideEffect(false);
         final String[] scripts = {
             "x = 1",
@@ -335,7 +335,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoSideEffectsGlobal() throws Exception {
+    void testNoSideEffectsGlobal() throws Exception {
         final JexlFeatures f = new JexlFeatures().sideEffectGlobal(false);
         final String[] scripts = {
             "x = 1",
@@ -366,7 +366,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testPragma() throws Exception {
+    void testPragma() throws Exception {
         final JexlFeatures f = new JexlFeatures().pragma(false);
         final String[] scripts = {
             "#pragma foo 42",
@@ -376,7 +376,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testPragmaAnywhere() throws Exception {
+    void testPragmaAnywhere() throws Exception {
         final JexlFeatures f = new JexlFeatures().pragmaAnywhere(false);
         final String[] scripts = {
                 "var x = 3;\n#pragma foo 42",
@@ -385,7 +385,7 @@ public class FeaturesTest extends JexlTestCase {
     }
 
     @Test
-    public void testReservedVars() throws Exception {
+    void testReservedVars() throws Exception {
         final JexlFeatures f = new 
JexlFeatures().reservedNames(Arrays.asList("foo", "bar"));
         final String[] scripts = {
             "var foo = 0;",
@@ -398,11 +398,11 @@ public class FeaturesTest extends JexlTestCase {
             "(bar1)->{ bar }",
             "var f = function(bar2) { bar2; }"
         };
-        assertOk(f, scriptsOk);
+        assertOk(scriptsOk);
     }
 
     @Test
-    public void testStructuredLiterals() throws Exception {
+    void testStructuredLiterals() throws Exception {
         final JexlFeatures f = new JexlFeatures().structuredLiteral(false);
         final String[] scripts = {
             "{1, 2, 3}",
@@ -411,6 +411,6 @@ public class FeaturesTest extends JexlTestCase {
             "(1 .. 5)"
         };
         checkFeature(f, scripts);
-        assertOk(f, scripts);
+        assertOk(scripts);
     }
 }
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 54a99ffb..8ec230e4 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -100,7 +100,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test402() {
+    void test402() {
         final JexlContext jc = new MapContext();
       // @formatter:off
       final String[] sources = {
@@ -118,7 +118,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test403() {
+    void test403() {
         // @formatter:off
         final String[] strings = {
             "  map1.`${item.a}` = 1;\n",
@@ -150,7 +150,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test404a() {
+    void test404a() {
         final JexlEngine jexl = new 
JexlBuilder().cache(64).strict(true).safe(false).create();
         Map<String, Object> a = Collections.singletonMap("b", 42);
         // access is constant
@@ -172,7 +172,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test404b() {
+    void test404b() {
       // @formatter:off
       final JexlEngine jexl = new JexlBuilder()
           .cache(64)
@@ -213,7 +213,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test406a() {
+    void test406a() {
         // @formatter:off
         final JexlEngine jexl = new JexlBuilder()
             .cache(64)
@@ -251,7 +251,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test407() {
+    void test407() {
         // Java version
         final double r = 99.0d + 7.82d - 99.0d - 7.82d;
         assertEquals(0d, r, 8.e-15); // Not zero, IEEE 754
@@ -267,7 +267,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test412() {
+    void test412() {
         final Map<Object, Object> ctl = new HashMap<>();
         ctl.put("one", 1);
         ctl.put("two", 2);
@@ -292,7 +292,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test413a() {
+    void test413a() {
         final JexlBuilder builder = new JexlBuilder();
         final JexlEngine jexl = builder.create();
         final JexlScript script = jexl.createScript("var c = 42; var f = y -> 
c += y; f(z)", "z");
@@ -301,7 +301,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test413b() {
+    void test413b() {
         final JexlBuilder builder = new JexlBuilder();
         final JexlOptions options = builder.options();
         options.setConstCapture(true);
@@ -313,7 +313,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test413c() {
+    void test413c() {
         final JexlBuilder builder = new JexlBuilder();
         final JexlEngine jexl = builder.create();
         final JexlScript script = jexl.createScript("#pragma jexl.options 
'+constCapture'\nvar c = 42; var f = y -> c += y; f(z)", "z");
@@ -322,7 +322,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test413d() {
+    void test413d() {
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true));
         final JexlEngine jexl = builder.create();
         final JexlException.Parsing xparse = 
assertThrows(JexlException.Parsing.class, () -> jexl.createScript("var c = 42; 
var f = y -> c += y; f(z)", "z"),
@@ -331,7 +331,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test415() {
+    void test415() {
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true));
         final JexlEngine jexl = builder.create();
         JexlScript script;
@@ -354,7 +354,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void test419() throws NoSuchMethodException {
+    void test419() throws NoSuchMethodException {
         // check RESTRICTED permissions denies call to 
System::currentTimeMillis()
         final Method currentTimeMillis = 
System.class.getMethod("currentTimeMillis");
         assertFalse(RESTRICTED.allow(currentTimeMillis));
@@ -382,7 +382,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void testDocBreakContinue() {
+    void testDocBreakContinue() {
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true));
         final JexlEngine jexl = builder.create();
         JexlScript script;
@@ -406,7 +406,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void testNamespaceVsTernary0() {
+    void testNamespaceVsTernary0() {
         final VinzContext ctxt = new VinzContext();
         ctxt.set("Users", "USERS");
         final JexlEngine jexl = new 
JexlBuilder().safe(false).strict(true).silent(false).create();
@@ -425,7 +425,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void testNamespaceVsTernary1() {
+    void testNamespaceVsTernary1() {
         final VinzContext ctxt = new VinzContext();
         ctxt.set("Users", "USERS");
         ctxt.set("vinz", new VinzCaller(ctxt));
@@ -465,7 +465,7 @@ public class Issues400Test {
     }
 
     @Test
-    public void testSortArray() {
+    void testSortArray() {
         final JexlEngine jexl = new 
JexlBuilder().safe(false).strict(true).silent(false).create();
         // test data, json like
         String src = 
"[{'id':1,'name':'John','type':9},{'id':2,'name':'Doe','type':7},{'id':3,'name':'Doe','type':10}]";
@@ -482,7 +482,7 @@ public class Issues400Test {
         assertEquals(9, m[1].get("type"));
     }
 
-    @Test public void test425() {
+    @Test void test425() {
         final JexlBuilder builder = new 
JexlBuilder().strictInterpolation(true);
         final JexlEngine jexl = builder.create();
         JexlScript script;
@@ -493,7 +493,7 @@ public class Issues400Test {
         assertEquals("42", result);
     }
 
-    @Test public void test426a() {
+    @Test void test426a() {
         String src = "let x = 10;\n" +
                 "let foo = () -> {\n" +
                 "x += 2;\n" +
@@ -510,7 +510,7 @@ public class Issues400Test {
         assertEquals(42, result);
     }
 
-    @Test public void test426b() {
+    @Test void test426b() {
         String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f()";
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true).referenceCapture(true));
         final JexlEngine jexl = builder.create();
@@ -521,7 +521,7 @@ public class Issues400Test {
         assertEquals(42, result);
     }
 
-    @Test public void test426c() {
+    @Test void test426c() {
         String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f";
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true).referenceCapture(true));
         final JexlEngine jexl = builder.create();
@@ -535,7 +535,7 @@ public class Issues400Test {
         assertEquals(42, result);
     }
 
-    @Test public void test426d() {
+    @Test void test426d() {
         String src = "let x = 10; let f = () -> { let x = 142; x }; x = 40; f";
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().referenceCapture(true));
         final JexlEngine jexl = builder.create();
@@ -550,7 +550,7 @@ public class Issues400Test {
     }
 
 
-    @Test public void test427a() {
+    @Test void test427a() {
         String src = "(x, y, z) -> x && y && z";
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true));
         final JexlEngine jexl = builder.create();
@@ -563,16 +563,51 @@ public class Issues400Test {
         assertEquals("", result);
     }
 
-    @Test public void test427b() {
+    @Test void test427b() {
+        OptContext optc = new OptContext();
         String src = "(x, y, z) -> x || y || z";
         final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(true));
         final JexlEngine jexl = builder.create();
+        JexlOptions options = builder.options();
+        optc.setOptions(options);
         JexlScript script;
         Object result;
         script = jexl.createScript(src);
-        result = script.execute(null, 0, "", 42);
+        result = script.execute(optc, 0, "", 42);
         assertEquals(42, result);
-        result = script.execute(null, true, 42, null);
+        result = script.execute(optc, true, 42, null);
         assertEquals(true, result);
+
+        options.setBooleanLogical(true);
+        result = script.execute(optc, 0, "", Double.NaN);
+        assertEquals(false, result);
+        result = script.execute(optc, 0, "", Collections.emptySet());
+        assertEquals(true, result);
+
+    }
+
+    @Test void test427c() {
+        String src = "function sanitize(const n) { n == 0 ? NaN : n }; 
sanitize(x) && 420 / x";
+        final JexlEngine jexl = new JexlBuilder().create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src, "x");
+        result = script.execute(null, 10);
+        assertEquals(42, result);
+        result = script.execute(null, 0);
+        assertTrue(Double.isNaN(((Number) result).doubleValue()));
+    }
+
+    public static class OptContext extends MapContext implements 
JexlContext.OptionsHandle {
+        private JexlOptions options;
+
+        @Override
+        public JexlOptions getEngineOptions() {
+            return options;
+        }
+
+        void setOptions(JexlOptions options) {
+            this.options = options;
+        }
     }
 }
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java 
b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index 8d596462..f541a760 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -157,7 +157,7 @@ public class LexicalTest {
         // ensure errors will throw
         options.setLexical(true);
         runLexical0(jexl, ctxt, "var x = 0; var x = 1;", feature);
-        runLexical0(jexl, ctxt, "var x = 0; for(var y : null) { var y = 1;", 
feature);
+        runLexical0(jexl, ctxt, "var x = 0; for(var y : null) { let y = 1; }", 
feature);
         runLexical0(jexl, ctxt, "var x = 0; for(var x : null) {};", feature);
         runLexical0(jexl, ctxt, "(x)->{ var x = 0; x; }", feature);
         runLexical0(jexl, ctxt, "var x; if (true) { if (true) { var x = 0; x; 
} }", feature);
@@ -170,9 +170,10 @@ public class LexicalTest {
         });
         assertNotNull(xany.toString());
         // no fail
-        final JexlScript script = jexl.createScript("var x = 32; (()->{ 
for(var x : null) { x; }})();");
+        final JexlScript script = jexl.createScript("var x = 32; y = (()->{ 
for(var x : [42]) { x; }})(); x");
         if (!feature) {
-            script.execute(ctxt, 42);
+            assertEquals(32, script.execute(ctxt, 42));
+            assertEquals(42, ctxt.get("y"));
         }
     }
 
@@ -315,7 +316,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testAnnotation() {
+    void testAnnotation() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         final JexlEngine jexl = new 
JexlBuilder().strict(true).features(f).create();
@@ -326,7 +327,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testCaptured0() {
+    void testCaptured0() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         final JexlEngine jexl = new 
JexlBuilder().strict(true).features(f).create();
@@ -338,7 +339,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testCaptured1() {
+    void testCaptured1() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         final JexlEngine jexl = new 
JexlBuilder().strict(true).features(f).create();
@@ -351,7 +352,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst0a() {
+    void testConst0a() {
         final JexlFeatures f = new JexlFeatures();
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         final JexlScript script = jexl.createScript(
@@ -362,7 +363,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst0b() {
+    void testConst0b() {
         final JexlFeatures f = new JexlFeatures();
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         final JexlScript script = jexl.createScript(
@@ -373,7 +374,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst1() {
+    void testConst1() {
         final JexlFeatures f = new JexlFeatures();
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         final JexlException.Parsing xparse = 
assertThrows(JexlException.Parsing.class, () -> jexl.createScript("const foo;  
foo"),
@@ -383,7 +384,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst2a() {
+    void testConst2a() {
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         for (final String op : Arrays.asList("=", "+=", "-=", "/=", "*=", 
"%=", "<<=", ">>=", ">>>=", "^=", "&=", "|=")) {
             final JexlException.Parsing xparse = 
assertThrows(JexlException.Parsing.class, () -> jexl.createScript("const foo = 
42;  foo " + op + " 1;"),
@@ -393,7 +394,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst2b() {
+    void testConst2b() {
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         for (final String op : Arrays.asList("=", "+=", "-=", "/=", "*=", 
"%=", "<<=", ">>=", ">>>=", "^=", "&=", "|=")) {
             final JexlException.Parsing xparse = 
assertThrows(JexlException.Parsing.class,
@@ -403,7 +404,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst2c() {
+    void testConst2c() {
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         for (final String op : Arrays.asList("=", "+=", "-=", "/=", "*=", 
"%=", "<<=", ">>=", ">>>=", "^=", "&=", "|=")) {
             final JexlScript script = jexl.createScript("{ const foo = 42; } { 
let foo  = 0; foo " + op + " 1; }");
@@ -412,7 +413,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst3a() {
+    void testConst3a() {
         final JexlEngine jexl = new JexlBuilder().create();
         // @formatter:off
         final List<String> srcs = Arrays.asList(
@@ -430,7 +431,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConst3b() {
+    void testConst3b() {
         final JexlEngine jexl = new JexlBuilder().create();
         // @formatter:off
         final List<String> srcs = Arrays.asList(
@@ -447,7 +448,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testConstCaptures() {
+    void testConstCaptures() {
         // @formatter:off
         final List<String> srcsFalse = Arrays.asList(
                 "const x = 0;  x = 1;",
@@ -475,7 +476,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testContextualOptions0() {
+    void testContextualOptions0() {
         final JexlFeatures f = new JexlFeatures();
         final JexlEngine jexl = new 
JexlBuilder().features(f).strict(true).create();
         final JexlEvalContext ctxt = new JexlEvalContext();
@@ -489,7 +490,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testContextualOptions1() {
+    void testContextualOptions1() {
         final JexlFeatures f = new JexlFeatures();
         final JexlEngine jexl = new 
JexlBuilder().features(f).strict(true).create();
         final JexlEvalContext ctxt = new TestContext();
@@ -518,7 +519,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testForVariable0a() {
+    void testForVariable0a() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         f.lexicalShade(true);
@@ -527,7 +528,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testForVariable0b() {
+    void testForVariable0b() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         f.lexicalShade(true);
@@ -536,7 +537,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testForVariable1a() {
+    void testForVariable1a() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         f.lexicalShade(true);
@@ -546,7 +547,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testForVariable1b() {
+    void testForVariable1b() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         f.lexicalShade(true);
@@ -556,7 +557,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testInnerAccess0() {
+    void testInnerAccess0() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         final JexlEngine jexl = new 
JexlBuilder().strict(true).features(f).create();
@@ -570,14 +571,14 @@ public class LexicalTest {
     }
 
     @Test
-    public void testInnerAccess1a() {
+    void testInnerAccess1a() {
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
         final JexlScript script = jexl.createScript("var x = 32; (()->{ 
for(var x : null) { var c = 0; {return x; }} })();");
         assertNotNull(script);
     }
 
     @Test
-    public void testInnerAccess1b() {
+    void testInnerAccess1b() {
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         final JexlScript script = jexl.createScript("let x = 32; (()->{ 
for(let x : null) { let c = 0; { return x; } } } )(); ");
         assertNotNull(script);
@@ -587,7 +588,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testInternalLexicalFeatures() {
+    void testInternalLexicalFeatures() {
         final String str = "42";
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
@@ -609,7 +610,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLet0() {
+    void testLet0() {
         final JexlFeatures f = new JexlFeatures();
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         final JexlScript script = jexl.createScript(
@@ -622,7 +623,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLetFail() {
+    void testLetFail() {
         final List<String> srcs = Arrays.asList(
             "let x = 0; var x = 1;",
             "var x = 0; let x = 1;",
@@ -636,7 +637,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLetSucceed() {
+    void testLetSucceed() {
         final List<String> srcs = Arrays.asList(
             "var x = 1; var x = 0;",
             "{ let x = 0; } var x = 1;",
@@ -651,17 +652,17 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical0a() {
+    void testLexical0a() {
         runLexical0(false);
     }
 
     @Test
-    public void testLexical0b() {
+    void testLexical0b() {
         runLexical0(true);
     }
 
     @Test
-    public void testLexical1() {
+    void testLexical1() {
         final JexlEngine jexl = new JexlBuilder().strict(true).create();
         final JexlEvalContext ctxt = new JexlEvalContext();
         final JexlOptions options = ctxt.getEngineOptions();
@@ -670,11 +671,11 @@ public class LexicalTest {
         Object result;
 
         final JexlScript script = jexl.createScript("var x = 0; for(var y : 
[1]) { var x = 42; return x; };");
-        JexlException xany = assertThrows(JexlException.class, () -> 
script.execute(ctxt));
+        JexlException xany = assertThrows(JexlException.Variable.class, () -> 
script.execute(ctxt));
         assertNotNull(xany.toString());
 
         final JexlScript script1 = jexl.createScript("(x)->{ if (x) { var x = 
7 * (x + x); x; } }");
-        xany = assertThrows(JexlException.class, () -> script.execute(ctxt, 
3));
+        xany = assertThrows(JexlException.Variable.class, () -> 
script.execute(ctxt, 3));
         assertNotNull(xany.toString());
 
         final JexlScript script3 = jexl.createScript("{ var x = 0; } var x = 
42; x");
@@ -683,27 +684,27 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical1a() {
+    void testLexical1a() {
         runLexical1(false);
     }
 
     @Test
-    public void testLexical1b() {
+    void testLexical1b() {
         runLexical1(true);
     }
 
     @Test
-    public void testLexical2a() {
+    void testLexical2a() {
         runLexical2(true);
     }
 
     @Test
-    public void testLexical2b() {
+    void testLexical2b() {
         runLexical2(false);
     }
 
     @Test
-    public void testLexical3() {
+    void testLexical3() {
         final String str = "var s = {}; for (var i : [1]) s.add(i); s";
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
         JexlScript e = jexl.createScript(str);
@@ -717,7 +718,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical4() {
+    void testLexical4() {
         final JexlEngine Jexl = new 
JexlBuilder().silent(false).strict(true).lexical(true).create();
         final JxltEngine Jxlt = Jexl.createJxltEngine();
         final JexlContext ctxt = new MapContext();
@@ -735,7 +736,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical5() {
+    void testLexical5() {
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
         final JexlContext ctxt = new DebugContext();
         Object result;
@@ -745,7 +746,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical6a() {
+    void testLexical6a() {
         final String str = "i = 0; { var i = 32; }; i";
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
         final JexlScript e = jexl.createScript(str);
@@ -755,7 +756,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical6a1() {
+    void testLexical6a1() {
         final String str = "i = 0; { var i = 32; }; i";
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
@@ -767,7 +768,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical6b() {
+    void testLexical6b() {
         final String str = "i = 0; { var i = 32; }; i";
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).lexicalShade(true).create();
         final JexlScript e = jexl.createScript(str);
@@ -777,7 +778,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical6c() {
+    void testLexical6c() {
         final String str = "i = 0; for (var i : [42]) i; i";
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).lexicalShade(false).create();
         final JexlScript e = jexl.createScript(str);
@@ -787,7 +788,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testLexical6d() {
+    void testLexical6d() {
         final String str = "i = 0; for (var i : [42]) i; i";
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).lexicalShade(true).create();
         final JexlScript e = jexl.createScript(str);
@@ -796,7 +797,7 @@ public class LexicalTest {
         assertNotNull(xany.toString());
     }
 
-    @Test public void testManyConst() {
+    @Test void testManyConst() {
         final String text = "const x = 1, y = 41; x + y";
         final JexlEngine jexl = new JexlBuilder().safe(true).create();
         final JexlScript script = jexl.createScript(text);
@@ -807,7 +808,7 @@ public class LexicalTest {
         assertNotEquals(s0, s1);
     }
 
-    @Test public void testManyLet() {
+    @Test void testManyLet() {
         final String text = "let x = 1, y = 41, z; x + y";
         final JexlEngine jexl = new JexlBuilder().safe(true).create();
         final JexlScript script = jexl.createScript(text);
@@ -819,7 +820,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testNamed() {
+    void testNamed() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         final JexlEngine jexl = new 
JexlBuilder().strict(true).features(f).create();
@@ -830,7 +831,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testOptionsPragma() {
+    void testOptionsPragma() {
         try {
             JexlOptions.setDefaultFlags("+safe", "-lexical", "-lexicalShade");
             final VarContext vars = new VarContext();
@@ -864,7 +865,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testParameter0() {
+    void testParameter0() {
         final String str = "function(u) {}";
         final JexlEngine jexl = new JexlBuilder().create();
         JexlScript e = jexl.createScript(str);
@@ -874,7 +875,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testParameter1() {
+    void testParameter1() {
         final JexlEngine jexl = new 
JexlBuilder().strict(true).lexical(true).create();
         final JexlContext jc = new MapContext();
         final String strs = "var s = function(x) { for (var i : 1..3) {if (i > 
2) return x}}; s(42)";
@@ -884,7 +885,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testPragmaNoop() {
+    void testPragmaNoop() {
         // unknow pragma
         final String str = "#pragma jexl.options 'no effect'\ni = -42; for 
(var i : [42]) i; i";
         final JexlEngine jexl = new 
JexlBuilder().lexical(false).strict(true).create();
@@ -895,7 +896,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testPragmaOptions() {
+    void testPragmaOptions() {
         // same as 6d but using a pragma
         final String str = "#pragma jexl.options '+strict +lexical 
+lexicalShade -safe'\n"
                 + "i = 0; for (var i : [42]) i; i";
@@ -907,7 +908,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testScopeFrame() {
+    void testScopeFrame() {
         final LexicalScope scope = new LexicalScope();
         runTestScope(scope, 0, 128, 2);
         runTestScope(scope, 33, 55, 1);
@@ -916,7 +917,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testSingleStatementDeclFail() {
+    void testSingleStatementDeclFail() {
         final List<String> srcs = Arrays.asList(
                 "if (true) let x ;",
                 "if (true) let x = 1;",
@@ -941,7 +942,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testSingleStatementVarSucceed() {
+    void testSingleStatementVarSucceed() {
         final List<String> srcs = Arrays.asList(
                 "if (true) var x = 1;",
                 "if (true) { 1 } else var x = 1;",
@@ -954,7 +955,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testUndeclaredVariable() {
+    void testUndeclaredVariable() {
         final JexlFeatures f = new JexlFeatures();
         f.lexical(true);
         f.lexicalShade(true);
@@ -963,7 +964,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testVarFail() {
+    void testVarFail() {
         final List<String> srcs = Arrays.asList(
                 "var x = 0; var x = 1;",
                 "var x = 0; let x = 1;",
@@ -979,7 +980,7 @@ public class LexicalTest {
     }
 
     @Test
-    public void testVarLoop0() {
+    void testVarLoop0() {
         final String src0 = "var count = 10;\n"
                 + "for (var i : 0 .. count-1) {\n"
                 + "  $out.add(i);\n"
diff --git a/src/test/java/org/apache/commons/jexl3/SpreadCache.java 
b/src/test/java/org/apache/commons/jexl3/SpreadCache.java
index c2a3e448..35679716 100644
--- a/src/test/java/org/apache/commons/jexl3/SpreadCache.java
+++ b/src/test/java/org/apache/commons/jexl3/SpreadCache.java
@@ -28,8 +28,8 @@ import org.apache.commons.jexl3.internal.SoftCache;
  * Creates a cache using an array of synchronized LinkedHashMap as backing 
store to spread contention.
  * <p>Just meant as a contention reducing mechanism for cache tests.</p>
  *
- * @param <K> the cached element&quote;s key
- * @param <V> the cached element&quote;s value
+ * @param <K> the cached element&quot;s key
+ * @param <V> the cached element&quot;s value
  * @return the cache instance
  */
 public class SpreadCache<K, V> extends SoftCache<K, V> {
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 17980c3b..229d21c5 100644
--- a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
+++ b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
@@ -46,7 +46,7 @@ import org.junit.jupiter.api.Test;
  * Tests sandbox features.
  */
 @SuppressWarnings({ "UnnecessaryBoxing", 
"AssertEqualsBetweenInconvertibleTypes" })
-public class SandboxTest extends JexlTestCase {
+class SandboxTest extends JexlTestCase {
     public abstract static class AbstractCallMeNot {
         public @NoJexl String NONO = "should not be accessible!";
 
@@ -213,7 +213,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testCantSeeMe() throws Exception {
+    void testCantSeeMe() {
         final JexlContext jc = new MapContext();
         final String expr = "foo.doIt()";
         JexlScript script;
@@ -233,7 +233,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testCtorAllow() throws Exception {
+    void testCtorAllow() {
         final String expr = "new('" + Foo.class.getName() + "', '42')";
         JexlScript script;
         Object result;
@@ -248,7 +248,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testCtorBlock() throws Exception {
+    void testCtorBlock() {
         final String expr = "new('" + Foo.class.getName() + "', '42')";
         final JexlScript script = JEXL.createScript(expr);
         Object result;
@@ -265,7 +265,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testGetAllow() throws Exception {
+    void testGetAllow() {
         final Foo foo = new Foo("42");
         final String expr = "foo.alias";
         JexlScript script;
@@ -286,7 +286,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testGetBlock() throws Exception {
+    void testGetBlock() {
         final String expr = "foo.alias";
         final JexlScript script = JEXL.createScript(expr, "foo");
         final Foo foo = new Foo("42");
@@ -303,7 +303,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testGetNullKeyAllowed0() throws Exception {
+    void testGetNullKeyAllowed0() {
         final JexlEngine jexl = new JexlBuilder().sandbox(new 
JexlSandbox(true)).create();
         final JexlExpression expression = jexl.createExpression("{null : 
'foo'}[null]");
         final Object o = expression.evaluate(null);
@@ -311,7 +311,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testGetNullKeyAllowed1() throws Exception {
+    void testGetNullKeyAllowed1() {
         final JexlSandbox sandbox = new JexlSandbox(true, true);
         final JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", 
false, true, true);
         p.read().add("quux");
@@ -330,7 +330,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testGetNullKeyBlocked() throws Exception {
+    void testGetNullKeyBlocked() {
         final JexlSandbox sandbox = new JexlSandbox(true, true);
         final JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", 
false, true, true);
         p.read().add(null);
@@ -349,7 +349,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testInheritedPermission0() {
+    void testInheritedPermission0() {
         final Foo386 foo = new Foo386();
         final JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(SomeInterface.class.getName(), true, true, true, 
true);
@@ -359,7 +359,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testInheritedPermission1() {
+    void testInheritedPermission1() {
         final Quux386 foo = new Quux386();
         final JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(Foo386.class.getName(), true, true, true, true);
@@ -369,7 +369,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testMethodAllow() throws Exception {
+    void testMethodAllow() {
         final Foo foo = new Foo("42");
         final String expr = "foo.Quux()";
         JexlScript script;
@@ -385,7 +385,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testMethodBlock() throws Exception {
+    void testMethodBlock() {
         final String expr = "foo.Quux()";
         final JexlScript script = JEXL.createScript(expr, "foo");
         final Foo foo = new Foo("42");
@@ -402,7 +402,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testMethodNoJexl() throws Exception {
+    void testMethodNoJexl() {
         final Foo foo = new Foo("42");
         // @formatter:off
         final String[] exprs = {
@@ -421,7 +421,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testNoJexl312() throws Exception {
+    void testNoJexl312() {
         final JexlContext ctxt = new MapContext();
         final JexlEngine sjexl = new 
JexlBuilder().safe(false).strict(true).create();
         final JexlScript foo = sjexl.createScript("x.getFoo()", "x");
@@ -429,7 +429,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testNonInheritedPermission0() {
+    void testNonInheritedPermission0() {
         final Foo386 foo = new Foo386();
         final JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(SomeInterface.class.getName(), false, true, true, 
true);
@@ -439,7 +439,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testNonInheritedPermission1() {
+    void testNonInheritedPermission1() {
         final Quux386 foo = new Quux386();
         final JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(Foo386.class.getName(), false, true, true, true);
@@ -449,11 +449,11 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testRestrict() throws Exception {
+    void testRestrict() {
         final JexlContext context = new MapContext();
         context.set("System", System.class);
         final JexlSandbox sandbox = new JexlSandbox();
-        // only allow call to currentTimeMillis (avoid exit, gc, loadLibrary, 
etc)
+        // only allow call to currentTimeMillis (avoid exit, gc, loadLibrary, 
etc.)
         sandbox.allow(System.class.getName()).execute("currentTimeMillis");
         // can not create a new file
         sandbox.block(java.io.File.class.getName()).execute("");
@@ -471,7 +471,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testSandboxInherit0() throws Exception {
+    void testSandboxInherit0() {
         Object result;
         final JexlContext ctxt = null;
         final List<String> foo = new ArrayList<>();
@@ -485,20 +485,16 @@ public class SandboxTest extends JexlTestCase {
 
         result = method.execute(ctxt, foo, "nothing");
         assertTrue((boolean) result);
-        result = null;
         result = get.execute(null, foo, Integer.valueOf(0));
         assertEquals("nothing", result);
-        result = null;
         result = set.execute(null, foo, Integer.valueOf(0), "42");
         assertEquals("42", result);
-
-        result = null;
         result = get.execute(null, foo, Integer.valueOf(0));
         assertEquals("42", result);
     }
 
     @Test
-    public void testSandboxInherit1() throws Exception {
+    void testSandboxInherit1() {
         Object result;
         final JexlContext ctxt = null;
         final Operation2 foo = new Operation2(12);
@@ -515,7 +511,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testSetAllow() throws Exception {
+    void testSetAllow() {
         final Foo foo = new Foo("42");
         final String expr = "foo.alias = $0";
         JexlScript script;
@@ -532,7 +528,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testSetBlock() throws Exception {
+    void testSetBlock() {
         final String expr = "foo.alias = $0";
         final JexlScript script1 = JEXL.createScript(expr, "foo", "$0");
         final Foo foo = new Foo("42");
@@ -550,7 +546,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testSetNullKeyAllowed0() throws Exception {
+    void testSetNullKeyAllowed0() {
         final Arithmetic350 a350 = new Arithmetic350(true);
         final JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(new 
JexlSandbox(true)).create();
         final JexlContext jc = new MapContext();
@@ -561,7 +557,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testSetNullKeyAllowed1() throws Exception {
+    void testSetNullKeyAllowed1() {
         final Arithmetic350 a350 = new Arithmetic350(true);
         final JexlSandbox sandbox = new JexlSandbox(true, true);
         final JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", 
true, false, true);
@@ -584,7 +580,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testSetNullKeyBlocked() throws Exception {
+    void testSetNullKeyBlocked() {
         final Arithmetic350 a350 = new Arithmetic350(true);
         final JexlSandbox sandbox = new JexlSandbox(true, true);
         final JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", 
true, false, true);
@@ -608,14 +604,14 @@ public class SandboxTest extends JexlTestCase {
     static class B extends A{}
 
     @Test
-    public void testPermission0() {
+    void testPermission0() {
         JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(I.class.getName(), true, true, true, false);
         System.out.println("permission A=" + 
sandbox.get(A.class.getName()).write());
         System.out.println("permission B=" + 
sandbox.get(B.class.getName()).write());
     }
     @Test
-    public void testPermission1() {
+    void testPermission1() {
         JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(I.class.getName(), true, true, true, false);
         System.out.println("permission B=" + 
sandbox.get(B.class.getName()).write());
@@ -623,7 +619,7 @@ public class SandboxTest extends JexlTestCase {
     }
 
     @Test
-    public void testIssue424() {
+    void testIssue424() {
         JexlSandbox sandbox = new JexlSandbox(false, true);
         sandbox.permissions(Map.class.getName(), true, true, true, true);
         String jexlCode = "x.foo = 'bar'";
diff --git 
a/src/test/java/org/apache/commons/jexl3/jexl342/ReferenceUberspect.java 
b/src/test/java/org/apache/commons/jexl3/jexl342/ReferenceUberspect.java
index ef7ee754..9c25ed58 100644
--- a/src/test/java/org/apache/commons/jexl3/jexl342/ReferenceUberspect.java
+++ b/src/test/java/org/apache/commons/jexl3/jexl342/ReferenceUberspect.java
@@ -37,7 +37,7 @@ import org.apache.commons.jexl3.introspection.JexlUberspect;
 
 /**
  * An Uberspect that handles references (java.lang.ref.Reference) and 
optionals (java.util.Optional).
- * <p>This illustrates JEXL&quote;s low level customization capabilities.</p>
+ * <p>This illustrates JEXL&quot;s low level customization capabilities.</p>
  * see JEXL-342.
  */
 public class ReferenceUberspect implements JexlUberspect {

Reply via email to