Skip to content

Commit ab48ac3

Browse files
committed
Deprecate local variable support in SpEL's internal ExpressionState
Since the Spring Expression Language does not actually support local variables in expressions, this commit deprecates all public APIs related to local variables in ExpressionState (namely, the two enterScope(...) variants that accept local variable data, setLocalVariable(), and lookupLocalVariable()). In addition, we no longer invoke `state.enterScope("index", ...)` in the Projection and Selection AST nodes since the $index local variable was never accessible within expressions anyway. See gh-23202 Closes gh-32004
1 parent c1f0faa commit ab48ac3

File tree

4 files changed

+15
-4
lines changed

4 files changed

+15
-4
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/ExpressionState.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ public void enterScope() {
217217
* name/value pair.
218218
* @param name the name of the local variable
219219
* @param value the value of the local variable
220+
* @deprecated as of 6.2 with no replacement; to be removed in 7.0
220221
*/
222+
@Deprecated(since = "6.2", forRemoval = true)
221223
public void enterScope(String name, Object value) {
222224
initVariableScopes().push(new VariableScope(name, value));
223225
initScopeRootObjects().push(getActiveContextObject());
@@ -228,7 +230,9 @@ public void enterScope(String name, Object value) {
228230
* context object} and a new local variable scope containing the supplied
229231
* name/value pairs.
230232
* @param variables a map containing name/value pairs for local variables
233+
* @deprecated as of 6.2 with no replacement; to be removed in 7.0
231234
*/
235+
@Deprecated(since = "6.2", forRemoval = true)
232236
public void enterScope(@Nullable Map<String, Object> variables) {
233237
initVariableScopes().push(new VariableScope(variables));
234238
initScopeRootObjects().push(getActiveContextObject());
@@ -246,7 +250,9 @@ public void exitScope() {
246250
* overwritten.
247251
* @param name the name of the local variable
248252
* @param value the value of the local variable
253+
* @deprecated as of 6.2 with no replacement; to be removed in 7.0
249254
*/
255+
@Deprecated(since = "6.2", forRemoval = true)
250256
public void setLocalVariable(String name, Object value) {
251257
initVariableScopes().element().setVariable(name, value);
252258
}
@@ -256,7 +262,9 @@ public void setLocalVariable(String name, Object value) {
256262
* @param name the name of the local variable
257263
* @return the value of the local variable, or {@code null} if the variable
258264
* does not exist in the current scope
265+
* @deprecated as of 6.2 with no replacement; to be removed in 7.0
259266
*/
267+
@Deprecated(since = "6.2", forRemoval = true)
260268
@Nullable
261269
public Object lookupLocalVariable(String name) {
262270
for (VariableScope scope : initVariableScopes()) {

spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException
9393
for (Object element : data) {
9494
try {
9595
state.pushActiveContextObject(new TypedValue(element));
96-
state.enterScope("index", result.size());
96+
state.enterScope();
9797
Object value = this.children[0].getValueInternal(state).getValue();
9898
if (value != null && operandIsArray) {
9999
arrayElementType = determineCommonType(arrayElementType, value.getClass());

spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,10 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException
139139
Arrays.asList(ObjectUtils.toObjectArray(operand)));
140140

141141
List<Object> result = new ArrayList<>();
142-
int index = 0;
143142
for (Object element : data) {
144143
try {
145144
state.pushActiveContextObject(new TypedValue(element));
146-
state.enterScope("index", index);
145+
state.enterScope();
147146
Object val = selectionCriteria.getValueInternal(state).getValue();
148147
if (val instanceof Boolean b) {
149148
if (b) {
@@ -157,7 +156,6 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException
157156
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
158157
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
159158
}
160-
index++;
161159
}
162160
finally {
163161
state.exitScope();

spring-expression/src/test/java/org/springframework/expression/spel/ExpressionStateTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ void construction() {
5656
}
5757

5858
@Test
59+
@SuppressWarnings("removal")
5960
void localVariables() {
6061
Object value = state.lookupLocalVariable("foo");
6162
assertThat(value).isNull();
@@ -86,6 +87,7 @@ void globalVariables() {
8687
}
8788

8889
@Test
90+
@SuppressWarnings("removal")
8991
void noVariableInterference() {
9092
TypedValue typedValue = state.lookupVariable("foo");
9193
assertThat(typedValue).isEqualTo(TypedValue.NULL);
@@ -99,6 +101,7 @@ void noVariableInterference() {
99101
}
100102

101103
@Test
104+
@SuppressWarnings("removal")
102105
void localVariableNestedScopes() {
103106
assertThat(state.lookupLocalVariable("foo")).isNull();
104107

@@ -157,6 +160,7 @@ void activeContextObject() {
157160
}
158161

159162
@Test
163+
@SuppressWarnings("removal")
160164
void populatedNestedScopes() {
161165
assertThat(state.lookupLocalVariable("foo")).isNull();
162166

@@ -186,6 +190,7 @@ void rootObjectConstructor() {
186190
}
187191

188192
@Test
193+
@SuppressWarnings("removal")
189194
void populatedNestedScopesMap() {
190195
assertThat(state.lookupLocalVariable("foo")).isNull();
191196
assertThat(state.lookupLocalVariable("goo")).isNull();

0 commit comments

Comments
 (0)