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.
4747 * (right now), so the names must be unique.
4848 *
4949 * @author Andy Clement
50+ * @author Juergen Hoeller
5051 * @since 3.0
5152 */
5253public 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