Comprehensive revision of SpEL's bytecode generation and number handling (BigInteger support, doubleValue fallback)

Issue: SPR-9913
This commit is contained in:
Juergen Hoeller 2014-10-06 23:29:43 +02:00
parent e58b33a593
commit d8160b3c09
66 changed files with 1458 additions and 1352 deletions

View File

@ -86,10 +86,10 @@ public class CodeFlow implements Opcodes {
}
/**
* @return the descriptor for the item currently on top of the stack (in the current scope)
* Return the descriptor for the item currently on top of the stack (in the current scope).
*/
public String lastDescriptor() {
if (this.compilationScopes.peek().size() == 0) {
if (this.compilationScopes.peek().isEmpty()) {
return null;
}
return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1);
@ -106,6 +106,7 @@ public class CodeFlow implements Opcodes {
}
}
/**
* Insert any necessary cast and value call to convert from a boxed type to a
* primitive value
@ -115,56 +116,56 @@ public class CodeFlow implements Opcodes {
*/
public static void insertUnboxInsns(MethodVisitor mv, char ch, String stackDescriptor) {
switch (ch) {
case 'I':
if (!stackDescriptor.equals("Ljava/lang/Integer")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
break;
case 'Z':
if (!stackDescriptor.equals("Ljava/lang/Boolean")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case 'B':
if (!stackDescriptor.equals("Ljava/lang/Byte")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
break;
case 'C':
if (!stackDescriptor.equals("Ljava/lang/Character")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case 'D':
if (!stackDescriptor.equals("Ljava/lang/Double")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case 'S':
if (!stackDescriptor.equals("Ljava/lang/Short")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
break;
case 'F':
if (!stackDescriptor.equals("Ljava/lang/Float")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
break;
case 'J':
if (!stackDescriptor.equals("Ljava/lang/Long")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
break;
default:
throw new IllegalArgumentException("Unboxing should not be attempted for descriptor '" + ch + "'");
case 'Z':
if (!stackDescriptor.equals("Ljava/lang/Boolean")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case 'B':
if (!stackDescriptor.equals("Ljava/lang/Byte")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
break;
case 'C':
if (!stackDescriptor.equals("Ljava/lang/Character")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case 'D':
if (!stackDescriptor.equals("Ljava/lang/Double")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case 'F':
if (!stackDescriptor.equals("Ljava/lang/Float")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
break;
case 'I':
if (!stackDescriptor.equals("Ljava/lang/Integer")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
break;
case 'J':
if (!stackDescriptor.equals("Ljava/lang/Long")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
break;
case 'S':
if (!stackDescriptor.equals("Ljava/lang/Short")) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
break;
default:
throw new IllegalArgumentException("Unboxing should not be attempted for descriptor '" + ch + "'");
}
}
@ -182,10 +183,10 @@ public class CodeFlow implements Opcodes {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (Class<?> param : params) {
sb.append(toJVMDescriptor(param));
sb.append(toJvmDescriptor(param));
}
sb.append(")");
sb.append(toJVMDescriptor(method.getReturnType()));
sb.append(toJvmDescriptor(method.getReturnType()));
return sb.toString();
}
@ -202,7 +203,7 @@ public class CodeFlow implements Opcodes {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (Class<?> param : params) {
sb.append(toJVMDescriptor(param));
sb.append(toJvmDescriptor(param));
}
sb.append(")V");
return sb.toString();
@ -216,7 +217,7 @@ public class CodeFlow implements Opcodes {
* @param clazz a class
* @return the JVM descriptor for the class
*/
public static String toJVMDescriptor(Class<?> clazz) {
public static String toJvmDescriptor(Class<?> clazz) {
StringBuilder sb = new StringBuilder();
if (clazz.isArray()) {
while (clazz.isArray()) {
@ -319,37 +320,37 @@ public class CodeFlow implements Opcodes {
return true;
}
if (desc1.length() == 1) {
if (desc1.equals("D")) {
if (desc1.equals("Z")) {
return desc2.equals("Ljava/lang/Boolean");
}
else if (desc1.equals("D")) {
return desc2.equals("Ljava/lang/Double");
}
else if (desc1.equals("F")) {
return desc2.equals("Ljava/lang/Float");
}
else if (desc1.equals("J")) {
return desc2.equals("Ljava/lang/Long");
}
else if (desc1.equals("I")) {
return desc2.equals("Ljava/lang/Integer");
}
else if (desc1.equals("Z")) {
return desc2.equals("Ljava/lang/Boolean");
else if (desc1.equals("J")) {
return desc2.equals("Ljava/lang/Long");
}
}
else if (desc2.length() == 1) {
if (desc2.equals("D")) {
if (desc2.equals("Z")) {
return desc1.equals("Ljava/lang/Boolean");
}
else if (desc2.equals("D")) {
return desc1.equals("Ljava/lang/Double");
}
else if (desc2.equals("F")) {
return desc1.equals("Ljava/lang/Float");
}
else if (desc2.equals("J")) {
return desc1.equals("Ljava/lang/Long");
}
else if (desc2.equals("I")) {
return desc1.equals("Ljava/lang/Integer");
}
else if (desc2.equals("Z")) {
return desc1.equals("Ljava/lang/Boolean");
else if (desc2.equals("J")) {
return desc1.equals("Ljava/lang/Long");
}
}
return false;
@ -366,17 +367,10 @@ public class CodeFlow implements Opcodes {
if (descriptor == null) {
return false;
}
if (descriptor.length( )== 1) {
return ("DFJZI".indexOf(descriptor.charAt(0)) != -1);
if (isPrimitiveOrUnboxableSupportedNumber(descriptor)) {
return true;
}
if (descriptor.startsWith("Ljava/lang/")) {
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") ||
descriptor.equals("Ljava/lang/Float") || descriptor.equals("Ljava/lang/Long") ||
descriptor.equals("Ljava/lang/Boolean")) {
return true;
}
}
return false;
return ("Z".equals(descriptor) || descriptor.equals("Ljava/lang/Boolean"));
}
/**
@ -391,17 +385,27 @@ public class CodeFlow implements Opcodes {
return false;
}
if (descriptor.length() == 1) {
return ("DFJI".indexOf(descriptor.charAt(0)) != -1);
return "DFIJ".contains(descriptor);
}
if (descriptor.startsWith("Ljava/lang/")) {
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") ||
descriptor.equals("Ljava/lang/Float") || descriptor.equals("Ljava/lang/Long")) {
String name = descriptor.substring("Ljava/lang/".length());
if (name.equals("Double") || name.equals("Float") || name.equals("Integer") || name.equals("Long")) {
return true;
}
}
return false;
}
/**
* Determine whether the given number is to be considered as an integer
* for the purposes of a numeric operation at the bytecode level.
* @param number the number to check
* @return {@code true} if it is an {@link Integer}, {@link Short} or {@link Byte}
*/
public static boolean isIntegerForNumericOp(Number number) {
return (number instanceof Integer || number instanceof Short || number instanceof Byte);
}
/**
* @param descriptor a descriptor for a type that should have a primitive representation
* @return the single character descriptor for a primitive input descriptor
@ -410,23 +414,32 @@ public class CodeFlow implements Opcodes {
if (descriptor.length() == 1) {
return descriptor.charAt(0);
}
if (descriptor.equals("Ljava/lang/Double")) {
return 'D';
else if (descriptor.equals("Ljava/lang/Boolean")) {
return 'Z';
}
else if (descriptor.equals("Ljava/lang/Integer")) {
return 'I';
else if (descriptor.equals("Ljava/lang/Byte")) {
return 'B';
}
else if (descriptor.equals("Ljava/lang/Character")) {
return 'C';
}
else if (descriptor.equals("Ljava/lang/Double")) {
return 'D';
}
else if (descriptor.equals("Ljava/lang/Float")) {
return 'F';
}
else if (descriptor.equals("Ljava/lang/Integer")) {
return 'I';
}
else if (descriptor.equals("Ljava/lang/Long")) {
return 'J';
}
else if (descriptor.equals("Ljava/lang/Boolean")) {
return 'Z';
else if (descriptor.equals("Ljava/lang/Short")) {
return 'S';
}
else {
throw new IllegalStateException("No primitive for '"+descriptor+"'");
throw new IllegalStateException("No primitive for '" + descriptor + "'");
}
}
@ -474,37 +487,37 @@ public class CodeFlow implements Opcodes {
*/
public static void insertBoxIfNecessary(MethodVisitor mv, char ch) {
switch (ch) {
case 'I':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
break;
case 'F':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
break;
case 'S':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
break;
case 'Z':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
break;
case 'J':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
break;
case 'D':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
break;
case 'C':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
break;
case 'B':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
break;
case 'L':
case 'Z':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
break;
case 'B':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
break;
case 'C':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
break;
case 'D':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
break;
case 'F':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
break;
case 'I':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
break;
case 'J':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
break;
case 'S':
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
break;
case 'L':
case 'V':
case '[':
// no box needed
break;
default:
throw new IllegalArgumentException("Boxing should not be attempted for descriptor '" + ch + "'");
case '[':
// no box needed
break;
default:
throw new IllegalArgumentException("Boxing should not be attempted for descriptor '" + ch + "'");
}
}
@ -522,14 +535,14 @@ public class CodeFlow implements Opcodes {
case 3:
return "I";
case 4:
if (name.equals("long")) {
return "J";
if (name.equals("byte")) {
return "B";
}
else if (name.equals("char")) {
return "C";
}
else if (name.equals("byte")) {
return "B";
else if (name.equals("long")) {
return "J";
}
else if (name.equals("void")) {
return "V";

View File

@ -45,8 +45,8 @@ public interface CompilablePropertyAccessor extends PropertyAccessor, Opcodes {
* using context information from the codeflow where necessary.
* @param propertyName the name of the property
* @param mv the Asm method visitor into which code should be generated
* @param codeflow the current state of the expression compiler
* @param cf the current state of the expression compiler
*/
void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow);
void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf);
}

View File

@ -20,9 +20,9 @@ import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
/**
* Base superclass for compiled expressions. Each generated compiled expression class will
* extend this class and implement one of the getValue() methods. It is not intended
* to subclassed by user code.
* Base superclass for compiled expressions. Each generated compiled expression class
* will extend this class and implement the {@link #getValue} method. It is not intended
* to be subclassed by user code.
*
* @author Andy Clement
* @since 4.1

View File

@ -16,8 +16,6 @@
package org.springframework.expression.spel;
import org.springframework.expression.spel.SpelParseException;
/**
* Wraps a real parse exception. This exception flows to the top parse method and then
* the wrapped exception is thrown as the real problem.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -28,16 +28,16 @@ import org.springframework.expression.TypedValue;
public interface SpelNode {
/**
* Evaluate the expression node in the context of the supplied expression state and
* return the value.
* Evaluate the expression node in the context of the supplied expression state
* and return the value.
* @param expressionState the current expression state (includes the context)
* @return the value of this node evaluated against the specified state
*/
Object getValue(ExpressionState expressionState) throws EvaluationException;
/**
* Evaluate the expression node in the context of the supplied expression state and
* return the typed value.
* Evaluate the expression node in the context of the supplied expression state
* and return the typed value.
* @param expressionState the current expression state (includes the context)
* @return the type value of this node evaluated against the specified state
*/
@ -45,21 +45,21 @@ public interface SpelNode {
/**
* Determine if this expression node will support a setValue() call.
*
* @param expressionState the current expression state (includes the context)
* @return true if the expression node will allow setValue()
* @throws EvaluationException if something went wrong trying to determine if the node supports writing
* @throws EvaluationException if something went wrong trying to determine
* if the node supports writing
*/
boolean isWritable(ExpressionState expressionState) throws EvaluationException;
/**
* Evaluate the expression to a node and then set the new value on that node. For
* example, if the expression evaluates to a property reference then the property will
* be set to the new value.
* Evaluate the expression to a node and then set the new value on that node.
* For example, if the expression evaluates to a property reference, then the
* property will be set to the new value.
* @param expressionState the current expression state (includes the context)
* @param newValue the new value
* @throws EvaluationException if any problem occurs evaluating the expression or
* setting the new value
* setting the new value
*/
void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException;
@ -82,8 +82,8 @@ public interface SpelNode {
/**
* Determine the class of the object passed in, unless it is already a class object.
* @param obj the object that the caller wants the class of
* @return the class of the object if it is not already a class object, or null if the
* object is null
* @return the class of the object if it is not already a class object,
* or {@code null} if the object is {@code null}
*/
Class<?> getObjectClass(Object obj);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -31,7 +31,6 @@ import org.springframework.expression.spel.ExpressionState;
*/
public class Assign extends SpelNodeImpl {
public Assign(int pos,SpelNodeImpl... operands) {
super(pos,operands);
}
@ -46,8 +45,7 @@ public class Assign extends SpelNodeImpl {
@Override
public String toStringAST() {
return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(
getChild(1).toStringAST()).toString();
return getChild(0).toStringAST() + "=" + getChild(1).toStringAST();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.springframework.expression.PropertyAccessor;
@ -27,7 +28,7 @@ import org.springframework.expression.PropertyAccessor;
* @author Andy Clement
* @since 3.0.2
*/
public class AstUtils {
public abstract class AstUtils {
/**
* Determines the set of property resolvers that should be used to try and access a
@ -36,28 +37,29 @@ public class AstUtils {
* target type (as opposed to 'general' resolvers that could work for any type) are
* placed at the start of the list. In addition, there are specific resolvers that
* exactly name the class in question and resolvers that name a specific class but it
* is a supertype of the class we have. These are put at the end of the specific
* resolvers set and will be tried after exactly matching accessors but before generic
* accessors.
* is a supertype of the class we have. These are put at the end of the specific resolvers
* set and will be tried after exactly matching accessors but before generic accessors.
* @param targetType the type upon which property access is being attempted
* @return a list of resolvers that should be tried in order to access the property
*/
public static List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
public static List<PropertyAccessor> getPropertyAccessorsToTry(
Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
for (PropertyAccessor resolver : propertyAccessors) {
Class<?>[] targets = resolver.getSpecificTargetClasses();
if (targets == null) { // generic resolver that says it can be used for any type
if (targets == null) { // generic resolver that says it can be used for any type
generalAccessors.add(resolver);
}
else {
if (targetType != null) {
int pos = 0;
for (Class<?> clazz : targets) {
if (clazz == targetType) { // put exact matches on the front to be tried first?
if (clazz == targetType) { // put exact matches on the front to be tried first?
specificAccessors.add(pos++, resolver);
}
else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the
else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the
// specificAccessor list
generalAccessors.add(resolver);
}
@ -65,9 +67,10 @@ public class AstUtils {
}
}
}
List<PropertyAccessor> resolvers = new ArrayList<PropertyAccessor>();
List<PropertyAccessor> resolvers = new LinkedList<PropertyAccessor>();
resolvers.addAll(specificAccessors);
resolvers.addAll(generalAccessors);
return resolvers;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -31,43 +31,40 @@ import org.springframework.expression.spel.SpelMessage;
*/
public class BeanReference extends SpelNodeImpl {
private final String beanname;
private final String beanName;
public BeanReference(int pos,String beanname) {
public BeanReference(int pos,String beanName) {
super(pos);
this.beanname = beanname;
this.beanName = beanName;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
BeanResolver beanResolver = state.getEvaluationContext().getBeanResolver();
if (beanResolver==null) {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanname);
if (beanResolver == null) {
throw new SpelEvaluationException(
getStartPosition(), SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanName);
}
try {
TypedValue bean = new TypedValue(beanResolver.resolve(
state.getEvaluationContext(), this.beanname));
return bean;
return new TypedValue(beanResolver.resolve(state.getEvaluationContext(), this.beanName));
}
catch (AccessException ae) {
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
this.beanname, ae.getMessage());
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
this.beanName, ex.getMessage());
}
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("@");
if (this.beanname.indexOf('.') == -1) {
sb.append(this.beanname);
StringBuilder sb = new StringBuilder("@");
if (!this.beanName.contains(".")) {
sb.append(this.beanName);
}
else {
sb.append("'").append(this.beanname).append("'");
sb.append("'").append(this.beanName).append("'");
}
return sb.toString();
}

View File

@ -48,14 +48,14 @@ public class BooleanLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
if (this.value == BooleanTypedValue.TRUE) {
mv.visitLdcInsn(1);
}
else {
mv.visitLdcInsn(0);
}
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -34,7 +34,7 @@ public class CompoundExpression extends SpelNodeImpl {
public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) {
super(pos, expressionComponents);
if (expressionComponents.length < 2) {
throw new IllegalStateException("Do not build compound expression less than one entry: " +
throw new IllegalStateException("Do not build compound expressions with less than two entries: " +
expressionComponents.length);
}
}
@ -45,6 +45,7 @@ public class CompoundExpression extends SpelNodeImpl {
if (getChildCount() == 1) {
return this.children[0].getValueRef(state);
}
SpelNodeImpl nextNode = this.children[0];
try {
TypedValue result = nextNode.getValueInternal(state);
@ -68,10 +69,10 @@ public class CompoundExpression extends SpelNodeImpl {
state.popActiveContextObject();
}
}
catch (SpelEvaluationException ee) {
catch (SpelEvaluationException ex) {
// Correct the position for the error before re-throwing
ee.setPosition(nextNode.getStartPosition());
throw ee;
ex.setPosition(nextNode.getStartPosition());
throw ex;
}
}
@ -85,7 +86,7 @@ public class CompoundExpression extends SpelNodeImpl {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
ValueRef ref = getValueRef(state);
TypedValue result = ref.getValue();
this.exitTypeDescriptor = this.children[this.children.length - 1].getExitDescriptor();
this.exitTypeDescriptor = this.children[this.children.length - 1].exitTypeDescriptor;
return result;
}
@ -122,17 +123,17 @@ public class CompoundExpression extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv,CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
// TODO could optimize T(SomeType).staticMethod - no need to generate the T() part
for (int i = 0; i < this.children.length;i++) {
SpelNodeImpl child = this.children[i];
if (child instanceof TypeReference && (i + 1) < this.children.length &&
this.children[i+1] instanceof MethodReference) {
this.children[i + 1] instanceof MethodReference) {
continue;
}
child.generateCode(mv, codeflow);
child.generateCode(mv, cf);
}
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -44,8 +44,7 @@ import org.springframework.expression.spel.support.ReflectiveConstructorExecutor
* Represents the invocation of a constructor. Either a constructor on a regular type or
* construction of an array. When an array is constructed, an initializer can be specified.
*
* <p>
* Examples:<br>
* <p>Examples:<br>
* new String('hello world')<br>
* new int[]{1,2,3,4}<br>
* new int[3] new int[3]{1,2,3}
@ -121,7 +120,7 @@ public class ConstructorReference extends SpelNodeImpl {
try {
return executorToUse.execute(state.getEvaluationContext(), arguments);
}
catch (AccessException ae) {
catch (AccessException ex) {
// Two reasons this can occur:
// 1. the method invoked actually threw a real exception
// 2. the method invoked was not passed the arguments it expected and has become 'stale'
@ -132,83 +131,81 @@ public class ConstructorReference extends SpelNodeImpl {
// To determine which situation it is, the AccessException will contain a cause.
// If the cause is an InvocationTargetException, a user exception was thrown inside the constructor.
// Otherwise the constructor could not be invoked.
if (ae.getCause() instanceof InvocationTargetException) {
if (ex.getCause() instanceof InvocationTargetException) {
// User exception was the root cause - exit now
Throwable rootCause = ae.getCause().getCause();
Throwable rootCause = ex.getCause().getCause();
if (rootCause instanceof RuntimeException) {
throw (RuntimeException) rootCause;
}
else {
String typename = (String) this.children[0].getValueInternal(state).getValue();
String typeName = (String) this.children[0].getValueInternal(state).getValue();
throw new SpelEvaluationException(getStartPosition(), rootCause,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, FormatHelper
.formatMethodForMessage("", argumentTypes));
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
// at this point we know it wasn't a user problem so worth a retry if a better candidate can be found
// At this point we know it wasn't a user problem so worth a retry if a better candidate can be found
this.cachedExecutor = null;
}
}
// either there was no accessor or it no longer exists
String typename = (String) this.children[0].getValueInternal(state).getValue();
executorToUse = findExecutorForConstructor(typename, argumentTypes, state);
// Either there was no accessor or it no longer exists
String typeName = (String) this.children[0].getValueInternal(state).getValue();
executorToUse = findExecutorForConstructor(typeName, argumentTypes, state);
try {
this.cachedExecutor = executorToUse;
if (this.cachedExecutor instanceof ReflectiveConstructorExecutor) {
this.exitTypeDescriptor = CodeFlow.toDescriptor(((ReflectiveConstructorExecutor)this.cachedExecutor).getConstructor().getDeclaringClass());
this.exitTypeDescriptor = CodeFlow.toDescriptor(
((ReflectiveConstructorExecutor) this.cachedExecutor).getConstructor().getDeclaringClass());
}
return executorToUse.execute(state.getEvaluationContext(), arguments);
}
catch (AccessException ae) {
throw new SpelEvaluationException(getStartPosition(), ae,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename,
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
/**
* Go through the list of registered constructor resolvers and see if any can find a constructor that takes the
* specified set of arguments.
* @param typename the type trying to be constructed
* Go through the list of registered constructor resolvers and see if any can find a
* constructor that takes the specified set of arguments.
* @param typeName the type trying to be constructed
* @param argumentTypes the types of the arguments supplied that the constructor must take
* @param state the current state of the expression
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
* @throws SpelEvaluationException if there is a problem locating the constructor
*/
private ConstructorExecutor findExecutorForConstructor(String typename,
private ConstructorExecutor findExecutorForConstructor(String typeName,
List<TypeDescriptor> argumentTypes, ExpressionState state)
throws SpelEvaluationException {
EvaluationContext eContext = state.getEvaluationContext();
List<ConstructorResolver> cResolvers = eContext.getConstructorResolvers();
if (cResolvers != null) {
for (ConstructorResolver ctorResolver : cResolvers) {
EvaluationContext evalContext = state.getEvaluationContext();
List<ConstructorResolver> ctorResolvers = evalContext.getConstructorResolvers();
if (ctorResolvers != null) {
for (ConstructorResolver ctorResolver : ctorResolvers) {
try {
ConstructorExecutor cEx = ctorResolver.resolve(state.getEvaluationContext(), typename,
argumentTypes);
if (cEx != null) {
return cEx;
ConstructorExecutor ce = ctorResolver.resolve(state.getEvaluationContext(), typeName, argumentTypes);
if (ce != null) {
return ce;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typename, FormatHelper
.formatMethodForMessage("", argumentTypes));
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("new ");
StringBuilder sb = new StringBuilder("new ");
int index = 0;
sb.append(getChild(index++).toStringAST());
sb.append("(");
@ -328,17 +325,20 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer, Class<?> componentType) {
TypeDescriptor toTypeDescriptor = TypeDescriptor.valueOf(componentType);
Object[] newObjectArray = (Object[]) newArray;
for (int i = 0; i < newObjectArray.length; i++) {
SpelNode elementNode = initializer.getChild(i);
Object arrayEntry = elementNode.getValue(state);
newObjectArray[i] = typeConverter.convertValue(arrayEntry, TypeDescriptor.forObject(arrayEntry), toTypeDescriptor);
newObjectArray[i] = typeConverter.convertValue(arrayEntry,
TypeDescriptor.forObject(arrayEntry), toTypeDescriptor);
}
}
private void populateByteArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
byte[] newByteArray = (byte[]) newArray;
for (int i = 0; i < newByteArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -348,6 +348,7 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateFloatArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
float[] newFloatArray = (float[]) newArray;
for (int i = 0; i < newFloatArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -357,6 +358,7 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateDoubleArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
double[] newDoubleArray = (double[]) newArray;
for (int i = 0; i < newDoubleArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -364,8 +366,9 @@ public class ConstructorReference extends SpelNodeImpl {
}
}
private void populateShortArray(ExpressionState state, Object newArray,
TypeConverter typeConverter, InlineList initializer) {
private void populateShortArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
short[] newShortArray = (short[]) newArray;
for (int i = 0; i < newShortArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -375,6 +378,7 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateLongArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
long[] newLongArray = (long[]) newArray;
for (int i = 0; i < newLongArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -384,6 +388,7 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateCharArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
char[] newCharArray = (char[]) newArray;
for (int i = 0; i < newCharArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -393,6 +398,7 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateBooleanArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
boolean[] newBooleanArray = (boolean[]) newArray;
for (int i = 0; i < newBooleanArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -402,6 +408,7 @@ public class ConstructorReference extends SpelNodeImpl {
private void populateIntArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer) {
int[] newIntArray = (int[]) newArray;
for (int i = 0; i < newIntArray.length; i++) {
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
@ -410,7 +417,7 @@ public class ConstructorReference extends SpelNodeImpl {
}
private boolean hasInitializer() {
return getChildCount() > 1;
return (getChildCount() > 1);
}
@Override
@ -419,46 +426,43 @@ public class ConstructorReference extends SpelNodeImpl {
this.exitTypeDescriptor == null) {
return false;
}
if (getChildCount() > 1) {
for (int c = 1, max = getChildCount();c < max; c++) {
if (!children[c].isCompilable()) {
if (!this.children[c].isCompilable()) {
return false;
}
}
}
ReflectiveConstructorExecutor executor = (ReflectiveConstructorExecutor)this.cachedExecutor;
ReflectiveConstructorExecutor executor = (ReflectiveConstructorExecutor) this.cachedExecutor;
Constructor<?> constructor = executor.getConstructor();
if (!Modifier.isPublic(constructor.getModifiers()) ||
!Modifier.isPublic(constructor.getDeclaringClass().getModifiers())) {
return false;
}
if (constructor.isVarArgs()) {
return false;
}
return true;
return (!constructor.isVarArgs() && Modifier.isPublic(constructor.getModifiers()) &&
Modifier.isPublic(constructor.getDeclaringClass().getModifiers()));
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
ReflectiveConstructorExecutor executor = ((ReflectiveConstructorExecutor) this.cachedExecutor);
Constructor<?> constructor = executor.getConstructor();
String classSlashedDescriptor = constructor.getDeclaringClass().getName().replace('.','/');
String classSlashedDescriptor = constructor.getDeclaringClass().getName().replace('.', '/');
String[] paramDescriptors = CodeFlow.toParamDescriptors(constructor);
mv.visitTypeInsn(NEW,classSlashedDescriptor);
mv.visitTypeInsn(NEW, classSlashedDescriptor);
mv.visitInsn(DUP);
for (int c = 1; c < children.length; c++) { // children[0] is the type of the constructor
SpelNodeImpl child = children[c];
codeflow.enterCompilationScope();
child.generateCode(mv, codeflow);
for (int c = 1; c < this.children.length; c++) { // children[0] is the type of the constructor
SpelNodeImpl child = this.children[c];
cf.enterCompilationScope();
child.generateCode(mv, cf);
// Check if need to box it for the method reference?
if (CodeFlow.isPrimitive(codeflow.lastDescriptor()) && (paramDescriptors[c-1].charAt(0)=='L')) {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
if (CodeFlow.isPrimitive(cf.lastDescriptor()) && paramDescriptors[c-1].charAt(0) == 'L') {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
}
codeflow.exitCompilationScope();
cf.exitCompilationScope();
}
mv.visitMethodInsn(INVOKESPECIAL,classSlashedDescriptor,"<init>",CodeFlow.createSignatureDescriptor(constructor),false);
codeflow.pushDescriptor(exitTypeDescriptor);
mv.visitMethodInsn(INVOKESPECIAL, classSlashedDescriptor, "<init>",
CodeFlow.createSignatureDescriptor(constructor), false);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -69,14 +69,14 @@ public class Elvis extends SpelNodeImpl {
SpelNodeImpl condition = this.children[0];
SpelNodeImpl ifNullValue = this.children[1];
return (condition.isCompilable() && ifNullValue.isCompilable() &&
condition.getExitDescriptor() != null && ifNullValue.getExitDescriptor() != null);
condition.exitTypeDescriptor != null && ifNullValue.exitTypeDescriptor != null);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
// exit type descriptor can be null if both components are literal expressions
computeExitTypeDescriptor();
this.children[0].generateCode(mv, codeflow);
this.children[0].generateCode(mv, cf);
Label elseTarget = new Label();
Label endOfIf = new Label();
mv.visitInsn(DUP);
@ -84,17 +84,17 @@ public class Elvis extends SpelNodeImpl {
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(elseTarget);
mv.visitInsn(POP);
this.children[1].generateCode(mv, codeflow);
if (!CodeFlow.isPrimitive(getExitDescriptor())) {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
this.children[1].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
}
mv.visitLabel(endOfIf);
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
private void computeExitTypeDescriptor() {
if (this.exitTypeDescriptor == null && this.children[0].getExitDescriptor() != null &&
this.children[1].getExitDescriptor() != null) {
if (this.exitTypeDescriptor == null && this.children[0].exitTypeDescriptor != null &&
this.children[1].exitTypeDescriptor != null) {
String conditionDescriptor = this.children[0].exitTypeDescriptor;
String ifNullValueDescriptor = this.children[1].exitTypeDescriptor;
if (conditionDescriptor.equals(ifNullValueDescriptor)) {

View File

@ -49,8 +49,8 @@ public class FloatLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
mv.visitLdcInsn(this.value.getValue());
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -34,8 +34,7 @@ public class FormatHelper {
* @return nicely formatted string, eg. foo(String,int)
*/
public static String formatMethodForMessage(String name, List<TypeDescriptor> argumentTypes) {
StringBuilder sb = new StringBuilder();
sb.append(name);
StringBuilder sb = new StringBuilder(name);
sb.append("(");
for (int i = 0; i < argumentTypes.size(); i++) {
if (i > 0) {
@ -63,23 +62,23 @@ public class FormatHelper {
if (clazz == null) {
return "null";
}
StringBuilder fmtd = new StringBuilder();
if (clazz.isArray()) {
StringBuilder sb = new StringBuilder();
int dims = 1;
Class<?> baseClass = clazz.getComponentType();
while (baseClass.isArray()) {
baseClass = baseClass.getComponentType();
dims++;
}
fmtd.append(baseClass.getName());
sb.append(baseClass.getName());
for (int i = 0; i < dims; i++) {
fmtd.append("[]");
sb.append("[]");
}
return sb.toString();
}
else {
fmtd.append(clazz.getName());
return clazz.getName();
}
return fmtd.toString();
}
}

View File

@ -103,6 +103,7 @@ public class FunctionReference extends SpelNodeImpl {
SpelMessage.FUNCTION_MUST_BE_STATIC,
method.getDeclaringClass().getName() + "." + method.getName(), this.name);
}
boolean argumentConversionOccurred = false;
// Convert arguments if necessary and remap them for varargs if required
if (functionArgs != null) {
@ -120,7 +121,7 @@ public class FunctionReference extends SpelNodeImpl {
this.method = method;
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
}
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrow(result));
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result));
}
catch (Exception ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
@ -142,8 +143,6 @@ public class FunctionReference extends SpelNodeImpl {
return sb.toString();
}
// to 'assign' to a function don't use the () suffix and so it is just a variable reference
/**
* Compute the arguments to the function, they are the children of this expression node.
* @return an array of argument values for the function call
@ -164,26 +163,26 @@ public class FunctionReference extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv,CodeFlow codeflow) {
String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.','/');
public void generateCode(MethodVisitor mv,CodeFlow cf) {
String methodDeclaringClassSlashedDescriptor = this.method.getDeclaringClass().getName().replace('.', '/');
String[] paramDescriptors = CodeFlow.toParamDescriptors(this.method);
for (int c = 0; c < this.children.length; c++) {
SpelNodeImpl child = this.children[c];
codeflow.enterCompilationScope();
child.generateCode(mv, codeflow);
cf.enterCompilationScope();
child.generateCode(mv, cf);
// Check if need to box it for the method reference?
if (CodeFlow.isPrimitive(codeflow.lastDescriptor()) && paramDescriptors[c].charAt(0) == 'L') {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
if (CodeFlow.isPrimitive(cf.lastDescriptor()) && paramDescriptors[c].charAt(0) == 'L') {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
}
else if (!codeflow.lastDescriptor().equals(paramDescriptors[c])) {
else if (!cf.lastDescriptor().equals(paramDescriptors[c])) {
// This would be unnecessary in the case of subtyping (e.g. method takes a Number but passed in is an Integer)
CodeFlow.insertCheckCast(mv, paramDescriptors[c]);
}
codeflow.exitCompilationScope();
cf.exitCompilationScope();
}
mv.visitMethodInsn(INVOKESTATIC, methodDeclaringClassSlashedDescriptor, this.method.getName(),
CodeFlow.createSignatureDescriptor(this.method),false);
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -28,7 +28,7 @@ public class Identifier extends SpelNodeImpl {
private final TypedValue id;
public Identifier(String payload,int pos) {
public Identifier(String payload, int pos) {
super(pos);
this.id = new TypedValue(payload);
}

View File

@ -51,6 +51,9 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
// TODO support correct syntax for multidimensional [][][] and not [,,,]
public class Indexer extends SpelNodeImpl {
private static enum IndexedType {ARRAY, LIST, MAP, STRING, OBJECT}
// These fields are used when the indexer is being used as a property read accessor.
// If the name and target type match these cached values then the cachedReadAccessor
// is used to read the property. If they do not match, the correct accessor is
@ -73,8 +76,6 @@ public class Indexer extends SpelNodeImpl {
private PropertyAccessor cachedWriteAccessor;
private static enum IndexedType { map, array, list, string, object; }
private IndexedType indexedType;
@ -103,7 +104,7 @@ public class Indexer extends SpelNodeImpl {
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
TypeDescriptor targetDescriptor = context.getTypeDescriptor();
TypedValue indexValue = null;
Object index = null;
@ -130,12 +131,11 @@ public class Indexer extends SpelNodeImpl {
// Indexing into a Map
if (targetObject instanceof Map) {
Object key = index;
if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) {
key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
if (targetDescriptor.getMapKeyTypeDescriptor() != null) {
key = state.convertValue(key, targetDescriptor.getMapKeyTypeDescriptor());
}
this.indexedType = IndexedType.map;
return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) targetObject, key,
targetObjectTypeDescriptor);
this.indexedType = IndexedType.MAP;
return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) targetObject, key, targetDescriptor);
}
if (targetObject == null) {
@ -147,47 +147,47 @@ public class Indexer extends SpelNodeImpl {
if (targetObject.getClass().isArray() || targetObject instanceof Collection || targetObject instanceof String) {
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
if (targetObject.getClass().isArray()) {
this.indexedType = IndexedType.array;
return new ArrayIndexingValueRef(state.getTypeConverter(), targetObject, idx, targetObjectTypeDescriptor);
this.indexedType = IndexedType.ARRAY;
return new ArrayIndexingValueRef(state.getTypeConverter(), targetObject, idx, targetDescriptor);
}
else if (targetObject instanceof Collection) {
if (targetObject instanceof List) {
this.indexedType = IndexedType.list;
this.indexedType = IndexedType.LIST;
}
return new CollectionIndexingValueRef((Collection<?>) targetObject, idx, targetObjectTypeDescriptor,
return new CollectionIndexingValueRef((Collection<?>) targetObject, idx, targetDescriptor,
state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections(),
state.getConfiguration().getMaximumAutoGrowSize());
}
else if (targetObject instanceof String) {
this.indexedType = IndexedType.string;
return new StringIndexingLValue((String) targetObject, idx, targetObjectTypeDescriptor);
else {
this.indexedType = IndexedType.STRING;
return new StringIndexingLValue((String) targetObject, idx, targetDescriptor);
}
}
// Try and treat the index value as a property of the context object
// TODO could call the conversion service to convert the value to a String
if (indexValue.getTypeDescriptor().getType() == String.class) {
this.indexedType = IndexedType.object;
if (String.class.equals(indexValue.getTypeDescriptor().getType())) {
this.indexedType = IndexedType.OBJECT;
return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(),
state.getEvaluationContext(), targetObjectTypeDescriptor);
state.getEvaluationContext(), targetDescriptor);
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
targetObjectTypeDescriptor.toString());
targetDescriptor.toString());
}
@Override
public boolean isCompilable() {
if (this.indexedType == IndexedType.array) {
if (this.indexedType == IndexedType.ARRAY) {
return (this.exitTypeDescriptor != null);
}
else if (this.indexedType == IndexedType.list) {
else if (this.indexedType == IndexedType.LIST) {
return this.children[0].isCompilable();
}
else if (this.indexedType == IndexedType.map) {
}
else if (this.indexedType == IndexedType.MAP) {
return (this.children[0] instanceof PropertyOrFieldReference || this.children[0].isCompilable());
}
else if (this.indexedType == IndexedType.object) {
else if (this.indexedType == IndexedType.OBJECT) {
// If the string name is changing the accessor is clearly going to change (so compilation is not possible)
if (this.cachedReadAccessor != null &&
(this.cachedReadAccessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) &&
@ -199,89 +199,64 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
String descriptor = codeflow.lastDescriptor();
public void generateCode(MethodVisitor mv, CodeFlow cf) {
String descriptor = cf.lastDescriptor();
if (descriptor == null) {
// Stack is empty, should use context object
codeflow.loadTarget(mv);
cf.loadTarget(mv);
}
if (this.indexedType == IndexedType.array) {
if ("I".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST,"[I");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(IALOAD);
}
else if ("D".equals(this.exitTypeDescriptor)) {
if (this.indexedType == IndexedType.ARRAY) {
int insn;
if ("D".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[D");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
mv.visitInsn(DALOAD);
}
else if ("J".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[J");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(LALOAD);
insn = DALOAD;
}
else if ("F".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[F");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(FALOAD);
insn = FALOAD;
}
else if ("J".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[J");
insn = LALOAD;
}
else if ("I".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[I");
insn = IALOAD;
}
else if ("S".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[S");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(SALOAD);
insn = SALOAD;
}
else if ("B".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[B");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(BALOAD);
insn = BALOAD;
}
else if ("C".equals(this.exitTypeDescriptor)) {
mv.visitTypeInsn(CHECKCAST, "[C");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(CALOAD);
insn = CALOAD;
}
else {
mv.visitTypeInsn(CHECKCAST, "["+ this.exitTypeDescriptor +
(CodeFlow.isPrimitiveArray(this.exitTypeDescriptor) ? "" : ";"));
//depthPlusOne(exitTypeDescriptor)+"Ljava/lang/Object;");
SpelNodeImpl index = this.children[0];
codeflow.enterCompilationScope();
index.generateCode(mv, codeflow);
codeflow.exitCompilationScope();
mv.visitInsn(AALOAD);
insn = AALOAD;
}
SpelNodeImpl index = this.children[0];
cf.enterCompilationScope();
index.generateCode(mv, cf);
cf.exitCompilationScope();
mv.visitInsn(insn);
}
else if (this.indexedType == IndexedType.list) {
else if (this.indexedType == IndexedType.LIST) {
mv.visitTypeInsn(CHECKCAST, "java/util/List");
codeflow.enterCompilationScope();
this.children[0].generateCode(mv, codeflow);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
this.children[0].generateCode(mv, cf);
cf.exitCompilationScope();
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
CodeFlow.insertCheckCast(mv, this.exitTypeDescriptor);
}
else if (this.indexedType == IndexedType.map) {
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
// a property/field reference
@ -291,22 +266,22 @@ public class Indexer extends SpelNodeImpl {
mv.visitLdcInsn(mapKeyName);
}
else {
codeflow.enterCompilationScope();
this.children[0].generateCode(mv, codeflow);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
this.children[0].generateCode(mv, cf);
cf.exitCompilationScope();
}
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
CodeFlow.insertCheckCast(mv, this.exitTypeDescriptor);
}
else if (this.indexedType == IndexedType.object) {
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 memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.', '/');
if (!isStatic) {
if (descriptor == null) {
codeflow.loadTarget(mv);
cf.loadTarget(mv);
}
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
@ -314,7 +289,7 @@ public class Indexer extends SpelNodeImpl {
}
if (member instanceof Field) {
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor,
member.getName(), CodeFlow.toJVMDescriptor(((Field) member).getType()));
member.getName(), CodeFlow.toJvmDescriptor(((Field) member).getType()));
}
else {
mv.visitMethodInsn(isStatic? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor,
@ -322,13 +297,12 @@ public class Indexer extends SpelNodeImpl {
}
}
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("[");
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0) {
sb.append(",");
@ -339,98 +313,60 @@ public class Indexer extends SpelNodeImpl {
return sb.toString();
}
private void setArrayElement(TypeConverter converter, Object ctx, int idx, Object newValue,
Class<?> arrayComponentType) throws EvaluationException {
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Integer) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Integer.class));
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Boolean) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Boolean.class));
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Character) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Character.class));
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Long) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Long.class));
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Short) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Short.class));
}
else if (arrayComponentType == Double.TYPE) {
if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Double) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Double.class));
array[idx] = convertValue(converter, newValue, Double.class);
}
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Float) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Float.class));
array[idx] = convertValue(converter, newValue, Float.class);
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
array[idx] = convertValue(converter, newValue, Long.class);
}
else if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
array[idx] = convertValue(converter, newValue, Integer.class);
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
array[idx] = convertValue(converter, newValue, Short.class);
}
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Byte) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Byte.class));
array[idx] = convertValue(converter, newValue, Byte.class);
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
array[idx] = convertValue(converter, newValue, Character.class);
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
array[idx] = convertValue(converter, newValue, Boolean.class);
}
else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
array[idx] = converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(arrayComponentType));
array[idx] = convertValue(converter, newValue, arrayComponentType);
}
}
private Object accessArrayElement(Object ctx, int idx) throws SpelEvaluationException {
Class<?> arrayComponentType = ctx.getClass().getComponentType();
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "I";
return array[idx];
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "Z";
return array[idx];
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "C";
return array[idx];
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "J";
return array[idx];
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "S";
return array[idx];
}
else if (arrayComponentType == Double.TYPE) {
if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "D";
@ -442,12 +378,42 @@ public class Indexer extends SpelNodeImpl {
this.exitTypeDescriptor = "F";
return array[idx];
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "J";
return array[idx];
}
else if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "I";
return array[idx];
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "S";
return array[idx];
}
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "B";
return array[idx];
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "C";
return array[idx];
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
this.exitTypeDescriptor = "Z";
return array[idx];
}
else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
@ -464,6 +430,11 @@ public class Indexer extends SpelNodeImpl {
}
}
@SuppressWarnings("unchecked")
private <T> T convertValue(TypeConverter converter, Object value, Class<T> targetType) {
return (T) converter.convertValue(value, TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType));
}
private class ArrayIndexingValueRef implements ValueRef {
@ -475,7 +446,6 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor typeDescriptor;
ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int index, TypeDescriptor typeDescriptor) {
this.typeConverter = typeConverter;
this.array = array;
@ -483,7 +453,6 @@ public class Indexer extends SpelNodeImpl {
this.typeDescriptor = typeDescriptor;
}
@Override
public TypedValue getValue() {
Object arrayElement = accessArrayElement(this.array, this.index);
@ -512,28 +481,27 @@ public class Indexer extends SpelNodeImpl {
private final Object key;
private final TypeDescriptor mapEntryTypeDescriptor;
private final TypeDescriptor mapEntryDescriptor;
public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) {
public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryDescriptor) {
this.typeConverter = typeConverter;
this.map = map;
this.key = key;
this.mapEntryTypeDescriptor = mapEntryTypeDescriptor;
this.mapEntryDescriptor = mapEntryDescriptor;
}
@Override
public TypedValue getValue() {
Object value = this.map.get(this.key);
exitTypeDescriptor = CodeFlow.toDescriptorFromObject(value);
return new TypedValue(value,
this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
return new TypedValue(value, this.mapEntryDescriptor.getMapValueTypeDescriptor(value));
}
@Override
public void setValue(Object newValue) {
if (this.mapEntryTypeDescriptor.getMapValueTypeDescriptor() != null) {
if (this.mapEntryDescriptor.getMapValueTypeDescriptor() != null) {
newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue),
this.mapEntryTypeDescriptor.getMapValueTypeDescriptor());
this.mapEntryDescriptor.getMapValueTypeDescriptor());
}
this.map.put(this.key, newValue);
}
@ -563,7 +531,6 @@ public class Indexer extends SpelNodeImpl {
this.targetObjectTypeDescriptor = targetObjectTypeDescriptor;
}
@Override
public TypedValue getValue() {
Class<?> targetObjectRuntimeClass = getObjectClass(this.targetObject);
@ -587,7 +554,8 @@ public class Indexer extends SpelNodeImpl {
Indexer.this.cachedReadName = this.name;
Indexer.this.cachedReadTargetType = targetObjectRuntimeClass;
if (accessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) {
ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor = (ReflectivePropertyAccessor.OptimalPropertyAccessor)accessor;
ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor;
Member member = optimalAccessor.member;
if (member instanceof Field) {
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(((Field)member).getType());
@ -672,7 +640,6 @@ public class Indexer extends SpelNodeImpl {
this.maximumSize = maximumSize;
}
@Override
public TypedValue getValue() {
growCollectionIfNecessary();

View File

@ -33,8 +33,8 @@ import org.springframework.expression.spel.SpelNode;
*/
public class InlineList extends SpelNodeImpl {
// if the list is purely literals, it is a constant value and can be computed and cached
TypedValue constant = null; // TODO must be immutable list
// If the list is purely literals, it is a constant value and can be computed and cached
private TypedValue constant = null; // TODO must be immutable list
public InlineList(int pos, SpelNodeImpl... args) {
@ -87,8 +87,8 @@ public class InlineList extends SpelNodeImpl {
}
else {
List<Object> returnValue = new ArrayList<Object>();
int childcount = getChildCount();
for (int c = 0; c < childcount; c++) {
int childCount = getChildCount();
for (int c = 0; c < childCount; c++) {
returnValue.add(getChild(c).getValue(expressionState));
}
return new TypedValue(returnValue);
@ -97,25 +97,24 @@ public class InlineList extends SpelNodeImpl {
@Override
public String toStringAST() {
StringBuilder s = new StringBuilder();
// string ast matches input string, not the 'toString()' of the resultant collection, which would use []
s.append('{');
StringBuilder sb = new StringBuilder("{");
// String ast matches input string, not the 'toString()' of the resultant collection, which would use []
int count = getChildCount();
for (int c = 0; c < count; c++) {
if (c > 0) {
s.append(',');
sb.append(",");
}
s.append(getChild(c).toStringAST());
sb.append(getChild(c).toStringAST());
}
s.append('}');
return s.toString();
sb.append("}");
return sb.toString();
}
/**
* @return whether this list is a constant value
* Return whether this list is a constant value.
*/
public boolean isConstant() {
return this.constant != null;
return (this.constant != null);
}
@SuppressWarnings("unchecked")

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2002-2014 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,8 +33,9 @@ import org.springframework.expression.spel.SpelNode;
*/
public class InlineMap extends SpelNodeImpl {
// if the map is purely literals, it is a constant value and can be computed and cached
TypedValue constant = null;
// If the map is purely literals, it is a constant value and can be computed and cached
private TypedValue constant = null;
public InlineMap(int pos, SpelNodeImpl... args) {
super(pos, args);
@ -43,9 +44,9 @@ public class InlineMap extends SpelNodeImpl {
/**
* If all the components of the list are constants, or lists/maps that themselves contain constants, then a constant list
* can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage
* created.
* If all the components of the list are constants, or lists/maps that themselves
* contain constants, then a constant list can be built to represent this node.
* This will speed up later getValue calls and reduce the amount of garbage created.
*/
private void checkIfConstant() {
boolean isConstant = true;
@ -74,13 +75,13 @@ public class InlineMap extends SpelNodeImpl {
}
if (isConstant) {
Map<Object,Object> constantMap = new LinkedHashMap<Object,Object>();
int childcount = getChildCount();
for (int c = 0; c < childcount; c++) {
int childCount = getChildCount();
for (int c = 0; c < childCount; c++) {
SpelNode keyChild = getChild(c++);
SpelNode valueChild = getChild(c);
Object key = null;
Object value = null;
if ((keyChild instanceof Literal)) {
if (keyChild instanceof Literal) {
key = ((Literal) keyChild).getLiteralValue().getValue();
}
else if (keyChild instanceof PropertyOrFieldReference) {
@ -132,19 +133,18 @@ public class InlineMap extends SpelNodeImpl {
@Override
public String toStringAST() {
StringBuilder s = new StringBuilder();
s.append('{');
StringBuilder sb = new StringBuilder("{");
int count = getChildCount();
for (int c = 0; c < count; c++) {
if (c > 0) {
s.append(',');
sb.append(",");
}
s.append(getChild(c++).toStringAST());
s.append(':');
s.append(getChild(c).toStringAST());
sb.append(getChild(c++).toStringAST());
sb.append(":");
sb.append(getChild(c).toStringAST());
}
s.append('}');
return s.toString();
sb.append("}");
return sb.toString();
}
/**

View File

@ -47,19 +47,19 @@ public class IntLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
int intValue = ((Integer)this.value.getValue()).intValue();
if (intValue==-1) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
int intValue = (Integer) this.value.getValue();
if (intValue == -1) {
// Not sure we can get here because -1 is OpMinus
mv.visitInsn(ICONST_M1);
}
else if (intValue >= 0 && intValue < 6) {
mv.visitInsn(ICONST_0+intValue);
mv.visitInsn(ICONST_0 + intValue);
}
else {
mv.visitLdcInsn(intValue);
}
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -64,8 +64,9 @@ public abstract class Literal extends SpelNodeImpl {
/**
* Process the string form of a number, using the specified base if supplied and return an appropriate literal to
* hold it. Any suffix to indicate a long will be taken into account (either 'l' or 'L' is supported).
* Process the string form of a number, using the specified base if supplied
* and return an appropriate literal to hold it. Any suffix to indicate a
* long will be taken into account (either 'l' or 'L' is supported).
* @param numberToken the token holding the number as its payload (eg. 1234 or 0xCAFE)
* @param radix the base of number
* @return a subtype of Literal that can represent it
@ -75,8 +76,8 @@ public abstract class Literal extends SpelNodeImpl {
int value = Integer.parseInt(numberToken, radix);
return new IntLiteral(numberToken, pos, value);
}
catch (NumberFormatException nfe) {
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_AN_INTEGER, numberToken));
catch (NumberFormatException ex) {
throw new InternalParseException(new SpelParseException(pos>>16, ex, SpelMessage.NOT_AN_INTEGER, numberToken));
}
}
@ -85,8 +86,8 @@ public abstract class Literal extends SpelNodeImpl {
long value = Long.parseLong(numberToken, radix);
return new LongLiteral(numberToken, pos, value);
}
catch (NumberFormatException nfe) {
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_LONG, numberToken));
catch (NumberFormatException ex) {
throw new InternalParseException(new SpelParseException(pos>>16, ex, SpelMessage.NOT_A_LONG, numberToken));
}
}
@ -101,8 +102,8 @@ public abstract class Literal extends SpelNodeImpl {
return new RealLiteral(numberToken, pos, value);
}
}
catch (NumberFormatException nfe) {
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_REAL, numberToken));
catch (NumberFormatException ex) {
throw new InternalParseException(new SpelParseException(pos>>16, ex, SpelMessage.NOT_A_REAL, numberToken));
}
}

View File

@ -48,9 +48,9 @@ public class LongLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
mv.visitLdcInsn(this.value.getValue());
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -71,7 +71,7 @@ public class MethodReference extends SpelNodeImpl {
Object[] arguments = getArguments(state);
if (state.getActiveContextObject().getValue() == null) {
throwIfNotNullSafe(getArgumentTypes(arguments));
return ValueRef.NullValueRef.instance;
return ValueRef.NullValueRef.INSTANCE;
}
return new MethodValueRef(state, arguments);
}
@ -101,7 +101,7 @@ public class MethodReference extends SpelNodeImpl {
try {
return executorToUse.execute(evaluationContext, value, arguments);
}
catch (AccessException ae) {
catch (AccessException ex) {
// Two reasons this can occur:
// 1. the method invoked actually threw a real exception
// 2. the method invoked was not passed the arguments it expected and
@ -113,7 +113,7 @@ public class MethodReference extends SpelNodeImpl {
// To determine the situation, the AccessException will contain a cause.
// If the cause is an InvocationTargetException, a user exception was
// thrown inside the method. Otherwise the method could not be invoked.
throwSimpleExceptionIfPossible(value, ae);
throwSimpleExceptionIfPossible(value, ex);
// At this point we know it wasn't a user problem so worth a retry if a
// better candidate can be found.
@ -216,9 +216,9 @@ public class MethodReference extends SpelNodeImpl {
* Decode the AccessException, throwing a lightweight evaluation exception or, if the
* cause was a RuntimeException, throw the RuntimeException directly.
*/
private void throwSimpleExceptionIfPossible(Object value, AccessException ae) {
if (ae.getCause() instanceof InvocationTargetException) {
Throwable rootCause = ae.getCause().getCause();
private void throwSimpleExceptionIfPossible(Object value, AccessException ex) {
if (ex.getCause() instanceof InvocationTargetException) {
Throwable rootCause = ex.getCause().getCause();
if (rootCause instanceof RuntimeException) {
throw (RuntimeException) rootCause;
}
@ -238,8 +238,8 @@ public class MethodReference extends SpelNodeImpl {
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append(this.name).append("(");
StringBuilder sb = new StringBuilder(this.name);
sb.append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0) {
sb.append(",");
@ -283,7 +283,7 @@ public class MethodReference extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
CachedMethodExecutor executorToCheck = this.cachedExecutor;
if (executorToCheck == null || !(executorToCheck.get() instanceof ReflectiveMethodExecutor)) {
throw new IllegalStateException("No applicable cached executor found: " + executorToCheck);
@ -291,10 +291,10 @@ public class MethodReference extends SpelNodeImpl {
Method method = ((ReflectiveMethodExecutor) executorToCheck.get()).getMethod();
boolean isStaticMethod = Modifier.isStatic(method.getModifiers());
String descriptor = codeflow.lastDescriptor();
String descriptor = cf.lastDescriptor();
if (descriptor == null && !isStaticMethod) {
codeflow.loadTarget(mv);
cf.loadTarget(mv);
}
boolean itf = method.getDeclaringClass().isInterface();
@ -307,21 +307,21 @@ public class MethodReference extends SpelNodeImpl {
String[] paramDescriptors = CodeFlow.toParamDescriptors(method);
for (int i = 0; i < this.children.length;i++) {
SpelNodeImpl child = this.children[i];
codeflow.enterCompilationScope();
child.generateCode(mv, codeflow);
cf.enterCompilationScope();
child.generateCode(mv, cf);
// Check if need to box it for the method reference?
if (CodeFlow.isPrimitive(codeflow.lastDescriptor()) && paramDescriptors[i].charAt(0) == 'L') {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
if (CodeFlow.isPrimitive(cf.lastDescriptor()) && paramDescriptors[i].charAt(0) == 'L') {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
}
else if (!codeflow.lastDescriptor().equals(paramDescriptors[i])) {
else if (!cf.lastDescriptor().equals(paramDescriptors[i])) {
// This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
CodeFlow.insertCheckCast(mv, paramDescriptors[i]);
}
codeflow.exitCompilationScope();
cf.exitCompilationScope();
}
mv.visitMethodInsn(isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL,
methodDeclaringClassSlashedDescriptor, method.getName(), CodeFlow.createSignatureDescriptor(method), itf);
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}

View File

@ -50,9 +50,9 @@ public class NullLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
mv.visitInsn(ACONST_NULL);
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -72,34 +72,31 @@ public class OpAnd extends Operator {
@Override
public boolean isCompilable() {
SpelNodeImpl left = getLeftOperand();
SpelNodeImpl right= getRightOperand();
if (!left.isCompilable() || !right.isCompilable()) {
return false;
}
return
CodeFlow.isBooleanCompatible(left.getExitDescriptor()) &&
CodeFlow.isBooleanCompatible(right.getExitDescriptor());
SpelNodeImpl right = getRightOperand();
return (left.isCompilable() && right.isCompilable() &&
CodeFlow.isBooleanCompatible(left.exitTypeDescriptor) &&
CodeFlow.isBooleanCompatible(right.exitTypeDescriptor));
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
// pseudo: if (!leftOperandValue) { result=false; } else { result=rightOperandValue; }
public void generateCode(MethodVisitor mv, CodeFlow cf) {
// Pseudo: if (!leftOperandValue) { result=false; } else { result=rightOperandValue; }
Label elseTarget = new Label();
Label endOfIf = new Label();
codeflow.enterCompilationScope();
getLeftOperand().generateCode(mv, codeflow);
codeflow.unboxBooleanIfNecessary(mv);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getLeftOperand().generateCode(mv, cf);
cf.unboxBooleanIfNecessary(mv);
cf.exitCompilationScope();
mv.visitJumpInsn(IFNE, elseTarget);
mv.visitLdcInsn(0); // FALSE
mv.visitJumpInsn(GOTO,endOfIf);
mv.visitLabel(elseTarget);
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.unboxBooleanIfNecessary(mv);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.unboxBooleanIfNecessary(mv);
cf.exitCompilationScope();
mv.visitLabel(endOfIf);
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
@ -31,12 +32,13 @@ import org.springframework.util.Assert;
* appropriate exceptions if the operand in question does not support decrement.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.2
*/
public class OpDec extends Operator {
private final boolean postfix; // false means prefix
private final boolean postfix; // false means prefix
public OpDec(int pos, boolean postfix, SpelNodeImpl... operands) {
@ -51,41 +53,45 @@ public class OpDec extends Operator {
SpelNodeImpl operand = getLeftOperand();
// The operand is going to be read and then assigned to, we don't want to evaluate it twice.
ValueRef lvalue = operand.getValueRef(state);
final TypedValue operandTypedValue = lvalue.getValue();//operand.getValueInternal(state);
final Object operandValue = operandTypedValue.getValue();
TypedValue operandTypedValue = lvalue.getValue(); //operand.getValueInternal(state);
Object operandValue = operandTypedValue.getValue();
TypedValue returnValue = operandTypedValue;
TypedValue newValue = null;
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof BigDecimal) {
newValue = new TypedValue(((BigDecimal) op1).subtract(BigDecimal.ONE),
operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(((BigDecimal) op1).subtract(BigDecimal.ONE), operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Double) {
newValue = new TypedValue(op1.doubleValue() - 1.0d,
operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() - 1.0f,
operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(op1.floatValue() - 1.0f, operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof BigInteger) {
newValue = new TypedValue(((BigInteger) op1).subtract(BigInteger.ONE), operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() - 1L,
operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(op1.longValue() - 1L, operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Integer) {
newValue = new TypedValue(op1.intValue() - 1, operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() - (short) 1,
operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(op1.shortValue() - (short) 1, operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Byte) {
newValue = new TypedValue(op1.byteValue() - (byte) 1, operandTypedValue.getTypeDescriptor());
}
else {
newValue = new TypedValue(op1.intValue() - 1,
operandTypedValue.getTypeDescriptor());
// Unknown Number subtype -> best guess is double decrement
newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor());
}
}
if (newValue == null) {
try {
newValue = state.operate(Operation.SUBTRACT, returnValue.getValue(), 1);
@ -93,7 +99,8 @@ public class OpDec extends Operator {
catch (SpelEvaluationException ex) {
if (ex.getMessageCode() == SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
// This means the operand is not decrementable
throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_DECREMENTABLE,operand.toStringAST());
throw new SpelEvaluationException(operand.getStartPosition(),
SpelMessage.OPERAND_NOT_DECREMENTABLE, operand.toStringAST());
}
else {
throw ex;
@ -126,7 +133,7 @@ public class OpDec extends Operator {
@Override
public String toStringAST() {
return new StringBuilder().append(getLeftOperand().toStringAST()).append("--").toString();
return getLeftOperand().toStringAST() + "--";
}
@Override

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import org.springframework.asm.MethodVisitor;
@ -37,7 +38,6 @@ import org.springframework.util.NumberUtils;
*/
public class OpDivide extends Operator {
public OpDivide(int pos, SpelNodeImpl... operands) {
super("/", pos, operands);
}
@ -58,64 +58,71 @@ public class OpDivide extends Operator {
int scale = Math.max(leftBigDecimal.scale(), rightBigDecimal.scale());
return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN));
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber instanceof Double && rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float && rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue());
}
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return new TypedValue(leftBigInteger.divide(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long && rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
return new TypedValue(leftNumber.longValue() / rightNumber.longValue());
}
else {
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
// TODO what about non-int result of the division?
return new TypedValue(leftNumber.intValue() / rightNumber.intValue());
}
else {
// Unknown Number subtypes -> best guess is double division
return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue());
}
}
return state.operate(Operation.DIVIDE, leftOperand, rightOperand);
}
@Override
public boolean isCompilable() {
if (!getLeftOperand().isCompilable()) {
return false;
}
if (this.children.length>1) {
if (this.children.length > 1) {
if (!getRightOperand().isCompilable()) {
return false;
}
}
return this.exitTypeDescriptor!=null;
return (this.exitTypeDescriptor != null);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
if (this.children.length > 1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
@ -131,10 +138,11 @@ public class OpDivide extends Operator {
mv.visitInsn(DDIV);
break;
default:
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
throw new IllegalStateException(
"Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
}
}
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -36,12 +36,13 @@ public class OpEQ extends Operator {
this.exitTypeDescriptor = "Z";
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
return BooleanTypedValue.forValue(equalityCheck(state, left, right));
}
@ -54,38 +55,38 @@ public class OpEQ extends Operator {
if (!left.isCompilable() || !right.isCompilable()) {
return false;
}
String leftdesc = left.getExitDescriptor();
String rightdesc = right.getExitDescriptor();
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftdesc, rightdesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers) {
return dc.areCompatible;
}
return true;
String leftDesc = left.exitTypeDescriptor;
String rightDesc = right.exitTypeDescriptor;
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
this.leftActualDescriptor, this.rightActualDescriptor);
return (!dc.areNumbers || dc.areCompatible);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
String leftDesc = getLeftOperand().getExitDescriptor();
String rightDesc = getRightOperand().getExitDescriptor();
public void generateCode(MethodVisitor mv, CodeFlow cf) {
String leftDesc = getLeftOperand().exitTypeDescriptor;
String rightDesc = getRightOperand().exitTypeDescriptor;
Label elseTarget = new Label();
Label endOfIf = new Label();
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
this.leftActualDescriptor, this.rightActualDescriptor);
if (dc.areNumbers && dc.areCompatible) {
char targetType = dc.compatibleType;
getLeftOperand().generateCode(mv, codeflow);
getLeftOperand().generateCode(mv, cf);
if (!leftPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.exitCompilationScope();
if (!rightPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
@ -110,28 +111,26 @@ public class OpEQ extends Operator {
}
}
else {
getLeftOperand().generateCode(mv, codeflow);
getRightOperand().generateCode(mv, codeflow);
getLeftOperand().generateCode(mv, cf);
getRightOperand().generateCode(mv, cf);
Label leftNotNull = new Label();
mv.visitInsn(DUP_X1); // Dup right on the top of the stack
mv.visitJumpInsn(IFNONNULL,leftNotNull);
// Right is null!
mv.visitInsn(SWAP);
mv.visitInsn(POP); // remove it
Label rightNotNull = new Label();
mv.visitJumpInsn(IFNONNULL, rightNotNull);
// Left is null too
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(rightNotNull);
mv.visitInsn(ICONST_0);
mv.visitJumpInsn(GOTO,endOfIf);
// Right is null!
mv.visitInsn(SWAP);
mv.visitInsn(POP); // remove it
Label rightNotNull = new Label();
mv.visitJumpInsn(IFNONNULL, rightNotNull);
// Left is null too
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(rightNotNull);
mv.visitInsn(ICONST_0);
mv.visitJumpInsn(GOTO,endOfIf);
mv.visitLabel(leftNotNull);
mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/Object","equals","(Ljava/lang/Object;)Z",false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
mv.visitLabel(endOfIf);
codeflow.pushDescriptor("Z");
cf.pushDescriptor("Z");
return;
}
mv.visitInsn(ICONST_1);
@ -139,7 +138,7 @@ public class OpEQ extends Operator {
mv.visitLabel(elseTarget);
mv.visitInsn(ICONST_0);
mv.visitLabel(endOfIf);
codeflow.pushDescriptor("Z");
cf.pushDescriptor("Z");
}
}

View File

@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -28,15 +30,15 @@ import org.springframework.util.NumberUtils;
* Implements greater-than-or-equal operator.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpGE extends Operator {
public OpGE(int pos, SpelNodeImpl... operands) {
super(">=", pos, operands);
this.exitTypeDescriptor="Z";
this.exitTypeDescriptor = "Z";
}
@ -45,8 +47,8 @@ public class OpGE extends Operator {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
@ -57,20 +59,35 @@ public class OpGE extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) >= 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() >= rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) >= 0);
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() >= rightNumber.longValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
}
else if (leftNumber instanceof Short || rightNumber instanceof Short) {
return BooleanTypedValue.forValue(leftNumber.shortValue() >= rightNumber.shortValue());
}
else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
return BooleanTypedValue.forValue(leftNumber.byteValue() >= rightNumber.byteValue());
}
else {
// Unknown Number subtypes -> best guess is double comparison
return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
}
}
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) >= 0);
}
@ -80,8 +97,8 @@ public class OpGE extends Operator {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
generateComparisonCode(mv, codeflow, IFLT, IF_ICMPLT);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
generateComparisonCode(mv, cf, IFLT, IF_ICMPLT);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -40,13 +41,14 @@ public class OpGT extends Operator {
this.exitTypeDescriptor = "Z";
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
@ -57,20 +59,33 @@ public class OpGT extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) > 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() > rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) > 0);
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() > rightNumber.longValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
}
else if (leftNumber instanceof Short || rightNumber instanceof Short) {
return BooleanTypedValue.forValue(leftNumber.shortValue() > rightNumber.shortValue());
}
else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
return BooleanTypedValue.forValue(leftNumber.byteValue() > rightNumber.byteValue());
}
else {
// Unknown Number subtypes -> best guess is double comparison
return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue());
}
}
if (left instanceof CharSequence && right instanceof CharSequence) {
@ -87,7 +102,8 @@ public class OpGT extends Operator {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
generateComparisonCode(mv, codeflow, IFLE, IF_ICMPLE);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
generateComparisonCode(mv, cf, IFLE, IF_ICMPLE);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
@ -31,12 +32,13 @@ import org.springframework.util.Assert;
* appropriate exceptions if the operand in question does not support increment.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.2
*/
public class OpInc extends Operator {
private final boolean postfix; // false means prefix
private final boolean postfix; // false means prefix
public OpInc(int pos, boolean postfix, SpelNodeImpl... operands) {
@ -49,41 +51,45 @@ public class OpInc extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl operand = getLeftOperand();
ValueRef valueRef = operand.getValueRef(state);
final TypedValue typedValue = valueRef.getValue();
final Object value = typedValue.getValue();
TypedValue typedValue = valueRef.getValue();
Object value = typedValue.getValue();
TypedValue returnValue = typedValue;
TypedValue newValue = null;
if (value instanceof Number) {
Number op1 = (Number) value;
if (op1 instanceof BigDecimal) {
newValue = new TypedValue(((BigDecimal) op1).add(BigDecimal.ONE),
typedValue.getTypeDescriptor());
newValue = new TypedValue(((BigDecimal) op1).add(BigDecimal.ONE), typedValue.getTypeDescriptor());
}
else if (op1 instanceof Double) {
newValue = new TypedValue(op1.doubleValue() + 1.0d,
typedValue.getTypeDescriptor());
newValue = new TypedValue(op1.doubleValue() + 1.0d, typedValue.getTypeDescriptor());
}
else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() + 1.0f,
typedValue.getTypeDescriptor());
newValue = new TypedValue(op1.floatValue() + 1.0f, typedValue.getTypeDescriptor());
}
else if (op1 instanceof BigInteger) {
newValue = new TypedValue(((BigInteger) op1).add(BigInteger.ONE), typedValue.getTypeDescriptor());
}
else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() + 1L,
typedValue.getTypeDescriptor());
newValue = new TypedValue(op1.longValue() + 1L, typedValue.getTypeDescriptor());
}
else if (op1 instanceof Integer) {
newValue = new TypedValue(op1.intValue() + 1, typedValue.getTypeDescriptor());
}
else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() + (short) 1,
typedValue.getTypeDescriptor());
newValue = new TypedValue(op1.shortValue() + (short) 1, typedValue.getTypeDescriptor());
}
else if (op1 instanceof Byte) {
newValue = new TypedValue(op1.byteValue() + (byte) 1, typedValue.getTypeDescriptor());
}
else {
newValue = new TypedValue(op1.intValue() + 1,
typedValue.getTypeDescriptor());
// Unknown Number subtype -> best guess is double increment
newValue = new TypedValue(op1.doubleValue() + 1.0d, typedValue.getTypeDescriptor());
}
}
if (newValue == null) {
try {
newValue = state.operate(Operation.ADD, returnValue.getValue(), 1);
@ -103,10 +109,9 @@ public class OpInc extends Operator {
valueRef.setValue(newValue.getValue());
}
catch (SpelEvaluationException see) {
// if unable to set the value the operand is not writable (e.g. 1++ )
// If unable to set the value the operand is not writable (e.g. 1++ )
if (see.getMessageCode() == SpelMessage.SETVALUE_NOT_SUPPORTED) {
throw new SpelEvaluationException(operand.getStartPosition(),
SpelMessage.OPERAND_NOT_INCREMENTABLE);
throw new SpelEvaluationException(operand.getStartPosition(), SpelMessage.OPERAND_NOT_INCREMENTABLE);
}
else {
throw see;
@ -114,7 +119,7 @@ public class OpInc extends Operator {
}
if (!this.postfix) {
// the return value is the new value, not the original value
// The return value is the new value, not the original value
returnValue = newValue;
}
@ -123,7 +128,7 @@ public class OpInc extends Operator {
@Override
public String toStringAST() {
return new StringBuilder().append(getLeftOperand().toStringAST()).append("++").toString();
return getLeftOperand().toStringAST() + "++";
}
@Override

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -29,26 +30,25 @@ import org.springframework.util.NumberUtils;
* Implements the less-than-or-equal operator.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpLE extends Operator {
public OpLE(int pos, SpelNodeImpl... operands) {
super("<=", pos, operands);
this.exitTypeDescriptor="Z";
this.exitTypeDescriptor = "Z";
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state)
throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
@ -59,20 +59,33 @@ public class OpLE extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) <= 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() <= rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) <= 0);
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
}
else if (leftNumber instanceof Short || rightNumber instanceof Short) {
return BooleanTypedValue.forValue(leftNumber.shortValue() <= rightNumber.shortValue());
}
else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
return BooleanTypedValue.forValue(leftNumber.byteValue() <= rightNumber.byteValue());
}
else {
// Unknown Number subtypes -> best guess is double comparison
return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
}
}
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) <= 0);
@ -84,8 +97,8 @@ public class OpLE extends Operator {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
generateComparisonCode(mv, codeflow, IFGT, IF_ICMPGT);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
generateComparisonCode(mv, cf, IFGT, IF_ICMPGT);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -40,13 +41,14 @@ public class OpLT extends Operator {
this.exitTypeDescriptor = "Z";
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
@ -57,20 +59,33 @@ public class OpLT extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return BooleanTypedValue.forValue(leftBigDecimal.compareTo(rightBigDecimal) < 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() < rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return BooleanTypedValue.forValue(leftBigInteger.compareTo(rightBigInteger) < 0);
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue());
}
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
}
else if (leftNumber instanceof Short || rightNumber instanceof Short) {
return BooleanTypedValue.forValue(leftNumber.shortValue() < rightNumber.shortValue());
}
else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
return BooleanTypedValue.forValue(leftNumber.byteValue() < rightNumber.byteValue());
}
else {
// Unknown Number subtypes -> best guess is double comparison
return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue());
}
}
if (left instanceof CharSequence && right instanceof CharSequence) {
@ -87,8 +102,8 @@ public class OpLT extends Operator {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
generateComparisonCode(mv, codeflow, IFGE, IF_ICMPGE);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
generateComparisonCode(mv, cf, IFGE, IF_ICMPGE);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -29,24 +30,22 @@ import org.springframework.util.NumberUtils;
/**
* The minus operator supports:
* <ul>
* <li>subtraction of {@code BigDecimal}
* <li>subtraction of doubles (floats are represented as doubles)
* <li>subtraction of longs
* <li>subtraction of integers
* <li>subtraction of an int from a string of one character (effectively decreasing that
* character), so 'd'-3='a'
* <li>subtraction of numbers
* <li>subtraction of an int from a string of one character
* (effectively decreasing that character), so 'd'-3='a'
* </ul>
* It can be used as a unary operator for numbers ({@code BigDecimal}/double/long/int).
*
* <p>It can be used as a unary operator for numbers.
* The standard promotions are performed when the operand types vary (double-int=double).
* For other options it defers to the registered overloader.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpMinus extends Operator {
public OpMinus(int pos, SpelNodeImpl... operands) {
super("-", pos, operands);
}
@ -54,38 +53,45 @@ public class OpMinus extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) {// If only one operand, then this is unary minus
if (rightOp == null) { // if only one operand, then this is unary minus
Object operand = leftOp.getValueInternal(state).getValue();
if (operand instanceof Number) {
Number n = (Number) operand;
if (operand instanceof BigDecimal) {
BigDecimal bdn = (BigDecimal) n;
return new TypedValue(bdn.negate());
return new TypedValue(((BigDecimal) operand).negate());
}
if (operand instanceof Double) {
else if (operand instanceof Double) {
this.exitTypeDescriptor = "D";
return new TypedValue(0 - n.doubleValue());
return new TypedValue(0 - ((Number) operand).doubleValue());
}
if (operand instanceof Float) {
else if (operand instanceof Float) {
this.exitTypeDescriptor = "F";
return new TypedValue(0 - n.floatValue());
return new TypedValue(0 - ((Number) operand).floatValue());
}
if (operand instanceof Long) {
else if (operand instanceof BigInteger) {
return new TypedValue(((BigInteger) operand).negate());
}
else if (operand instanceof Long) {
this.exitTypeDescriptor = "J";
return new TypedValue(0 - n.longValue());
return new TypedValue(0 - ((Number) operand).longValue());
}
else if (operand instanceof Integer) {
this.exitTypeDescriptor = "I";
return new TypedValue(0 - ((Number) operand).intValue());
}
else if (operand instanceof Short) {
return new TypedValue(0 - ((Number) operand).shortValue());
}
else if (operand instanceof Byte) {
return new TypedValue(0 - ((Number) operand).byteValue());
}
else {
// Unknown Number subtypes -> best guess is double subtraction
return new TypedValue(0 - ((Number) operand).doubleValue());
}
this.exitTypeDescriptor = "I";
return new TypedValue(0 - n.intValue());
}
return state.operate(Operation.SUBTRACT, operand, null);
}
@ -101,37 +107,46 @@ public class OpMinus extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.subtract(rightBigDecimal));
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber instanceof Double && rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float && rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
return new TypedValue(leftNumber.floatValue() - rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long && rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return new TypedValue(leftBigInteger.subtract(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
return new TypedValue(leftNumber.longValue() - rightNumber.longValue());
}
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() - rightNumber.intValue());
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
return new TypedValue(leftNumber.intValue() - rightNumber.intValue());
}
else {
// Unknown Number subtypes -> best guess is double subtraction
return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue());
}
}
else if (left instanceof String && right instanceof Integer
&& ((String) left).length() == 1) {
if (left instanceof String && right instanceof Integer && ((String) left).length() == 1) {
String theString = (String) left;
Integer theInteger = (Integer) right;
// implements character - int (ie. b - 1 = a)
return new TypedValue(Character.toString((char)
(theString.charAt(0) - theInteger)));
// Implements character - int (ie. b - 1 = a)
return new TypedValue(Character.toString((char) (theString.charAt(0) - theInteger)));
}
return state.operate(Operation.SUBTRACT, left, right);
@ -139,45 +154,47 @@ public class OpMinus extends Operator {
@Override
public String toStringAST() {
if (getRightOperand() == null) { // unary minus
return new StringBuilder().append("-").append(getLeftOperand().toStringAST()).toString();
if (getRightOperand() == null) { // unary minus
return "-" + getLeftOperand().toStringAST();
}
return super.toStringAST();
}
@Override
public SpelNodeImpl getRightOperand() {
if (this.children.length<2) {return null;}
if (this.children.length < 2) {
return null;
}
return this.children[1];
}
@Override
public boolean isCompilable() {
if (!getLeftOperand().isCompilable()) {
return false;
}
if (this.children.length>1) {
if (!getRightOperand().isCompilable()) {
return false;
}
if (this.children.length > 1) {
if (!getRightOperand().isCompilable()) {
return false;
}
}
return this.exitTypeDescriptor!=null;
return (this.exitTypeDescriptor != null);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
}
if (this.children.length>1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
@ -186,14 +203,15 @@ public class OpMinus extends Operator {
case 'J':
mv.visitInsn(LSUB);
break;
case 'F':
case 'F':
mv.visitInsn(FSUB);
break;
case 'D':
mv.visitInsn(DSUB);
break;
break;
default:
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
throw new IllegalStateException(
"Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
}
}
else {
@ -204,17 +222,18 @@ public class OpMinus extends Operator {
case 'J':
mv.visitInsn(LNEG);
break;
case 'F':
case 'F':
mv.visitInsn(FNEG);
break;
case 'D':
mv.visitInsn(DNEG);
break;
break;
default:
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
}
throw new IllegalStateException(
"Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
}
}
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -30,12 +31,12 @@ import org.springframework.util.NumberUtils;
* Implements the modulus operator.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpModulus extends Operator {
public OpModulus(int pos, SpelNodeImpl... operands) {
super("%", pos, operands);
}
@ -45,6 +46,7 @@ public class OpModulus extends Operator {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
Object rightOperand = getRightOperand().getValueInternal(state).getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
@ -54,37 +56,44 @@ public class OpModulus extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.remainder(rightBigDecimal));
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber instanceof Double && rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float && rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
return new TypedValue(leftNumber.floatValue() % rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long && rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return new TypedValue(leftBigInteger.remainder(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
return new TypedValue(leftNumber.longValue() % rightNumber.longValue());
}
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
return new TypedValue(leftNumber.intValue() % rightNumber.intValue());
}
else {
// Unknown Number subtypes -> best guess is double division
return new TypedValue(leftNumber.doubleValue() % rightNumber.doubleValue());
}
return new TypedValue(leftNumber.intValue() % rightNumber.intValue());
}
return state.operate(Operation.MODULUS, leftOperand, rightOperand);
}
@Override
public boolean isCompilable() {
if (!getLeftOperand().isCompilable()) {
@ -97,21 +106,21 @@ public class OpModulus extends Operator {
}
return this.exitTypeDescriptor!=null;
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
if (this.children.length > 1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
@ -127,10 +136,11 @@ public class OpModulus extends Operator {
mv.visitInsn(DREM);
break;
default:
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
throw new IllegalStateException(
"Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
}
}
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException;
@ -30,25 +31,26 @@ import org.springframework.util.NumberUtils;
* Implements the {@code multiply} operator.
*
* <p>Conversions and promotions are handled as defined in
* <a href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html">Section
* 5.6.2 of the Java Language Specification</a>, with the addiction of {@code BigDecimal} management:
* <a href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html">Section 5.6.2 of the
* Java Language Specification</a>, with the addiction of {@code BigDecimal}/{@code BigInteger} management:
*
* <p>If any of the operands is of a reference type, unboxing conversion (Section 5.1.8)
* is performed. Then:<br>
* If either operand is of type {@code BigDecimal}, the other is converted to {@code BigDecimal}.<br>
* If either operand is of type double, the other is converted to double.<br>
* Otherwise, if either operand is of type float, the other is converted to float.<br>
* If either operand is of type {@code BigInteger}, the other is converted to {@code BigInteger}.<br>
* Otherwise, if either operand is of type long, the other is converted to long.<br>
* Otherwise, both operands are converted to type int.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpMultiply extends Operator {
public OpMultiply(int pos, SpelNodeImpl... operands) {
super("*", pos, operands);
}
@ -60,55 +62,60 @@ public class OpMultiply extends Operator {
* for types not supported here.
* <p>Supported operand types:
* <ul>
* <li>{@code BigDecimal}
* <li>doubles
* <li>longs
* <li>integers
* <li>numbers
* <li>String and int ('abc' * 2 == 'abcabc')
* </ul>
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
Object rightOperand = getRightOperand().getValueInternal(state).getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
Number rightNumber = (Number) rightOperand;
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.multiply(rightBigDecimal));
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber instanceof Double && rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
return new TypedValue(leftNumber.doubleValue()
* rightNumber.doubleValue());
return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float && rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long && rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return new TypedValue(leftBigInteger.multiply(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
}
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
else {
// Unknown Number subtypes -> best guess is double multiplication
return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
}
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
else if (leftOperand instanceof String && rightOperand instanceof Integer) {
if (leftOperand instanceof String && rightOperand instanceof Integer) {
int repeats = (Integer) rightOperand;
StringBuilder result = new StringBuilder();
for (int i = 0; i < repeats; i++) {
@ -119,34 +126,34 @@ public class OpMultiply extends Operator {
return state.operate(Operation.MULTIPLY, leftOperand, rightOperand);
}
@Override
public boolean isCompilable() {
if (!getLeftOperand().isCompilable()) {
return false;
}
if (this.children.length>1) {
if (this.children.length > 1) {
if (!getRightOperand().isCompilable()) {
return false;
}
}
return this.exitTypeDescriptor!=null;
return (this.exitTypeDescriptor != null);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
if (this.children.length>1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
@ -162,10 +169,11 @@ public class OpMultiply extends Operator {
mv.visitInsn(DMUL);
break;
default:
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
throw new IllegalStateException(
"Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
}
}
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -54,19 +54,17 @@ public class OpNE extends Operator {
if (!left.isCompilable() || !right.isCompilable()) {
return false;
}
String leftdesc = left.getExitDescriptor();
String rightdesc = right.getExitDescriptor();
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftdesc, rightdesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers) {
return dc.areCompatible;
}
return true;
String leftDesc = left.exitTypeDescriptor;
String rightDesc = right.exitTypeDescriptor;
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
return (!dc.areNumbers || dc.areCompatible);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
String leftDesc = getLeftOperand().getExitDescriptor();
String rightDesc = getRightOperand().getExitDescriptor();
public void generateCode(MethodVisitor mv, CodeFlow cf) {
String leftDesc = getLeftOperand().exitTypeDescriptor;
String rightDesc = getRightOperand().exitTypeDescriptor;
Label elseTarget = new Label();
Label endOfIf = new Label();
boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
@ -77,14 +75,14 @@ public class OpNE extends Operator {
if (dc.areNumbers && dc.areCompatible) {
char targetType = dc.compatibleType;
getLeftOperand().generateCode(mv, codeflow);
getLeftOperand().generateCode(mv, cf);
if (!leftPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.exitCompilationScope();
if (!rightPrim) {
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
@ -109,8 +107,8 @@ public class OpNE extends Operator {
}
}
else {
getLeftOperand().generateCode(mv, codeflow);
getRightOperand().generateCode(mv, codeflow);
getLeftOperand().generateCode(mv, cf);
getRightOperand().generateCode(mv, cf);
mv.visitJumpInsn(IF_ACMPEQ, elseTarget);
}
mv.visitInsn(ICONST_1);
@ -118,7 +116,7 @@ public class OpNE extends Operator {
mv.visitLabel(elseTarget);
mv.visitInsn(ICONST_0);
mv.visitLabel(endOfIf);
codeflow.pushDescriptor("Z");
cf.pushDescriptor("Z");
}
}

View File

@ -71,34 +71,31 @@ public class OpOr extends Operator {
@Override
public boolean isCompilable() {
SpelNodeImpl left = getLeftOperand();
SpelNodeImpl right= getRightOperand();
if (!left.isCompilable() || !right.isCompilable()) {
return false;
}
return
CodeFlow.isBooleanCompatible(left.getExitDescriptor()) &&
CodeFlow.isBooleanCompatible(right.getExitDescriptor());
SpelNodeImpl right = getRightOperand();
return (left.isCompilable() && right.isCompilable() &&
CodeFlow.isBooleanCompatible(left.exitTypeDescriptor) &&
CodeFlow.isBooleanCompatible(right.exitTypeDescriptor));
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
// pseudo: if (leftOperandValue) { result=true; } else { result=rightOperandValue; }
Label elseTarget = new Label();
Label endOfIf = new Label();
codeflow.enterCompilationScope();
getLeftOperand().generateCode(mv, codeflow);
codeflow.unboxBooleanIfNecessary(mv);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getLeftOperand().generateCode(mv, cf);
cf.unboxBooleanIfNecessary(mv);
cf.exitCompilationScope();
mv.visitJumpInsn(IFEQ, elseTarget);
mv.visitLdcInsn(1); // TRUE
mv.visitJumpInsn(GOTO,endOfIf);
mv.visitLabel(elseTarget);
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.unboxBooleanIfNecessary(mv);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.unboxBooleanIfNecessary(mv);
cf.exitCompilationScope();
mv.visitLabel(endOfIf);
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.MethodVisitor;
import org.springframework.core.convert.TypeDescriptor;
@ -32,24 +33,22 @@ import org.springframework.util.NumberUtils;
/**
* The plus operator will:
* <ul>
* <li>add {@code BigDecimal}
* <li>add doubles (floats are represented as doubles)
* <li>add longs
* <li>add integers
* <li>add numbers
* <li>concatenate strings
* </ul>
* It can be used as a unary operator for numbers ({@code BigDecimal}/double/long/int).
*
* <p>It can be used as a unary operator for numbers.
* The standard promotions are performed when the operand types vary (double+int=double).
* For other options it defers to the registered overloader.
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Ivo Smid
* @author Giovanni Dall'Oglio Risso
* @since 3.0
*/
public class OpPlus extends Operator {
public OpPlus(int pos, SpelNodeImpl... operands) {
super("+", pos, operands);
Assert.notEmpty(operands);
@ -61,30 +60,30 @@ public class OpPlus extends Operator {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) { // If only one operand, then this is unary plus
if (rightOp == null) { // if only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state).getValue();
if (operandOne instanceof Number) {
if (operandOne instanceof Double || operandOne instanceof Long || operandOne instanceof BigDecimal) {
if (operandOne instanceof Double || operandOne instanceof Long) {
this.exitTypeDescriptor = (operandOne instanceof Double)?"D":"J";
}
return new TypedValue(operandOne);
if (operandOne instanceof Double) {
this.exitTypeDescriptor = "D";
}
if (operandOne instanceof Float) {
else if (operandOne instanceof Float) {
this.exitTypeDescriptor = "F";
return new TypedValue(((Number) operandOne).floatValue());
}
this.exitTypeDescriptor = "I";
return new TypedValue(((Number) operandOne).intValue());
else if (operandOne instanceof Long) {
this.exitTypeDescriptor = "J";
}
else if (operandOne instanceof Integer) {
this.exitTypeDescriptor = "I";
}
return new TypedValue(operandOne);
}
return state.operate(Operation.ADD, operandOne, null);
}
final TypedValue operandOneValue = leftOp.getValueInternal(state);
final Object leftOperand = operandOneValue.getValue();
final TypedValue operandTwoValue = rightOp.getValueInternal(state);
final Object rightOperand = operandTwoValue.getValue();
TypedValue operandOneValue = leftOp.getValueInternal(state);
Object leftOperand = operandOneValue.getValue();
TypedValue operandTwoValue = rightOp.getValueInternal(state);
Object rightOperand = operandTwoValue.getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
Number leftNumber = (Number) leftOperand;
@ -95,47 +94,53 @@ public class OpPlus extends Operator {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.add(rightBigDecimal));
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber instanceof Double && rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "D";
}
return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float && rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "F";
}
return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long && rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return new TypedValue(leftBigInteger.add(rightBigInteger));
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber.getClass() == rightNumber.getClass()) {
this.exitTypeDescriptor = "J";
}
return new TypedValue(leftNumber.longValue() + rightNumber.longValue());
}
// TODO what about overflow?
this.exitTypeDescriptor = "I";
return new TypedValue(leftNumber.intValue() + rightNumber.intValue());
else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
this.exitTypeDescriptor = "I";
}
return new TypedValue(leftNumber.intValue() + rightNumber.intValue());
}
else {
// Unknown Number subtypes -> best guess is double addition
return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
}
}
if (leftOperand instanceof String && rightOperand instanceof String) {
return new TypedValue(new StringBuilder((String) leftOperand).append(
(String) rightOperand).toString());
return new TypedValue((String) leftOperand + rightOperand);
}
if (leftOperand instanceof String) {
StringBuilder result = new StringBuilder((String) leftOperand);
result.append((rightOperand == null ? "null" : convertTypedValueToString(
operandTwoValue, state)));
return new TypedValue(result.toString());
return new TypedValue(
leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
}
if (rightOperand instanceof String) {
StringBuilder result = new StringBuilder((leftOperand == null ? "null"
: convertTypedValueToString(operandOneValue, state)));
result.append((String) rightOperand);
return new TypedValue(result.toString());
return new TypedValue(
(leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
}
return state.operate(Operation.ADD, leftOperand, rightOperand);
@ -143,8 +148,8 @@ public class OpPlus extends Operator {
@Override
public String toStringAST() {
if (this.children.length<2) { // unary plus
return new StringBuilder().append("+").append(getLeftOperand().toStringAST()).toString();
if (this.children.length < 2) { // unary plus
return "+" + getLeftOperand().toStringAST();
}
return super.toStringAST();
}
@ -160,21 +165,17 @@ public class OpPlus extends Operator {
/**
* Convert operand value to string using registered converter or using
* {@code toString} method.
*
* @param value typed value to be converted
* @param state expression state
* @return {@code TypedValue} instance converted to {@code String}
*/
private static String convertTypedValueToString(TypedValue value, ExpressionState state) {
final TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
final TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class);
TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class);
if (typeConverter.canConvert(value.getTypeDescriptor(), typeDescriptor)) {
final Object obj = typeConverter.convertValue(value.getValue(),
value.getTypeDescriptor(), typeDescriptor);
return String.valueOf(obj);
return String.valueOf(typeConverter.convertValue(value.getValue(),
value.getTypeDescriptor(), typeDescriptor));
}
return String.valueOf(value.getValue());
}
@ -183,28 +184,28 @@ public class OpPlus extends Operator {
if (!getLeftOperand().isCompilable()) {
return false;
}
if (this.children.length>1) {
if (this.children.length > 1) {
if (!getRightOperand().isCompilable()) {
return false;
}
}
return this.exitTypeDescriptor!=null;
return (this.exitTypeDescriptor != null);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
getLeftOperand().generateCode(mv, codeflow);
String leftdesc = getLeftOperand().getExitDescriptor();
if (!CodeFlow.isPrimitive(leftdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftdesc);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
if (!CodeFlow.isPrimitive(leftDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
}
if (this.children.length>1) {
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
String rightdesc = getRightOperand().getExitDescriptor();
codeflow.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightdesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightdesc);
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
cf.exitCompilationScope();
if (!CodeFlow.isPrimitive(rightDesc)) {
CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
}
switch (this.exitTypeDescriptor.charAt(0)) {
case 'I':
@ -220,10 +221,11 @@ public class OpPlus extends Operator {
mv.visitInsn(DADD);
break;
default:
throw new IllegalStateException("Unrecognized exit descriptor: '"+this.exitTypeDescriptor+"'");
throw new IllegalStateException(
"Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
}
}
codeflow.pushDescriptor(this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
@ -44,7 +45,11 @@ public abstract class Operator extends SpelNodeImpl {
// descriptors are not providing enough information (for example a generic type
// whose accessors seem to only be returning 'Object' - the actual descriptors may
// indicate 'int')
protected String leftActualDescriptor, rightActualDescriptor;
protected String leftActualDescriptor;
protected String rightActualDescriptor;
public Operator(String payload,int pos,SpelNodeImpl... operands) {
super(pos, operands);
@ -69,8 +74,7 @@ public abstract class Operator extends SpelNodeImpl {
*/
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("(");
StringBuilder sb = new StringBuilder("(");
sb.append(getChild(0).toStringAST());
for (int i = 1; i < getChildCount(); i++) {
sb.append(" ").append(getOperatorName()).append(" ");
@ -86,69 +90,70 @@ public abstract class Operator extends SpelNodeImpl {
if (!left.isCompilable() || !right.isCompilable()) {
return false;
}
// Supported operand types for equals (at the moment)
String leftDesc = left.getExitDescriptor();
String rightDesc= right.getExitDescriptor();
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
if (dc.areNumbers) {
return dc.areCompatible;
}
return false;
String leftDesc = left.exitTypeDescriptor;
String rightDesc = right.exitTypeDescriptor;
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
this.leftActualDescriptor, this.rightActualDescriptor);
return (dc.areNumbers && dc.areCompatible);
}
/**
* Numeric comparison operators share very similar generated code, only differing in
* two comparison instructions.
*/
protected void generateComparisonCode(MethodVisitor mv, CodeFlow codeflow, int compareInstruction1,
int compareInstruction2) {
String leftDesc = getLeftOperand().getExitDescriptor();
String rightDesc = getRightOperand().getExitDescriptor();
protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compInstruction1, int compInstruction2) {
String leftDesc = getLeftOperand().exitTypeDescriptor;
String rightDesc = getRightOperand().exitTypeDescriptor;
boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc);
boolean unboxRight = !CodeFlow.isPrimitive(rightDesc);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor);
DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
this.leftActualDescriptor, this.rightActualDescriptor);
char targetType = dc.compatibleType;//CodeFlow.toPrimitiveTargetDesc(leftDesc);
getLeftOperand().generateCode(mv, codeflow);
getLeftOperand().generateCode(mv, cf);
if (unboxLeft) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
codeflow.enterCompilationScope();
getRightOperand().generateCode(mv, codeflow);
codeflow.exitCompilationScope();
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
cf.exitCompilationScope();
if (unboxRight) {
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
}
// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
Label elseTarget = new Label();
Label endOfIf = new Label();
if (targetType=='D') {
mv.visitInsn(DCMPG);
mv.visitJumpInsn(compareInstruction1, elseTarget);
mv.visitJumpInsn(compInstruction1, elseTarget);
}
else if (targetType=='F') {
mv.visitInsn(FCMPG);
mv.visitJumpInsn(compareInstruction1, elseTarget);
mv.visitJumpInsn(compInstruction1, elseTarget);
}
else if (targetType=='J') {
mv.visitInsn(LCMP);
mv.visitJumpInsn(compareInstruction1, elseTarget);
mv.visitJumpInsn(compInstruction1, elseTarget);
}
else if (targetType=='I') {
mv.visitJumpInsn(compareInstruction2, elseTarget);
mv.visitJumpInsn(compInstruction2, elseTarget);
}
else {
throw new IllegalStateException("Unexpected descriptor "+leftDesc);
}
// Other numbers are not yet supported (isCompilable will not have returned true)
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO,endOfIf);
mv.visitLabel(elseTarget);
mv.visitInsn(ICONST_0);
mv.visitLabel(endOfIf);
codeflow.pushDescriptor("Z");
cf.pushDescriptor("Z");
}
protected boolean equalityCheck(ExpressionState state, Object left, Object right) {
@ -161,20 +166,33 @@ public abstract class Operator extends SpelNodeImpl {
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return (leftBigDecimal == null ? rightBigDecimal == null : leftBigDecimal.compareTo(rightBigDecimal) == 0);
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return (leftNumber.doubleValue() == rightNumber.doubleValue());
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return (leftNumber.floatValue() == rightNumber.floatValue());
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return (leftBigInteger == null ? rightBigInteger == null : leftBigInteger.compareTo(rightBigInteger) == 0);
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return (leftNumber.longValue() == rightNumber.longValue());
}
return (leftNumber.intValue() == rightNumber.intValue());
else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
return (leftNumber.intValue() == rightNumber.intValue());
}
else if (leftNumber instanceof Short || rightNumber instanceof Short) {
return (leftNumber.shortValue() == rightNumber.shortValue());
}
else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
return (leftNumber.byteValue() == rightNumber.byteValue());
}
else {
// Unknown Number subtypes -> best guess is double comparison
return (leftNumber.doubleValue() == rightNumber.doubleValue());
}
}
if (left instanceof CharSequence && right instanceof CharSequence) {
@ -195,16 +213,21 @@ public abstract class Operator extends SpelNodeImpl {
return false;
}
/**
* A descriptor comparison encapsulates the result of comparing descriptor for two operands and
* describes at what level they are compatible.
*/
protected static class DescriptorComparison {
static DescriptorComparison NOT_NUMBERS = new DescriptorComparison(false,false,' ');
static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true,false,' ');
static DescriptorComparison NOT_NUMBERS = new DescriptorComparison(false, false, ' ');
static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true, false, ' ');
final boolean areNumbers; // Were the two compared descriptor both for numbers?
final boolean areCompatible; // If they were numbers, were they compatible?
final char compatibleType; // When compatible, what is the descriptor of the common type
private DescriptorComparison(boolean areNumbers, boolean areCompatible, char compatibleType) {
@ -220,18 +243,19 @@ public abstract class Operator extends SpelNodeImpl {
* For generic types with unbound type variables the declared descriptor discovered may be 'Object' but
* from the actual descriptor it is possible to observe that the objects are really numeric values (e.g.
* ints).
*
* @param leftDeclaredDescriptor the statically determinable left descriptor
* @param rightDeclaredDescriptor the statically determinable right descriptor
* @param leftActualDescriptor the dynamic/runtime left object descriptor
* @param rightActualDescriptor the dynamic/runtime right object descriptor
* @return a DescriptorComparison object indicating the type of compatibility, if any
*/
public static DescriptorComparison checkNumericCompatibility(String leftDeclaredDescriptor, String rightDeclaredDescriptor, String leftActualDescriptor, String rightActualDescriptor) {
public static DescriptorComparison checkNumericCompatibility(String leftDeclaredDescriptor,
String rightDeclaredDescriptor, String leftActualDescriptor, String rightActualDescriptor) {
String ld = leftDeclaredDescriptor;
String rd = rightDeclaredDescriptor;
boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld) ;
boolean leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
// If the declared descriptors aren't providing the information, try the actual descriptors
@ -256,7 +280,6 @@ public abstract class Operator extends SpelNodeImpl {
return DescriptorComparison.NOT_NUMBERS;
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -40,6 +40,7 @@ public class OperatorBetween extends Operator {
super("between", pos, operands);
}
/**
* Returns a boolean based on whether a value is in the range expressed. The first
* operand is any value whilst the second is a list of two values - those two values
@ -56,13 +57,13 @@ public class OperatorBetween extends Operator {
throw new SpelEvaluationException(getRightOperand().getStartPosition(),
SpelMessage.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST);
}
List<?> l = (List<?>) right;
Object low = l.get(0);
Object high = l.get(1);
TypeComparator comparator = state.getTypeComparator();
List<?> list = (List<?>) right;
Object low = list.get(0);
Object high = list.get(1);
TypeComparator comp = state.getTypeComparator();
try {
return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 &&
comparator.compare(left, high) <= 0));
return BooleanTypedValue.forValue(comp.compare(left, low) >= 0 && comp.compare(left, high) <= 0);
}
catch (SpelEvaluationException ex) {
ex.setPosition(getStartPosition());

View File

@ -81,10 +81,10 @@ public class OperatorInstanceof extends Operator {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
getLeftOperand().generateCode(mv, codeflow);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
getLeftOperand().generateCode(mv, cf);
mv.visitTypeInsn(INSTANCEOF,Type.getInternalName(this.type));
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -44,10 +44,10 @@ public class OperatorMatches extends Operator {
/**
* Check the first operand matches the regex specified as the second operand.
* @param state the expression state
* @return true if the first operand matches the regex specified as the second
* operand, otherwise false
* @throws EvaluationException if there is a problem evaluating the expression (e.g.
* the regex is invalid)
* @return {@code true} if the first operand matches the regex specified as the
* second operand, otherwise {@code false}
* @throws EvaluationException if there is a problem evaluating the expression
* (e.g. the regex is invalid)
*/
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
@ -55,21 +55,23 @@ public class OperatorMatches extends Operator {
SpelNodeImpl rightOp = getRightOperand();
Object left = leftOp.getValue(state, String.class);
Object right = getRightOperand().getValueInternal(state).getValue();
if (!(left instanceof String)) {
throw new SpelEvaluationException(leftOp.getStartPosition(),
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left);
}
if (!(right instanceof String)) {
throw new SpelEvaluationException(rightOp.getStartPosition(),
SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right);
}
try {
if (!(left instanceof String)) {
throw new SpelEvaluationException(leftOp.getStartPosition(),
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left);
}
if (!(right instanceof String)) {
throw new SpelEvaluationException(rightOp.getStartPosition(),
SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right);
}
Pattern pattern = Pattern.compile((String) right);
Matcher matcher = pattern.matcher((String) left);
return BooleanTypedValue.forValue(matcher.matches());
}
catch (PatternSyntaxException pse) {
throw new SpelEvaluationException(rightOp.getStartPosition(), pse, SpelMessage.INVALID_PATTERN, right);
catch (PatternSyntaxException ex) {
throw new SpelEvaluationException(rightOp.getStartPosition(), ex, SpelMessage.INVALID_PATTERN, right);
}
}

View File

@ -33,7 +33,7 @@ import org.springframework.expression.spel.support.BooleanTypedValue;
* @author Oliver Becker
* @since 3.0
*/
public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator
public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so does not extend BinaryOperator
public OperatorNot(int pos, SpelNodeImpl operand) {
super(pos, operand);
@ -58,21 +58,19 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("!").append(getChild(0).toStringAST());
return sb.toString();
return "!" + getChild(0).toStringAST();
}
@Override
public boolean isCompilable() {
SpelNodeImpl child = this.children[0];
return child.isCompilable() && CodeFlow.isBooleanCompatible(child.getExitDescriptor());
return (child.isCompilable() && CodeFlow.isBooleanCompatible(child.exitTypeDescriptor));
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
this.children[0].generateCode(mv, codeflow);
codeflow.unboxBooleanIfNecessary(mv);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
this.children[0].generateCode(mv, cf);
cf.unboxBooleanIfNecessary(mv);
Label elseTarget = new Label();
Label endOfIf = new Label();
mv.visitJumpInsn(IFNE,elseTarget);
@ -81,7 +79,7 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
mv.visitLabel(elseTarget);
mv.visitInsn(ICONST_0); // FALSE
mv.visitLabel(endOfIf);
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -17,6 +17,7 @@
package org.springframework.expression.spel.ast;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
@ -54,22 +55,19 @@ public class OperatorPower extends Operator {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.pow(rightNumber.intValue()));
}
if (leftNumber instanceof Double || rightNumber instanceof Double) {
else if (leftNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
return new TypedValue(leftBigInteger.pow(rightNumber.intValue()));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(Math.pow(leftNumber.doubleValue(), rightNumber.doubleValue()));
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(Math.pow(leftNumber.floatValue(), rightNumber.floatValue()));
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
double d = Math.pow(leftNumber.longValue(), rightNumber.longValue());
return new TypedValue((long) d);
}
double d = Math.pow(leftNumber.longValue(), rightNumber.longValue());
if (d > Integer.MAX_VALUE) {
double d = Math.pow(leftNumber.doubleValue(), rightNumber.doubleValue());
if (d > Integer.MAX_VALUE || leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue((long) d);
}
else {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -44,6 +44,7 @@ public class Projection extends SpelNodeImpl {
private final boolean nullSafe;
public Projection(boolean nullSafe, int pos, SpelNodeImpl expression) {
super(pos, expression);
this.nullSafe = nullSafe;
@ -80,7 +81,7 @@ public class Projection extends SpelNodeImpl {
state.popActiveContextObject();
}
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); // TODO unable to build correct type descriptor
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); // TODO unable to build correct type descriptor
}
if (operand instanceof Collection || operandIsArray) {
@ -118,20 +119,18 @@ public class Projection extends SpelNodeImpl {
if (operand==null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.instance;
return ValueRef.NullValueRef.INSTANCE;
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null");
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null");
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE,
operand.getClass().getName());
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
return sb.append("![").append(getChild(0).toStringAST()).append("]").toString();
return "![" + getChild(0).toStringAST() + "]";
}
private Class<?> determineCommonType(Class<?> oldType, Class<?> newType) {

View File

@ -87,10 +87,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
return tv;
}
private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext eContext,
private TypedValue getValueInternal(TypedValue contextObject, EvaluationContext evalContext,
boolean isAutoGrowNullReferences) throws EvaluationException {
TypedValue result = readProperty(contextObject, eContext, this.name);
TypedValue result = readProperty(contextObject, evalContext, this.name);
// Dynamically create the objects if the user has requested that optional behavior
if (result.getValue() == null && isAutoGrowNullReferences &&
@ -101,10 +101,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
// Create a new collection or map ready for the indexer
if (resultDescriptor.getType().equals(List.class)) {
try {
if (isWritableProperty(this.name, contextObject,eContext)) {
if (isWritableProperty(this.name, contextObject, evalContext)) {
List<?> newList = ArrayList.class.newInstance();
writeProperty(contextObject, eContext, this.name, newList);
result = readProperty(contextObject, eContext, this.name);
writeProperty(contextObject, evalContext, this.name, newList);
result = readProperty(contextObject, evalContext, this.name);
}
}
catch (InstantiationException ex) {
@ -118,10 +118,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
else {
try {
if (isWritableProperty(this.name,contextObject,eContext)) {
if (isWritableProperty(this.name,contextObject, evalContext)) {
Map<?,?> newMap = HashMap.class.newInstance();
writeProperty(contextObject, eContext, this.name, newMap);
result = readProperty(contextObject, eContext, this.name);
writeProperty(contextObject, evalContext, this.name, newMap);
result = readProperty(contextObject, evalContext, this.name);
}
}
catch (InstantiationException ex) {
@ -137,10 +137,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
else {
// 'simple' object
try {
if (isWritableProperty(this.name,contextObject,eContext)) {
if (isWritableProperty(this.name,contextObject, evalContext)) {
Object newObject = result.getTypeDescriptor().getType().newInstance();
writeProperty(contextObject, eContext, this.name, newObject);
result = readProperty(contextObject, eContext, this.name);
writeProperty(contextObject, evalContext, this.name, newObject);
result = readProperty(contextObject, evalContext, this.name);
}
}
catch (InstantiationException ex) {
@ -176,7 +176,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
* @return the value of the property
* @throws EvaluationException if any problem accessing the property or it cannot be found
*/
private TypedValue readProperty(TypedValue contextObject, EvaluationContext eContext, String name)
private TypedValue readProperty(TypedValue contextObject, EvaluationContext evalContext, String name)
throws EvaluationException {
Object targetObject = contextObject.getValue();
@ -187,9 +187,9 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
PropertyAccessor accessorToUse = this.cachedReadAccessor;
if (accessorToUse != null) {
try {
return accessorToUse.read(eContext, contextObject.getValue(), name);
return accessorToUse.read(evalContext, contextObject.getValue(), name);
}
catch (AccessException ae) {
catch (AccessException ex) {
// this is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up
this.cachedReadAccessor = null;
@ -197,25 +197,25 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
// get the accessor and use it. If they are not cacheable but report they can read the property
// then ask them to read it
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(eContext, contextObject.getValue(), name)) {
if (accessor.canRead(evalContext, contextObject.getValue(), name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
eContext, contextObject.getValue(), name);
evalContext, contextObject.getValue(), name);
}
this.cachedReadAccessor = accessor;
return accessor.read(eContext, contextObject.getValue(), name);
return accessor.read(evalContext, contextObject.getValue(), name);
}
}
}
catch (AccessException ae) {
throw new SpelEvaluationException(ae, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage());
catch (AccessException ex) {
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ex.getMessage());
}
}
if (contextObject.getValue() == null) {
@ -227,7 +227,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
}
private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue)
private void writeProperty(TypedValue contextObject, EvaluationContext evalContext, String name, Object newValue)
throws EvaluationException {
if (contextObject.getValue() == null && this.nullSafe) {
@ -237,10 +237,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
if (accessorToUse != null) {
try {
accessorToUse.write(eContext, contextObject.getValue(), name, newValue);
accessorToUse.write(evalContext, contextObject.getValue(), name, newValue);
return;
}
catch (AccessException ae) {
catch (AccessException ex) {
// this is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up
this.cachedWriteAccessor = null;
@ -248,23 +248,23 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
if (accessor.canWrite(evalContext, contextObject.getValue(), name)) {
this.cachedWriteAccessor = accessor;
accessor.write(eContext, contextObject.getValue(), name, newValue);
accessor.write(evalContext, contextObject.getValue(), name, newValue);
return;
}
}
}
catch (AccessException ae) {
throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
name, ae.getMessage());
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
name, ex.getMessage());
}
}
if (contextObject.getValue()==null) {
if (contextObject.getValue() == null) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name);
}
else {
@ -273,19 +273,19 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
}
public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext eContext)
public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext evalContext)
throws EvaluationException {
List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
try {
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
if (accessor.canWrite(evalContext, contextObject.getValue(), name)) {
return true;
}
}
catch (AccessException ae) {
catch (AccessException ex) {
// let others try
}
}
@ -343,9 +343,9 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
((CompilablePropertyAccessor) this.cachedReadAccessor).generateCode(this.name, mv, codeflow);
codeflow.pushDescriptor(this.exitTypeDescriptor);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
((CompilablePropertyAccessor) this.cachedReadAccessor).generateCode(this.name, mv, cf);
cf.pushDescriptor(this.exitTypeDescriptor);
}
@ -355,21 +355,21 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
private final TypedValue contextObject;
private final EvaluationContext eContext;
private final EvaluationContext evalContext;
private final boolean autoGrowNullReferences;
public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject,
EvaluationContext evaluationContext, boolean autoGrowNullReferences) {
EvaluationContext evalContext, boolean autoGrowNullReferences) {
this.ref = propertyOrFieldReference;
this.contextObject = activeContextObject;
this.eContext = evaluationContext;
this.evalContext = evalContext;
this.autoGrowNullReferences = autoGrowNullReferences;
}
@Override
public TypedValue getValue() {
TypedValue value = this.ref.getValueInternal(this.contextObject, this.eContext, this.autoGrowNullReferences);
TypedValue value = this.ref.getValueInternal(this.contextObject, this.evalContext, this.autoGrowNullReferences);
if (this.ref.cachedReadAccessor instanceof CompilablePropertyAccessor) {
CompilablePropertyAccessor accessor = (CompilablePropertyAccessor) this.ref.cachedReadAccessor;
this.ref.exitTypeDescriptor = CodeFlow.toDescriptor(accessor.getPropertyType());
@ -379,7 +379,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
@Override
public void setValue(Object newValue) {
this.ref.writeProperty(this.contextObject, this.eContext, this.ref.name, newValue);
this.ref.writeProperty(this.contextObject, this.evalContext, this.ref.name, newValue);
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -35,8 +35,8 @@ public class QualifiedIdentifier extends SpelNodeImpl {
private TypedValue value;
public QualifiedIdentifier(int pos,SpelNodeImpl... operands) {
super(pos,operands);
public QualifiedIdentifier(int pos, SpelNodeImpl... operands) {
super(pos, operands);
}

View File

@ -49,9 +49,9 @@ public class RealLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
mv.visitLdcInsn(this.value.getValue());
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -59,13 +59,13 @@ public class Selection extends SpelNodeImpl {
public Selection(boolean nullSafe, int variant, int pos, SpelNodeImpl expression) {
super(pos, expression != null ? new SpelNodeImpl[] { expression }
: new SpelNodeImpl[] {});
super(pos, expression);
Assert.notNull(expression, "Expression must not be null");
this.nullSafe = nullSafe;
this.variant = variant;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
return getValueRef(state).getValue();
@ -84,30 +84,30 @@ public class Selection extends SpelNodeImpl {
Object lastKey = null;
for (Map.Entry<?, ?> entry : mapdata.entrySet()) {
try {
TypedValue kvpair = new TypedValue(entry);
state.pushActiveContextObject(kvpair);
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
TypedValue kvPair = new TypedValue(entry);
state.pushActiveContextObject(kvPair);
Object val = selectionCriteria.getValueInternal(state).getValue();
if (val instanceof Boolean) {
if ((Boolean) val) {
if (this.variant == FIRST) {
result.put(entry.getKey(),entry.getValue());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
result.put(entry.getKey(), entry.getValue());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
}
result.put(entry.getKey(),entry.getValue());
result.put(entry.getKey(), entry.getValue());
lastKey = entry.getKey();
}
}
else {
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
}
}
finally {
state.popActiveContextObject();
}
}
if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(null),this);
if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(null), this);
}
if (this.variant == LAST) {
@ -122,29 +122,29 @@ public class Selection extends SpelNodeImpl {
if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
List<Object> data = new ArrayList<Object>();
Collection<?> c = (operand instanceof Collection) ?
(Collection<?>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand));
data.addAll(c);
Collection<?> coll = (operand instanceof Collection ?
(Collection<?>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand)));
data.addAll(coll);
List<Object> result = new ArrayList<Object>();
int idx = 0;
int index = 0;
for (Object element : data) {
try {
state.pushActiveContextObject(new TypedValue(element));
state.enterScope("index", idx);
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
state.enterScope("index", index);
Object val = selectionCriteria.getValueInternal(state).getValue();
if (val instanceof Boolean) {
if ((Boolean) val) {
if (this.variant == FIRST) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(element),this);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(element), this);
}
result.add(element);
}
}
else {
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);
}
idx++;
index++;
}
finally {
state.exitScope();
@ -153,7 +153,7 @@ public class Selection extends SpelNodeImpl {
}
if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
return ValueRef.NullValueRef.instance;
return ValueRef.NullValueRef.INSTANCE;
}
if (this.variant == LAST) {
@ -163,20 +163,20 @@ public class Selection extends SpelNodeImpl {
if (operand instanceof Collection) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementTypeDescriptor().getType());
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(
op.getTypeDescriptor().getElementTypeDescriptor().getType());
Object resultArray = Array.newInstance(elementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this);
}
if (operand==null) {
if (operand == null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.instance;
return ValueRef.NullValueRef.INSTANCE;
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.INVALID_TYPE_FOR_SELECTION, "null");
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION, "null");
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.INVALID_TYPE_FOR_SELECTION, operand.getClass().getName());
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
operand.getClass().getName());
}
@Override

View File

@ -28,10 +28,11 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* The common supertype of all AST nodes in a parsed Spring Expression Language format
* expression.
* The common supertype of all AST nodes in a parsed Spring Expression Language
* format expression.
*
* @author Andy Clement
* @since 3.0
@ -62,11 +63,11 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
this.pos = pos;
// pos combines start and end so can never be zero because tokens cannot be zero length
Assert.isTrue(pos != 0);
if (operands != null && operands.length > 0) {
Assert.isTrue(pos != 0, "Pos must not be 0");
if (!ObjectUtils.isEmpty(operands)) {
this.children = operands;
for (SpelNodeImpl childnode : operands) {
childnode.parent = this;
for (SpelNodeImpl childNode : operands) {
childNode.parent = this;
}
}
}
@ -195,10 +196,10 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
* example it will include information about the type of the object currently
* on the stack.
* @param mv the ASM MethodVisitor into which code should be generated
* @param codeflow a context object with info about what is on the stack
* @param cf a context object with info about what is on the stack
*/
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
throw new IllegalStateException(this.getClass().getName()+" has no generateCode(..) method");
public void generateCode(MethodVisitor mv, CodeFlow cf) {
throw new IllegalStateException(getClass().getName() +" has no generateCode(..) method");
}
public String getExitDescriptor() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -56,9 +56,9 @@ public class StringLiteral extends Literal {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
mv.visitLdcInsn(this.value.getValue());
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -34,17 +34,17 @@ import org.springframework.expression.spel.SpelMessage;
*/
public class Ternary extends SpelNodeImpl {
public Ternary(int pos, SpelNodeImpl... args) {
super(pos,args);
}
/**
* Evaluate the condition and if true evaluate the first alternative, otherwise
* evaluate the second alternative.
* @param state the expression state
* @throws EvaluationException if the condition does not evaluate correctly to a
* boolean or there is a problem executing the chosen alternative
* @throws EvaluationException if the condition does not evaluate correctly to
* a boolean or there is a problem executing the chosen alternative
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
@ -53,25 +53,19 @@ public class Ternary extends SpelNodeImpl {
throw new SpelEvaluationException(getChild(0).getStartPosition(),
SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}
TypedValue result = null;
if (value.booleanValue()) {
result = this.children[1].getValueInternal(state);
}
else {
result = this.children[2].getValueInternal(state);
}
TypedValue result = this.children[value ? 1 : 2].getValueInternal(state);
computeExitTypeDescriptor();
return result;
}
@Override
public String toStringAST() {
return new StringBuilder().append(getChild(0).toStringAST()).append(" ? ").append(getChild(1).toStringAST())
.append(" : ").append(getChild(2).toStringAST()).toString();
return getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST();
}
private void computeExitTypeDescriptor() {
if (exitTypeDescriptor == null && this.children[1].getExitDescriptor()!=null && this.children[2].getExitDescriptor()!=null) {
if (this.exitTypeDescriptor == null && this.children[1].exitTypeDescriptor != null &&
this.children[2].exitTypeDescriptor != null) {
String leftDescriptor = this.children[1].exitTypeDescriptor;
String rightDescriptor = this.children[2].exitTypeDescriptor;
if (leftDescriptor.equals(rightDescriptor)) {
@ -95,43 +89,40 @@ public class Ternary extends SpelNodeImpl {
SpelNodeImpl condition = this.children[0];
SpelNodeImpl left = this.children[1];
SpelNodeImpl right = this.children[2];
if (!(condition.isCompilable() && left.isCompilable() && right.isCompilable())) {
return false;
}
return CodeFlow.isBooleanCompatible(condition.exitTypeDescriptor) &&
left.getExitDescriptor()!=null &&
right.getExitDescriptor()!=null;
return (condition.isCompilable() && left.isCompilable() && right.isCompilable() &&
CodeFlow.isBooleanCompatible(condition.exitTypeDescriptor) &&
left.exitTypeDescriptor != null && right.exitTypeDescriptor != null);
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
// May reach here without it computed if all elements are literals
computeExitTypeDescriptor();
codeflow.enterCompilationScope();
this.children[0].generateCode(mv, codeflow);
if (!CodeFlow.isPrimitive(codeflow.lastDescriptor())) {
CodeFlow.insertUnboxInsns(mv, 'Z', codeflow.lastDescriptor());
cf.enterCompilationScope();
this.children[0].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(cf.lastDescriptor())) {
CodeFlow.insertUnboxInsns(mv, 'Z', cf.lastDescriptor());
}
codeflow.exitCompilationScope();
cf.exitCompilationScope();
Label elseTarget = new Label();
Label endOfIf = new Label();
mv.visitJumpInsn(IFEQ, elseTarget);
codeflow.enterCompilationScope();
this.children[1].generateCode(mv, codeflow);
if (!CodeFlow.isPrimitive(getExitDescriptor())) {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
cf.enterCompilationScope();
this.children[1].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
}
codeflow.exitCompilationScope();
cf.exitCompilationScope();
mv.visitJumpInsn(GOTO, endOfIf);
mv.visitLabel(elseTarget);
codeflow.enterCompilationScope();
this.children[2].generateCode(mv, codeflow);
if (!CodeFlow.isPrimitive(getExitDescriptor())) {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
cf.enterCompilationScope();
this.children[2].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
}
codeflow.exitCompilationScope();
cf.exitCompilationScope();
mv.visitLabel(endOfIf);
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
/**
@ -66,11 +67,10 @@ public enum TypeCode {
return TypeCode.OBJECT;
}
public static TypeCode forClass(Class<?> c) {
public static TypeCode forClass(Class<?> clazz) {
TypeCode[] allValues = TypeCode.values();
for (int i = 0; i < allValues.length; i++) {
TypeCode typeCode = allValues[i];
if (c == typeCode.getType()) {
for (TypeCode typeCode : allValues) {
if (clazz == typeCode.getType()) {
return typeCode;
}
}

View File

@ -50,19 +50,18 @@ public class TypeReference extends SpelNodeImpl {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// TODO possible optimization here if we cache the discovered type reference, but can we do that?
String typename = (String) this.children[0].getValueInternal(state).getValue();
if (typename.indexOf('.') == -1 && Character.isLowerCase(typename.charAt(0))) {
TypeCode tc = TypeCode.valueOf(typename.toUpperCase());
String typeName = (String) this.children[0].getValueInternal(state).getValue();
if (!typeName.contains(".") && Character.isLowerCase(typeName.charAt(0))) {
TypeCode tc = TypeCode.valueOf(typeName.toUpperCase());
if (tc != TypeCode.OBJECT) {
// it is a primitive type
Class<?> clazz = tc.getType();
clazz = makeArrayIfNecessary(clazz);
// It is a primitive type
Class<?> clazz = makeArrayIfNecessary(tc.getType());
this.exitTypeDescriptor = "Ljava/lang/Class";
this.type = clazz;
return new TypedValue(clazz);
}
}
Class<?> clazz = state.findType(typename);
Class<?> clazz = state.findType(typeName);
clazz = makeArrayIfNecessary(clazz);
this.exitTypeDescriptor = "Ljava/lang/Class";
this.type = clazz;
@ -81,10 +80,9 @@ public class TypeReference extends SpelNodeImpl {
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("T(");
StringBuilder sb = new StringBuilder("T(");
sb.append(getChild(0).toStringAST());
for (int d=0;d<this.dimensions;d++) {
for (int d = 0; d < this.dimensions; d++) {
sb.append("[]");
}
sb.append(")");
@ -97,41 +95,41 @@ public class TypeReference extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
// TODO Future optimization - if followed by a static method call, skip generating code here
if (type.isPrimitive()) {
if (type == Integer.TYPE) {
if (this.type.isPrimitive()) {
if (this.type == Integer.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
}
else if (type == Boolean.TYPE) {
else if (this.type == Boolean.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
}
else if (type == Byte.TYPE) {
else if (this.type == Byte.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;");
}
else if (type == Short.TYPE) {
else if (this.type == Short.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;");
}
else if (type == Double.TYPE) {
else if (this.type == Double.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;");
}
else if (type == Character.TYPE) {
else if (this.type == Character.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;");
}
else if (type == Float.TYPE) {
else if (this.type == Float.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
}
else if (type == Long.TYPE) {
else if (this.type == Long.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
}
else if (type == Boolean.TYPE) {
else if (this.type == Boolean.TYPE) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
}
}
else {
mv.visitLdcInsn(Type.getType(type));
mv.visitLdcInsn(Type.getType(this.type));
}
codeflow.pushDescriptor(getExitDescriptor());
cf.pushDescriptor(this.exitTypeDescriptor);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -59,7 +59,7 @@ public interface ValueRef {
*/
static class NullValueRef implements ValueRef {
static NullValueRef instance = new NullValueRef();
static final NullValueRef INSTANCE = new NullValueRef();
@Override
public TypedValue getValue() {
@ -71,14 +71,13 @@ public interface ValueRef {
// The exception position '0' isn't right but the overhead of creating
// instances of this per node (where the node is solely for error reporting)
// would be unfortunate.
throw new SpelEvaluationException(0,SpelMessage.NOT_ASSIGNABLE,"null");
throw new SpelEvaluationException(0, SpelMessage.NOT_ASSIGNABLE, "null");
}
@Override
public boolean isWritable() {
return false;
}
}
@ -88,7 +87,8 @@ public interface ValueRef {
static class TypedValueHolderValueRef implements ValueRef {
private final TypedValue typedValue;
private final SpelNodeImpl node; // used only for error reporting
private final SpelNodeImpl node; // used only for error reporting
public TypedValueHolderValueRef(TypedValue typedValue,SpelNodeImpl node) {
this.typedValue = typedValue;
@ -97,20 +97,18 @@ public interface ValueRef {
@Override
public TypedValue getValue() {
return typedValue;
return this.typedValue;
}
@Override
public void setValue(Object newValue) {
throw new SpelEvaluationException(
node.pos, SpelMessage.NOT_ASSIGNABLE, node.toStringAST());
throw new SpelEvaluationException(this.node.pos, SpelMessage.NOT_ASSIGNABLE, this.node.toStringAST());
}
@Override
public boolean isWritable() {
return false;
}
}
}

View File

@ -79,7 +79,7 @@ public class VariableReference extends SpelNodeImpl {
// then an IllegalAccessError will occur.
// If resorting to Object isn't sufficient, the hierarchy could be traversed for
// the first public type.
this.exitTypeDescriptor ="Ljava/lang/Object";
this.exitTypeDescriptor = "Ljava/lang/Object";
}
else {
this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(value);
@ -139,11 +139,11 @@ public class VariableReference extends SpelNodeImpl {
@Override
public boolean isCompilable() {
return getExitDescriptor()!=null;
return this.exitTypeDescriptor!=null;
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(MethodVisitor mv, CodeFlow cf) {
if (this.name.equals(ROOT)) {
mv.visitVarInsn(ALOAD,1);
}
@ -152,8 +152,8 @@ public class VariableReference extends SpelNodeImpl {
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKEINTERFACE, "org/springframework/expression/EvaluationContext", "lookupVariable", "(Ljava/lang/String;)Ljava/lang/Object;",true);
}
CodeFlow.insertCheckCast(mv,getExitDescriptor());
codeflow.pushDescriptor(getExitDescriptor());
CodeFlow.insertCheckCast(mv,this.exitTypeDescriptor);
cf.pushDescriptor(this.exitTypeDescriptor);
}

View File

@ -76,7 +76,7 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Hand written SpEL parser. Instances are reusable but are not thread safe.
* Hand written SpEL parser. Instances are reusable but are not thread-safe.
*
* @author Andy Clement
* @author Phillip Webb
@ -87,6 +87,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+");
private final SpelParserConfiguration configuration;
// For rules that build nodes, they are stacked here for return
private final Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>();
// The expression being parsed
private String expressionString;
@ -99,11 +104,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// Current location in the token stream when processing tokens
private int tokenStreamPointer;
// For rules that build nodes, they are stacked here for return
private final Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>();
private final SpelParserConfiguration configuration;
/**
* Create a parser with some configured behavior.
@ -126,7 +126,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
this.constructedNodes.clear();
SpelNodeImpl ast = eatExpression();
if (moreTokens()) {
throw new SpelParseException(peekToken().startpos, SpelMessage.MORE_INPUT, toString(nextToken()));
throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
}
Assert.isTrue(this.constructedNodes.isEmpty());
return new SpelExpression(expressionString, ast, this.configuration);
@ -146,36 +146,36 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl expr = eatLogicalOrExpression();
if (moreTokens()) {
Token t = peekToken();
if (t.kind==TokenKind.ASSIGN) { // a=b
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1));
if (t.kind == TokenKind.ASSIGN) { // a=b
if (expr == null) {
expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1));
}
nextToken();
SpelNodeImpl assignedValue = eatLogicalOrExpression();
return new Assign(toPos(t),expr,assignedValue);
return new Assign(toPos(t), expr, assignedValue);
}
if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-2));
if (t.kind == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
if (expr == null) {
expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 2));
}
nextToken(); // elvis has left the building
nextToken(); // elvis has left the building
SpelNodeImpl valueIfNull = eatExpression();
if (valueIfNull==null) {
valueIfNull = new NullLiteral(toPos(t.startpos+1,t.endpos+1));
valueIfNull = new NullLiteral(toPos(t.startPos + 1, t.endPos + 1));
}
return new Elvis(toPos(t),expr,valueIfNull);
return new Elvis(toPos(t), expr, valueIfNull);
}
if (t.kind==TokenKind.QMARK) { // a?b:c
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1));
if (t.kind == TokenKind.QMARK) { // a?b:c
if (expr == null) {
expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1));
}
nextToken();
SpelNodeImpl ifTrueExprValue = eatExpression();
eatToken(TokenKind.COLON);
SpelNodeImpl ifFalseExprValue = eatExpression();
return new Ternary(toPos(t),expr,ifTrueExprValue,ifFalseExprValue);
return new Ternary(toPos(t), expr, ifTrueExprValue, ifFalseExprValue);
}
}
return expr;
@ -185,10 +185,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatLogicalOrExpression() {
SpelNodeImpl expr = eatLogicalAndExpression();
while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) {
Token t = nextToken(); //consume OR
Token t = nextToken(); //consume OR
SpelNodeImpl rhExpr = eatLogicalAndExpression();
checkOperands(t,expr,rhExpr);
expr = new OpOr(toPos(t),expr,rhExpr);
checkOperands(t, expr, rhExpr);
expr = new OpOr(toPos(t), expr, rhExpr);
}
return expr;
}
@ -197,10 +197,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatLogicalAndExpression() {
SpelNodeImpl expr = eatRelationalExpression();
while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) {
Token t = nextToken();// consume 'AND'
Token t = nextToken(); // consume 'AND'
SpelNodeImpl rhExpr = eatRelationalExpression();
checkOperands(t,expr,rhExpr);
expr = new OpAnd(toPos(t),expr,rhExpr);
checkOperands(t, expr, rhExpr);
expr = new OpAnd(toPos(t), expr, rhExpr);
}
return expr;
}
@ -210,9 +210,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl expr = eatSumExpression();
Token relationalOperatorToken = maybeEatRelationalOperator();
if (relationalOperatorToken != null) {
Token t = nextToken(); //consume relational operator token
Token t = nextToken(); // consume relational operator token
SpelNodeImpl rhExpr = eatSumExpression();
checkOperands(t,expr,rhExpr);
checkOperands(t, expr, rhExpr);
TokenKind tk = relationalOperatorToken.kind;
if (relationalOperatorToken.isNumericRelationalOperator()) {
@ -271,9 +271,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatProductExpression() {
SpelNodeImpl expr = eatPowerIncDecExpression();
while (peekToken(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) {
Token t = nextToken(); // consume STAR/DIV/MOD
Token t = nextToken(); // consume STAR/DIV/MOD
SpelNodeImpl rhExpr = eatPowerIncDecExpression();
checkOperands(t,expr,rhExpr);
checkOperands(t, expr, rhExpr);
if (t.kind == TokenKind.STAR) {
expr = new OpMultiply(toPos(t), expr, rhExpr);
}
@ -292,18 +292,18 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatPowerIncDecExpression() {
SpelNodeImpl expr = eatUnaryExpression();
if (peekToken(TokenKind.POWER)) {
Token t = nextToken();//consume POWER
Token t = nextToken(); //consume POWER
SpelNodeImpl rhExpr = eatUnaryExpression();
checkRightOperand(t,rhExpr);
return new OperatorPower(toPos(t),expr, rhExpr);
return new OperatorPower(toPos(t), expr, rhExpr);
}
if (expr!=null && peekToken(TokenKind.INC,TokenKind.DEC)) {
Token t = nextToken();//consume INC/DEC
if (t.getKind()==TokenKind.INC) {
return new OpInc(toPos(t),true,expr);
if (expr != null && peekToken(TokenKind.INC,TokenKind.DEC)) {
Token t = nextToken(); //consume INC/DEC
if (t.getKind() == TokenKind.INC) {
return new OpInc(toPos(t), true, expr);
}
return new OpDec(toPos(t),true,expr);
return new OpDec(toPos(t), true, expr);
}
return expr;
@ -340,7 +340,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?);
private SpelNodeImpl eatPrimaryExpression() {
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>();
SpelNodeImpl start = eatStartNode(); // always a start node
SpelNodeImpl start = eatStartNode(); // always a start node
nodes.add(start);
while (maybeEatNode()) {
nodes.add(pop());
@ -401,10 +401,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
if (peekToken() == null) {
// unexpectedly ran out of data
raiseInternalException(t.startpos, SpelMessage.OOD);
raiseInternalException(t.startPos, SpelMessage.OOD);
}
else {
raiseInternalException(t.startpos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT,
raiseInternalException(t.startPos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT,
toString(peekToken()));
}
return null;
@ -424,13 +424,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
Token functionOrVariableName = eatToken(TokenKind.IDENTIFIER);
SpelNodeImpl[] args = maybeEatMethodArgs();
if (args == null) {
push(new VariableReference(functionOrVariableName.data, toPos(t.startpos,
functionOrVariableName.endpos)));
push(new VariableReference(functionOrVariableName.data, toPos(t.startPos,
functionOrVariableName.endPos)));
return true;
}
push(new FunctionReference(functionOrVariableName.data, toPos(t.startpos,
functionOrVariableName.endpos), args));
push(new FunctionReference(functionOrVariableName.data, toPos(t.startPos,
functionOrVariableName.endPos), args));
return true;
}
@ -457,10 +457,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
* Used for consuming arguments for either a method or a constructor call
*/
private void consumeArguments(List<SpelNodeImpl> accumulatedArguments) {
int pos = peekToken().startpos;
Token next = null;
int pos = peekToken().startPos;
Token next;
do {
nextToken();// consume ( (first time through) or comma (subsequent times)
nextToken(); // consume ( (first time through) or comma (subsequent times)
Token t = peekToken();
if (t == null) {
raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS);
@ -483,7 +483,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// not found at the end of the expression
return this.expressionString.length();
}
return t.startpos;
return t.startPos;
}
//startNode
@ -504,16 +504,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
else if (maybeEatParenExpression()) {
return pop();
}
else if (maybeEatTypeReference() || maybeEatNullReference()
|| maybeEatConstructorReference() || maybeEatMethodOrProperty(false)
|| maybeEatFunctionOrVar()) {
else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() ||
maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
return pop();
}
else if (maybeEatBeanReference()) {
return pop();
}
else if (maybeEatProjection(false) || maybeEatSelection(false)
|| maybeEatIndexer()) {
else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
return pop();
}
else if (maybeEatInlineListOrMap()) {
@ -530,22 +528,22 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (peekToken(TokenKind.BEAN_REF)) {
Token beanRefToken = nextToken();
Token beanNameToken = null;
String beanname = null;
String beanName = null;
if (peekToken(TokenKind.IDENTIFIER)) {
beanNameToken = eatToken(TokenKind.IDENTIFIER);
beanname = beanNameToken.data;
beanName = beanNameToken.data;
}
else if (peekToken(TokenKind.LITERAL_STRING)) {
beanNameToken = eatToken(TokenKind.LITERAL_STRING);
beanname = beanNameToken.stringValue();
beanname = beanname.substring(1, beanname.length() - 1);
beanName = beanNameToken.stringValue();
beanName = beanName.substring(1, beanName.length() - 1);
}
else {
raiseInternalException(beanRefToken.startpos,
raiseInternalException(beanRefToken.startPos,
SpelMessage.INVALID_BEAN_REFERENCE);
}
BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname);
BeanReference beanReference = new BeanReference(toPos(beanNameToken) ,beanName);
this.constructedNodes.push(beanReference);
return true;
}
@ -564,7 +562,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// dotted qualified id
// Are there array dimensions?
int dims = 0;
while (peekToken(TokenKind.LSQUARE,true)) {
while (peekToken(TokenKind.LSQUARE, true)) {
eatToken(TokenKind.RSQUARE);
dims++;
}
@ -611,12 +609,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
Token closingCurly = peekToken();
if (peekToken(TokenKind.RCURLY, true)) {
// empty list '{}'
expr = new InlineList(toPos(t.startpos,closingCurly.endpos));
expr = new InlineList(toPos(t.startPos,closingCurly.endPos));
}
else if (peekToken(TokenKind.COLON,true)) {
closingCurly = eatToken(TokenKind.RCURLY);
// empty map '{:}'
expr = new InlineMap(toPos(t.startpos,closingCurly.endpos));
expr = new InlineMap(toPos(t.startPos,closingCurly.endPos));
}
else {
SpelNodeImpl firstExpression = eatExpression();
@ -629,7 +627,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>();
listElements.add(firstExpression);
closingCurly = eatToken(TokenKind.RCURLY);
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
expr = new InlineList(toPos(t.startPos,closingCurly.endPos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
}
else if (peekToken(TokenKind.COMMA, true)) { // multi item list
List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>();
@ -639,7 +637,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
while (peekToken(TokenKind.COMMA,true));
closingCurly = eatToken(TokenKind.RCURLY);
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
expr = new InlineList(toPos(t.startPos,closingCurly.endPos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
}
else if (peekToken(TokenKind.COLON, true)) { // map!
@ -652,10 +650,10 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
mapElements.add(eatExpression());
}
closingCurly = eatToken(TokenKind.RCURLY);
expr = new InlineMap(toPos(t.startpos,closingCurly.endpos),mapElements.toArray(new SpelNodeImpl[mapElements.size()]));
expr = new InlineMap(toPos(t.startPos,closingCurly.endPos),mapElements.toArray(new SpelNodeImpl[mapElements.size()]));
}
else {
raiseInternalException(t.startpos, SpelMessage.OOD);
raiseInternalException(t.startPos, SpelMessage.OOD);
}
}
this.constructedNodes.push(expr);
@ -669,7 +667,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
SpelNodeImpl expr = eatExpression();
eatToken(TokenKind.RSQUARE);
this.constructedNodes.push(new Indexer(toPos(t),expr));
this.constructedNodes.push(new Indexer(toPos(t), expr));
return true;
}
@ -714,7 +712,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (node == null) {
raiseInternalException( this.expressionString.length(), SpelMessage.OOD);
}
raiseInternalException(node.startpos, SpelMessage.NOT_EXPECTED_TOKEN,
raiseInternalException(node.startPos, SpelMessage.NOT_EXPECTED_TOKEN,
"qualified ID", node.getKind().toString().toLowerCase());
}
int pos = toPos(qualifiedIdPieces.getFirst().getStartPosition(), qualifiedIdPieces.getLast().getEndPosition());
@ -729,7 +727,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
return true;
}
String value = node.stringValue();
return StringUtils.hasLength(value) && VALID_QUALIFIED_ID_PATTERN.matcher(value).matches();
return (StringUtils.hasLength(value) && VALID_QUALIFIED_ID_PATTERN.matcher(value).matches());
}
// This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but
@ -744,7 +742,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
return true;
}
// methodreference
push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args));
push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data, toPos(methodOrPropertyName), args));
// TODO what is the end position for a method reference? the name or the last arg?
return true;
}
@ -807,7 +805,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// | NULL_LITERAL
private boolean maybeEatLiteral() {
Token t = peekToken();
if (t==null) {
if (t == null) {
return false;
}
if (t.kind == TokenKind.LITERAL_INT) {
@ -890,7 +888,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
raiseInternalException( this.expressionString.length(), SpelMessage.OOD);
}
if (t.kind != expectedKind) {
raiseInternalException(t.startpos,SpelMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase());
raiseInternalException(t.startPos, SpelMessage.NOT_EXPECTED_TOKEN,
expectedKind.toString().toLowerCase(), t.getKind().toString().toLowerCase());
}
return t;
}
@ -943,7 +942,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
return false;
}
Token t = peekToken();
return t.kind==TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString);
return t.kind == TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString);
}
private boolean peekSelectToken() {
@ -974,8 +973,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
private void raiseInternalException(int pos, SpelMessage message, Object... inserts) {
throw new InternalParseException(new SpelParseException(this.expressionString,
pos, message, inserts));
throw new InternalParseException(new SpelParseException(this.expressionString, pos, message, inserts));
}
public String toString(Token t) {
@ -992,13 +990,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private void checkLeftOperand(Token token, SpelNodeImpl operandExpression) {
if (operandExpression==null) {
raiseInternalException(token.startpos,SpelMessage.LEFT_OPERAND_PROBLEM);
raiseInternalException(token.startPos, SpelMessage.LEFT_OPERAND_PROBLEM);
}
}
private void checkRightOperand(Token token, SpelNodeImpl operandExpression) {
if (operandExpression==null) {
raiseInternalException(token.startpos,SpelMessage.RIGHT_OPERAND_PROBLEM);
if (operandExpression == null) {
raiseInternalException(token.startPos, SpelMessage.RIGHT_OPERAND_PROBLEM);
}
}
@ -1006,7 +1004,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
* Compress the start and end of a token into a single int.
*/
private int toPos(Token t) {
return (t.startpos<<16) + t.endpos;
return (t.startPos<<16) + t.endPos;
}
private int toPos(int start,int end) {

View File

@ -141,21 +141,23 @@ public class SpelCompiler implements Opcodes {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "org/springframework/expression/spel/CompiledExpression", "<init>", "()V",false);
mv.visitMethodInsn(INVOKESPECIAL, "org/springframework/expression/spel/CompiledExpression",
"<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
// Create getValue() method
mv = cw.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null,
new String[]{"org/springframework/expression/EvaluationException"});
mv = cw.visitMethod(ACC_PUBLIC, "getValue",
"(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null,
new String[ ]{"org/springframework/expression/EvaluationException"});
mv.visitCode();
CodeFlow codeflow = new CodeFlow();
CodeFlow cf = new CodeFlow();
// Ask the expression AST to generate the body of the method
try {
expressionToCompile.generateCode(mv, codeflow);
expressionToCompile.generateCode(mv, cf);
}
catch (IllegalStateException ex) {
if (logger.isDebugEnabled()) {
@ -165,19 +167,19 @@ public class SpelCompiler implements Opcodes {
return null;
}
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor());
if ("V".equals(codeflow.lastDescriptor())) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor());
if ("V".equals(cf.lastDescriptor())) {
mv.visitInsn(ACONST_NULL);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS
mv.visitMaxs(0, 0); // not supplied due to COMPUTE_MAXS
mv.visitEnd();
cw.visitEnd();
byte[] data = cw.toByteArray();
// TODO need to make this conditionally occur based on a debug flag
// dump(expressionToCompile.toStringAST(), clazzName, data);
return (Class<? extends CompiledExpression>) ccl.defineClass(clazzName.replaceAll("/","."),data);
return (Class<? extends CompiledExpression>) this.ccl.defineClass(clazzName.replaceAll("/", "."), data);
}
@ -249,7 +251,8 @@ public class SpelCompiler implements Opcodes {
fos.close();
}
catch (IOException ex) {
throw new IllegalStateException("Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
throw new IllegalStateException(
"Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
}
}
@ -259,7 +262,7 @@ public class SpelCompiler implements Opcodes {
*/
public static class ChildClassLoader extends URLClassLoader {
private static URL[] NO_URLS = new URL[0];
private static final URL[] NO_URLS = new URL[0];
public ChildClassLoader(ClassLoader classloader) {
super(NO_URLS, classloader);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -29,25 +29,25 @@ class Token {
String data;
int startpos; // index of first character
int startPos; // index of first character
int endpos; // index of char after the last character
int endPos; // index of char after the last character
/**
* Constructor for use when there is no particular data for the token (eg. TRUE or
* '+')
* @param startpos the exact start
* @param endpos the index to the last character
* Constructor for use when there is no particular data for the token
* (e.g. TRUE or '+')
* @param startPos the exact start
* @param endPos the index to the last character
*/
Token(TokenKind tokenKind, int startpos, int endpos) {
Token(TokenKind tokenKind, int startPos, int endPos) {
this.kind = tokenKind;
this.startpos = startpos;
this.endpos = endpos;
this.startPos = startPos;
this.endPos = endPos;
}
Token(TokenKind tokenKind, char[] tokenData, int pos, int endpos) {
this(tokenKind,pos,endpos);
Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) {
this(tokenKind, startPos, endPos);
this.data = new String(tokenData);
}
@ -64,16 +64,17 @@ class Token {
s.append(":").append(this.data);
}
s.append("]");
s.append("(").append(this.startpos).append(",").append(this.endpos).append(")");
s.append("(").append(this.startPos).append(",").append(this.endPos).append(")");
return s.toString();
}
public boolean isIdentifier() {
return this.kind==TokenKind.IDENTIFIER;
return (this.kind == TokenKind.IDENTIFIER);
}
public boolean isNumericRelationalOperator() {
return this.kind==TokenKind.GT || this.kind==TokenKind.GE || this.kind==TokenKind.LT || this.kind==TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE;
return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT ||
this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE);
}
public String stringValue() {
@ -81,14 +82,15 @@ class Token {
}
public Token asInstanceOfToken() {
return new Token(TokenKind.INSTANCEOF,this.startpos,this.endpos);
return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos);
}
public Token asMatchesToken() {
return new Token(TokenKind.MATCHES,this.startpos,this.endpos);
return new Token(TokenKind.MATCHES, this.startPos, this.endPos);
}
public Token asBetweenToken() {
return new Token(TokenKind.BETWEEN,this.startpos,this.endpos);
return new Token(TokenKind.BETWEEN, this.startPos, this.endPos);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -121,14 +121,14 @@ enum TokenKind {
DEC("--");
char[] tokenChars;
final char[] tokenChars;
private boolean hasPayload; // is there more to this token than simply the kind
final private boolean hasPayload; // is there more to this token than simply the kind
private TokenKind(String tokenString) {
this.tokenChars = tokenString.toCharArray();
this.hasPayload = this.tokenChars.length==0;
this.hasPayload = (this.tokenChars.length == 0);
}
private TokenKind() {
@ -138,7 +138,7 @@ enum TokenKind {
@Override
public String toString() {
return this.name()+(this.tokenChars.length!=0?"("+new String(this.tokenChars)+")":"");
return (name() + (this.tokenChars.length !=0 ? "(" + new String(this.tokenChars) +")" : ""));
}
public boolean hasPayload() {
@ -148,4 +148,5 @@ enum TokenKind {
public int getLength() {
return this.tokenChars.length;
}
}

View File

@ -76,9 +76,9 @@ class Tokenizer {
List<Token> tokens = new ArrayList<Token>();
public Tokenizer(String inputdata) {
this.expressionString = inputdata;
this.toProcess = (inputdata + "\0").toCharArray();
public Tokenizer(String inputData) {
this.expressionString = inputData;
this.toProcess = (inputData + "\0").toCharArray();
this.max = this.toProcess.length;
this.pos = 0;
process();
@ -266,11 +266,9 @@ class Tokenizer {
break;
case '\\':
throw new InternalParseException(
new SpelParseException(this.expressionString, this.pos,
SpelMessage.UNEXPECTED_ESCAPE_CHAR));
new SpelParseException(this.expressionString, this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR));
default:
throw new IllegalStateException("Cannot handle ("
+ Integer.valueOf(ch) + ") '" + ch + "'");
throw new IllegalStateException("Cannot handle (" + Integer.valueOf(ch) + ") '" + ch + "'");
}
}
}
@ -297,8 +295,8 @@ class Tokenizer {
}
}
if (ch == 0) {
throw new InternalParseException(new SpelParseException(this.expressionString,
start, SpelMessage.NON_TERMINATING_QUOTED_STRING));
throw new InternalParseException(new SpelParseException(this.expressionString, start,
SpelMessage.NON_TERMINATING_QUOTED_STRING));
}
}
this.pos++;
@ -595,4 +593,5 @@ class Tokenizer {
}
return (FLAGS[ch] & IS_HEXDIGIT) != 0;
}
}

View File

@ -455,7 +455,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
* This method will just return the ReflectivePropertyAccessor instance if it is unable to build
* something more optimal.
*/
public PropertyAccessor createOptimalAccessor(EvaluationContext eContext, Object target, String name) {
public PropertyAccessor createOptimalAccessor(EvaluationContext evalContext, Object target, String name) {
// Don't be clever for arrays or null target
if (target == null) {
return this;
@ -484,7 +484,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
if (invocationTarget == null || invocationTarget.member instanceof Field) {
Field field = (Field) (invocationTarget==null?null:invocationTarget.member);
Field field = (invocationTarget != null ? (Field) invocationTarget.member : null);
if (field == null) {
field = findField(name, type, target instanceof Class);
if (field != null) {
@ -541,16 +541,13 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return false;
}
CacheKey otherKey = (CacheKey) other;
boolean rtn = true;
rtn &= this.clazz.equals(otherKey.clazz);
rtn &= this.name.equals(otherKey.name);
rtn &= this.targetIsClass == otherKey.targetIsClass;
return rtn;
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name) &&
this.targetIsClass == otherKey.targetIsClass);
}
@Override
public int hashCode() {
return this.clazz.hashCode() * 29 + this.name.hashCode();
return (this.clazz.hashCode() * 29 + this.name.hashCode());
}
@Override
@ -662,12 +659,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
@Override
public boolean isCompilable() {
// If non public must continue to use reflection
if (!Modifier.isPublic(this.member.getModifiers()) ||
!Modifier.isPublic(this.member.getDeclaringClass().getModifiers())) {
return false;
}
return true;
return (Modifier.isPublic(this.member.getModifiers()) &&
Modifier.isPublic(this.member.getDeclaringClass().getModifiers()));
}
@Override
@ -681,13 +674,13 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) {
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
boolean isStatic = Modifier.isStatic(this.member.getModifiers());
String descriptor = codeflow.lastDescriptor();
String descriptor = cf.lastDescriptor();
String memberDeclaringClassSlashedDescriptor = this.member.getDeclaringClass().getName().replace('.', '/');
if (!isStatic) {
if (descriptor == null) {
codeflow.loadTarget(mv);
cf.loadTarget(mv);
}
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
@ -695,7 +688,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
if (this.member instanceof Field) {
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor,
this.member.getName(), CodeFlow.toJVMDescriptor(((Field) this.member).getType()));
this.member.getName(), CodeFlow.toJvmDescriptor(((Field) this.member).getType()));
}
else {
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor,

View File

@ -17,6 +17,7 @@
package org.springframework.expression.spel;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.junit.Test;
@ -414,7 +415,6 @@ public class OperatorTests extends AbstractExpressionTests {
// string concatenation
evaluate("'abc'+'def'","abcdef",String.class);
//
evaluate("5 + new Integer('37')",42,Integer.class);
}
@ -423,17 +423,17 @@ public class OperatorTests extends AbstractExpressionTests {
evaluate("'c' - 2", "a", String.class);
evaluate("3.0f - 5.0f", -2.0f, Float.class);
evaluateAndCheckError("'ab' - 2", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
evaluateAndCheckError("2-'ab'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
evaluateAndCheckError("2-'ab'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
SpelExpression expr = (SpelExpression)parser.parseExpression("-3");
assertEquals("-3",expr.toStringAST());
assertEquals("-3", expr.toStringAST());
expr = (SpelExpression)parser.parseExpression("2-3");
assertEquals("(2 - 3)",expr.toStringAST());
assertEquals("(2 - 3)", expr.toStringAST());
evaluate("-5d",-5d,Double.class);
evaluate("-5L",-5L,Long.class);
evaluate("-5",-5,Integer.class);
evaluate("-5", -5, Integer.class);
evaluate("-new java.math.BigDecimal('5')", new BigDecimal("-5"),BigDecimal.class);
evaluateAndCheckError("-'abc'",SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
evaluateAndCheckError("-'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
}
@Test
@ -483,7 +483,6 @@ public class OperatorTests extends AbstractExpressionTests {
evaluate("6.0d % 3.5d", 2.5d, Double.class);
}
@Test
public void testBigDecimals() {
evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class);
@ -545,7 +544,7 @@ public class OperatorTests extends AbstractExpressionTests {
evaluate("3^2",9,Integer.class);
evaluate("3.0d^2.0d",9.0d,Double.class);
evaluate("3L^2L",9L,Long.class);
evaluate("(2^32)^2",9223372036854775807L,Long.class);
evaluate("(2^32)^2", 9223372036854775807L, Long.class);
evaluate("new java.math.BigDecimal('5') ^ 3", new BigDecimal("125"), BigDecimal.class);
}
@ -588,6 +587,17 @@ public class OperatorTests extends AbstractExpressionTests {
evaluate("3L - 50L", -47L, Long.class);
}
@Test
public void testBigIntegers() {
evaluate("3 + new java.math.BigInteger('5')", new BigInteger("8"), BigInteger.class);
evaluate("3 - new java.math.BigInteger('5')", new BigInteger("-2"), BigInteger.class);
evaluate("3 * new java.math.BigInteger('5')", new BigInteger("15"), BigInteger.class);
evaluate("3 / new java.math.BigInteger('5')", new BigInteger("0"), BigInteger.class);
evaluate("5 % new java.math.BigInteger('3')", new BigInteger("2"), BigInteger.class);
evaluate("new java.math.BigInteger('5') % 3", new BigInteger("2"), BigInteger.class);
evaluate("new java.math.BigInteger('5') ^ 3", new BigInteger("125"), BigInteger.class);
}
private Operator getOperatorNode(SpelExpression expr) {
SpelNode node = expr.getAST();

View File

@ -256,6 +256,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
Object resultC = expression.getValue(new TestClass1(),Object.class);
assertEquals(null,resultI);
assertEquals(null,resultC);
assertEquals(null,resultC);
}
@Test
@ -2616,7 +2617,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
@Override
public void generateCode(String propertyName, MethodVisitor mv,CodeFlow codeflow) {
public void generateCode(String propertyName, MethodVisitor mv,CodeFlow cf) {
if (method == null) {
try {
method = Payload2.class.getDeclaredMethod("getField", String.class);
@ -2624,10 +2625,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
catch (Exception e) {
}
}
String descriptor = codeflow.lastDescriptor();
String descriptor = cf.lastDescriptor();
String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/');
if (descriptor == null) {
codeflow.loadTarget(mv);
cf.loadTarget(mv);
}
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
@ -2684,10 +2685,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
@Override
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) {
String descriptor = codeflow.lastDescriptor();
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
String descriptor = cf.lastDescriptor();
if (descriptor == null) {
codeflow.loadTarget(mv);
cf.loadTarget(mv);
}
mv.visitLdcInsn(propertyName);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get","(Ljava/lang/Object;)Ljava/lang/Object;",true);

View File

@ -328,14 +328,14 @@ public class SpelParserTests {
public void token() {
Token token = new Token(TokenKind.NOT, 0, 3);
assertEquals(TokenKind.NOT, token.kind);
assertEquals(0, token.startpos);
assertEquals(3, token.endpos);
assertEquals(0, token.startPos);
assertEquals(3, token.endPos);
assertEquals("[NOT(!)](0,3)", token.toString());
token = new Token(TokenKind.LITERAL_STRING, "abc".toCharArray(), 0, 3);
assertEquals(TokenKind.LITERAL_STRING, token.kind);
assertEquals(0, token.startpos);
assertEquals(3, token.endpos);
assertEquals(0, token.startPos);
assertEquals(3, token.endPos);
assertEquals("[LITERAL_STRING:abc](0,3)", token.toString());
}