This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new f2fff1b571 Fix BZ 66325 - Correct concurrency issue in lambda expression evaluation f2fff1b571 is described below commit f2fff1b571e02f2fa7adc6a7104750872b37c629 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 a30788b515..d5af9fdae5 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-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org