This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch JEXL-456
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git

commit 622c955af213e4d522bd04334322edefcd17b289
Author: Henrib <[email protected]>
AuthorDate: Fri Feb 13 12:46:28 2026 +0100

    JEXL-456: perform cleanup in parent parser if child parser emits a parsing 
exception;
---
 .../apache/commons/jexl3/parser/JexlParser.java    | 10 ++-
 .../org/apache/commons/jexl3/Issues400Test.java    | 74 ++++++++++++++++++++++
 2 files changed, 82 insertions(+), 2 deletions(-)

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 6a64a7db..1dff648e 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -777,7 +777,7 @@ public abstract class JexlParser extends StringParser 
implements JexlScriptParse
                 final Scope unitScope = blockScopes.get(unit);
                 // follow through potential capture
                 if (blockScope != unitScope) {
-                    final int declared = 
blockScope.getCaptureDeclaration(lexical);
+                    final int declared = blockScope != null ? 
blockScope.getCaptureDeclaration(lexical) : -1;
                     if (declared >= 0) {
                         lexical = declared;
                     }
@@ -961,7 +961,13 @@ public abstract class JexlParser extends StringParser 
implements JexlScriptParse
      */
     @Override
     public ASTJexlScript jxltParse(final JexlInfo info, final JexlFeatures 
features, final String src, final Scope scope) {
-        return new Parser(this).parse(info, features, src, scope);
+        JexlFeatures previous = getFeatures();
+        try {
+            return new Parser(this).parse(info, features, src, scope);
+        } catch (JexlException.Parsing ex) {
+            cleanup(previous);
+            throw ex;
+        }
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 4ebdce4d..3fcb7514 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -1067,5 +1067,79 @@ public class Issues400Test {
         Assertions.assertEquals("World Hello World ! ~", writer.toString());
     }
 
+    @Test
+    void testIssue350_456_strict() {
+        final JexlEngine jexl = new JexlBuilder().strict(true).create();
+        final JxltEngine jxlt = jexl.createJxltEngine();
+        // creation/parse is OK
+        final JxltEngine.Template template = jxlt.createTemplate("$$ var foo = 
'foo';\n$$ var bar = 'bar';\n${foo + bar}");
+        final StringWriter writer = new StringWriter();
+            template.evaluate(null, writer);
+            Assertions.assertEquals("foobar", writer.toString());
+    }
+
+    @Test
+    void testIssue350_456_notStrict() {
+        final JexlEngine jexl = new JexlBuilder().strict(false).create();
+        final JxltEngine jxlt = jexl.createJxltEngine();
+        // creation/parse is OK
+        final JxltEngine.Template template = jxlt.createTemplate("$$ var foo = 
'foo';\n$$ var bar = 'bar';\n${foo + bar}");
+        final StringWriter writer = new StringWriter();
+        template.evaluate(null, writer);
+        Assertions.assertEquals("foobar", writer.toString());
+    }
+
+    @Test
+    void testIssue350_456_strictWithVariable() {
+        final JexlEngine jexl = new JexlBuilder().strict(true).create();
+        final JxltEngine jxlt = jexl.createJxltEngine();
+        // creation/parse is OK
+        final JxltEngine.Template template = jxlt.createTemplate("$$ var foo = 
'foo';\n$$ var bar = 'bar';\n${foo + bar}");
+        // add a '$$' global context variable
+        JexlContext ctxt = new MapContext();
+        ctxt.set("$$", "");
+        final StringWriter writer = new StringWriter();
+        template.evaluate(ctxt, writer);
+        Assertions.assertEquals("foobar", writer.toString());
+    }
+
+    @Test
+    void testIssue36x_456_var() {
+        final JexlEngine jexl = new JexlBuilder().create();
+        final JxltEngine jxlt = jexl.createJxltEngine();
+        // OK
+        jexl.createScript("var foo = 0;\nfoo = 42;");
+        try {
+            jxlt.createTemplate("$$ var foo = 'foo'; if (true) { var foo = 
'bar'; var err =&; }");
+            Assertions.fail("should have thrown a parsing error in '&'");
+        } catch (JexlException xjexl) {
+            // parsing error in '&'
+            Assertions.assertTrue(xjexl.getMessage().contains("&"));
+        }
+        // JEXL-456: java.lang.NullPointerException: Cannot invoke 
"org.apache.commons.jexl3.internal.Scope.getCaptureDeclaration(int)" because 
"blockScope" is null
+        jexl.createScript("var foo = 0;\nfoo = 42;");
+        // JEXL-456: same error with the template creation below
+        jxlt.createTemplate("$$ var foo = 'foo'; foo = 'bar';");
+    }
+
+    @Test
+    void testIssue36x_456_let() {
+        final JexlEngine jexl = new Engine32(new JexlBuilder().strict(true));
+        // OK
+        jexl.createScript("let foo = 0;\nfoo = 42;");
+        final JxltEngine jxlt = jexl.createJxltEngine();
+        try {
+            jxlt.createTemplate("$$ var err =&;");
+            Assertions.fail("should have thrown a parsing error in '&'");
+        } catch (JexlException xjexl) {
+            // parsing error in '&'
+            Assertions.assertTrue(xjexl.getMessage().contains("&"));
+        }
+        // JEXL-456: parsing error in 'foo: variable is already declared'
+        jexl.createScript("let foo = 0;\nfoo = 42;");
+        // JEXL-456: same error with the template creation below
+        jxlt.createTemplate("$$ let foo = 0;\nfoo = 42;");
+    }
+
 }
 

Reply via email to