You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2022/10/28 08:25:07 UTC

[tomcat] branch 9.0.x updated: Fix BZ 66325 - Correct concurrency issue in lambda expression evaluation

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

markt 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 60cc92a25d Fix BZ 66325 - Correct concurrency issue in lambda expression evaluation
60cc92a25d is described below

commit 60cc92a25ddc0f0532c437e11b4d2eae5c7d77ac
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Oct 28 09:23:01 2022 +0100

    Fix BZ 66325 - Correct concurrency issue in lambda expression evaluation
    
    https://bz.apache.org/bugzilla/show_bug.cgi?id=66325
---
 java/org/apache/el/lang/EvaluationContext.java     | 35 +++++++++++++
 .../el/lang/LambdaExpressionNestedState.java       | 51 +++++++++++++++++++
 java/org/apache/el/parser/AstLambdaExpression.java | 57 +++++-----------------
 webapps/docs/changelog.xml                         |  8 +++
 4 files changed, 107 insertions(+), 44 deletions(-)

diff --git a/java/org/apache/el/lang/EvaluationContext.java b/java/org/apache/el/lang/EvaluationContext.java
index c69f852173..f62080f34f 100644
--- a/java/org/apache/el/lang/EvaluationContext.java
+++ b/java/org/apache/el/lang/EvaluationContext.java
@@ -27,6 +27,8 @@ import javax.el.FunctionMapper;
 import javax.el.ImportHandler;
 import javax.el.VariableMapper;
 
+import org.apache.el.util.MessageFactory;
+
 public final class EvaluationContext extends ELContext {
 
     private final ELContext elContext;
@@ -35,6 +37,8 @@ public final class EvaluationContext extends ELContext {
 
     private final VariableMapper varMapper;
 
+    private LambdaExpressionNestedState lambdaExpressionNestedState;
+
     public EvaluationContext(ELContext elContext, FunctionMapper fnMapper,
             VariableMapper varMapper) {
         this.elContext = elContext;
@@ -153,4 +157,35 @@ public final class EvaluationContext extends ELContext {
     public Object convertToType(Object obj, Class<?> type) {
         return elContext.convertToType(obj, type);
     }
+
+
+    public LambdaExpressionNestedState getLambdaExpressionNestedState() {
+        // State is stored in the EvaluationContext instance associated with the
+        // outermost lambda expression of a set of nested expressions.
+
+        if (lambdaExpressionNestedState != null) {
+            // This instance is storing state so it must be associated with the
+            // outermost lambda expression.
+            return lambdaExpressionNestedState;
+        }
+
+        // Check to see if the associated lambda expression is nested as state
+        // will be stored in the EvaluationContext associated with the outermost
+        // lambda expression.
+        if (elContext instanceof EvaluationContext) {
+            return ((EvaluationContext) elContext).getLambdaExpressionNestedState();
+        }
+
+        return null;
+    }
+
+
+    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
new file mode 100644
index 0000000000..f4a4d47c70
--- /dev/null
+++ b/java/org/apache/el/lang/LambdaExpressionNestedState.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.el.lang;
+
+/**
+ * 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
+ * expressions.
+ * <p>
+ * The sets of nodes for parsed expressions are cached and, as a result, a set
+ * of nodes may be being used by multiple concurrent threads. This means any
+ * state relating to evaluation cannot be stored in the nodes. State is
+ * therefore stored in the {@link EvaluationContext} which is created, used for
+ * a single evaluation and then discarded.
+ */
+public final class LambdaExpressionNestedState {
+
+    private int nestingCount = 0;
+    private boolean hasFormalParameters = false;
+
+    public void incrementNestingCount() {
+        nestingCount++;
+    }
+
+    public int getNestingCount() {
+        return nestingCount;
+    }
+
+    public void setHasFormalParameters() {
+        hasFormalParameters = true;
+    }
+
+    public boolean getHasFormalParameters() {
+        return hasFormalParameters;
+    }
+}
diff --git a/java/org/apache/el/parser/AstLambdaExpression.java b/java/org/apache/el/parser/AstLambdaExpression.java
index f719c33553..97a059fca7 100644
--- a/java/org/apache/el/parser/AstLambdaExpression.java
+++ b/java/org/apache/el/parser/AstLambdaExpression.java
@@ -25,12 +25,11 @@ import javax.el.LambdaExpression;
 
 import org.apache.el.ValueExpressionImpl;
 import org.apache.el.lang.EvaluationContext;
+import org.apache.el.lang.LambdaExpressionNestedState;
 import org.apache.el.util.MessageFactory;
 
 public class AstLambdaExpression extends SimpleNode {
 
-    private NestedState nestedState = null;
-
     public AstLambdaExpression(int id) {
         super(id);
     }
@@ -40,7 +39,14 @@ public class AstLambdaExpression extends SimpleNode {
 
         // Correct evaluation requires knowledge of the whole set of nested
         // expressions, not just the current expression
-        NestedState state = getNestedState();
+        LambdaExpressionNestedState state = ctx.getLambdaExpressionNestedState();
+        if (state == null) {
+            // This must be an outer lambda expression. Create and populate the
+            // state.
+            state = new LambdaExpressionNestedState();
+            populateNestedState(state);
+            ctx.setLambdaExpressionNestedState(state);
+        }
 
         // Check that there are not more sets of parameters than there are
         // nested expressions.
@@ -109,29 +115,15 @@ public class AstLambdaExpression extends SimpleNode {
     }
 
 
-    private NestedState getNestedState() {
-        if (nestedState == null) {
-            setNestedState(new NestedState());
-        }
-        return nestedState;
-    }
-
-
-    private void setNestedState(NestedState nestedState) {
-        if (this.nestedState != null) {
-            // Should never happen
-            throw new IllegalStateException(MessageFactory.get("error.lambda.wrongNestedState"));
-        }
-        this.nestedState = nestedState;
-
+    private void populateNestedState(LambdaExpressionNestedState lambdaExpressionNestedState) {
         // Increment the nesting count for the current expression
-        nestedState.incrementNestingCount();
+        lambdaExpressionNestedState.incrementNestingCount();
 
         if (jjtGetNumChildren() > 1) {
             Node firstChild = jjtGetChild(0);
             if (firstChild instanceof AstLambdaParameters) {
                 if (firstChild.jjtGetNumChildren() > 0) {
-                    nestedState.setHasFormalParameters();
+                    lambdaExpressionNestedState.setHasFormalParameters();
                 }
             } else {
                 // Can't be a lambda expression
@@ -139,7 +131,7 @@ public class AstLambdaExpression extends SimpleNode {
             }
             Node secondChild = jjtGetChild(1);
             if (secondChild instanceof AstLambdaExpression) {
-                ((AstLambdaExpression) secondChild).setNestedState(nestedState);
+                ((AstLambdaExpression) secondChild).populateNestedState(lambdaExpressionNestedState);
             }
         }
     }
@@ -155,28 +147,5 @@ public class AstLambdaExpression extends SimpleNode {
         }
         return result.toString();
     }
-
-
-    private static class NestedState {
-
-        private int nestingCount = 0;
-        private boolean hasFormalParameters = false;
-
-        private void incrementNestingCount() {
-            nestingCount++;
-        }
-
-        private int getNestingCount() {
-            return nestingCount;
-        }
-
-        private void setHasFormalParameters() {
-            hasFormalParameters = true;
-        }
-
-        private boolean getHasFormalParameters() {
-            return hasFormalParameters;
-        }
-    }
 }
 /* JavaCC - OriginalChecksum=071159eff10c8e15ec612c765ae4480a (do not edit this line) */
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c0f8af8471..4559821a27 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -114,6 +114,14 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        <bug>66325</bug>: Fix concurrency issue in evaluation of expression
+        language containing lambda expressions. (markt)
+      </fix>
+    </changelog>
+  </subsection>
   <subsection name="Other">
     <changelog>
       <update>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org