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.
30
30
import org .springframework .expression .spel .SpelEvaluationException ;
31
31
import org .springframework .expression .spel .SpelMessage ;
32
32
import org .springframework .expression .spel .support .ReflectionHelper ;
33
+ import org .springframework .util .Assert ;
33
34
import org .springframework .util .ClassUtils ;
34
35
import org .springframework .util .ReflectionUtils ;
35
36
45
46
* (right now), so the names must be unique.
46
47
*
47
48
* @author Andy Clement
49
+ * @author Juergen Hoeller
48
50
* @since 3.0
49
51
*/
50
52
public class FunctionReference extends SpelNodeImpl {
@@ -53,9 +55,7 @@ public class FunctionReference extends SpelNodeImpl {
53
55
54
56
// Captures the most recently used method for the function invocation *if* the method
55
57
// 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 ;
59
59
60
60
61
61
public FunctionReference (String functionName , int pos , SpelNodeImpl ... arguments ) {
@@ -67,7 +67,7 @@ public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments
67
67
@ Override
68
68
public TypedValue getValueInternal (ExpressionState state ) throws EvaluationException {
69
69
TypedValue value = state .lookupVariable (this .name );
70
- if (value == null ) {
70
+ if (value == TypedValue . NULL ) {
71
71
throw new SpelEvaluationException (getStartPosition (), SpelMessage .FUNCTION_NOT_DEFINED , this .name );
72
72
}
73
73
@@ -87,50 +87,54 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
87
87
}
88
88
89
89
/**
90
- * Execute a function represented as a java.lang.reflect.Method.
90
+ * Execute a function represented as a {@code java.lang.reflect.Method} .
91
91
* @param state the expression evaluation state
92
92
* @param method the method to invoke
93
93
* @return the return value of the invoked Java method
94
94
* @throws EvaluationException if there is any problem invoking the method
95
95
*/
96
96
private TypedValue executeFunctionJLRMethod (ExpressionState state , Method method ) throws EvaluationException {
97
- this .method = null ;
98
97
Object [] functionArgs = getArguments (state );
99
98
100
- if (!method .isVarArgs () && method .getParameterTypes (). length != functionArgs .length ) {
99
+ if (!method .isVarArgs () && method .getParameterCount () != functionArgs .length ) {
101
100
throw new SpelEvaluationException (SpelMessage .INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION ,
102
- functionArgs .length , method .getParameterTypes (). length );
101
+ functionArgs .length , method .getParameterCount () );
103
102
}
104
103
// Only static methods can be called in this way
105
104
if (!Modifier .isStatic (method .getModifiers ())) {
106
105
throw new SpelEvaluationException (getStartPosition (),
107
106
SpelMessage .FUNCTION_MUST_BE_STATIC , ClassUtils .getQualifiedMethodName (method ), this .name );
108
107
}
109
108
110
- this .argumentConversionOccurred = false ;
111
109
// 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 );
116
112
if (method .isVarArgs ()) {
117
113
functionArgs = ReflectionHelper .setupArgumentsForVarargsInvocation (
118
114
method .getParameterTypes (), functionArgs );
119
115
}
116
+ boolean compilable = false ;
120
117
121
118
try {
122
119
ReflectionUtils .makeAccessible (method );
123
120
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 ;
128
122
return new TypedValue (result , new TypeDescriptor (new MethodParameter (method , -1 )).narrow (result ));
129
123
}
130
124
catch (Exception ex ) {
131
125
throw new SpelEvaluationException (getStartPosition (), ex , SpelMessage .EXCEPTION_DURING_FUNCTION_CALL ,
132
126
this .name , ex .getMessage ());
133
127
}
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
+ }
134
138
}
135
139
136
140
@ Override
@@ -162,12 +166,13 @@ private Object[] getArguments(ExpressionState state) throws EvaluationException
162
166
163
167
@ Override
164
168
public boolean isCompilable () {
165
- if (this .method == null || this .argumentConversionOccurred ) {
169
+ Method method = this .method ;
170
+ if (method == null ) {
166
171
return false ;
167
172
}
168
- int methodModifiers = this . method .getModifiers ();
173
+ int methodModifiers = method .getModifiers ();
169
174
if (!Modifier .isStatic (methodModifiers ) || !Modifier .isPublic (methodModifiers ) ||
170
- !Modifier .isPublic (this . method .getDeclaringClass ().getModifiers ())) {
175
+ !Modifier .isPublic (method .getDeclaringClass ().getModifiers ())) {
171
176
return false ;
172
177
}
173
178
for (SpelNodeImpl child : this .children ) {
@@ -179,11 +184,13 @@ public boolean isCompilable() {
179
184
}
180
185
181
186
@ 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 );
187
194
cf .pushDescriptor (this .exitTypeDescriptor );
188
195
}
189
196
0 commit comments