Comprehensive revision of SpEL's bytecode generation and number handling (BigInteger support, doubleValue fallback)
Issue: SPR-9913
This commit is contained in:
parent
e58b33a593
commit
d8160b3c09
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue