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 aecb3934 JEXL-425, JEXL-426 : added tests; - moved tests to 
appropriate test classes; - ensure 3.4 compatibility through flags, remediation 
example with new semantics;
aecb3934 is described below

commit aecb3934d3702d60c456750051374ca756f20826
Author: Henrib <hbies...@gmail.com>
AuthorDate: Sat Aug 31 17:12:20 2024 +0200

    JEXL-425, JEXL-426 : added tests;
    - moved tests to appropriate test classes;
    - ensure 3.4 compatibility through flags, remediation example with new 
semantics;
---
 .../org/apache/commons/jexl3/JexlArithmetic.java   |  32 +++
 .../apache/commons/jexl3/internal/Interpreter.java |   2 +-
 .../jexl3/internal/introspection/Uberspect.java    |  30 +--
 .../commons/jexl3/parser/ASTIdentifierAccess.java  |  36 +--
 .../org/apache/commons/jexl3/ArithmeticTest.java   |  62 +++++
 .../java/org/apache/commons/jexl3/BuilderTest.java |  10 +-
 .../org/apache/commons/jexl3/Issues400Test.java    | 128 ----------
 .../java/org/apache/commons/jexl3/JXLTTest.java    | 273 +++++++++++++++------
 .../java/org/apache/commons/jexl3/LambdaTest.java  |  62 +++++
 9 files changed, 386 insertions(+), 249 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java 
b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 76d1a7d7..ee09ebb7 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -1715,6 +1715,38 @@ public class JexlArithmetic {
         throw new CoercionException("Long coercion: ("+ arg +")");
     }
 
+    /**
+     * Parse an identifier which must be of the form:
+     * 0|([1-9][0-9]*)
+     * @param id the identifier
+     * @return an integer or null
+     */
+    public static Integer parseIdentifier(final Object id) {
+        if (id instanceof Number) {
+            return ((Number) id).intValue();
+        }
+        // hand coded because the was no way to fail on leading '0's using 
NumberFormat
+        if (id instanceof CharSequence) {
+            final CharSequence str = (CharSequence) id;
+            final int length = str.length();
+            // can not be empty string and can not be longer than 
Integer.MAX_VALUE representation
+            if (length > 0 && length <= 10) {
+                int val = 0;
+                for (int i = 0; i < length; ++i) {
+                    final char c = str.charAt(i);
+                    // leading 0s but no just 0, numeric only
+                    if ((c == '0' && val == 0 && length > 1) || (c < '0' || c 
> '9')) {
+                        return null;
+                    }
+                    val *= 10;
+                    val += c - '0';
+                }
+                return val;
+            }
+        }
+        return null;
+    }
+
     /**
      * Positivize value (unary plus for numbers).
      * <p>C/C++/C#/Java perform integral promotion of the operand, ie
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 3479c92a..6096ff86 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -493,7 +493,7 @@ public class Interpreter extends InterpreterBase {
                 if (options.isStrictInterpolation()) {
                     return inter;
                 }
-                final Integer id = ASTIdentifierAccess.parseIdentifier(inter);
+                final Integer id = JexlArithmetic.parseIdentifier(inter);
                 return id != null ? id : eval;
             }
         }
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java 
b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
index bdbfb373..7608e738 100644
--- 
a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
+++ 
b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
@@ -331,7 +331,7 @@ public class Uberspect implements JexlUberspect {
     public JexlPropertyGet getPropertyGet(
             final List<PropertyResolver> resolvers, final Object obj, final 
Object identifier
     ) {
-        final Class<?> claz = obj.getClass();
+        final Class<?> clazz = obj.getClass();
         final String property = AbstractExecutor.castString(identifier);
         final Introspector is = base();
         final List<PropertyResolver> r = resolvers == null ? 
strategy.apply(null, obj) : resolvers;
@@ -341,33 +341,33 @@ public class Uberspect implements JexlUberspect {
                 switch ((JexlResolver) resolver) {
                     case PROPERTY:
                         // first try for a getFoo() type of property (also 
getfoo() )
-                        executor = PropertyGetExecutor.discover(is, claz, 
property);
+                        executor = PropertyGetExecutor.discover(is, clazz, 
property);
                         if (executor == null) {
-                            executor = BooleanGetExecutor.discover(is, claz, 
property);
+                            executor = BooleanGetExecutor.discover(is, clazz, 
property);
                         }
                         break;
                     case MAP:
                         // let's see if we are a map...
-                        executor = MapGetExecutor.discover(is, claz, 
identifier);
+                        executor = MapGetExecutor.discover(is, clazz, 
identifier);
                         break;
                     case LIST:
                         // let's see if this is a list or array
                         final Integer index = 
AbstractExecutor.castInteger(identifier);
                         if (index != null) {
-                            executor = ListGetExecutor.discover(is, claz, 
index);
+                            executor = ListGetExecutor.discover(is, clazz, 
index);
                         }
                         break;
                     case DUCK:
                         // if that didn't work, look for get(foo)
-                        executor = DuckGetExecutor.discover(is, claz, 
identifier);
+                        executor = DuckGetExecutor.discover(is, clazz, 
identifier);
                         if (executor == null && property != null && property 
!= identifier) {
                             // look for get("foo") if we did not try yet (just 
above)
-                            executor = DuckGetExecutor.discover(is, claz, 
property);
+                            executor = DuckGetExecutor.discover(is, clazz, 
property);
                         }
                         break;
                     case FIELD:
                         // a field may be? (can not be a number)
-                        executor = FieldGetExecutor.discover(is, claz, 
property);
+                        executor = FieldGetExecutor.discover(is, clazz, 
property);
                         // static class fields (enums included)
                         if (obj instanceof Class<?>) {
                             executor = FieldGetExecutor.discover(is, 
(Class<?>) obj, property);
@@ -399,7 +399,7 @@ public class Uberspect implements JexlUberspect {
     public JexlPropertySet getPropertySet(
             final List<PropertyResolver> resolvers, final Object obj, final 
Object identifier, final Object arg
     ) {
-        final Class<?> claz = obj.getClass();
+        final Class<?> clazz = obj.getClass();
         final String property = AbstractExecutor.castString(identifier);
         final Introspector is = base();
         final List<PropertyResolver> actual = resolvers == null ? 
strategy.apply(null, obj) : resolvers;
@@ -409,30 +409,30 @@ public class Uberspect implements JexlUberspect {
                 switch ((JexlResolver) resolver) {
                     case PROPERTY:
                         // first try for a setFoo() type of property (also 
setfoo() )
-                        executor = PropertySetExecutor.discover(is, claz, 
property, arg);
+                        executor = PropertySetExecutor.discover(is, clazz, 
property, arg);
                         break;
                     case MAP:
                         // let's see if we are a map...
-                        executor = MapSetExecutor.discover(is, claz, 
identifier, arg);
+                        executor = MapSetExecutor.discover(is, clazz, 
identifier, arg);
                         break;
                     case LIST:
                         // let's see if we can convert the identifier to an 
int,
                         // if obj is an array or a list, we can still do 
something
                         final Integer index = 
AbstractExecutor.castInteger(identifier);
                         if (index != null) {
-                            executor = ListSetExecutor.discover(is, claz, 
identifier, arg);
+                            executor = ListSetExecutor.discover(is, clazz, 
identifier, arg);
                         }
                         break;
                     case DUCK:
                         // if that didn't work, look for set(foo)
-                        executor = DuckSetExecutor.discover(is, claz, 
identifier, arg);
+                        executor = DuckSetExecutor.discover(is, clazz, 
identifier, arg);
                         if (executor == null && property != null && property 
!= identifier) {
-                            executor = DuckSetExecutor.discover(is, claz, 
property, arg);
+                            executor = DuckSetExecutor.discover(is, clazz, 
property, arg);
                         }
                         break;
                     case FIELD:
                         // a field may be?
-                        executor = FieldSetExecutor.discover(is, claz, 
property, arg);
+                        executor = FieldSetExecutor.discover(is, clazz, 
property, arg);
                         break;
                     case CONTAINER:
                     default:
diff --git 
a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java
index 7943996c..893471ee 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTIdentifierAccess.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.jexl3.parser;
 
+import org.apache.commons.jexl3.JexlArithmetic;
+
 /**
  * Identifiers, variables and registers.
  */
@@ -23,38 +25,6 @@ public class ASTIdentifierAccess extends JexlNode {
     /**
      */
     private static final long serialVersionUID = 1L;
-    /**
-     * Parse an identifier which must be of the form:
-     * 0|([1-9][0-9]*)
-     * @param id the identifier
-     * @return an integer or null
-     */
-    public static Integer parseIdentifier(final String id) {
-        // hand coded because the was no way to fail on leading '0's using 
NumberFormat
-        if (id != null) {
-            final int length = id.length();
-            int val = 0;
-            for (int i = 0; i < length; ++i) {
-                final char c = id.charAt(i);
-                // leading 0s but no just 0, NaN
-                if (c == '0') {
-                    if (length == 1) {
-                        return 0;
-                    }
-                    if (val == 0) {
-                        return null;
-                    }
-                } // any non numeric, NaN
-                else if (c < '0' || c > '9') {
-                    return null;
-                }
-                val *= 10;
-                val += c - '0';
-            }
-            return val;
-        }
-        return null;
-    }
 
     private String name;
     private Integer identifier;
@@ -99,7 +69,7 @@ public class ASTIdentifierAccess extends JexlNode {
 
     void setIdentifier(final String id) {
         name = id;
-        identifier = parseIdentifier(id);
+        identifier = JexlArithmetic.parseIdentifier(id);
     }
 
     @Override
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java 
b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 6d5b9059..484041fc 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -31,6 +31,7 @@ import java.math.BigInteger;
 import java.math.MathContext;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -2254,4 +2255,65 @@ public class ArithmeticTest extends JexlTestCase {
             assertTrue(getJavaVersion() > 11);
         }
     }
+
+    @Test void testShortCircuitAnd() {
+        String src = "(x, y, z) -> x && y && z";
+        final JexlBuilder builder = new JexlBuilder();
+        final JexlEngine jexl = builder.create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src);
+        result = script.execute(null, true, "foo", 42);
+        assertEquals(42, result);
+        result = script.execute(null, true, "", 42);
+        assertEquals("", result);
+    }
+
+    @Test void testShortCircuitOr() {
+        OptContext optc = new OptContext();
+        String src = "(x, y, z) -> x || y || z";
+        final JexlBuilder builder = new JexlBuilder();
+        final JexlEngine jexl = builder.create();
+        JexlOptions options = builder.options();
+        optc.setOptions(options);
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src);
+        result = script.execute(optc, 0, "", 42);
+        assertEquals(42, result);
+        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 testLogicalValue() {
+        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/BuilderTest.java 
b/src/test/java/org/apache/commons/jexl3/BuilderTest.java
index 465b584e..9f0bd222 100644
--- a/src/test/java/org/apache/commons/jexl3/BuilderTest.java
+++ b/src/test/java/org/apache/commons/jexl3/BuilderTest.java
@@ -37,7 +37,7 @@ public class BuilderTest {
     }
 
     @Test
-    public void testFlags() {
+    void testFlags() {
         assertTrue(builder().antish(true).antish());
         assertFalse(builder().antish(false).antish());
         assertTrue(builder().cancellable(true).cancellable());
@@ -54,10 +54,14 @@ public class BuilderTest {
         assertFalse(builder().silent(false).silent());
         assertTrue(builder().strict(true).strict());
         assertFalse(builder().strict(false).strict());
+        
assertTrue(builder().booleanLogical(true).options().isBooleanLogical());
+        
assertFalse(builder().booleanLogical(false).options().isBooleanLogical());
+        
assertTrue(builder().strictInterpolation(true).options().isStrictInterpolation());
+        
assertFalse(builder().strictInterpolation(false).options().isStrictInterpolation());
     }
 
     @Test
-    public void testOther() {
+    void testOther() {
         final ClassLoader cls = getClass().getClassLoader().getParent();
         assertEquals(cls, builder().loader(cls).loader());
         final Charset cs = StandardCharsets.UTF_16;
@@ -71,7 +75,7 @@ public class BuilderTest {
     }
 
     @Test
-    public void testValues() {
+    void testValues() {
         assertEquals(1, builder().collectMode(1).collectMode());
         assertEquals(0, builder().collectMode(0).collectMode());
         assertEquals(32, builder().cacheThreshold(32).cacheThreshold());
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 8ec230e4..df141753 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -482,132 +482,4 @@ public class Issues400Test {
         assertEquals(9, m[1].get("type"));
     }
 
-    @Test void test425() {
-        final JexlBuilder builder = new 
JexlBuilder().strictInterpolation(true);
-        final JexlEngine jexl = builder.create();
-        JexlScript script;
-        Object result;
-        script = jexl.createScript("let x = 42; let y = `${x}`; y");
-        result = script.execute(null);
-        assertTrue(result instanceof String);
-        assertEquals("42", result);
-    }
-
-    @Test void test426a() {
-        String src = "let x = 10;\n" +
-                "let foo = () -> {\n" +
-                "x += 2;\n" +
-                "}\n" +
-                "x = 40;\n" +
-                "foo();\n" +
-                "x";
-        final JexlBuilder builder = new JexlBuilder().features(new 
JexlFeatures().constCapture(false).referenceCapture(true));
-        final JexlEngine jexl = builder.create();
-        JexlScript script;
-        Object result;
-        script = jexl.createScript(src);
-        result = script.execute(null);
-        assertEquals(42, result);
-    }
-
-    @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();
-        JexlScript script;
-        Object result;
-        script = jexl.createScript(src);
-        result = script.execute(null);
-        assertEquals(42, result);
-    }
-
-    @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();
-        JexlScript script;
-        Object result;
-        script = jexl.createScript(src);
-        result = script.execute(null);
-        assertTrue(result instanceof JexlScript);
-        script = jexl.createScript("f()", "f");
-        result = script.execute(null, result);
-        assertEquals(42, result);
-    }
-
-    @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();
-        JexlScript script;
-        Object result;
-        script = jexl.createScript(src);
-        result = script.execute(null);
-        assertTrue(result instanceof JexlScript);
-        script = jexl.createScript("f()", "f");
-        result = script.execute(null, result);
-        assertEquals(142, result);
-    }
-
-
-    @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();
-        JexlScript script;
-        Object result;
-        script = jexl.createScript(src);
-        result = script.execute(null, true, "foo", 42);
-        assertEquals(42, result);
-        result = script.execute(null, true, "", 42);
-        assertEquals("", result);
-    }
-
-    @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(optc, 0, "", 42);
-        assertEquals(42, result);
-        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/JXLTTest.java 
b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index 889f6a8b..39937ee6 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -30,9 +30,12 @@ import java.io.StringReader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.commons.jexl3.internal.Debugger;
@@ -44,6 +47,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 
@@ -138,14 +142,14 @@ public class JXLTTest extends JexlTestCase {
         }
     };
 
-       public static List<JexlBuilder> engines() {
-           final JexlFeatures f = new JexlFeatures();
-           f.lexical(true).lexicalShade(true);
-          return Arrays.<JexlBuilder>asList(
-                  new 
JexlBuilder().silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
-                  new 
JexlBuilder().features(f).silent(false).cache(128).strict(true),
-                  new JexlBuilder().silent(false).cache(128).strict(true));
-       }
+   public static List<JexlBuilder> engines() {
+       final JexlFeatures f = new JexlFeatures();
+       f.lexical(true).lexicalShade(true);
+      return Arrays.asList(
+              new 
JexlBuilder().silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
+              new 
JexlBuilder().features(f).silent(false).cache(128).strict(true),
+              new JexlBuilder().silent(false).cache(128).strict(true));
+   }
 
    private static String refactor(final TemplateDebugger td, final 
JxltEngine.Template ts) {
     final boolean dbg = td.debug(ts);
@@ -210,7 +214,7 @@ public class JXLTTest extends JexlTestCase {
 
     @BeforeEach
     @Override
-    public void setUp() throws Exception {
+    public void setUp() {
         // ensure jul logging is only error
         
java.util.logging.Logger.getLogger(org.apache.commons.jexl3.JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
         context = new JexlEvalContext(vars);
@@ -225,7 +229,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311a(final JexlBuilder builder) throws Exception {
+    void test311a(final JexlBuilder builder) {
         init(builder);
         final JexlContext ctx = null;
         // @formatter:off
@@ -243,7 +247,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311b(final JexlBuilder builder) throws Exception {
+    void test311b(final JexlBuilder builder) {
         init(builder);
         final JexlContext ctx311 = new Context311();
         // @formatter:off
@@ -261,7 +265,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311c(final JexlBuilder builder) throws Exception {
+    void test311c(final JexlBuilder builder) {
         init(builder);
         final Context311 ctx311 = new Context311();
         ctx311.newOptions().setLexical(true);
@@ -280,7 +284,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311d(final JexlBuilder builder) throws Exception {
+    void test311d(final JexlBuilder builder) {
         init(builder);
         final Context311 ctx311 = new Context311();
         ctx311.newOptions().setLexical(true);
@@ -299,7 +303,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311e(final JexlBuilder builder) throws Exception {
+    void test311e(final JexlBuilder builder) {
         init(builder);
         final Context311 ctx311 = new Context311();
         ctx311.newOptions().setLexical(true);
@@ -316,7 +320,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311f(final JexlBuilder builder) throws Exception {
+    void test311f(final JexlBuilder builder) {
         init(builder);
         final Context311 ctx311 = new Context311();
         ctx311.newOptions().setLexical(true);
@@ -333,7 +337,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311g(final JexlBuilder builder) throws Exception {
+    void test311g(final JexlBuilder builder) {
         init(builder);
         final Context311 ctx311 = new Context311();
         ctx311.newOptions().setLexical(true);
@@ -350,7 +354,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311h(final JexlBuilder builder) throws Exception {
+    void test311h(final JexlBuilder builder) {
         init(builder);
         final Context311 ctx311 = new Context311();
         ctx311.newOptions().setLexical(true);
@@ -362,7 +366,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test311i(final JexlBuilder builder) throws Exception {
+    void test311i(final JexlBuilder builder) {
         init(builder);
         final JexlContext ctx311 = new Context311();
         // @formatter:off
@@ -380,7 +384,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test315(final JexlBuilder builder) throws Exception {
+    void test315(final JexlBuilder builder) {
         init(builder);
         String s315;
         StringWriter strw;
@@ -411,7 +415,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void test42(final JexlBuilder builder) throws Exception {
+    void test42(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String test42
@@ -449,9 +453,141 @@ public class JXLTTest extends JexlTestCase {
         assertEquals(test42, refactored);
     }
 
+
+    @Test
+    void testParseIdentifier() {
+        assertNull(JexlArithmetic.parseIdentifier(null));
+        assertNull(JexlArithmetic.parseIdentifier(""));
+        assertNull(JexlArithmetic.parseIdentifier("za"));
+        assertNull(JexlArithmetic.parseIdentifier("a"));
+        assertNull(JexlArithmetic.parseIdentifier("00"));
+        assertNull(JexlArithmetic.parseIdentifier("01"));
+        assertNull(JexlArithmetic.parseIdentifier("001"));
+        assertNull(JexlArithmetic.parseIdentifier("12345678901"));
+
+        assertEquals(0, JexlArithmetic.parseIdentifier("0"));
+        assertEquals(10, JexlArithmetic.parseIdentifier("10"));
+        assertEquals(100, JexlArithmetic.parseIdentifier("100"));
+        assertEquals(42, JexlArithmetic.parseIdentifier("42"));
+        assertEquals(42000, JexlArithmetic.parseIdentifier("42000"));
+
+        assertEquals(42, JexlArithmetic.parseIdentifier(42));
+    }
+
+    /**
+     * A remediation to strict interpolation that consistently attempts to 
coerce to integer
+     * index for lists and keys for maps. (see JEXL-425)
+     */
+    public static class Arithmetic425 extends JexlArithmetic {
+        public Arithmetic425(boolean astrict) {
+            super(astrict);
+        }
+
+        public Object propertyGet(List list, String property) {
+            Integer id = JexlArithmetic.parseIdentifier(property);
+            return id == null? JexlEngine.TRY_FAILED : list.get(id);
+        }
+
+        public Object propertySet(List list, String property, Object value) {
+            Integer id = JexlArithmetic.parseIdentifier(property);
+            if (id != null) {
+                return list.set(id, value);
+            }
+            return JexlEngine.TRY_FAILED;
+        }
+
+        public Object propertyGet(Map list, String property) {
+            // determine if keys are integers by performing a check on first 
one
+            if (list.keySet().stream().findFirst().orElse(null) instanceof 
Number) {
+                Integer id = JexlArithmetic.parseIdentifier(property);
+                if (id != null) {
+                    return list.get(id);
+                }
+            }
+            return JexlEngine.TRY_FAILED;
+        }
+
+        public Object propertySet(Map list, String property, Object value) {
+            // determine if keys are integers by performing a check on first 
one
+            if (list.keySet().stream().findFirst().orElse(null) instanceof 
Number) {
+                Integer id = JexlArithmetic.parseIdentifier(property);
+                if (id != null) {
+                    list.put(id, value);
+                    return value;
+                }
+            }
+            return JexlEngine.TRY_FAILED;
+        }
+    }
+
+    @Test void test425a() {
+        final String S42 = "fourty-two";
+        final JexlBuilder builder = new 
JexlBuilder().strictInterpolation(true);
+        final JexlEngine jexl = builder.create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript("let x = 42; let y = `${x}`; y");
+        result = script.execute(null);
+        assertTrue(result instanceof String);
+        assertEquals("42", result);
+        Map<Object,Object> map = Collections.singletonMap("42", S42);
+
+        script = jexl.createScript("let x = 42; map.`${x}`", "map");
+        result = script.execute(null, map);
+        assertEquals(S42, result);
+        List<String> list = Collections.singletonList(S42);
+
+        JexlScript finalScript = script;
+        assertThrows(JexlException.Property.class, () -> 
finalScript.execute(null, list));
+        script = jexl.createScript("let x = 0; list[x]", "list");
+        assertEquals(S42, result);
+    }
+
+    @Test void test425b() {
+        final String S42 = "fourty-two";
+        final JexlEngine jexl = new 
JexlBuilder().strictInterpolation(false).create();
+        run425bc(jexl, false);
+        run425bc(jexl, false);
+    }
+
+    @Test void test425c() {
+        JexlEngine jexl = new JexlBuilder()
+                .cache(8)
+                .arithmetic(new Arithmetic425(true))
+                .strictInterpolation(true).create();
+        run425bc(jexl, true);
+        run425bc(jexl, true);
+    }
+
+    void run425bc(JexlEngine jexl, boolean strictInterpolation) {
+        final String S42 = "fourty-two";
+        JexlScript script;
+        Object result;
+        script = jexl.createScript("let x = 42; let y = `${x}`; y");
+        result = script.execute(null);
+        assertEquals(!strictInterpolation? 42 : "42", result);
+        Map<Object,Object> map = Collections.singletonMap(0, S42);
+        script = jexl.createScript("let x = 0; map.`${x}`", "map");
+        result = script.execute(null, map);
+        assertEquals(S42, result);
+        List<String> list = Collections.singletonList(S42);
+        result = script.execute(null, list);
+        assertEquals(S42, result);
+
+        map = new HashMap<>(map);
+        map.put(0, "nothing");
+        script = jexl.createScript("let x = 0; map.`${x}` = S42", "map", 
"S42");
+        result = script.execute(null, map, S42);
+        assertEquals(S42, result);
+        list = new ArrayList<>(list);
+        list.set(0, "nothing");
+        result = script.execute(null, map, S42);
+        assertEquals(S42, result);
+    }
+
     @ParameterizedTest
     @MethodSource("engines")
-    public void testAssign(final JexlBuilder builder) throws Exception {
+    void testAssign(final JexlBuilder builder) {
         init(builder);
         final Froboz froboz = new Froboz(32);
         context.set("froboz", froboz);
@@ -465,7 +601,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testBadContextNested(final JexlBuilder builder) throws 
Exception {
+    void testBadContextNested(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Expression expr = 
JXLT.createExpression("#{${hi}+'.world'}");
         final JexlContext none = null;
@@ -475,7 +611,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testCharAtBug(final JexlBuilder builder) throws Exception {
+    void testCharAtBug(final JexlBuilder builder) {
         init(builder);
         context.set("foo", "abcdef");
         final JexlOptions options = context.getEngineOptions();
@@ -492,12 +628,11 @@ public class JXLTTest extends JexlTestCase {
         } finally {
             options.setSilent(false);
         }
-
     }
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testCommentedTemplate0(final JexlBuilder builder) throws 
Exception {
+    void testCommentedTemplate0(final JexlBuilder builder) {
         init(builder);
         final JexlContext ctxt = new MapContext();
         final JexlEngine jexl = new JexlBuilder().create();
@@ -516,7 +651,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testCommentedTemplate1(final JexlBuilder builder) throws 
Exception {
+    void testCommentedTemplate1(final JexlBuilder builder) {
         init(builder);
         final JexlContext ctxt = new MapContext();
         final JexlEngine jexl = new JexlBuilder().create();
@@ -540,7 +675,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testComposite(final JexlBuilder builder) throws Exception {
+    void testComposite(final JexlBuilder builder) {
         init(builder);
         final String source = "Dear ${p} ${name};";
         final JxltEngine.Expression expr = JXLT.createExpression(source);
@@ -558,7 +693,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testConstant0(final JexlBuilder builder) throws Exception {
+    void testConstant0(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         final String source = "Hello World!";
@@ -573,7 +708,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testConstant2(final JexlBuilder builder) throws Exception {
+    void testConstant2(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         final String source = "${size({'map':123,'map2':456})}";
@@ -588,7 +723,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testConstant3(final JexlBuilder builder) throws Exception {
+    void testConstant3(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         final String source = "#{size({'map':123,'map2':456})}";
@@ -603,7 +738,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testConstant4(final JexlBuilder builder) throws Exception {
+    void testConstant4(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         final String source = "#{ ${size({'1':2,'2': 3})} }";
@@ -618,7 +753,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testConstantTemplate(final JexlBuilder builder) {
+    void testConstantTemplate(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String src = "<script>\n" +
@@ -642,7 +777,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testDbgEscapes(final JexlBuilder builder) throws Exception {
+    void testDbgEscapes(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String[] srcs = {
@@ -664,7 +799,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testDeferred(final JexlBuilder builder) throws Exception {
+    void testDeferred(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         final String source = "#{'world'}";
@@ -680,7 +815,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testEscape(final JexlBuilder builder) throws Exception {
+    void testEscape(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         JxltEngine.Expression expr;
@@ -696,7 +831,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testEscapeString(final JexlBuilder builder) throws Exception {
+    void testEscapeString(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Expression expr = 
JXLT.createExpression("\\\"${'world\\'s finest'}\\\"");
         final JexlContext none = null;
@@ -706,7 +841,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testImmediate(final JexlBuilder builder) throws Exception {
+    void testImmediate(final JexlBuilder builder) {
         init(builder);
         final JexlContext none = null;
         final String source = "${'Hello ' + 'World!'}";
@@ -722,7 +857,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testImmediateTemplate(final JexlBuilder builder) throws 
Exception {
+    void testImmediateTemplate(final JexlBuilder builder) {
         init(builder);
         context.set("tables", new String[]{"table1", "table2"});
         context.set("w" ,"x=1");
@@ -745,7 +880,7 @@ public class JXLTTest extends JexlTestCase {
     }
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInheritedDebugger(final JexlBuilder builder) throws 
Exception {
+    void testInheritedDebugger(final JexlBuilder builder) {
         init(builder);
         final String src = "if ($A) { $B + 1; } else { $C - 2 }";
         final JexlEngine jexl = JXLT.getEngine();
@@ -763,7 +898,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInterpolation(final JexlBuilder builder) throws Exception {
+    void testInterpolation(final JexlBuilder builder) {
         init(builder);
         final String expr =  "`Hello \n${user}`";
         final JexlScript script = ENGINE.createScript(expr);
@@ -777,7 +912,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInterpolationGlobal(final JexlBuilder builder) throws 
Exception {
+    void testInterpolationGlobal(final JexlBuilder builder) {
         init(builder);
         if (isLexicalShade()) {
             context.set("user", null);
@@ -789,7 +924,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInterpolationLocal(final JexlBuilder builder) throws 
Exception {
+    void testInterpolationLocal(final JexlBuilder builder) {
         init(builder);
         final String expr =  "var user='Henrib'; `Hello \n${user}`";
         final Object value = ENGINE.createScript(expr).execute(context);
@@ -798,7 +933,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInterpolationLvsG(final JexlBuilder builder) throws 
Exception {
+    void testInterpolationLvsG(final JexlBuilder builder) {
         init(builder);
         if (isLexicalShade()) {
             context.set("user", null);
@@ -810,7 +945,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInterpolationLvsG2(final JexlBuilder builder) throws 
Exception {
+    void testInterpolationLvsG2(final JexlBuilder builder) {
         init(builder);
         if (isLexicalShade()) {
             context.set("user", null);
@@ -822,7 +957,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testInterpolationParameter(final JexlBuilder builder) throws 
Exception {
+    void testInterpolationParameter(final JexlBuilder builder) {
         init(builder);
         final String expr =  "(user)->{`Hello \n${user}`}";
         final JexlScript script = ENGINE.createScript(expr);
@@ -834,7 +969,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testLexicalTemplate(final JexlBuilder builder) throws 
Exception {
+    void testLexicalTemplate(final JexlBuilder builder) {
         init(builder);
         final JexlOptions opts = new JexlOptions();
         final JexlContext ctxt = new PragmaticContext(opts);
@@ -872,7 +1007,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testMalformed(final JexlBuilder builder) throws Exception {
+    void testMalformed(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Exception xjexl = 
assertThrows(JxltEngine.Exception.class, () -> 
JXLT.createExpression("${'world'"), "should be malformed");
         LOGGER.debug(xjexl.getMessage());
@@ -880,7 +1015,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testMalformedNested(final JexlBuilder builder) throws 
Exception {
+    void testMalformedNested(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Exception xjexl = 
assertThrows(JxltEngine.Exception.class, () -> JXLT.createExpression("#{${hi} 
world}"), "should be malformed");
         LOGGER.debug(xjexl.getMessage());
@@ -888,7 +1023,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testMalformedNested2(final JexlBuilder builder) throws 
Exception {
+    void testMalformedNested2(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Exception xjexl = 
assertThrows(JxltEngine.Exception.class, () -> JXLT.createExpression("#{${hi} 
world}"), "should be malformed");
         LOGGER.debug(xjexl.getMessage());
@@ -896,7 +1031,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testNested(final JexlBuilder builder) throws Exception {
+    void testNested(final JexlBuilder builder) {
         init(builder);
         final String source = "#{${hi}+'.world'}";
         final JxltEngine.Expression expr = JXLT.createExpression(source);
@@ -916,7 +1051,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testNestedTemplate(final JexlBuilder builder) throws Exception 
{
+    void testNestedTemplate(final JexlBuilder builder) {
         init(builder);
         final String source = "#{${hi}+'.world'}";
         final JxltEngine.Template expr = JXLT.createTemplate(source, "hi");
@@ -932,7 +1067,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testNonEscapeString(final JexlBuilder builder) throws 
Exception {
+    void testNonEscapeString(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Expression expr = 
JXLT.createExpression("c:\\some\\windows\\path");
         final JexlContext none = null;
@@ -942,7 +1077,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testOneLiner(final JexlBuilder builder) throws Exception {
+    void testOneLiner(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Template t = JXLT.createTemplate("$$", new 
StringReader("fourty-two"));
         final StringWriter strw = new StringWriter();
@@ -953,7 +1088,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testOneLinerVar(final JexlBuilder builder) throws Exception {
+    void testOneLinerVar(final JexlBuilder builder) {
         init(builder);
         final JxltEngine.Template t = JXLT.createTemplate("$$", new 
StringReader("fourty-${x}"));
         final StringWriter strw = new StringWriter();
@@ -965,7 +1100,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testPrepareEvaluate(final JexlBuilder builder) throws 
Exception {
+    void testPrepareEvaluate(final JexlBuilder builder) {
         init(builder);
         final String source = "Dear #{p} ${name};";
         final JxltEngine.Expression expr = JXLT.createExpression("Dear #{p} 
${name};");
@@ -994,7 +1129,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testPrepareTemplate(final JexlBuilder builder) throws 
Exception {
+    void testPrepareTemplate(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String source
@@ -1029,7 +1164,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testReport(final JexlBuilder builder) throws Exception {
+    void testReport(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String rpt
@@ -1058,7 +1193,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testReport1(final JexlBuilder builder) throws Exception {
+    void testReport1(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String rpt
@@ -1090,7 +1225,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testReport2(final JexlBuilder builder) throws Exception {
+    void testReport2(final JexlBuilder builder) {
         init(builder);
         // @formatter:off
         final String rpt
@@ -1126,7 +1261,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testSanboxed311i(final JexlBuilder builder) throws Exception {
+    void testSanboxed311i(final JexlBuilder builder) {
         init(builder);
         /// this uberspect can not access jexl3 classes (besides test)
         final Uberspect uberspect = new 
Uberspect(LogFactory.getLog(JXLTTest.class), null, NOJEXL3);
@@ -1149,7 +1284,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testSanboxedTemplate(final JexlBuilder builder) throws 
Exception {
+    void testSanboxedTemplate(final JexlBuilder builder) {
         init(builder);
         final String src = "Hello ${user}";
         final JexlContext ctxt = new MapContext();
@@ -1171,7 +1306,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testStatement(final JexlBuilder builder) throws Exception {
+    void testStatement(final JexlBuilder builder) {
         init(builder);
         final Froboz froboz = new Froboz(32);
         context.set("froboz", froboz);
@@ -1185,7 +1320,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testTemplate0(final JexlBuilder builder) throws Exception {
+    void testTemplate0(final JexlBuilder builder) {
         init(builder);
         final String source = "   $$ if(x) {\nx is ${x}\n   $$ } else {\n${'no 
x'}\n$$ }\n";
         StringWriter strw;
@@ -1211,7 +1346,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testTemplate1(final JexlBuilder builder) throws Exception {
+    void testTemplate1(final JexlBuilder builder) {
         init(builder);
         final String source = "$$ if(x) {\nx is ${x}\n$$ } else {\n${'no 
x'}\n$$ }\n";
         StringWriter strw;
@@ -1234,7 +1369,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testTemplate10(final JexlBuilder builder) throws Exception {
+    void testTemplate10(final JexlBuilder builder) {
         init(builder);
         final String source = "$$(x)->{ if(x) {\nx is ${x}\n$$ } else {\n${'no 
x'}\n$$ } }\n";
         StringWriter strw;
@@ -1255,7 +1390,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testTemplate2(final JexlBuilder builder) throws Exception {
+    void testTemplate2(final JexlBuilder builder) {
         init(builder);
         final String source = "The answer: ${x}";
         StringWriter strw;
@@ -1273,7 +1408,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testTemplateOutOfScope(final JexlBuilder builder) throws 
Exception {
+    void testTemplateOutOfScope(final JexlBuilder builder) {
         init(builder);
         final JexlOptions opts = new JexlOptions();
         opts.setCancellable(false);
@@ -1306,7 +1441,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testTemplatePragmaPro50(final JexlBuilder builder) throws 
Exception {
+    void testTemplatePragmaPro50(final JexlBuilder builder) {
         init(builder);
         final JexlOptions opts = new JexlOptions();
         opts.setCancellable(false);
@@ -1330,7 +1465,7 @@ public class JXLTTest extends JexlTestCase {
 
     @ParameterizedTest
     @MethodSource("engines")
-    public void testWriter(final JexlBuilder builder) throws Exception {
+    void testWriter(final JexlBuilder builder) {
         init(builder);
         final Froboz froboz = new Froboz(42);
         final Writer writer = new FrobozWriter(new StringWriter());
diff --git a/src/test/java/org/apache/commons/jexl3/LambdaTest.java 
b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
index 7579ef33..ae205180 100644
--- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
@@ -555,4 +555,66 @@ public class LambdaTest extends JexlTestCase {
         assertEquals(42, result);
     }
 
+    /**
+     * see JEXL-426
+     */
+    @Test void testRefCapture1() {
+        String src = "let x = 10;\n" +
+                "let foo = () -> {\n" +
+                "x += 2;\n" +
+                "}\n" +
+                "x = 40;\n" +
+                "foo();\n" +
+                "x";
+        final JexlEngine jexl = new JexlBuilder()
+                .features(new JexlFeatures().referenceCapture(true))
+                .create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src);
+        result = script.execute(null);
+        assertEquals(42, result);
+    }
+
+    @Test void testRefCapture2() {
+        String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f()";
+        final JexlEngine jexl = new JexlBuilder()
+                .features(new 
JexlFeatures().constCapture(true).referenceCapture(true))
+                .create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src);
+        result = script.execute(null);
+        assertEquals(42, result);
+    }
+
+    @Test void testRefCapture3() {
+        String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f";
+        final JexlEngine jexl = new JexlBuilder()
+                .features(new 
JexlFeatures().constCapture(true).referenceCapture(true))
+                .create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src);
+        result = script.execute(null);
+        assertTrue(result instanceof JexlScript);
+        script = jexl.createScript("f()", "f");
+        result = script.execute(null, result);
+        assertEquals(42, result);
+    }
+
+    @Test void testRefCapture4() {
+        String src = "let x = 10; let f = () -> { let x = 142; x }; x = 40; f";
+        final JexlEngine jexl = new JexlBuilder()
+                .features(new JexlFeatures().referenceCapture(true))
+                .create();
+        JexlScript script;
+        Object result;
+        script = jexl.createScript(src);
+        result = script.execute(null);
+        assertTrue(result instanceof JexlScript);
+        script = jexl.createScript("f()", "f");
+        result = script.execute(null, result);
+        assertEquals(142, result);
+    }
 }

Reply via email to