Polishing (backported from master)

This commit is contained in:
Juergen Hoeller
2015-12-14 00:47:33 +01:00
parent b64d752906
commit 8c8380715d
13 changed files with 242 additions and 218 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -445,15 +445,14 @@ public class ConstructorReference extends SpelNodeImpl {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
ReflectiveConstructorExecutor executor = ((ReflectiveConstructorExecutor) this.cachedExecutor);
Constructor<?> constructor = executor.getConstructor();
String classSlashedDescriptor = constructor.getDeclaringClass().getName().replace('.', '/');
mv.visitTypeInsn(NEW, classSlashedDescriptor);
String classDesc = constructor.getDeclaringClass().getName().replace('.', '/');
mv.visitTypeInsn(NEW, classDesc);
mv.visitInsn(DUP);
// 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);
SpelNodeImpl[] arguments = new SpelNodeImpl[children.length - 1];
System.arraycopy(children, 1, arguments, 0, children.length - 1);
generateCodeForArguments(mv, cf, constructor, arguments);
mv.visitMethodInsn(INVOKESPECIAL, classSlashedDescriptor, "<init>",
CodeFlow.createSignatureDescriptor(constructor), false);
mv.visitMethodInsn(INVOKESPECIAL, classDesc, "<init>", CodeFlow.createSignatureDescriptor(constructor), false);
cf.pushDescriptor(this.exitTypeDescriptor);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,15 +33,15 @@ import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.util.ReflectionUtils;
/**
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in
* the context prior to the expression being evaluated or within the expression itself
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined
* in the context prior to the expression being evaluated or within the expression itself
* using a lambda function definition. For example: Lambda function definition in an
* expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
* "#isEven(37)". Functions may also be static java methods, registered in the context
* prior to invocation of the expression.
*
* <p>Functions are very simplistic, the arguments are not part of the definition (right
* now), so the names must be unique.
* <p>Functions are very simplistic, the arguments are not part of the definition
* (right now), so the names must be unique.
*
* @author Andy Clement
* @since 3.0
@@ -72,7 +72,8 @@ public class FunctionReference extends SpelNodeImpl {
// Two possibilities: a lambda function or a Java static method registered as a function
if (!(value.getValue() instanceof Method)) {
throw new SpelEvaluationException(SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, value.getClass());
throw new SpelEvaluationException(
SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, value.getClass());
}
try {
@@ -113,7 +114,8 @@ public class FunctionReference extends SpelNodeImpl {
argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
}
if (method.isVarArgs()) {
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
functionArgs =
ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
}
try {
@@ -160,13 +162,12 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public boolean isCompilable() {
if (this.method == null || argumentConversionOccurred) {
if (this.method == null || this.argumentConversionOccurred) {
return false;
}
int methodModifiers = this.method.getModifiers();
if (!Modifier.isStatic(methodModifiers) ||
!Modifier.isPublic(methodModifiers) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
if (!Modifier.isStatic(methodModifiers) || !Modifier.isPublic(methodModifiers) ||
!Modifier.isPublic(this.method.getDeclaringClass().getModifiers())) {
return false;
}
for (SpelNodeImpl child : this.children) {
@@ -179,9 +180,9 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public void generateCode(MethodVisitor mv,CodeFlow cf) {
String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.', '/');
generateCodeForArguments(mv, cf, method, this.children);
mv.visitMethodInsn(INVOKESTATIC, methodDeclaringClassSlashedDescriptor, this.method.getName(),
String classDesc = this.method.getDeclaringClass().getName().replace('.', '/');
generateCodeForArguments(mv, cf, this.method, this.children);
mv.visitMethodInsn(INVOKESTATIC, classDesc, this.method.getName(),
CodeFlow.createSignatureDescriptor(this.method), false);
cf.pushDescriptor(this.exitTypeDescriptor);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -248,6 +248,7 @@ public class Indexer extends SpelNodeImpl {
cf.exitCompilationScope();
mv.visitInsn(insn);
}
else if (this.indexedType == IndexedType.LIST) {
mv.visitTypeInsn(CHECKCAST, "java/util/List");
cf.enterCompilationScope();
@@ -255,6 +256,7 @@ public class Indexer extends SpelNodeImpl {
cf.exitCompilationScope();
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
}
else if (this.indexedType == IndexedType.MAP) {
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
// Special case when the key is an unquoted string literal that will be parsed as
@@ -271,27 +273,30 @@ public class Indexer extends SpelNodeImpl {
}
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
}
else if (this.indexedType == IndexedType.OBJECT) {
ReflectivePropertyAccessor.OptimalPropertyAccessor accessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) this.cachedReadAccessor;
Member member = accessor.member;
boolean isStatic = Modifier.isStatic(member.getModifiers());
String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.', '/');
String classDesc = member.getDeclaringClass().getName().replace('.', '/');
if (!isStatic) {
if (descriptor == null) {
cf.loadTarget(mv);
}
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
if (descriptor == null || !classDesc.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, classDesc);
}
}
if (member instanceof Field) {
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor,
member.getName(), CodeFlow.toJvmDescriptor(((Field) member).getType()));
if (member instanceof Method) {
mv.visitMethodInsn((isStatic? INVOKESTATIC : INVOKEVIRTUAL), classDesc, member.getName(),
CodeFlow.createSignatureDescriptor((Method) member), false);
}
else {
mv.visitMethodInsn(isStatic? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor,
member.getName(), CodeFlow.createSignatureDescriptor((Method) member), false);
mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, member.getName(),
CodeFlow.toJvmDescriptor(((Field) member).getType()));
}
}
@@ -555,12 +560,8 @@ public class Indexer extends SpelNodeImpl {
ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor;
Member member = optimalAccessor.member;
if (member instanceof Field) {
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(((Field)member).getType());
}
else {
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(((Method)member).getReturnType());
}
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(member instanceof Method ?
((Method) member).getReturnType() : ((Field) member).getType());
}
return accessor.read(this.evaluationContext, this.targetObject, this.name);
}

View File

@@ -239,15 +239,15 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
// 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;
int childCount = arguments.length;
// Fulfill all the parameter requirements except the last one
for (p = 0; p < paramDescriptors.length-1;p++) {
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];
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)) {
@@ -256,10 +256,10 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
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);
CodeFlow.insertNewArrayCode(mv, childCount - p, arraytype);
// Package up the remaining arguments into the array
int arrayindex = 0;
while (p < childcount) {
while (p < childCount) {
SpelNodeImpl child = arguments[p];
mv.visitInsn(DUP);
CodeFlow.insertOptimalLoad(mv, arrayindex++);
@@ -280,20 +280,20 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
* 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) {
protected static void generateCodeForArgument(MethodVisitor mv, CodeFlow cf, SpelNodeImpl argument, String paramDesc) {
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') {
if (primitiveOnStack && paramDesc.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 (paramDesc.length() == 1 && !primitiveOnStack) {
CodeFlow.insertUnboxInsns(mv, paramDesc.charAt(0), cf.lastDescriptor());
}
else if (!cf.lastDescriptor().equals(paramDescriptor)) {
else if (!cf.lastDescriptor().equals(paramDesc)) {
// This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
CodeFlow.insertCheckCast(mv, paramDescriptor);
CodeFlow.insertCheckCast(mv, paramDesc);
}
cf.exitCompilationScope();
}

View File

@@ -238,11 +238,13 @@ public class ReflectionHelper {
* @return true if some kind of conversion occurred on the argument
* @throws SpelEvaluationException if there is a problem with conversion
*/
public static boolean convertAllArguments(TypeConverter converter, Object[] arguments, Method method) throws SpelEvaluationException {
Integer varargsPosition = method.isVarArgs() ? method.getParameterTypes().length-1:null;
public static boolean convertAllArguments(TypeConverter converter, Object[] arguments, Method method)
throws SpelEvaluationException {
Integer varargsPosition = (method.isVarArgs() ? method.getParameterTypes().length - 1 : null);
return convertArguments(converter, arguments, method, varargsPosition);
}
/**
* Takes an input set of argument values and converts them to the types specified as the
* required parameter types. The arguments are converted 'in-place' in the input array.