16
16
17
17
package org .springframework .expression .spel .ast ;
18
18
19
- import java .lang .reflect .Constructor ;
19
+ import java .lang .reflect .Executable ;
20
20
import java .lang .reflect .Member ;
21
- import java .lang .reflect .Method ;
22
21
import java .util .function .Supplier ;
23
22
24
23
import org .springframework .asm .MethodVisitor ;
25
24
import org .springframework .asm .Opcodes ;
25
+ import org .springframework .asm .Type ;
26
26
import org .springframework .expression .EvaluationException ;
27
27
import org .springframework .expression .TypedValue ;
28
28
import org .springframework .expression .common .ExpressionUtils ;
33
33
import org .springframework .expression .spel .SpelNode ;
34
34
import org .springframework .lang .Nullable ;
35
35
import org .springframework .util .Assert ;
36
+ import org .springframework .util .ClassUtils ;
36
37
import org .springframework .util .ObjectUtils ;
38
+ import org .springframework .util .StringUtils ;
37
39
38
40
/**
39
41
* The common supertype of all AST nodes in a parsed Spring Expression Language
@@ -216,20 +218,37 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException
216
218
* @param cf the current codeflow
217
219
* @param member the method or constructor for which arguments are being set up
218
220
* @param arguments the expression nodes for the expression supplied argument values
221
+ * @deprecated As of Spring Framework 6.2, in favor of
222
+ * {@link #generateCodeForArguments(MethodVisitor, CodeFlow, Executable, SpelNodeImpl[])}
219
223
*/
224
+ @ Deprecated (since = "6.2" )
220
225
protected static void generateCodeForArguments (MethodVisitor mv , CodeFlow cf , Member member , SpelNodeImpl [] arguments ) {
221
- String [] paramDescriptors = null ;
222
- boolean isVarargs = false ;
223
- if (member instanceof Constructor <?> ctor ) {
224
- paramDescriptors = CodeFlow .toDescriptors (ctor .getParameterTypes ());
225
- isVarargs = ctor .isVarArgs ();
226
+ if (member instanceof Executable executable ) {
227
+ generateCodeForArguments (mv , cf , executable , arguments );
226
228
}
227
- else { // Method
228
- Method method = (Method )member ;
229
- paramDescriptors = CodeFlow .toDescriptors (method .getParameterTypes ());
230
- isVarargs = method .isVarArgs ();
231
- }
232
- if (isVarargs ) {
229
+ throw new IllegalArgumentException (
230
+ "The supplied member must be an instance of java.lang.reflect.Executable: " + member );
231
+ }
232
+
233
+ /**
234
+ * Generate code that handles building the argument values for the specified
235
+ * {@link Executable} (method or constructor).
236
+ * <p>This method takes into account whether the invoked executable was
237
+ * declared to accept varargs, and if it was then the argument values will be
238
+ * appropriately packaged into an array.
239
+ * @param mv the method visitor where code should be generated
240
+ * @param cf the current {@link CodeFlow}
241
+ * @param executable the {@link Executable} (method or constructor) for which
242
+ * arguments are being set up
243
+ * @param arguments the expression nodes for the expression supplied argument
244
+ * values
245
+ * @since 6.2
246
+ */
247
+ protected static void generateCodeForArguments (MethodVisitor mv , CodeFlow cf , Executable executable , SpelNodeImpl [] arguments ) {
248
+ Class <?>[] parameterTypes = executable .getParameterTypes ();
249
+ String [] paramDescriptors = CodeFlow .toDescriptors (parameterTypes );
250
+
251
+ if (executable .isVarArgs ()) {
233
252
// The final parameter may or may not need packaging into an array, or nothing may
234
253
// have been passed to satisfy the varargs and so something needs to be built.
235
254
int p = 0 ; // Current supplied argument being processed
@@ -241,13 +260,18 @@ protected static void generateCodeForArguments(MethodVisitor mv, CodeFlow cf, Me
241
260
}
242
261
243
262
SpelNodeImpl lastChild = (childCount == 0 ? null : arguments [childCount - 1 ]);
244
- String arrayType = paramDescriptors [paramDescriptors .length - 1 ];
263
+ ClassLoader classLoader = executable .getDeclaringClass ().getClassLoader ();
264
+ Class <?> lastChildType = (lastChild != null ?
265
+ loadClassForExitDescriptor (lastChild .getExitDescriptor (), classLoader ) : null );
266
+ Class <?> lastParameterType = parameterTypes [parameterTypes .length - 1 ];
267
+
245
268
// Determine if the final passed argument is already suitably packaged in array
246
269
// form to be passed to the method
247
- if (lastChild != null && arrayType . equals ( lastChild . getExitDescriptor () )) {
270
+ if (lastChild != null && lastChildType != null && lastParameterType . isAssignableFrom ( lastChildType )) {
248
271
cf .generateCodeForArgument (mv , lastChild , paramDescriptors [p ]);
249
272
}
250
273
else {
274
+ String arrayType = paramDescriptors [paramDescriptors .length - 1 ];
251
275
arrayType = arrayType .substring (1 ); // trim the leading '[', may leave other '['
252
276
// build array big enough to hold remaining arguments
253
277
CodeFlow .insertNewArrayCode (mv , childCount - p , arrayType );
@@ -270,6 +294,19 @@ protected static void generateCodeForArguments(MethodVisitor mv, CodeFlow cf, Me
270
294
}
271
295
}
272
296
297
+ @ Nullable
298
+ private static Class <?> loadClassForExitDescriptor (@ Nullable String exitDescriptor , ClassLoader classLoader ) {
299
+ if (!StringUtils .hasText (exitDescriptor )) {
300
+ return null ;
301
+ }
302
+ String typeDescriptor = exitDescriptor ;
303
+ if (typeDescriptor .startsWith ("[" ) || typeDescriptor .startsWith ("L" )) {
304
+ typeDescriptor += ";" ;
305
+ }
306
+ String className = Type .getType (typeDescriptor ).getClassName ();
307
+ return ClassUtils .resolveClassName (className , classLoader );
308
+ }
309
+
273
310
/**
274
311
* Ask an argument to generate its bytecode and then follow it up
275
312
* with any boxing/unboxing/checkcasting to ensure it matches the expected parameter descriptor.
0 commit comments