Skip to content

Commit a9bad58

Browse files
committed
FunctionReference's method field is volatile
Issue: SPR-16255 (cherry picked from commit 6a1fe0b)
1 parent 6f6ff33 commit a9bad58

File tree

1 file changed

+33
-26
lines changed

1 file changed

+33
-26
lines changed

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

Lines changed: 33 additions & 26 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.
@@ -30,6 +30,7 @@
3030
import org.springframework.expression.spel.SpelEvaluationException;
3131
import org.springframework.expression.spel.SpelMessage;
3232
import org.springframework.expression.spel.support.ReflectionHelper;
33+
import org.springframework.util.Assert;
3334
import org.springframework.util.ClassUtils;
3435
import org.springframework.util.ReflectionUtils;
3536

@@ -45,6 +46,7 @@
4546
* (right now), so the names must be unique.
4647
*
4748
* @author Andy Clement
49+
* @author Juergen Hoeller
4850
* @since 3.0
4951
*/
5052
public class FunctionReference extends SpelNodeImpl {
@@ -53,9 +55,7 @@ public class FunctionReference extends SpelNodeImpl {
5355

5456
// Captures the most recently used method for the function invocation *if* the method
5557
// can safely be used for compilation (i.e. no argument conversion is going on)
56-
private Method method;
57-
58-
private boolean argumentConversionOccurred;
58+
private volatile Method method;
5959

6060

6161
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
@@ -67,7 +67,7 @@ public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments
6767
@Override
6868
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
6969
TypedValue value = state.lookupVariable(this.name);
70-
if (value == null) {
70+
if (value == TypedValue.NULL) {
7171
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
7272
}
7373

@@ -87,50 +87,54 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
8787
}
8888

8989
/**
90-
* Execute a function represented as a java.lang.reflect.Method.
90+
* Execute a function represented as a {@code java.lang.reflect.Method}.
9191
* @param state the expression evaluation state
9292
* @param method the method to invoke
9393
* @return the return value of the invoked Java method
9494
* @throws EvaluationException if there is any problem invoking the method
9595
*/
9696
private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
97-
this.method = null;
9897
Object[] functionArgs = getArguments(state);
9998

100-
if (!method.isVarArgs() && method.getParameterTypes().length != functionArgs.length) {
99+
if (!method.isVarArgs() && method.getParameterCount() != functionArgs.length) {
101100
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
102-
functionArgs.length, method.getParameterTypes().length);
101+
functionArgs.length, method.getParameterCount());
103102
}
104103
// Only static methods can be called in this way
105104
if (!Modifier.isStatic(method.getModifiers())) {
106105
throw new SpelEvaluationException(getStartPosition(),
107106
SpelMessage.FUNCTION_MUST_BE_STATIC, ClassUtils.getQualifiedMethodName(method), this.name);
108107
}
109108

110-
this.argumentConversionOccurred = false;
111109
// Convert arguments if necessary and remap them for varargs if required
112-
if (functionArgs != null) {
113-
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
114-
this.argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
115-
}
110+
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
111+
boolean argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
116112
if (method.isVarArgs()) {
117113
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
118114
method.getParameterTypes(), functionArgs);
119115
}
116+
boolean compilable = false;
120117

121118
try {
122119
ReflectionUtils.makeAccessible(method);
123120
Object result = method.invoke(method.getClass(), functionArgs);
124-
if (!argumentConversionOccurred) {
125-
this.method = method;
126-
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
127-
}
121+
compilable = !argumentConversionOccurred;
128122
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result));
129123
}
130124
catch (Exception ex) {
131125
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
132126
this.name, ex.getMessage());
133127
}
128+
finally {
129+
if (compilable) {
130+
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
131+
this.method = method;
132+
}
133+
else {
134+
this.exitTypeDescriptor = null;
135+
this.method = null;
136+
}
137+
}
134138
}
135139

136140
@Override
@@ -162,12 +166,13 @@ private Object[] getArguments(ExpressionState state) throws EvaluationException
162166

163167
@Override
164168
public boolean isCompilable() {
165-
if (this.method == null || this.argumentConversionOccurred) {
169+
Method method = this.method;
170+
if (method == null) {
166171
return false;
167172
}
168-
int methodModifiers = this.method.getModifiers();
173+
int methodModifiers = method.getModifiers();
169174
if (!Modifier.isStatic(methodModifiers) || !Modifier.isPublic(methodModifiers) ||
170-
!Modifier.isPublic(this.method.getDeclaringClass().getModifiers())) {
175+
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
171176
return false;
172177
}
173178
for (SpelNodeImpl child : this.children) {
@@ -179,11 +184,13 @@ public boolean isCompilable() {
179184
}
180185

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

0 commit comments

Comments
 (0)