This is an automated email from the ASF dual-hosted git repository.
rmaucher pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 2d6a97c6df Fix evaluation of some lambda expressions
2d6a97c6df is described below
commit 2d6a97c6df0369d4189e747a30eaedca5939fc15
Author: remm <[email protected]>
AuthorDate: Tue Sep 1 11:07:31 2026 +0200
Fix evaluation of some lambda expressions
Found by code review.
Co authored with OpenCode.
---
java/org/apache/el/lang/EvaluationContext.java | 13 +++-------
.../el/lang/LambdaExpressionNestedState.java | 27 +++++++++++++++++--
java/org/apache/el/parser/AstLambdaExpression.java | 9 ++++---
.../apache/el/parser/TestAstLambdaExpression.java | 30 ++++++++++++++++++++++
webapps/docs/changelog.xml | 7 +++++
5 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/java/org/apache/el/lang/EvaluationContext.java
b/java/org/apache/el/lang/EvaluationContext.java
index 1eeb8bb838..4fbeea2541 100644
--- a/java/org/apache/el/lang/EvaluationContext.java
+++ b/java/org/apache/el/lang/EvaluationContext.java
@@ -27,8 +27,6 @@ import javax.el.FunctionMapper;
import javax.el.ImportHandler;
import javax.el.VariableMapper;
-import org.apache.el.util.MessageFactory;
-
/**
* Extended EL context that wraps an existing ELContext and provides additional
* functionality for lambda expression evaluation.
@@ -201,18 +199,13 @@ public final class EvaluationContext extends ELContext {
/**
- * Sets the lambda expression nested state for this context.
+ * Sets the lambda expression nested state for this context. Any existing
state is replaced. This happens when a
+ * sibling lambda expression (one that is not nested inside the lambda
expression that created the existing state)
+ * is evaluated during the same evaluation.
*
* @param lambdaExpressionNestedState The lambda expression nested state
- *
- * @throws IllegalStateException if a nested state has already been set
*/
public void setLambdaExpressionNestedState(LambdaExpressionNestedState
lambdaExpressionNestedState) {
- if (this.lambdaExpressionNestedState != null) {
- // Should never happen
- throw new
IllegalStateException(MessageFactory.get("error.lambda.wrongNestedState"));
- }
-
this.lambdaExpressionNestedState = lambdaExpressionNestedState;
}
}
diff --git a/java/org/apache/el/lang/LambdaExpressionNestedState.java
b/java/org/apache/el/lang/LambdaExpressionNestedState.java
index 32b3cf27ed..4839315508 100644
--- a/java/org/apache/el/lang/LambdaExpressionNestedState.java
+++ b/java/org/apache/el/lang/LambdaExpressionNestedState.java
@@ -16,6 +16,8 @@
*/
package org.apache.el.lang;
+import org.apache.el.parser.Node;
+
/**
* Stores the state required for correct evaluation of lambda expressions.
Lambda expressions may be nested. Correct
* evaluation requires knowledge not just of the current lambda expression,
but also of any nested and nesting
@@ -27,13 +29,34 @@ package org.apache.el.lang;
*/
public final class LambdaExpressionNestedState {
+ private final Node root;
+
private int nestingCount = 0;
private boolean hasFormalParameters = false;
/**
- * Default constructor.
+ * Constructor.
+ *
+ * @param root The lambda expression node that created this state
+ */
+ public LambdaExpressionNestedState(Node root) {
+ this.root = root;
+ }
+
+ /**
+ * Returns whether the given node is the node that created this state or a
descendant of that node.
+ *
+ * @param node The node to check
+ *
+ * @return {@code true} if the given node is the node that created this
state or a descendant of that node
*/
- public LambdaExpressionNestedState() {
+ public boolean contains(Node node) {
+ for (Node n = node; n != null; n = n.jjtGetParent()) {
+ if (n == root) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/java/org/apache/el/parser/AstLambdaExpression.java
b/java/org/apache/el/parser/AstLambdaExpression.java
index f373131b37..db7303246b 100644
--- a/java/org/apache/el/parser/AstLambdaExpression.java
+++ b/java/org/apache/el/parser/AstLambdaExpression.java
@@ -41,10 +41,11 @@ public class AstLambdaExpression extends SimpleNode {
// Correct evaluation requires knowledge of the whole set of nested
// expressions, not just the current expression
LambdaExpressionNestedState state =
ctx.getLambdaExpressionNestedState();
- if (state == null) {
- // This must be an outer lambda expression. Create and populate the
- // state.
- state = new LambdaExpressionNestedState();
+ if (state == null || !state.contains(this)) {
+ // This is an outer lambda expression or a sibling of the lambda
+ // expression that created the stored state. Create and populate
+ // the state.
+ state = new LambdaExpressionNestedState(this);
populateNestedState(state);
ctx.setLambdaExpressionNestedState(state);
}
diff --git a/test/org/apache/el/parser/TestAstLambdaExpression.java
b/test/org/apache/el/parser/TestAstLambdaExpression.java
index 75a02f4e02..94936925a8 100644
--- a/test/org/apache/el/parser/TestAstLambdaExpression.java
+++ b/test/org/apache/el/parser/TestAstLambdaExpression.java
@@ -241,4 +241,34 @@ public class TestAstLambdaExpression {
processor.getValue("foo:v = (x)->x+1; foo:v(0)",
Integer.class);
Assert.assertEquals(Integer.valueOf(1), result);
}
+
+
+ @Test
+ public void testSiblings01() {
+ ELProcessor processor = new ELProcessor();
+ // The second sibling lambda expression must not be checked against the
+ // nested state created by the first sibling
+ Object result = processor.getValue("(c->c)(3) + (a->b->b)(1)(2)",
Integer.class);
+ Assert.assertEquals(Integer.valueOf(5), result);
+ }
+
+
+ @Test(expected=ELException.class)
+ public void testSiblings02() {
+ ELProcessor processor = new ELProcessor();
+ // The second sibling lambda expression has more sets of method
+ // parameters than it has nested lambda expressions. The nested state
+ // created by the first sibling must not mask the error.
+ processor.getValue("(a->b->b)(1)(2) + (c->c)(3)(4)", Integer.class);
+ }
+
+
+ @Test
+ public void testSiblings03() {
+ ELProcessor processor = new ELProcessor();
+ // The second sibling lambda expression must be auto-invoked. The
+ // nested state created by the first sibling must not block it.
+ Object result = processor.getValue("(c->c)(3) + (()->21)",
Integer.class);
+ Assert.assertEquals(Integer.valueOf(24), result);
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d46ec0ee09..c987996a9c 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -208,6 +208,13 @@
</fix>
</changelog>
</subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ Fix EL evaluation of some lambda expressions. (remm)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Cluster">
<changelog>
<fix>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]