Resolved package tangle in spring-expression
This commit is contained in:
parent
a40e42479c
commit
28a3cd50aa
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.expression.spel;
|
package org.springframework.expression.spel;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Member;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -26,7 +25,6 @@ import java.util.Stack;
|
||||||
import org.springframework.asm.ClassWriter;
|
import org.springframework.asm.ClassWriter;
|
||||||
import org.springframework.asm.MethodVisitor;
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -646,7 +644,12 @@ public class CodeFlow implements Opcodes {
|
||||||
return toDescriptors(ctor.getParameterTypes());
|
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;
|
int typesCount = types.length;
|
||||||
String[] descriptors = new String[typesCount];
|
String[] descriptors = new String[typesCount];
|
||||||
for (int p = 0; p < typesCount; p++) {
|
for (int p = 0; p < typesCount; p++) {
|
||||||
|
@ -832,89 +835,5 @@ 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
|
// children[0] is the type of the constructor, don't want to include that in argument processing
|
||||||
SpelNodeImpl[] arguments = new SpelNodeImpl[children.length-1];
|
SpelNodeImpl[] arguments = new SpelNodeImpl[children.length-1];
|
||||||
System.arraycopy(children, 1, arguments, 0, 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>",
|
mv.visitMethodInsn(INVOKESPECIAL, classSlashedDescriptor, "<init>",
|
||||||
CodeFlow.createSignatureDescriptor(constructor), false);
|
CodeFlow.createSignatureDescriptor(constructor), false);
|
||||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||||
|
|
|
@ -180,7 +180,7 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
@Override
|
@Override
|
||||||
public void generateCode(MethodVisitor mv,CodeFlow cf) {
|
public void generateCode(MethodVisitor mv,CodeFlow cf) {
|
||||||
String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.', '/');
|
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(),
|
mv.visitMethodInsn(INVOKESTATIC, methodDeclaringClassSlashedDescriptor, this.method.getName(),
|
||||||
CodeFlow.createSignatureDescriptor(this.method), false);
|
CodeFlow.createSignatureDescriptor(this.method), false);
|
||||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||||
|
|
|
@ -313,7 +313,7 @@ public class MethodReference extends SpelNodeImpl {
|
||||||
CodeFlow.insertCheckCast(mv, "L"+ methodDeclaringClassSlashedDescriptor);
|
CodeFlow.insertCheckCast(mv, "L"+ methodDeclaringClassSlashedDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CodeFlow.generateCodeForArguments(mv, cf, method, children);
|
generateCodeForArguments(mv, cf, method, children);
|
||||||
mv.visitMethodInsn(isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL,
|
mv.visitMethodInsn(isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL,
|
||||||
methodDeclaringClassSlashedDescriptor, method.getName(), CodeFlow.createSignatureDescriptor(method), itf);
|
methodDeclaringClassSlashedDescriptor, method.getName(), CodeFlow.createSignatureDescriptor(method), itf);
|
||||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.expression.spel.ast;
|
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.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
|
@ -208,4 +212,89 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
||||||
|
|
||||||
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
|
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