Skip to content

Commit 6a1fe0b

Browse files
committed
FunctionReference's method field is volatile
Issue: SPR-16255
1 parent 0a06bce commit 6a1fe0b

File tree

1 file changed

+28
-20
lines changed

1 file changed

+28
-20
lines changed

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

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@
4747
* (right now), so the names must be unique.
4848
*
4949
* @author Andy Clement
50+
* @author Juergen Hoeller
5051
* @since 3.0
5152
*/
5253
public class FunctionReference extends SpelNodeImpl {
@@ -56,9 +57,7 @@ public class FunctionReference extends SpelNodeImpl {
5657
// Captures the most recently used method for the function invocation *if* the method
5758
// can safely be used for compilation (i.e. no argument conversion is going on)
5859
@Nullable
59-
private Method method;
60-
61-
private boolean argumentConversionOccurred;
60+
private volatile Method method;
6261

6362

6463
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
@@ -90,14 +89,13 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
9089
}
9190

9291
/**
93-
* Execute a function represented as a java.lang.reflect.Method.
92+
* Execute a function represented as a {@code java.lang.reflect.Method}.
9493
* @param state the expression evaluation state
9594
* @param method the method to invoke
9695
* @return the return value of the invoked Java method
9796
* @throws EvaluationException if there is any problem invoking the method
9897
*/
9998
private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
100-
this.method = null;
10199
Object[] functionArgs = getArguments(state);
102100

103101
if (!method.isVarArgs() && method.getParameterCount() != functionArgs.length) {
@@ -112,25 +110,33 @@ private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method
112110

113111
// Convert arguments if necessary and remap them for varargs if required
114112
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
115-
argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
113+
boolean argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
116114
if (method.isVarArgs()) {
117115
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
118116
method.getParameterTypes(), functionArgs);
119117
}
118+
boolean compilable = false;
120119

121120
try {
122121
ReflectionUtils.makeAccessible(method);
123122
Object result = method.invoke(method.getClass(), functionArgs);
124-
if (!argumentConversionOccurred) {
125-
this.method = method;
126-
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
127-
}
123+
compilable = !argumentConversionOccurred;
128124
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result));
129125
}
130126
catch (Exception ex) {
131127
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
132128
this.name, ex.getMessage());
133129
}
130+
finally {
131+
if (compilable) {
132+
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
133+
this.method = method;
134+
}
135+
else {
136+
this.exitTypeDescriptor = null;
137+
this.method = null;
138+
}
139+
}
134140
}
135141

136142
@Override
@@ -162,12 +168,13 @@ private Object[] getArguments(ExpressionState state) throws EvaluationException
162168

163169
@Override
164170
public boolean isCompilable() {
165-
if (this.method == null || this.argumentConversionOccurred) {
171+
Method method = this.method;
172+
if (method == null) {
166173
return false;
167174
}
168-
int methodModifiers = this.method.getModifiers();
175+
int methodModifiers = method.getModifiers();
169176
if (!Modifier.isStatic(methodModifiers) || !Modifier.isPublic(methodModifiers) ||
170-
!Modifier.isPublic(this.method.getDeclaringClass().getModifiers())) {
177+
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
171178
return false;
172179
}
173180
for (SpelNodeImpl child : this.children) {
@@ -179,12 +186,13 @@ public boolean isCompilable() {
179186
}
180187

181188
@Override
182-
public void generateCode(MethodVisitor mv,CodeFlow cf) {
183-
Assert.state(this.method != null, "No method handle");
184-
String classDesc = this.method.getDeclaringClass().getName().replace('.', '/');
185-
generateCodeForArguments(mv, cf, this.method, this.children);
186-
mv.visitMethodInsn(INVOKESTATIC, classDesc, this.method.getName(),
187-
CodeFlow.createSignatureDescriptor(this.method), false);
189+
public void generateCode(MethodVisitor mv, CodeFlow cf) {
190+
Method method = this.method;
191+
Assert.state(method != null, "No method handle");
192+
String classDesc = method.getDeclaringClass().getName().replace('.', '/');
193+
generateCodeForArguments(mv, cf, method, this.children);
194+
mv.visitMethodInsn(INVOKESTATIC, classDesc, method.getName(),
195+
CodeFlow.createSignatureDescriptor(method), false);
188196
cf.pushDescriptor(this.exitTypeDescriptor);
189197
}
190198

0 commit comments

Comments
 (0)