@@ -83,11 +83,7 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
83
83
TypeDescriptor targetType = state .getActiveContextObject ().getTypeDescriptor ();
84
84
Object [] arguments = getArguments (state );
85
85
TypedValue result = getValueInternal (evaluationContext , value , targetType , arguments );
86
- if (cachedExecutor .get () instanceof ReflectiveMethodExecutor ) {
87
- ReflectiveMethodExecutor executor = (ReflectiveMethodExecutor ) cachedExecutor .get ();
88
- Method method = executor .getMethod ();
89
- exitTypeDescriptor = CodeFlow .toDescriptor (method .getReturnType ());
90
- }
86
+ updateExitTypeDescriptor ();
91
87
return result ;
92
88
}
93
89
@@ -172,7 +168,9 @@ private List<TypeDescriptor> getArgumentTypes(Object... arguments) {
172
168
return Collections .unmodifiableList (descriptors );
173
169
}
174
170
175
- private MethodExecutor getCachedExecutor (EvaluationContext evaluationContext , Object value , TypeDescriptor target , List <TypeDescriptor > argumentTypes ) {
171
+ private MethodExecutor getCachedExecutor (EvaluationContext evaluationContext , Object value ,
172
+ TypeDescriptor target , List <TypeDescriptor > argumentTypes ) {
173
+
176
174
List <MethodResolver > methodResolvers = evaluationContext .getMethodResolvers ();
177
175
if (methodResolvers == null || methodResolvers .size () != 1 ||
178
176
!(methodResolvers .get (0 ) instanceof ReflectiveMethodResolver )) {
@@ -230,6 +228,14 @@ private void throwSimpleExceptionIfPossible(Object value, AccessException ae) {
230
228
}
231
229
}
232
230
231
+ private void updateExitTypeDescriptor () {
232
+ CachedMethodExecutor executorToCheck = this .cachedExecutor ;
233
+ if (executorToCheck .get () instanceof ReflectiveMethodExecutor ) {
234
+ Method method = ((ReflectiveMethodExecutor ) executorToCheck .get ()).getMethod ();
235
+ this .exitTypeDescriptor = CodeFlow .toDescriptor (method .getReturnType ());
236
+ }
237
+ }
238
+
233
239
@ Override
234
240
public String toStringAST () {
235
241
StringBuilder sb = new StringBuilder ();
@@ -244,6 +250,80 @@ public String toStringAST() {
244
250
return sb .toString ();
245
251
}
246
252
253
+ /**
254
+ * A method reference is compilable if it has been resolved to a reflectively accessible method
255
+ * and the child nodes (arguments to the method) are also compilable.
256
+ */
257
+ @ Override
258
+ public boolean isCompilable () {
259
+ CachedMethodExecutor executorToCheck = this .cachedExecutor ;
260
+ if (executorToCheck == null || !(executorToCheck .get () instanceof ReflectiveMethodExecutor )) {
261
+ return false ;
262
+ }
263
+
264
+ for (SpelNodeImpl child : this .children ) {
265
+ if (!child .isCompilable ()) {
266
+ return false ;
267
+ }
268
+ }
269
+
270
+ ReflectiveMethodExecutor executor = (ReflectiveMethodExecutor ) executorToCheck .get ();
271
+ Method method = executor .getMethod ();
272
+ if (!Modifier .isPublic (method .getModifiers ()) || !Modifier .isPublic (method .getDeclaringClass ().getModifiers ())) {
273
+ return false ;
274
+ }
275
+ if (method .isVarArgs ()) {
276
+ return false ;
277
+ }
278
+ if (executor .didArgumentConversionOccur ()) {
279
+ return false ;
280
+ }
281
+
282
+ return true ;
283
+ }
284
+
285
+ @ Override
286
+ public void generateCode (MethodVisitor mv , CodeFlow codeflow ) {
287
+ CachedMethodExecutor executorToCheck = this .cachedExecutor ;
288
+ if (executorToCheck == null || !(executorToCheck .get () instanceof ReflectiveMethodExecutor )) {
289
+ throw new IllegalStateException ("No applicable cached executor found: " + executorToCheck );
290
+ }
291
+
292
+ Method method = ((ReflectiveMethodExecutor ) executorToCheck .get ()).getMethod ();
293
+ boolean isStaticMethod = Modifier .isStatic (method .getModifiers ());
294
+ String descriptor = codeflow .lastDescriptor ();
295
+
296
+ if (descriptor == null && !isStaticMethod ) {
297
+ codeflow .loadTarget (mv );
298
+ }
299
+
300
+ boolean itf = method .getDeclaringClass ().isInterface ();
301
+ String methodDeclaringClassSlashedDescriptor = method .getDeclaringClass ().getName ().replace ('.' , '/' );
302
+ if (!isStaticMethod ) {
303
+ if (descriptor == null || !descriptor .equals (methodDeclaringClassSlashedDescriptor )) {
304
+ mv .visitTypeInsn (CHECKCAST , methodDeclaringClassSlashedDescriptor );
305
+ }
306
+ }
307
+ String [] paramDescriptors = CodeFlow .toParamDescriptors (method );
308
+ for (int i = 0 ; i < this .children .length ;i ++) {
309
+ SpelNodeImpl child = this .children [i ];
310
+ codeflow .enterCompilationScope ();
311
+ child .generateCode (mv , codeflow );
312
+ // Check if need to box it for the method reference?
313
+ if (CodeFlow .isPrimitive (codeflow .lastDescriptor ()) && paramDescriptors [i ].charAt (0 ) == 'L' ) {
314
+ CodeFlow .insertBoxIfNecessary (mv , codeflow .lastDescriptor ().charAt (0 ));
315
+ }
316
+ else if (!codeflow .lastDescriptor ().equals (paramDescriptors [i ])) {
317
+ // This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
318
+ CodeFlow .insertCheckCast (mv , paramDescriptors [i ]);
319
+ }
320
+ codeflow .exitCompilationScope ();
321
+ }
322
+ mv .visitMethodInsn (isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL ,
323
+ methodDeclaringClassSlashedDescriptor , method .getName (), CodeFlow .createSignatureDescriptor (method ), itf );
324
+ codeflow .pushDescriptor (this .exitTypeDescriptor );
325
+ }
326
+
247
327
248
328
private class MethodValueRef implements ValueRef {
249
329
@@ -264,12 +344,9 @@ public MethodValueRef(ExpressionState state, Object[] arguments) {
264
344
265
345
@ Override
266
346
public TypedValue getValue () {
267
- TypedValue result = MethodReference .this .getValueInternal (this .evaluationContext , this .value , this .targetType , this .arguments );
268
- if (MethodReference .this .cachedExecutor .get () instanceof ReflectiveMethodExecutor ) {
269
- ReflectiveMethodExecutor executor = (ReflectiveMethodExecutor ) MethodReference .this .cachedExecutor .get ();
270
- Method method = executor .getMethod ();
271
- MethodReference .this .exitTypeDescriptor = CodeFlow .toDescriptor (method .getReturnType ());
272
- }
347
+ TypedValue result = MethodReference .this .getValueInternal (
348
+ this .evaluationContext , this .value , this .targetType , this .arguments );
349
+ updateExitTypeDescriptor ();
273
350
return result ;
274
351
}
275
352
@@ -313,70 +390,4 @@ public MethodExecutor get() {
313
390
}
314
391
}
315
392
316
- /**
317
- * A method reference is compilable if it has been resolved to a reflectively accessible method
318
- * and the child nodes (arguments to the method) are also compilable.
319
- */
320
- @ Override
321
- public boolean isCompilable () {
322
- if (this .cachedExecutor == null || !(this .cachedExecutor .get () instanceof ReflectiveMethodExecutor )) {
323
- return false ;
324
- }
325
- for (SpelNodeImpl child : children ) {
326
- if (!child .isCompilable ()) {
327
- return false ;
328
- }
329
- }
330
- ReflectiveMethodExecutor executor = (ReflectiveMethodExecutor ) this .cachedExecutor .get ();
331
- Method method = executor .getMethod ();
332
- if (!Modifier .isPublic (method .getModifiers ()) || !Modifier .isPublic (method .getDeclaringClass ().getModifiers ())) {
333
- return false ;
334
- }
335
- if (method .isVarArgs ()) {
336
- return false ;
337
- }
338
- if (executor .didArgumentConversionOccur ()) {
339
- return false ;
340
- }
341
- return true ;
342
- }
343
-
344
- @ Override
345
- public void generateCode (MethodVisitor mv ,CodeFlow codeflow ) {
346
- ReflectiveMethodExecutor executor = (ReflectiveMethodExecutor ) this .cachedExecutor .get ();
347
- Method method = executor .getMethod ();
348
- boolean isStaticMethod = Modifier .isStatic (method .getModifiers ());
349
- String descriptor = codeflow .lastDescriptor ();
350
-
351
- if (descriptor == null && !isStaticMethod ) {
352
- codeflow .loadTarget (mv );
353
- }
354
-
355
- boolean itf = method .getDeclaringClass ().isInterface ();
356
- String methodDeclaringClassSlashedDescriptor = method .getDeclaringClass ().getName ().replace ('.' ,'/' );
357
- if (!isStaticMethod ) {
358
- if (descriptor == null || !descriptor .equals (methodDeclaringClassSlashedDescriptor )) {
359
- mv .visitTypeInsn (CHECKCAST , methodDeclaringClassSlashedDescriptor );
360
- }
361
- }
362
- String [] paramDescriptors = CodeFlow .toParamDescriptors (method );
363
- for (int c = 0 ; c < this .children .length ;c ++) {
364
- SpelNodeImpl child = this .children [c ];
365
- codeflow .enterCompilationScope ();
366
- child .generateCode (mv , codeflow );
367
- // Check if need to box it for the method reference?
368
- if (CodeFlow .isPrimitive (codeflow .lastDescriptor ()) && (paramDescriptors [c ].charAt (0 )=='L' )) {
369
- CodeFlow .insertBoxIfNecessary (mv , codeflow .lastDescriptor ().charAt (0 ));
370
- }
371
- else if (!codeflow .lastDescriptor ().equals (paramDescriptors [c ])) {
372
- // This would be unnecessary in the case of subtyping (e.g. method takes a Number but passed in is an Integer)
373
- CodeFlow .insertCheckCast (mv , paramDescriptors [c ]);
374
- }
375
- codeflow .exitCompilationScope ();
376
- }
377
- mv .visitMethodInsn (isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL ,
378
- methodDeclaringClassSlashedDescriptor , method .getName (), CodeFlow .createSignatureDescriptor (method ), itf );
379
- codeflow .pushDescriptor (this .exitTypeDescriptor );
380
- }
381
-
382
393
}
0 commit comments