1
1
/*
2
- * Copyright 2002-2017 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
47
47
* (right now), so the names must be unique.
48
48
*
49
49
* @author Andy Clement
50
+ * @author Juergen Hoeller
50
51
* @since 3.0
51
52
*/
52
53
public class FunctionReference extends SpelNodeImpl {
@@ -56,9 +57,7 @@ public class FunctionReference extends SpelNodeImpl {
56
57
// Captures the most recently used method for the function invocation *if* the method
57
58
// can safely be used for compilation (i.e. no argument conversion is going on)
58
59
@ Nullable
59
- private Method method ;
60
-
61
- private boolean argumentConversionOccurred ;
60
+ private volatile Method method ;
62
61
63
62
64
63
public FunctionReference (String functionName , int pos , SpelNodeImpl ... arguments ) {
@@ -90,14 +89,13 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
90
89
}
91
90
92
91
/**
93
- * Execute a function represented as a java.lang.reflect.Method.
92
+ * Execute a function represented as a {@code java.lang.reflect.Method} .
94
93
* @param state the expression evaluation state
95
94
* @param method the method to invoke
96
95
* @return the return value of the invoked Java method
97
96
* @throws EvaluationException if there is any problem invoking the method
98
97
*/
99
98
private TypedValue executeFunctionJLRMethod (ExpressionState state , Method method ) throws EvaluationException {
100
- this .method = null ;
101
99
Object [] functionArgs = getArguments (state );
102
100
103
101
if (!method .isVarArgs () && method .getParameterCount () != functionArgs .length ) {
@@ -112,25 +110,33 @@ private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method
112
110
113
111
// Convert arguments if necessary and remap them for varargs if required
114
112
TypeConverter converter = state .getEvaluationContext ().getTypeConverter ();
115
- argumentConversionOccurred = ReflectionHelper .convertAllArguments (converter , functionArgs , method );
113
+ boolean argumentConversionOccurred = ReflectionHelper .convertAllArguments (converter , functionArgs , method );
116
114
if (method .isVarArgs ()) {
117
115
functionArgs = ReflectionHelper .setupArgumentsForVarargsInvocation (
118
116
method .getParameterTypes (), functionArgs );
119
117
}
118
+ boolean compilable = false ;
120
119
121
120
try {
122
121
ReflectionUtils .makeAccessible (method );
123
122
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 ;
128
124
return new TypedValue (result , new TypeDescriptor (new MethodParameter (method , -1 )).narrow (result ));
129
125
}
130
126
catch (Exception ex ) {
131
127
throw new SpelEvaluationException (getStartPosition (), ex , SpelMessage .EXCEPTION_DURING_FUNCTION_CALL ,
132
128
this .name , ex .getMessage ());
133
129
}
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
+ }
134
140
}
135
141
136
142
@ Override
@@ -162,12 +168,13 @@ private Object[] getArguments(ExpressionState state) throws EvaluationException
162
168
163
169
@ Override
164
170
public boolean isCompilable () {
165
- if (this .method == null || this .argumentConversionOccurred ) {
171
+ Method method = this .method ;
172
+ if (method == null ) {
166
173
return false ;
167
174
}
168
- int methodModifiers = this . method .getModifiers ();
175
+ int methodModifiers = method .getModifiers ();
169
176
if (!Modifier .isStatic (methodModifiers ) || !Modifier .isPublic (methodModifiers ) ||
170
- !Modifier .isPublic (this . method .getDeclaringClass ().getModifiers ())) {
177
+ !Modifier .isPublic (method .getDeclaringClass ().getModifiers ())) {
171
178
return false ;
172
179
}
173
180
for (SpelNodeImpl child : this .children ) {
@@ -179,12 +186,13 @@ public boolean isCompilable() {
179
186
}
180
187
181
188
@ 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 );
188
196
cf .pushDescriptor (this .exitTypeDescriptor );
189
197
}
190
198
0 commit comments