Revision of SpelCompiler support, resolving a subpackage cycle through moving CodeFlow and CompilablePropertyAccessor to the main spel package
Also contains explicit ClassLoader management, passed through StandardBeanExpressionResolver and SpelParserConfiguration to SpelCompiler lookup. Issue: SPR-10943
This commit is contained in:
parent
8fb592712c
commit
bad74dc836
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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.
|
||||
|
|
@ -27,6 +27,7 @@ import org.springframework.core.convert.ConversionService;
|
|||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeConverter;
|
||||
|
|
@ -58,7 +59,7 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
|
|||
|
||||
private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
|
||||
|
||||
private ExpressionParser expressionParser = new SpelExpressionParser();
|
||||
private ExpressionParser expressionParser;
|
||||
|
||||
private final Map<String, Expression> expressionCache = new ConcurrentHashMap<String, Expression>(256);
|
||||
|
||||
|
|
@ -81,6 +82,23 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code StandardBeanExpressionResolver} with default settings.
|
||||
*/
|
||||
public StandardBeanExpressionResolver() {
|
||||
this.expressionParser = new SpelExpressionParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code StandardBeanExpressionResolver} with the given bean class loader,
|
||||
* using it as the basis for expression compilation.
|
||||
* @param beanClassLoader the factory's bean class loader
|
||||
*/
|
||||
public StandardBeanExpressionResolver(ClassLoader beanClassLoader) {
|
||||
this.expressionParser = new SpelExpressionParser(new SpelParserConfiguration(null, beanClassLoader));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the prefix that an expression string starts with.
|
||||
* The default is "#{".
|
||||
|
|
|
|||
|
|
@ -548,7 +548,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
// Tell the internal bean factory to use the context's class loader etc.
|
||||
beanFactory.setBeanClassLoader(getClassLoader());
|
||||
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
|
||||
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
|
||||
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
|
||||
|
||||
// Configure the bean factory with context callbacks.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
|
@ -40,13 +40,15 @@ public class CodeFlow implements Opcodes {
|
|||
* sub-expressions like the expressions for the argument values in a method invocation
|
||||
* expression.
|
||||
*/
|
||||
private Stack<ArrayList<String>> compilationScopes;
|
||||
private final Stack<ArrayList<String>> compilationScopes;
|
||||
|
||||
|
||||
public CodeFlow() {
|
||||
compilationScopes = new Stack<ArrayList<String>>();
|
||||
compilationScopes.add(new ArrayList<String>());
|
||||
this.compilationScopes = new Stack<ArrayList<String>>();
|
||||
this.compilationScopes.add(new ArrayList<String>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Push the byte code to load the target (i.e. what was passed as the first argument
|
||||
* to CompiledExpression.getValue(target, context))
|
||||
|
|
@ -62,7 +64,7 @@ public class CodeFlow implements Opcodes {
|
|||
*/
|
||||
public void pushDescriptor(String descriptor) {
|
||||
Assert.notNull(descriptor);
|
||||
compilationScopes.peek().add(descriptor);
|
||||
this.compilationScopes.peek().add(descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -71,7 +73,7 @@ public class CodeFlow implements Opcodes {
|
|||
* each argument will be evaluated in a new scope.
|
||||
*/
|
||||
public void enterCompilationScope() {
|
||||
compilationScopes.push(new ArrayList<String>());
|
||||
this.compilationScopes.push(new ArrayList<String>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,18 +82,17 @@ public class CodeFlow implements Opcodes {
|
|||
* returns us to the previous (outer) scope.
|
||||
*/
|
||||
public void exitCompilationScope() {
|
||||
compilationScopes.pop();
|
||||
this.compilationScopes.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 (compilationScopes.peek().size()==0) {
|
||||
if (this.compilationScopes.peek().size() == 0) {
|
||||
return null;
|
||||
}
|
||||
return compilationScopes.peek().get(compilationScopes.peek().size()-1);
|
||||
return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,13 +106,14 @@ public class CodeFlow implements Opcodes {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert any necessary cast and value call to convert from a boxed type to a
|
||||
* primitive value
|
||||
* @param mv the method visitor into which instructions should be inserted
|
||||
* @param ch the primitive type desired as output
|
||||
* @param isObject indicates whether the type on the stack is being thought of as
|
||||
* Object (and so requires a cast)
|
||||
* @param isObject indicates whether the type on the stack is being thought of
|
||||
* as Object (and so requires a cast)
|
||||
*/
|
||||
public static void insertUnboxInsns(MethodVisitor mv, char ch, boolean isObject) {
|
||||
switch (ch) {
|
||||
|
|
@ -179,14 +181,14 @@ public class CodeFlow implements Opcodes {
|
|||
*/
|
||||
public static String createSignatureDescriptor(Method method) {
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("(");
|
||||
for (int i = 0, max = params.length; i < max; i++) {
|
||||
s.append(toJVMDescriptor(params[i]));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (Class<?> param : params) {
|
||||
sb.append(toJVMDescriptor(param));
|
||||
}
|
||||
s.append(")");
|
||||
s.append(toJVMDescriptor(method.getReturnType()));
|
||||
return s.toString();
|
||||
sb.append(")");
|
||||
sb.append(toJVMDescriptor(method.getReturnType()));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -199,13 +201,13 @@ public class CodeFlow implements Opcodes {
|
|||
*/
|
||||
public static String createSignatureDescriptor(Constructor<?> ctor) {
|
||||
Class<?>[] params = ctor.getParameterTypes();
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("(");
|
||||
for (int i = 0, max = params.length; i < max; i++) {
|
||||
s.append(toJVMDescriptor(params[i]));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (Class<?> param : params) {
|
||||
sb.append(toJVMDescriptor(param));
|
||||
}
|
||||
s.append(")V");
|
||||
return s.toString();
|
||||
sb.append(")V");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -213,7 +215,6 @@ public class CodeFlow implements Opcodes {
|
|||
* used in the compilation process, this is the one the JVM wants, so this one
|
||||
* includes any necessary trailing semicolon (e.g. Ljava/lang/String; rather than
|
||||
* Ljava/lang/String)
|
||||
*
|
||||
* @param clazz a class
|
||||
* @return the JVM descriptor for the class
|
||||
*/
|
||||
|
|
@ -262,39 +263,39 @@ public class CodeFlow implements Opcodes {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determine the descriptor for an object instance (or null).
|
||||
* @param value an object (possibly null)
|
||||
* @return the type descriptor for the object (descriptor is "Ljava/lang/Object" for
|
||||
* null value)
|
||||
* Determine the descriptor for an object instance (or {@code null}).
|
||||
* @param value an object (possibly {@code null})
|
||||
* @return the type descriptor for the object
|
||||
* (descriptor is "Ljava/lang/Object" for {@code null} value)
|
||||
*/
|
||||
public static String toDescriptorFromObject(Object value) {
|
||||
if (value == null) {
|
||||
return "Ljava/lang/Object";
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return toDescriptor(value.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param descriptor type descriptor
|
||||
* @return true if the descriptor is for a boolean primitive or boolean reference type
|
||||
* @return {@code true} if the descriptor is for a boolean primitive or boolean reference type
|
||||
*/
|
||||
public static boolean isBooleanCompatible(String descriptor) {
|
||||
return descriptor != null
|
||||
&& (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean"));
|
||||
return (descriptor != null && (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param descriptor type descriptor
|
||||
* @return true if the descriptor is for a primitive type
|
||||
* @return {@code true} if the descriptor is for a primitive type
|
||||
*/
|
||||
public static boolean isPrimitive(String descriptor) {
|
||||
return descriptor!=null && descriptor.length()==1;
|
||||
return (descriptor != null && descriptor.length() == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param descriptor the descriptor for a possible primitive array
|
||||
* @return true if the descriptor is for a primitive array (e.g. "[[I")
|
||||
* @return {@code true} if the descriptor is for a primitive array (e.g. "[[I")
|
||||
*/
|
||||
public static boolean isPrimitiveArray(String descriptor) {
|
||||
boolean primitive = true;
|
||||
|
|
@ -312,14 +313,13 @@ public class CodeFlow implements Opcodes {
|
|||
/**
|
||||
* Determine if boxing/unboxing can get from one type to the other. Assumes at least
|
||||
* one of the types is in boxed form (i.e. single char descriptor).
|
||||
*
|
||||
* @return true if it is possible to get (via boxing) from one descriptor to the other
|
||||
* @return {@code true} if it is possible to get (via boxing) from one descriptor to the other
|
||||
*/
|
||||
public static boolean areBoxingCompatible(String desc1, String desc2) {
|
||||
if (desc1.equals(desc2)) {
|
||||
return true;
|
||||
}
|
||||
if (desc1.length()==1) {
|
||||
if (desc1.length() == 1) {
|
||||
if (desc1.equals("D")) {
|
||||
return desc2.equals("Ljava/lang/Double");
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ public class CodeFlow implements Opcodes {
|
|||
return desc2.equals("Ljava/lang/Boolean");
|
||||
}
|
||||
}
|
||||
else if (desc2.length()==1) {
|
||||
else if (desc2.length() == 1) {
|
||||
if (desc2.equals("D")) {
|
||||
return desc1.equals("Ljava/lang/Double");
|
||||
}
|
||||
|
|
@ -361,14 +361,14 @@ public class CodeFlow implements Opcodes {
|
|||
* compilation process only (currently) supports certain number types. These are
|
||||
* double, float, long and int.
|
||||
* @param descriptor the descriptor for a type
|
||||
* @return true if the descriptor is for a supported numeric type or boolean
|
||||
* @return {@code true} if the descriptor is for a supported numeric type or boolean
|
||||
*/
|
||||
public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(String descriptor) {
|
||||
if (descriptor==null) {
|
||||
if (descriptor == null) {
|
||||
return false;
|
||||
}
|
||||
if (descriptor.length()==1) {
|
||||
return "DFJZI".indexOf(descriptor.charAt(0))!=-1;
|
||||
if (descriptor.length( )== 1) {
|
||||
return ("DFJZI".indexOf(descriptor.charAt(0)) != -1);
|
||||
}
|
||||
if (descriptor.startsWith("Ljava/lang/")) {
|
||||
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") ||
|
||||
|
|
@ -385,14 +385,14 @@ public class CodeFlow implements Opcodes {
|
|||
* process only (currently) supports certain number types. These are double, float,
|
||||
* long and int.
|
||||
* @param descriptor the descriptor for a type
|
||||
* @return true if the descriptor is for a supported numeric type
|
||||
* @return {@code true} if the descriptor is for a supported numeric type
|
||||
*/
|
||||
public static boolean isPrimitiveOrUnboxableSupportedNumber(String descriptor) {
|
||||
if (descriptor==null) {
|
||||
if (descriptor == null) {
|
||||
return false;
|
||||
}
|
||||
if (descriptor.length()==1) {
|
||||
return "DFJI".indexOf(descriptor.charAt(0))!=-1;
|
||||
if (descriptor.length() == 1) {
|
||||
return ("DFJI".indexOf(descriptor.charAt(0)) != -1);
|
||||
}
|
||||
if (descriptor.startsWith("Ljava/lang/")) {
|
||||
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") ||
|
||||
|
|
@ -404,12 +404,11 @@ public class CodeFlow implements Opcodes {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param descriptor a descriptor for a type that should have a primitive
|
||||
* representation
|
||||
* @param descriptor a descriptor for a type that should have a primitive representation
|
||||
* @return the single character descriptor for a primitive input descriptor
|
||||
*/
|
||||
public static char toPrimitiveTargetDesc(String descriptor) {
|
||||
if (descriptor.length()==1) {
|
||||
if (descriptor.length() == 1) {
|
||||
return descriptor.charAt(0);
|
||||
}
|
||||
if (descriptor.equals("Ljava/lang/Double")) {
|
||||
|
|
@ -438,13 +437,13 @@ public class CodeFlow implements Opcodes {
|
|||
* @param descriptor the descriptor of the type to cast to
|
||||
*/
|
||||
public static void insertCheckCast(MethodVisitor mv, String descriptor) {
|
||||
if (descriptor.length()!=1) {
|
||||
if (descriptor.charAt(0)=='[') {
|
||||
if (CodeFlow.isPrimitiveArray(descriptor)) {
|
||||
if (descriptor.length() != 1) {
|
||||
if (descriptor.charAt(0) == '[') {
|
||||
if (isPrimitiveArray(descriptor)) {
|
||||
mv.visitTypeInsn(CHECKCAST, descriptor);
|
||||
}
|
||||
else {
|
||||
mv.visitTypeInsn(CHECKCAST, descriptor+";");
|
||||
mv.visitTypeInsn(CHECKCAST, descriptor + ";");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -557,14 +556,14 @@ public class CodeFlow implements Opcodes {
|
|||
}
|
||||
else {
|
||||
if (name.charAt(0) != '[') {
|
||||
return new StringBuilder("L").append(type.getName().replace('.', '/')).toString();
|
||||
return "L" + type.getName().replace('.', '/');
|
||||
}
|
||||
else {
|
||||
if (name.endsWith(";")) {
|
||||
return name.substring(0, name.length() - 1).replace('.', '/');
|
||||
}
|
||||
else {
|
||||
return name; // array has primitive component type
|
||||
return name; // array has primitive component type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -595,7 +594,7 @@ public class CodeFlow implements Opcodes {
|
|||
int typesCount = types.length;
|
||||
String[] descriptors = new String[typesCount];
|
||||
for (int p = 0; p < typesCount; p++) {
|
||||
descriptors[p] = CodeFlow.toDescriptor(types[p]);
|
||||
descriptors[p] = toDescriptor(types[p]);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -14,13 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
|
||||
/**
|
||||
* A compilable property accessor is able to generate bytecode that represents
|
||||
|
|
@ -33,21 +31,22 @@ import org.springframework.expression.spel.standard.CodeFlow;
|
|||
public interface CompilablePropertyAccessor extends PropertyAccessor, Opcodes {
|
||||
|
||||
/**
|
||||
* @return true if this property accessor is currently suitable for compilation.
|
||||
* Return {@code true} if this property accessor is currently suitable for compilation.
|
||||
*/
|
||||
boolean isCompilable();
|
||||
|
||||
/**
|
||||
* Generate the bytecode the performs the access operation into the specified MethodVisitor using
|
||||
* context information from the codeflow where necessary.
|
||||
* @param propertyReference the property reference for which code is being generated
|
||||
* Return the type of the accessed property - may only be known once an access has occurred.
|
||||
*/
|
||||
Class<?> getPropertyType();
|
||||
|
||||
/**
|
||||
* Generate the bytecode the performs the access operation into the specified MethodVisitor
|
||||
* 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
|
||||
*/
|
||||
void generateCode(PropertyOrFieldReference propertyReference, MethodVisitor mv, CodeFlow codeflow);
|
||||
void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow);
|
||||
|
||||
/**
|
||||
* @return the type of the accessed property - may only be known once an access has occurred.
|
||||
*/
|
||||
Class<?> getPropertyType();
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
|
@ -30,8 +30,8 @@ import org.springframework.expression.EvaluationException;
|
|||
public abstract class CompiledExpression {
|
||||
|
||||
/**
|
||||
* Subclasses of CompiledExpression generated by SpelCompiler will provide an implementation of
|
||||
* this method.
|
||||
* Subclasses of CompiledExpression generated by SpelCompiler will provide an
|
||||
* implementation of this method.
|
||||
*/
|
||||
public abstract Object getValue(Object target, EvaluationContext context) throws EvaluationException;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
|
@ -24,22 +24,24 @@ package org.springframework.expression.spel;
|
|||
* @since 4.1
|
||||
*/
|
||||
public enum SpelCompilerMode {
|
||||
/**
|
||||
|
||||
/**
|
||||
* The compiler is switched off, this is the default.
|
||||
*/
|
||||
off,
|
||||
|
||||
OFF,
|
||||
|
||||
/**
|
||||
* In immediate mode, expressions are compiled as soon as possible (usually after 1 interpreted run).
|
||||
* If a compiled expression fails it will throw an exception to the caller.
|
||||
*/
|
||||
immediate,
|
||||
|
||||
IMMEDIATE,
|
||||
|
||||
/**
|
||||
* In mixed mode, expression evaluate silently switches between interpreted and compiled over time.
|
||||
* After a number of runs the expression gets compiled. If it later fails (possibly due to inferred
|
||||
* type information changing) then that will be caught internally and the system switches back to
|
||||
* interpreted mode. It may subsequently compile it again later.
|
||||
*/
|
||||
mixed
|
||||
}
|
||||
MIXED
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,24 +30,42 @@ import org.springframework.core.SpringProperties;
|
|||
*/
|
||||
public class SpelParserConfiguration {
|
||||
|
||||
private final boolean autoGrowNullReferences;
|
||||
|
||||
private final boolean autoGrowCollections;
|
||||
|
||||
private static SpelCompilerMode defaultCompilerMode = SpelCompilerMode.off;
|
||||
|
||||
private SpelCompilerMode compilerMode;
|
||||
|
||||
private final int maximumAutoGrowSize;
|
||||
private static final SpelCompilerMode defaultCompilerMode;
|
||||
|
||||
static {
|
||||
String compilerMode = SpringProperties.getProperty("spring.expression.compiler.mode");
|
||||
if (compilerMode != null) {
|
||||
defaultCompilerMode = SpelCompilerMode.valueOf(compilerMode.toLowerCase());
|
||||
// System.out.println("SpelCompiler: switched to "+defaultCompilerMode+" mode");
|
||||
}
|
||||
defaultCompilerMode = (compilerMode != null ?
|
||||
SpelCompilerMode.valueOf(compilerMode.toUpperCase()) : SpelCompilerMode.OFF);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final SpelCompilerMode compilerMode;
|
||||
|
||||
private final ClassLoader compilerClassLoader;
|
||||
|
||||
private final boolean autoGrowNullReferences;
|
||||
|
||||
private final boolean autoGrowCollections;
|
||||
|
||||
private final int maximumAutoGrowSize;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link SpelParserConfiguration} instance with default settings.
|
||||
*/
|
||||
public SpelParserConfiguration() {
|
||||
this(null, null, false, false, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpelParserConfiguration} instance.
|
||||
* @param compilerMode the compiler mode for the parser
|
||||
* @param compilerClassLoader the ClassLoader to use as the basis for expression compilation
|
||||
*/
|
||||
public SpelParserConfiguration(SpelCompilerMode compilerMode, ClassLoader compilerClassLoader) {
|
||||
this(compilerMode, compilerClassLoader, false, false, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpelParserConfiguration} instance.
|
||||
* @param autoGrowNullReferences if null references should automatically grow
|
||||
|
|
@ -55,7 +73,7 @@ public class SpelParserConfiguration {
|
|||
* @see #SpelParserConfiguration(boolean, boolean, int)
|
||||
*/
|
||||
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) {
|
||||
this(autoGrowNullReferences, autoGrowCollections, Integer.MAX_VALUE);
|
||||
this(null, null, autoGrowNullReferences, autoGrowCollections, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -65,19 +83,28 @@ public class SpelParserConfiguration {
|
|||
* @param maximumAutoGrowSize the maximum size that the collection can auto grow
|
||||
*/
|
||||
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
|
||||
this(null, null, autoGrowNullReferences, autoGrowCollections, maximumAutoGrowSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpelParserConfiguration} instance.
|
||||
* @param compilerMode the compiler mode that parsers using this configuration object should use
|
||||
* @param compilerClassLoader the ClassLoader to use as the basis for expression compilation
|
||||
* @param autoGrowNullReferences if null references should automatically grow
|
||||
* @param autoGrowCollections if collections should automatically grow
|
||||
* @param maximumAutoGrowSize the maximum size that the collection can auto grow
|
||||
*/
|
||||
public SpelParserConfiguration(SpelCompilerMode compilerMode, ClassLoader compilerClassLoader,
|
||||
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
|
||||
|
||||
this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode);
|
||||
this.compilerClassLoader = compilerClassLoader;
|
||||
this.autoGrowNullReferences = autoGrowNullReferences;
|
||||
this.autoGrowCollections = autoGrowCollections;
|
||||
this.maximumAutoGrowSize = maximumAutoGrowSize;
|
||||
this.compilerMode = defaultCompilerMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param compilerMode the compiler mode that parsers using this configuration object should use
|
||||
*/
|
||||
public void setCompilerMode(SpelCompilerMode compilerMode) {
|
||||
this.compilerMode = compilerMode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the configuration mode for parsers using this configuration object
|
||||
*/
|
||||
|
|
@ -85,6 +112,13 @@ public class SpelParserConfiguration {
|
|||
return this.compilerMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ClassLoader to use as the basis for expression compilation
|
||||
*/
|
||||
public ClassLoader getCompilerClassLoader() {
|
||||
return this.compilerClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if {@code null} references should be automatically grown
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package org.springframework.expression.spel.ast;
|
|||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
/**
|
||||
* Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ import org.springframework.expression.EvaluationException;
|
|||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.common.ExpressionUtils;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.ReflectiveConstructorExecutor;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import org.springframework.asm.Label;
|
|||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
/**
|
||||
* Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents a float literal.
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ import org.springframework.core.convert.TypeDescriptor;
|
|||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.ReflectionHelper;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ import org.springframework.expression.EvaluationException;
|
|||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents an integer literal.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents a long integer literal.
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ import org.springframework.expression.ExpressionInvocationTargetException;
|
|||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.MethodResolver;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.ReflectiveMethodExecutor;
|
||||
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents null.
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import org.springframework.asm.Label;
|
|||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,16 +16,15 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ package org.springframework.expression.spel.ast;
|
|||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@
|
|||
*/
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,14 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,15 +16,14 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ package org.springframework.expression.spel.ast;
|
|||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package org.springframework.expression.spel.ast;
|
|||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,17 +16,16 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import org.springframework.asm.MethodVisitor;
|
|||
import org.springframework.asm.Type;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package org.springframework.expression.spel.ast;
|
|||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -24,15 +24,15 @@ import java.util.Map;
|
|||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.CompilablePropertyAccessor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.CompilablePropertyAccessor;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
||||
|
||||
/**
|
||||
|
|
@ -331,19 +331,14 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
|||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
if (this.cachedReadAccessor == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.cachedReadAccessor instanceof CompilablePropertyAccessor) {
|
||||
return ((CompilablePropertyAccessor)this.cachedReadAccessor).isCompilable();
|
||||
}
|
||||
return false;
|
||||
return (this.cachedReadAccessor instanceof CompilablePropertyAccessor &&
|
||||
((CompilablePropertyAccessor) this.cachedReadAccessor).isCompilable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(MethodVisitor mv,CodeFlow codeflow) {
|
||||
((CompilablePropertyAccessor)this.cachedReadAccessor).generateCode(this, mv, codeflow);
|
||||
codeflow.pushDescriptor(exitTypeDescriptor);
|
||||
public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
|
||||
((CompilablePropertyAccessor) this.cachedReadAccessor).generateCode(this.name, mv, codeflow);
|
||||
codeflow.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents a real literal.
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ import org.springframework.asm.Opcodes;
|
|||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.common.ExpressionUtils;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
|
@ -58,6 +58,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
|||
*/
|
||||
protected String exitTypeDescriptor;
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -184,7 +185,6 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
|||
* Check whether a node can be compiled to bytecode. The reasoning in each node may
|
||||
* be different but will typically involve checking whether the exit type descriptor
|
||||
* of the node is known and any relevant child nodes are compilable.
|
||||
*
|
||||
* @return true if this node can be compiled to bytecode
|
||||
*/
|
||||
public boolean isCompilable() {
|
||||
|
|
@ -196,7 +196,6 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
|||
* the current expression being compiled is available in the codeflow object. For
|
||||
* 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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
|
|||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents a string literal.
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import org.springframework.asm.Label;
|
|||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
/**
|
||||
* Represents a ternary expression, for example: "someCheck()?true:false".
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import org.springframework.asm.MethodVisitor;
|
|||
import org.springframework.asm.Type;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
/**
|
||||
* Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)"
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package org.springframework.expression.spel.ast;
|
|||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
|
||||
/**
|
||||
* Represents a variable reference, eg. #someVar. Note this is different to a *local*
|
||||
|
|
|
|||
|
|
@ -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,7 +29,48 @@ import org.springframework.expression.spel.InternalParseException;
|
|||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelParseException;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.ast.*;
|
||||
import org.springframework.expression.spel.ast.Assign;
|
||||
import org.springframework.expression.spel.ast.BeanReference;
|
||||
import org.springframework.expression.spel.ast.BooleanLiteral;
|
||||
import org.springframework.expression.spel.ast.CompoundExpression;
|
||||
import org.springframework.expression.spel.ast.ConstructorReference;
|
||||
import org.springframework.expression.spel.ast.Elvis;
|
||||
import org.springframework.expression.spel.ast.FunctionReference;
|
||||
import org.springframework.expression.spel.ast.Identifier;
|
||||
import org.springframework.expression.spel.ast.Indexer;
|
||||
import org.springframework.expression.spel.ast.InlineList;
|
||||
import org.springframework.expression.spel.ast.Literal;
|
||||
import org.springframework.expression.spel.ast.MethodReference;
|
||||
import org.springframework.expression.spel.ast.NullLiteral;
|
||||
import org.springframework.expression.spel.ast.OpAnd;
|
||||
import org.springframework.expression.spel.ast.OpDec;
|
||||
import org.springframework.expression.spel.ast.OpDivide;
|
||||
import org.springframework.expression.spel.ast.OpEQ;
|
||||
import org.springframework.expression.spel.ast.OpGE;
|
||||
import org.springframework.expression.spel.ast.OpGT;
|
||||
import org.springframework.expression.spel.ast.OpInc;
|
||||
import org.springframework.expression.spel.ast.OpLE;
|
||||
import org.springframework.expression.spel.ast.OpLT;
|
||||
import org.springframework.expression.spel.ast.OpMinus;
|
||||
import org.springframework.expression.spel.ast.OpModulus;
|
||||
import org.springframework.expression.spel.ast.OpMultiply;
|
||||
import org.springframework.expression.spel.ast.OpNE;
|
||||
import org.springframework.expression.spel.ast.OpOr;
|
||||
import org.springframework.expression.spel.ast.OpPlus;
|
||||
import org.springframework.expression.spel.ast.OperatorBetween;
|
||||
import org.springframework.expression.spel.ast.OperatorInstanceof;
|
||||
import org.springframework.expression.spel.ast.OperatorMatches;
|
||||
import org.springframework.expression.spel.ast.OperatorNot;
|
||||
import org.springframework.expression.spel.ast.OperatorPower;
|
||||
import org.springframework.expression.spel.ast.Projection;
|
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
|
||||
import org.springframework.expression.spel.ast.QualifiedIdentifier;
|
||||
import org.springframework.expression.spel.ast.Selection;
|
||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||
import org.springframework.expression.spel.ast.StringLiteral;
|
||||
import org.springframework.expression.spel.ast.Ternary;
|
||||
import org.springframework.expression.spel.ast.TypeReference;
|
||||
import org.springframework.expression.spel.ast.VariableReference;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -84,13 +125,13 @@ 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);
|
||||
}
|
||||
catch (InternalParseException ipe) {
|
||||
throw ipe.getCause();
|
||||
catch (InternalParseException ex) {
|
||||
throw ex.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
|
@ -20,26 +20,30 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.asm.ClassWriter;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.CompiledExpression;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* A SpelCompiler will take a regular parsed expression and create (and load) a class
|
||||
* containing byte code that does the same thing as that expression. The compiled form of
|
||||
* an expression will evaluate far faster than the interpreted form.
|
||||
* <p>
|
||||
* The SpelCompiler is not currently handling all expression types but covers many of the
|
||||
* common cases. The framework is extensible to cover more cases in the future. For
|
||||
*
|
||||
* <p>The SpelCompiler is not currently handling all expression types but covers many of
|
||||
* the common cases. The framework is extensible to cover more cases in the future. For
|
||||
* absolute maximum speed there is *no checking* in the compiled code. The compiled
|
||||
* version of the expression uses information learned during interpreted runs of the
|
||||
* expression when it generates the byte code. For example if it knows that a particular
|
||||
|
|
@ -48,64 +52,40 @@ import org.springframework.util.ClassUtils;
|
|||
* performance but should the dereference result in something other than a map, the
|
||||
* compiled expression will fail - like a ClassCastException would occur if passing data
|
||||
* of an unexpected type in a regular Java program.
|
||||
* <p>
|
||||
* Due to the lack of checking there are likely some expressions that should never be
|
||||
*
|
||||
* <p>Due to the lack of checking there are likely some expressions that should never be
|
||||
* compiled, for example if an expression is continuously dealing with different types of
|
||||
* data. Due to these cases the compiler is something that must be selectively turned on
|
||||
* for an associated SpelExpressionParser (through the {@link SpelParserConfiguration}
|
||||
* object), it is not on by default.
|
||||
* <p>
|
||||
* Individual expressions can be compiled by calling
|
||||
* <tt>SpelCompiler.compile(expression)</tt>.
|
||||
*
|
||||
* <p>Individual expressions can be compiled by calling {@code SpelCompiler.compile(expression)}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SpelCompiler implements Opcodes {
|
||||
|
||||
// Default number of times to interpret an expression before compiling it
|
||||
private static int DEFAULT_INTERPRETED_COUNT_THRESHOLD = 100;
|
||||
|
||||
// Once an expression is evaluated the threshold number of times, it will be a candidate for compilation
|
||||
public static int interpretedCountThreshold = DEFAULT_INTERPRETED_COUNT_THRESHOLD;
|
||||
|
||||
// Useful for debugging
|
||||
public static final boolean verbose = false;
|
||||
private static final Log logger = LogFactory.getLog(SpelCompiler.class);
|
||||
|
||||
// A compiler is created for each classloader, it manages a child class loader of that
|
||||
// classloader and the child is used to load the compiled expressions.
|
||||
private static Map<ClassLoader,SpelCompiler> compilers = Collections.synchronizedMap(new WeakHashMap<ClassLoader,SpelCompiler>());
|
||||
private static final Map<ClassLoader, SpelCompiler> compilers =
|
||||
new ConcurrentReferenceHashMap<ClassLoader, SpelCompiler>();
|
||||
|
||||
// The child classloader used to load the compiled expression classes
|
||||
private ChildClassLoader ccl;
|
||||
|
||||
// The child ClassLoader used to load the compiled expression classes
|
||||
private final ChildClassLoader ccl;
|
||||
|
||||
// counter suffix for generated classes within this SpelCompiler instance
|
||||
private int suffixId;
|
||||
private final AtomicInteger suffixId = new AtomicInteger(1);
|
||||
|
||||
/**
|
||||
* Factory method for compiler instances. The returned SpelCompiler will
|
||||
* attach a class loader as the child of the default class loader and this
|
||||
* child will be used to load compiled expressions.
|
||||
*
|
||||
* @return a SpelCompiler instance
|
||||
*/
|
||||
public static SpelCompiler getCompiler() {
|
||||
ClassLoader classloader = ClassUtils.getDefaultClassLoader();
|
||||
synchronized (compilers) {
|
||||
SpelCompiler compiler = compilers.get(classloader);
|
||||
if (compiler == null) {
|
||||
compiler = new SpelCompiler(classloader);
|
||||
compilers.put(classloader,compiler);
|
||||
}
|
||||
return compiler;
|
||||
}
|
||||
}
|
||||
|
||||
private SpelCompiler(ClassLoader classloader) {
|
||||
this.ccl = new ChildClassLoader(classloader);
|
||||
this.suffixId = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt compilation of the supplied expression. A check is
|
||||
* made to see if it is compilable before compilation proceeds. The
|
||||
|
|
@ -118,31 +98,27 @@ public class SpelCompiler implements Opcodes {
|
|||
*/
|
||||
public CompiledExpression compile(SpelNodeImpl expression) {
|
||||
if (expression.isCompilable()) {
|
||||
if (verbose) {
|
||||
System.out.println("SpEL: compiling " + expression.toStringAST());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SpEL: compiling " + expression.toStringAST());
|
||||
}
|
||||
Class<? extends CompiledExpression> clazz = createExpressionClass(expression);
|
||||
try {
|
||||
CompiledExpression instance = clazz.newInstance();
|
||||
return instance;
|
||||
return clazz.newInstance();
|
||||
}
|
||||
catch (InstantiationException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
catch (IllegalAccessException iae) {
|
||||
iae.printStackTrace();
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to instantiate CompiledExpression", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (verbose) {
|
||||
System.out.println("SpEL: unable to compile " + expression.toStringAST());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SpEL: unable to compile " + expression.toStringAST());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized int getNextSuffix() {
|
||||
return suffixId++;
|
||||
private int getNextSuffix() {
|
||||
return this.suffixId.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -152,7 +128,6 @@ public class SpelCompiler implements Opcodes {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
|
||||
|
||||
// Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression'
|
||||
String clazzName = "spel/Ex" + getNextSuffix();
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
|
||||
|
|
@ -179,7 +154,7 @@ public class SpelCompiler implements Opcodes {
|
|||
expressionToCompile.generateCode(mv,codeflow);
|
||||
|
||||
CodeFlow.insertBoxIfNecessary(mv,codeflow.lastDescriptor());
|
||||
if (codeflow.lastDescriptor() == "V") {
|
||||
if ("V".equals(codeflow.lastDescriptor())) {
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
}
|
||||
mv.visitInsn(ARETURN);
|
||||
|
|
@ -190,44 +165,79 @@ public class SpelCompiler implements Opcodes {
|
|||
byte[] data = cw.toByteArray();
|
||||
// TODO need to make this conditionally occur based on a debug flag
|
||||
// dump(expressionToCompile.toStringAST(), clazzName, data);
|
||||
Class<? extends CompiledExpression> clazz = (Class<? extends CompiledExpression>) ccl.defineClass(clazzName.replaceAll("/","."),data);
|
||||
return clazz;
|
||||
return (Class<? extends CompiledExpression>) ccl.defineClass(clazzName.replaceAll("/","."),data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method for compiler instances. The returned SpelCompiler will
|
||||
* attach a class loader as the child of the given class loader and this
|
||||
* child will be used to load compiled expressions.
|
||||
* @param classLoader the ClassLoader to use as the basis for compilation
|
||||
* @return a corresponding SpelCompiler instance
|
||||
*/
|
||||
public static SpelCompiler getCompiler(ClassLoader classLoader) {
|
||||
ClassLoader clToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
|
||||
synchronized (compilers) {
|
||||
SpelCompiler compiler = compilers.get(clToUse);
|
||||
if (compiler == null) {
|
||||
compiler = new SpelCompiler(clToUse);
|
||||
compilers.put(clToUse, compiler);
|
||||
}
|
||||
return compiler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging purposes, dump the specified byte code into a file on the disk. Not
|
||||
* yet hooked in, needs conditionally calling based on a sys prop.
|
||||
*
|
||||
* Request that an attempt is made to compile the specified expression. It may fail if
|
||||
* components of the expression are not suitable for compilation or the data types
|
||||
* involved are not suitable for compilation. Used for testing.
|
||||
* @return true if the expression was successfully compiled
|
||||
*/
|
||||
public static boolean compile(Expression expression) {
|
||||
return (expression instanceof SpelExpression && ((SpelExpression) expression).compileExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to revert to the interpreter for expression evaluation. Any compiled form
|
||||
* is discarded but can be recreated by later recompiling again.
|
||||
* @param expression the expression
|
||||
*/
|
||||
public static void revertToInterpreted(Expression expression) {
|
||||
if (expression instanceof SpelExpression) {
|
||||
((SpelExpression) expression).revertToInterpreted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging purposes, dump the specified byte code into a file on the disk.
|
||||
* Not yet hooked in, needs conditionally calling based on a sys prop.
|
||||
* @param expressionText the text of the expression compiled
|
||||
* @param name the name of the class being used for the compiled expression
|
||||
* @param bytecode the bytecode for the generated class
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static void dump(String expressionText, String name, byte[] bytecode) {
|
||||
name = name.replace('.', '/');
|
||||
String dir = "";
|
||||
if (name.indexOf('/') != -1) {
|
||||
dir = name.substring(0, name.lastIndexOf('/'));
|
||||
}
|
||||
String dumplocation = null;
|
||||
String nameToUse = name.replace('.', '/');
|
||||
String dir = (nameToUse.indexOf('/') != -1 ? nameToUse.substring(0, nameToUse.lastIndexOf('/')) : "");
|
||||
String dumpLocation = null;
|
||||
try {
|
||||
File tempfile = null;
|
||||
tempfile = File.createTempFile("tmp", null);
|
||||
tempfile.delete();
|
||||
File f = new File(tempfile, dir);
|
||||
File tempFile = File.createTempFile("tmp", null);
|
||||
dumpLocation = tempFile + File.separator + nameToUse + ".class";
|
||||
tempFile.delete();
|
||||
File f = new File(tempFile, dir);
|
||||
f.mkdirs();
|
||||
dumplocation = tempfile + File.separator + name + ".class";
|
||||
System.out.println("Expression '" + expressionText + "' compiled code dumped to "
|
||||
+ dumplocation);
|
||||
f = new File(dumplocation);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Expression '" + expressionText + "' compiled code dumped to " + dumpLocation);
|
||||
}
|
||||
f = new File(dumpLocation);
|
||||
FileOutputStream fos = new FileOutputStream(f);
|
||||
fos.write(bytecode);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new IllegalStateException("Unexpected problem dumping class "
|
||||
+ name + " into " + dumplocation, ioe);
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,32 +256,6 @@ public class SpelCompiler implements Opcodes {
|
|||
public Class<?> defineClass(String name, byte[] bytes) {
|
||||
return super.defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that an attempt is made to compile the specified expression. It may fail if
|
||||
* components of the expression are not suitable for compilation or the data types
|
||||
* involved are not suitable for compilation. Used for testing.
|
||||
* @return true if the expression was successfully compiled
|
||||
*/
|
||||
public static boolean compile(Expression expression) {
|
||||
if (expression instanceof SpelExpression) {
|
||||
SpelExpression spelExpression = (SpelExpression)expression;
|
||||
return spelExpression.compileExpression();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to revert to the interpreter for expression evaluation. Any compiled form
|
||||
* is discarded but can be recreated by later recompiling again.
|
||||
* @param expression the expression
|
||||
*/
|
||||
public static void revertToInterpreted(Expression expression) {
|
||||
if (expression instanceof SpelExpression) {
|
||||
SpelExpression spelExpression = (SpelExpression)expression;
|
||||
spelExpression.revertToInterpreted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,25 +44,32 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class SpelExpression implements Expression {
|
||||
|
||||
// Number of times to interpret an expression before compiling it
|
||||
private static final int INTERPRETED_COUNT_THRESHOLD = 100;
|
||||
|
||||
// Number of times to try compiling an expression before giving up
|
||||
private static final int FAILED_ATTEMPTS_THRESHOLD = 100;
|
||||
|
||||
|
||||
private final String expression;
|
||||
|
||||
// Holds the compiled form of the expression (if it has been compiled)
|
||||
private CompiledExpression compiledAst;
|
||||
|
||||
private SpelNodeImpl ast;
|
||||
private final SpelNodeImpl ast;
|
||||
|
||||
private final SpelParserConfiguration configuration;
|
||||
|
||||
// the default context is used if no override is supplied by the user
|
||||
private EvaluationContext defaultContext;
|
||||
private EvaluationContext evaluationContext;
|
||||
|
||||
// Holds the compiled form of the expression (if it has been compiled)
|
||||
private CompiledExpression compiledAst;
|
||||
|
||||
// Count of many times as the expression been interpreted - can trigger compilation
|
||||
// when certain limit reached
|
||||
private int interpretedCount = 0;
|
||||
private volatile int interpretedCount = 0;
|
||||
|
||||
// The number of times compilation was attempted and failed - enables us to eventually
|
||||
// give up trying to compile it when it just doesn't seem to be possible.
|
||||
private int failedAttempts = 0;
|
||||
private volatile int failedAttempts = 0;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -75,23 +82,44 @@ public class SpelExpression implements Expression {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the evaluation context that will be used if none is specified on an evaluation call.
|
||||
* @param evaluationContext the evaluation context to use
|
||||
*/
|
||||
public void setEvaluationContext(EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default evaluation context that will be used if none is supplied on an evaluation call.
|
||||
* @return the default evaluation context
|
||||
*/
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
if (this.evaluationContext == null) {
|
||||
this.evaluationContext = new StandardEvaluationContext();
|
||||
}
|
||||
return this.evaluationContext;
|
||||
}
|
||||
|
||||
|
||||
// implementing Expression
|
||||
|
||||
@Override
|
||||
public Object getValue() throws EvaluationException {
|
||||
Object result = null;
|
||||
if (compiledAst != null) {
|
||||
Object result;
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
return this.compiledAst.getValue(null,null);
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -103,19 +131,20 @@ public class SpelExpression implements Expression {
|
|||
|
||||
@Override
|
||||
public Object getValue(Object rootObject) throws EvaluationException {
|
||||
Object result = null;
|
||||
if (compiledAst!=null) {
|
||||
Object result;
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
return this.compiledAst.getValue(rootObject,null);
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,23 +157,25 @@ public class SpelExpression implements Expression {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
||||
if (compiledAst!=null) {
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
Object result = this.compiledAst.getValue(null,null);
|
||||
if (expectedResultType == null) {
|
||||
return (T)result;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,23 +188,25 @@ public class SpelExpression implements Expression {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws EvaluationException {
|
||||
if (compiledAst!=null) {
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
Object result = this.compiledAst.getValue(rootObject,null);
|
||||
Object result = this.compiledAst.getValue(rootObject, null);
|
||||
if (expectedResultType == null) {
|
||||
return (T)result;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,15 +223,16 @@ public class SpelExpression implements Expression {
|
|||
try {
|
||||
Object result = this.compiledAst.getValue(null,context);
|
||||
return result;
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -211,18 +245,19 @@ public class SpelExpression implements Expression {
|
|||
@Override
|
||||
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
Assert.notNull(context, "The EvaluationContext is required");
|
||||
if (compiledAst!=null) {
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
return this.compiledAst.getValue(rootObject,context);
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -235,23 +270,25 @@ public class SpelExpression implements Expression {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
|
||||
if (compiledAst!=null) {
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
Object result = this.compiledAst.getValue(null,context);
|
||||
if (expectedResultType!=null) {
|
||||
return (T) result;
|
||||
} else {
|
||||
if (expectedResultType != null) {
|
||||
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
else {
|
||||
return (T) result;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -264,23 +301,25 @@ public class SpelExpression implements Expression {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) throws EvaluationException {
|
||||
if (compiledAst!=null) {
|
||||
if (this.compiledAst != null) {
|
||||
try {
|
||||
Object result = this.compiledAst.getValue(rootObject,context);
|
||||
if (expectedResultType!=null) {
|
||||
return (T) result;
|
||||
} else {
|
||||
if (expectedResultType != null) {
|
||||
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
else {
|
||||
return (T) result;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// If running in mixed mode, revert to interpreted
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) {
|
||||
interpretedCount = 0;
|
||||
compiledAst = null;
|
||||
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
|
||||
this.interpretedCount = 0;
|
||||
this.compiledAst = null;
|
||||
}
|
||||
else {
|
||||
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
||||
throw new SpelEvaluationException(t,SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -305,14 +344,14 @@ public class SpelExpression implements Expression {
|
|||
Assert.notNull(context, "The EvaluationContext is required");
|
||||
ExpressionState eState = new ExpressionState(context, this.configuration);
|
||||
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
|
||||
return typeDescriptor != null ? typeDescriptor.getType() : null;
|
||||
return (typeDescriptor != null ? typeDescriptor.getType() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration);
|
||||
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
|
||||
return typeDescriptor != null ? typeDescriptor.getType() : null;
|
||||
return (typeDescriptor != null ? typeDescriptor.getType() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -379,6 +418,7 @@ public class SpelExpression implements Expression {
|
|||
this.ast.setValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration), value);
|
||||
}
|
||||
|
||||
|
||||
// impl only
|
||||
|
||||
/**
|
||||
|
|
@ -386,17 +426,17 @@ public class SpelExpression implements Expression {
|
|||
* @param expressionState the expression state used to determine compilation mode
|
||||
*/
|
||||
private void checkCompile(ExpressionState expressionState) {
|
||||
interpretedCount++;
|
||||
this.interpretedCount++;
|
||||
SpelCompilerMode compilerMode = expressionState.getConfiguration().getCompilerMode();
|
||||
if (compilerMode != SpelCompilerMode.off) {
|
||||
if (compilerMode == SpelCompilerMode.immediate) {
|
||||
if (interpretedCount > 1) {
|
||||
if (compilerMode != SpelCompilerMode.OFF) {
|
||||
if (compilerMode == SpelCompilerMode.IMMEDIATE) {
|
||||
if (this.interpretedCount > 1) {
|
||||
compileExpression();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// compilerMode = SpelCompilerMode.mixed
|
||||
if (interpretedCount > SpelCompiler.interpretedCountThreshold) {
|
||||
// compilerMode = SpelCompilerMode.MIXED
|
||||
if (this.interpretedCount > INTERPRETED_COUNT_THRESHOLD) {
|
||||
compileExpression();
|
||||
}
|
||||
}
|
||||
|
|
@ -410,20 +450,20 @@ public class SpelExpression implements Expression {
|
|||
* no longer considered suitable for compilation.
|
||||
*/
|
||||
public boolean compileExpression() {
|
||||
if (failedAttempts > 100) {
|
||||
if (this.failedAttempts > FAILED_ATTEMPTS_THRESHOLD) {
|
||||
// Don't try again
|
||||
return false;
|
||||
}
|
||||
if (this.compiledAst == null) {
|
||||
synchronized (expression) {
|
||||
// Possibly compiled by another thread before this thread got into the
|
||||
// sync block
|
||||
synchronized (this.expression) {
|
||||
// Possibly compiled by another thread before this thread got into the sync block
|
||||
if (this.compiledAst != null) {
|
||||
return true;
|
||||
}
|
||||
this.compiledAst = SpelCompiler.getCompiler().compile(this.ast);
|
||||
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
|
||||
this.compiledAst = compiler.compile(this.ast);
|
||||
if (this.compiledAst == null) {
|
||||
failedAttempts++;
|
||||
this.failedAttempts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -458,25 +498,6 @@ public class SpelExpression implements Expression {
|
|||
return this.ast.toStringAST();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default evaluation context that will be used if none is supplied on an evaluation call
|
||||
* @return the default evaluation context
|
||||
*/
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
if (this.defaultContext == null) {
|
||||
this.defaultContext = new StandardEvaluationContext();
|
||||
}
|
||||
return this.defaultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the evaluation context that will be used if none is specified on an evaluation call.
|
||||
* @param context an evaluation context
|
||||
*/
|
||||
public void setEvaluationContext(EvaluationContext context) {
|
||||
this.defaultContext = context;
|
||||
}
|
||||
|
||||
private TypedValue toTypedValue(Object object) {
|
||||
if (object == null) {
|
||||
return TypedValue.NULL;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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,14 +35,14 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser {
|
|||
|
||||
|
||||
/**
|
||||
* Create a parser with standard configuration.
|
||||
* Create a parser with default settings.
|
||||
*/
|
||||
public SpelExpressionParser() {
|
||||
this.configuration = new SpelParserConfiguration(false, false);
|
||||
this.configuration = new SpelParserConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a parser with some configured behavior.
|
||||
* Create a parser with the specified configuration.
|
||||
* @param configuration custom configuration options
|
||||
*/
|
||||
public SpelExpressionParser(SpelParserConfiguration configuration) {
|
||||
|
|
@ -51,13 +51,13 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser {
|
|||
}
|
||||
|
||||
|
||||
public SpelExpression parseRaw(String expressionString) throws ParseException {
|
||||
return doParseExpression(expressionString, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
|
||||
return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
|
||||
}
|
||||
|
||||
public SpelExpression parseRaw(String expressionString) throws ParseException {
|
||||
return doParseExpression(expressionString, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,13 +35,12 @@ import org.springframework.core.convert.Property;
|
|||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.CompilablePropertyAccessor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.CompilablePropertyAccessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -370,10 +369,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
Method[] methods = getSortedClassMethods(clazz);
|
||||
for (String methodSuffix : methodSuffixes) {
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(prefix + methodSuffix)
|
||||
&& method.getParameterTypes().length == numberOfParams
|
||||
&& (!mustBeStatic || Modifier.isStatic(method.getModifiers()))
|
||||
&& (requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
|
||||
if (method.getName().equals(prefix + methodSuffix) &&
|
||||
method.getParameterTypes().length == numberOfParams &&
|
||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers())) &&
|
||||
(requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
|
@ -649,16 +648,6 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
}
|
||||
throw new AccessException("Neither getter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
if (member instanceof Field) {
|
||||
return ((Field)member).getType();
|
||||
}
|
||||
else {
|
||||
return ((Method)member).getReturnType();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) {
|
||||
|
|
@ -678,11 +667,20 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(PropertyOrFieldReference propertyReference, MethodVisitor mv,CodeFlow codeflow) {
|
||||
boolean isStatic = Modifier.isStatic(member.getModifiers());
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
if (member instanceof Field) {
|
||||
return ((Field) member).getType();
|
||||
}
|
||||
else {
|
||||
return ((Method) member).getReturnType();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) {
|
||||
boolean isStatic = Modifier.isStatic(member.getModifiers());
|
||||
String descriptor = codeflow.lastDescriptor();
|
||||
String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.','/');
|
||||
if (!isStatic) {
|
||||
|
|
@ -694,9 +692,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
}
|
||||
}
|
||||
if (member instanceof Field) {
|
||||
mv.visitFieldInsn(isStatic?GETSTATIC:GETFIELD,memberDeclaringClassSlashedDescriptor,member.getName(),CodeFlow.toJVMDescriptor(((Field) member).getType()));
|
||||
} else {
|
||||
mv.visitMethodInsn(isStatic?INVOKESTATIC:INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, member.getName(),CodeFlow.createSignatureDescriptor((Method)member),false);
|
||||
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor,
|
||||
member.getName(), CodeFlow.toJVMDescriptor(((Field) member).getType()));
|
||||
}
|
||||
else {
|
||||
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor,
|
||||
member.getName(), CodeFlow.createSignatureDescriptor((Method) member),false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -54,6 +54,8 @@ public class StandardEvaluationContext implements EvaluationContext {
|
|||
|
||||
private List<MethodResolver> methodResolvers;
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
private ReflectiveMethodResolver reflectiveMethodResolver;
|
||||
|
||||
private List<PropertyAccessor> propertyAccessors;
|
||||
|
|
@ -68,15 +70,12 @@ public class StandardEvaluationContext implements EvaluationContext {
|
|||
|
||||
private final Map<String, Object> variables = new HashMap<String, Object>();
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
|
||||
public StandardEvaluationContext() {
|
||||
setRootObject(null);
|
||||
}
|
||||
|
||||
public StandardEvaluationContext(Object rootObject) {
|
||||
this();
|
||||
setRootObject(rootObject);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,18 +25,16 @@ import java.util.Map;
|
|||
import java.util.StringTokenizer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.CompilablePropertyAccessor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ast.CompoundExpression;
|
||||
import org.springframework.expression.spel.ast.OpLT;
|
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
|
||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||
import org.springframework.expression.spel.ast.Ternary;
|
||||
import org.springframework.expression.spel.standard.CodeFlow;
|
||||
import org.springframework.expression.spel.standard.SpelCompiler;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
|
@ -2174,63 +2172,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
|||
vc = expression.getValue(payload);
|
||||
assertNull(vc);
|
||||
}
|
||||
|
||||
static class MyAccessor implements CompilablePropertyAccessor {
|
||||
|
||||
private Method method;
|
||||
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return new Class[]{Payload2.class};
|
||||
}
|
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
// target is a Payload2 instance
|
||||
return true;
|
||||
}
|
||||
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
Payload2 payload2 = (Payload2)target;
|
||||
return new TypedValue(payload2.getField(name));
|
||||
}
|
||||
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(PropertyOrFieldReference propertyReference, MethodVisitor mv,CodeFlow codeflow) {
|
||||
if (method == null) {
|
||||
try {
|
||||
method = Payload2.class.getDeclaredMethod("getField", String.class);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
String descriptor = codeflow.lastDescriptor();
|
||||
String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/');
|
||||
if (descriptor == null) {
|
||||
codeflow.loadTarget(mv);
|
||||
}
|
||||
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
|
||||
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
|
||||
}
|
||||
mv.visitLdcInsn(propertyReference.getName());
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createSignatureDescriptor(method),false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void variantGetter() throws Exception {
|
||||
Payload2Holder holder = new Payload2Holder();
|
||||
|
|
@ -2257,9 +2199,67 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
|||
// v = expression.getValue(ctx,holder);
|
||||
// }
|
||||
// System.out.println((System.currentTimeMillis()-stime));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static class MyAccessor implements CompilablePropertyAccessor {
|
||||
|
||||
private Method method;
|
||||
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return new Class[]{Payload2.class};
|
||||
}
|
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
// target is a Payload2 instance
|
||||
return true;
|
||||
}
|
||||
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
Payload2 payload2 = (Payload2)target;
|
||||
return new TypedValue(payload2.getField(name));
|
||||
}
|
||||
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(String propertyName, MethodVisitor mv,CodeFlow codeflow) {
|
||||
if (method == null) {
|
||||
try {
|
||||
method = Payload2.class.getDeclaredMethod("getField", String.class);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
String descriptor = codeflow.lastDescriptor();
|
||||
String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/');
|
||||
if (descriptor == null) {
|
||||
codeflow.loadTarget(mv);
|
||||
}
|
||||
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
|
||||
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
|
||||
}
|
||||
mv.visitLdcInsn(propertyName);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createSignatureDescriptor(method),false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class CompilableMapAccessor implements CompilablePropertyAccessor {
|
||||
|
||||
@Override
|
||||
|
|
@ -2295,40 +2295,23 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
|||
return new Class[] {Map.class};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exception thrown from {@code read} in order to reset a cached
|
||||
* PropertyAccessor, allowing other accessors to have a try.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class MapAccessException extends AccessException {
|
||||
|
||||
private final String key;
|
||||
|
||||
public MapAccessException(String key) {
|
||||
super(null);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Map does not contain a value for key '" + this.key + "'";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void generateCode(PropertyOrFieldReference propertyReference,
|
||||
MethodVisitor mv, CodeFlow codeflow) {
|
||||
public Class<?> getPropertyType() {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) {
|
||||
String descriptor = codeflow.lastDescriptor();
|
||||
if (descriptor == null) {
|
||||
codeflow.loadTarget(mv);
|
||||
}
|
||||
mv.visitLdcInsn(propertyReference.getName());
|
||||
mv.visitLdcInsn(propertyName);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get","(Ljava/lang/Object;)Ljava/lang/Object;",true);
|
||||
|
||||
// if (method == null) {
|
||||
|
|
@ -2348,12 +2331,27 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
|||
// mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createDescriptor(method));
|
||||
// 6: invokeinterface #6, 2; //InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
return Object.class;
|
||||
|
||||
/**
|
||||
* Exception thrown from {@code read} in order to reset a cached
|
||||
* PropertyAccessor, allowing other accessors to have a try.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class MapAccessException extends AccessException {
|
||||
|
||||
private final String key;
|
||||
|
||||
public MapAccessException(String key) {
|
||||
super(null);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Map does not contain a value for key '" + this.key + "'";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue