Resolved package tangle in spring-expression
This commit is contained in:
parent
a40e42479c
commit
28a3cd50aa
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.expression.spel;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -26,7 +25,6 @@ import java.util.Stack;
|
|||
import org.springframework.asm.ClassWriter;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -646,7 +644,12 @@ public class CodeFlow implements Opcodes {
|
|||
return toDescriptors(ctor.getParameterTypes());
|
||||
}
|
||||
|
||||
private static String[] toDescriptors(Class<?>[] types) {
|
||||
/**
|
||||
* Create an array of descriptors from an array of classes.
|
||||
* @param types the input array of classes
|
||||
* @return an array of descriptors
|
||||
*/
|
||||
public static String[] toDescriptors(Class<?>[] types) {
|
||||
int typesCount = types.length;
|
||||
String[] descriptors = new String[typesCount];
|
||||
for (int p = 0; p < typesCount; p++) {
|
||||
|
@ -831,90 +834,6 @@ public class CodeFlow implements Opcodes {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate code that handles building the argument values for the specified method. This method will take account
|
||||
* of whether the invoked method is a varargs method and if it is then the argument values will be appropriately
|
||||
* packaged into an array.
|
||||
* @param mv the method visitor where code should be generated
|
||||
* @param cf the current codeflow
|
||||
* @param member the method or constructor for which arguments are being setup
|
||||
* @param arguments the expression nodes for the expression supplied argument values
|
||||
*/
|
||||
public static void generateCodeForArguments(MethodVisitor mv, CodeFlow cf, Member member, SpelNodeImpl[] arguments) {
|
||||
String[] paramDescriptors = null;
|
||||
boolean isVarargs = false;
|
||||
if (member instanceof Constructor) {
|
||||
Constructor<?> ctor = (Constructor<?>)member;
|
||||
paramDescriptors = toDescriptors(ctor.getParameterTypes());
|
||||
isVarargs = ctor.isVarArgs();
|
||||
}
|
||||
else { // Method
|
||||
Method method = (Method)member;
|
||||
paramDescriptors = toDescriptors(method.getParameterTypes());
|
||||
isVarargs = method.isVarArgs();
|
||||
}
|
||||
if (isVarargs) {
|
||||
// The final parameter may or may not need packaging into an array, or nothing may
|
||||
// have been passed to satisfy the varargs and so something needs to be built.
|
||||
int p = 0; // Current supplied argument being processed
|
||||
int childcount = arguments.length;
|
||||
|
||||
// Fulfill all the parameter requirements except the last one
|
||||
for (p = 0; p < paramDescriptors.length-1;p++) {
|
||||
generateCodeForArgument(mv, cf, arguments[p], paramDescriptors[p]);
|
||||
}
|
||||
|
||||
SpelNodeImpl lastchild = (childcount == 0 ? null : arguments[childcount-1]);
|
||||
String arraytype = paramDescriptors[paramDescriptors.length-1];
|
||||
// Determine if the final passed argument is already suitably packaged in array
|
||||
// form to be passed to the method
|
||||
if (lastchild != null && lastchild.getExitDescriptor().equals(arraytype)) {
|
||||
generateCodeForArgument(mv, cf, lastchild, paramDescriptors[p]);
|
||||
}
|
||||
else {
|
||||
arraytype = arraytype.substring(1); // trim the leading '[', may leave other '['
|
||||
// build array big enough to hold remaining arguments
|
||||
CodeFlow.insertNewArrayCode(mv, childcount-p, arraytype);
|
||||
// Package up the remaining arguments into the array
|
||||
int arrayindex = 0;
|
||||
while (p < childcount) {
|
||||
SpelNodeImpl child = arguments[p];
|
||||
mv.visitInsn(DUP);
|
||||
CodeFlow.insertOptimalLoad(mv, arrayindex++);
|
||||
generateCodeForArgument(mv, cf, child, arraytype);
|
||||
CodeFlow.insertArrayStore(mv, arraytype);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < paramDescriptors.length;i++) {
|
||||
generateCodeForArgument(mv, cf, arguments[i], paramDescriptors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask an argument to generate its bytecode and then follow it up
|
||||
* with any boxing/unboxing/checkcasting to ensure it matches the expected parameter descriptor.
|
||||
*/
|
||||
public static void generateCodeForArgument(MethodVisitor mv, CodeFlow cf, SpelNodeImpl argument, String paramDescriptor) {
|
||||
cf.enterCompilationScope();
|
||||
argument.generateCode(mv, cf);
|
||||
boolean primitiveOnStack = CodeFlow.isPrimitive(cf.lastDescriptor());
|
||||
// Check if need to box it for the method reference?
|
||||
if (primitiveOnStack && paramDescriptor.charAt(0) == 'L') {
|
||||
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
|
||||
}
|
||||
else if (paramDescriptor.length() == 1 && !primitiveOnStack) {
|
||||
CodeFlow.insertUnboxInsns(mv, paramDescriptor.charAt(0), cf.lastDescriptor());
|
||||
}
|
||||
else if (!cf.lastDescriptor().equals(paramDescriptor)) {
|
||||
// This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
|
||||
CodeFlow.insertCheckCast(mv, paramDescriptor);
|
||||
}
|
||||
cf.exitCompilationScope();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -451,7 +451,7 @@ public class ConstructorReference extends SpelNodeImpl {
|
|||
// children[0] is the type of the constructor, don't want to include that in argument processing
|
||||
SpelNodeImpl[] arguments = new SpelNodeImpl[children.length-1];
|
||||
System.arraycopy(children, 1, arguments, 0, children.length-1);
|
||||
CodeFlow.generateCodeForArguments(mv, cf, constructor, arguments);
|
||||
generateCodeForArguments(mv, cf, constructor, arguments);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, classSlashedDescriptor, "<init>",
|
||||
CodeFlow.createSignatureDescriptor(constructor), false);
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
|
|
|
@ -180,7 +180,7 @@ public class FunctionReference extends SpelNodeImpl {
|
|||
@Override
|
||||
public void generateCode(MethodVisitor mv,CodeFlow cf) {
|
||||
String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.', '/');
|
||||
CodeFlow.generateCodeForArguments(mv, cf, method, this.children);
|
||||
generateCodeForArguments(mv, cf, method, this.children);
|
||||
mv.visitMethodInsn(INVOKESTATIC, methodDeclaringClassSlashedDescriptor, this.method.getName(),
|
||||
CodeFlow.createSignatureDescriptor(this.method), false);
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
|
|
|
@ -313,7 +313,7 @@ public class MethodReference extends SpelNodeImpl {
|
|||
CodeFlow.insertCheckCast(mv, "L"+ methodDeclaringClassSlashedDescriptor);
|
||||
}
|
||||
}
|
||||
CodeFlow.generateCodeForArguments(mv, cf, method, children);
|
||||
generateCodeForArguments(mv, cf, method, children);
|
||||
mv.visitMethodInsn(isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL,
|
||||
methodDeclaringClassSlashedDescriptor, method.getName(), CodeFlow.createSignatureDescriptor(method), itf);
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
|
@ -208,4 +212,89 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
|||
|
||||
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
|
||||
|
||||
|
||||
/**
|
||||
* Generate code that handles building the argument values for the specified method. This method will take account
|
||||
* of whether the invoked method is a varargs method and if it is then the argument values will be appropriately
|
||||
* packaged into an array.
|
||||
* @param mv the method visitor where code should be generated
|
||||
* @param cf the current codeflow
|
||||
* @param member the method or constructor for which arguments are being setup
|
||||
* @param arguments the expression nodes for the expression supplied argument values
|
||||
*/
|
||||
protected static void generateCodeForArguments(MethodVisitor mv, CodeFlow cf, Member member, SpelNodeImpl[] arguments) {
|
||||
String[] paramDescriptors = null;
|
||||
boolean isVarargs = false;
|
||||
if (member instanceof Constructor) {
|
||||
Constructor<?> ctor = (Constructor<?>)member;
|
||||
paramDescriptors = CodeFlow.toDescriptors(ctor.getParameterTypes());
|
||||
isVarargs = ctor.isVarArgs();
|
||||
}
|
||||
else { // Method
|
||||
Method method = (Method)member;
|
||||
paramDescriptors = CodeFlow.toDescriptors(method.getParameterTypes());
|
||||
isVarargs = method.isVarArgs();
|
||||
}
|
||||
if (isVarargs) {
|
||||
// The final parameter may or may not need packaging into an array, or nothing may
|
||||
// have been passed to satisfy the varargs and so something needs to be built.
|
||||
int p = 0; // Current supplied argument being processed
|
||||
int childcount = arguments.length;
|
||||
|
||||
// Fulfill all the parameter requirements except the last one
|
||||
for (p = 0; p < paramDescriptors.length-1;p++) {
|
||||
generateCodeForArgument(mv, cf, arguments[p], paramDescriptors[p]);
|
||||
}
|
||||
|
||||
SpelNodeImpl lastchild = (childcount == 0 ? null : arguments[childcount-1]);
|
||||
String arraytype = paramDescriptors[paramDescriptors.length-1];
|
||||
// Determine if the final passed argument is already suitably packaged in array
|
||||
// form to be passed to the method
|
||||
if (lastchild != null && lastchild.getExitDescriptor().equals(arraytype)) {
|
||||
generateCodeForArgument(mv, cf, lastchild, paramDescriptors[p]);
|
||||
}
|
||||
else {
|
||||
arraytype = arraytype.substring(1); // trim the leading '[', may leave other '['
|
||||
// build array big enough to hold remaining arguments
|
||||
CodeFlow.insertNewArrayCode(mv, childcount-p, arraytype);
|
||||
// Package up the remaining arguments into the array
|
||||
int arrayindex = 0;
|
||||
while (p < childcount) {
|
||||
SpelNodeImpl child = arguments[p];
|
||||
mv.visitInsn(DUP);
|
||||
CodeFlow.insertOptimalLoad(mv, arrayindex++);
|
||||
generateCodeForArgument(mv, cf, child, arraytype);
|
||||
CodeFlow.insertArrayStore(mv, arraytype);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < paramDescriptors.length;i++) {
|
||||
generateCodeForArgument(mv, cf, arguments[i], paramDescriptors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask an argument to generate its bytecode and then follow it up
|
||||
* with any boxing/unboxing/checkcasting to ensure it matches the expected parameter descriptor.
|
||||
*/
|
||||
protected static void generateCodeForArgument(MethodVisitor mv, CodeFlow cf, SpelNodeImpl argument, String paramDescriptor) {
|
||||
cf.enterCompilationScope();
|
||||
argument.generateCode(mv, cf);
|
||||
boolean primitiveOnStack = CodeFlow.isPrimitive(cf.lastDescriptor());
|
||||
// Check if need to box it for the method reference?
|
||||
if (primitiveOnStack && paramDescriptor.charAt(0) == 'L') {
|
||||
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
|
||||
}
|
||||
else if (paramDescriptor.length() == 1 && !primitiveOnStack) {
|
||||
CodeFlow.insertUnboxInsns(mv, paramDescriptor.charAt(0), cf.lastDescriptor());
|
||||
}
|
||||
else if (!cf.lastDescriptor().equals(paramDescriptor)) {
|
||||
// This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
|
||||
CodeFlow.insertCheckCast(mv, paramDescriptor);
|
||||
}
|
||||
cf.exitCompilationScope();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue