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:
Juergen Hoeller 2014-07-16 14:41:46 +02:00
parent 8fb592712c
commit bad74dc836
47 changed files with 600 additions and 511 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Expression;
import org.springframework.expression.ExpressionParser; import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext; import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.expression.spel.support.StandardTypeConverter;
@ -58,7 +59,7 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX; 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); 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. * Set the prefix that an expression string starts with.
* The default is "#{". * The default is "#{".

View File

@ -548,7 +548,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc. // Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks. // Configure the bean factory with context callbacks.

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.expression.spel.standard; package org.springframework.expression.spel;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; 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 * sub-expressions like the expressions for the argument values in a method invocation
* expression. * expression.
*/ */
private Stack<ArrayList<String>> compilationScopes; private final Stack<ArrayList<String>> compilationScopes;
public CodeFlow() { public CodeFlow() {
compilationScopes = new Stack<ArrayList<String>>(); this.compilationScopes = new Stack<ArrayList<String>>();
compilationScopes.add(new 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 * Push the byte code to load the target (i.e. what was passed as the first argument
* to CompiledExpression.getValue(target, context)) * to CompiledExpression.getValue(target, context))
@ -62,7 +64,7 @@ public class CodeFlow implements Opcodes {
*/ */
public void pushDescriptor(String descriptor) { public void pushDescriptor(String descriptor) {
Assert.notNull(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. * each argument will be evaluated in a new scope.
*/ */
public void enterCompilationScope() { 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. * returns us to the previous (outer) scope.
*/ */
public void exitCompilationScope() { public void exitCompilationScope() {
compilationScopes.pop(); this.compilationScopes.pop();
} }
/** /**
* @return the descriptor for the item currently on top of the stack (in the current * @return the descriptor for the item currently on top of the stack (in the current scope)
* scope)
*/ */
public String lastDescriptor() { public String lastDescriptor() {
if (compilationScopes.peek().size()==0) { if (this.compilationScopes.peek().size() == 0) {
return null; 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 * Insert any necessary cast and value call to convert from a boxed type to a
* primitive value * primitive value
* @param mv the method visitor into which instructions should be inserted * @param mv the method visitor into which instructions should be inserted
* @param ch the primitive type desired as output * @param ch the primitive type desired as output
* @param isObject indicates whether the type on the stack is being thought of as * @param isObject indicates whether the type on the stack is being thought of
* Object (and so requires a cast) * as Object (and so requires a cast)
*/ */
public static void insertUnboxInsns(MethodVisitor mv, char ch, boolean isObject) { public static void insertUnboxInsns(MethodVisitor mv, char ch, boolean isObject) {
switch (ch) { switch (ch) {
@ -179,14 +181,14 @@ public class CodeFlow implements Opcodes {
*/ */
public static String createSignatureDescriptor(Method method) { public static String createSignatureDescriptor(Method method) {
Class<?>[] params = method.getParameterTypes(); Class<?>[] params = method.getParameterTypes();
StringBuilder s = new StringBuilder(); StringBuilder sb = new StringBuilder();
s.append("("); sb.append("(");
for (int i = 0, max = params.length; i < max; i++) { for (Class<?> param : params) {
s.append(toJVMDescriptor(params[i])); sb.append(toJVMDescriptor(param));
} }
s.append(")"); sb.append(")");
s.append(toJVMDescriptor(method.getReturnType())); sb.append(toJVMDescriptor(method.getReturnType()));
return s.toString(); return sb.toString();
} }
/** /**
@ -199,13 +201,13 @@ public class CodeFlow implements Opcodes {
*/ */
public static String createSignatureDescriptor(Constructor<?> ctor) { public static String createSignatureDescriptor(Constructor<?> ctor) {
Class<?>[] params = ctor.getParameterTypes(); Class<?>[] params = ctor.getParameterTypes();
StringBuilder s = new StringBuilder(); StringBuilder sb = new StringBuilder();
s.append("("); sb.append("(");
for (int i = 0, max = params.length; i < max; i++) { for (Class<?> param : params) {
s.append(toJVMDescriptor(params[i])); sb.append(toJVMDescriptor(param));
} }
s.append(")V"); sb.append(")V");
return s.toString(); 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 * 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 * includes any necessary trailing semicolon (e.g. Ljava/lang/String; rather than
* Ljava/lang/String) * Ljava/lang/String)
*
* @param clazz a class * @param clazz a class
* @return the JVM descriptor for the 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). * Determine the descriptor for an object instance (or {@code null}).
* @param value an object (possibly null) * @param value an object (possibly {@code null})
* @return the type descriptor for the object (descriptor is "Ljava/lang/Object" for * @return the type descriptor for the object
* null value) * (descriptor is "Ljava/lang/Object" for {@code null} value)
*/ */
public static String toDescriptorFromObject(Object value) { public static String toDescriptorFromObject(Object value) {
if (value == null) { if (value == null) {
return "Ljava/lang/Object"; return "Ljava/lang/Object";
} else { }
else {
return toDescriptor(value.getClass()); return toDescriptor(value.getClass());
} }
} }
/** /**
* @param descriptor type descriptor * @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) { public static boolean isBooleanCompatible(String descriptor) {
return descriptor != null return (descriptor != null && (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean")));
&& (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean"));
} }
/** /**
* @param descriptor type descriptor * @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) { 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 * @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) { public static boolean isPrimitiveArray(String descriptor) {
boolean primitive = true; 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 * 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). * one of the types is in boxed form (i.e. single char descriptor).
* * @return {@code true} if it is possible to get (via boxing) from one descriptor to the other
* @return true if it is possible to get (via boxing) from one descriptor to the other
*/ */
public static boolean areBoxingCompatible(String desc1, String desc2) { public static boolean areBoxingCompatible(String desc1, String desc2) {
if (desc1.equals(desc2)) { if (desc1.equals(desc2)) {
return true; return true;
} }
if (desc1.length()==1) { if (desc1.length() == 1) {
if (desc1.equals("D")) { if (desc1.equals("D")) {
return desc2.equals("Ljava/lang/Double"); return desc2.equals("Ljava/lang/Double");
} }
@ -336,7 +336,7 @@ public class CodeFlow implements Opcodes {
return desc2.equals("Ljava/lang/Boolean"); return desc2.equals("Ljava/lang/Boolean");
} }
} }
else if (desc2.length()==1) { else if (desc2.length() == 1) {
if (desc2.equals("D")) { if (desc2.equals("D")) {
return desc1.equals("Ljava/lang/Double"); 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 * compilation process only (currently) supports certain number types. These are
* double, float, long and int. * double, float, long and int.
* @param descriptor the descriptor for a type * @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) { public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(String descriptor) {
if (descriptor==null) { if (descriptor == null) {
return false; return false;
} }
if (descriptor.length()==1) { if (descriptor.length( )== 1) {
return "DFJZI".indexOf(descriptor.charAt(0))!=-1; return ("DFJZI".indexOf(descriptor.charAt(0)) != -1);
} }
if (descriptor.startsWith("Ljava/lang/")) { if (descriptor.startsWith("Ljava/lang/")) {
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") || 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, * process only (currently) supports certain number types. These are double, float,
* long and int. * long and int.
* @param descriptor the descriptor for a type * @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) { public static boolean isPrimitiveOrUnboxableSupportedNumber(String descriptor) {
if (descriptor==null) { if (descriptor == null) {
return false; return false;
} }
if (descriptor.length()==1) { if (descriptor.length() == 1) {
return "DFJI".indexOf(descriptor.charAt(0))!=-1; return ("DFJI".indexOf(descriptor.charAt(0)) != -1);
} }
if (descriptor.startsWith("Ljava/lang/")) { if (descriptor.startsWith("Ljava/lang/")) {
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") || 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 * @param descriptor a descriptor for a type that should have a primitive representation
* representation
* @return the single character descriptor for a primitive input descriptor * @return the single character descriptor for a primitive input descriptor
*/ */
public static char toPrimitiveTargetDesc(String descriptor) { public static char toPrimitiveTargetDesc(String descriptor) {
if (descriptor.length()==1) { if (descriptor.length() == 1) {
return descriptor.charAt(0); return descriptor.charAt(0);
} }
if (descriptor.equals("Ljava/lang/Double")) { 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 * @param descriptor the descriptor of the type to cast to
*/ */
public static void insertCheckCast(MethodVisitor mv, String descriptor) { public static void insertCheckCast(MethodVisitor mv, String descriptor) {
if (descriptor.length()!=1) { if (descriptor.length() != 1) {
if (descriptor.charAt(0)=='[') { if (descriptor.charAt(0) == '[') {
if (CodeFlow.isPrimitiveArray(descriptor)) { if (isPrimitiveArray(descriptor)) {
mv.visitTypeInsn(CHECKCAST, descriptor); mv.visitTypeInsn(CHECKCAST, descriptor);
} }
else { else {
mv.visitTypeInsn(CHECKCAST, descriptor+";"); mv.visitTypeInsn(CHECKCAST, descriptor + ";");
} }
} }
else { else {
@ -557,14 +556,14 @@ public class CodeFlow implements Opcodes {
} }
else { else {
if (name.charAt(0) != '[') { if (name.charAt(0) != '[') {
return new StringBuilder("L").append(type.getName().replace('.', '/')).toString(); return "L" + type.getName().replace('.', '/');
} }
else { else {
if (name.endsWith(";")) { if (name.endsWith(";")) {
return name.substring(0, name.length() - 1).replace('.', '/'); return name.substring(0, name.length() - 1).replace('.', '/');
} }
else { 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; int typesCount = types.length;
String[] descriptors = new String[typesCount]; String[] descriptors = new String[typesCount];
for (int p = 0; p < typesCount; p++) { for (int p = 0; p < typesCount; p++) {
descriptors[p] = CodeFlow.toDescriptor(types[p]); descriptors[p] = toDescriptor(types[p]);
} }
return descriptors; return descriptors;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2014 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,13 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.expression; package org.springframework.expression.spel;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes; import org.springframework.asm.Opcodes;
import org.springframework.expression.spel.ast.PropertyOrFieldReference; import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.spel.standard.CodeFlow;
/** /**
* A compilable property accessor is able to generate bytecode that represents * 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 { 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(); boolean isCompilable();
/** /**
* Generate the bytecode the performs the access operation into the specified MethodVisitor using * Return the type of the accessed property - may only be known once an access has occurred.
* context information from the codeflow where necessary. */
* @param propertyReference the property reference for which code is being generated 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 mv the Asm method visitor into which code should be generated
* @param codeflow the current state of the expression compiler * @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();
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -30,8 +30,8 @@ import org.springframework.expression.EvaluationException;
public abstract class CompiledExpression { public abstract class CompiledExpression {
/** /**
* Subclasses of CompiledExpression generated by SpelCompiler will provide an implementation of * Subclasses of CompiledExpression generated by SpelCompiler will provide an
* this method. * implementation of this method.
*/ */
public abstract Object getValue(Object target, EvaluationContext context) throws EvaluationException; public abstract Object getValue(Object target, EvaluationContext context) throws EvaluationException;

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -24,16 +24,17 @@ package org.springframework.expression.spel;
* @since 4.1 * @since 4.1
*/ */
public enum SpelCompilerMode { public enum SpelCompilerMode {
/** /**
* The compiler is switched off, this is the default. * 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). * 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. * 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. * In mixed mode, expression evaluate silently switches between interpreted and compiled over time.
@ -41,5 +42,6 @@ public enum SpelCompilerMode {
* type information changing) then that will be caught internally and the system switches back to * type information changing) then that will be caught internally and the system switches back to
* interpreted mode. It may subsequently compile it again later. * interpreted mode. It may subsequently compile it again later.
*/ */
mixed MIXED
} }

View File

@ -30,22 +30,40 @@ import org.springframework.core.SpringProperties;
*/ */
public class SpelParserConfiguration { public class SpelParserConfiguration {
private static final SpelCompilerMode defaultCompilerMode;
static {
String compilerMode = SpringProperties.getProperty("spring.expression.compiler.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 autoGrowNullReferences;
private final boolean autoGrowCollections; private final boolean autoGrowCollections;
private static SpelCompilerMode defaultCompilerMode = SpelCompilerMode.off;
private SpelCompilerMode compilerMode;
private final int maximumAutoGrowSize; private final int maximumAutoGrowSize;
static {
String compilerMode = SpringProperties.getProperty("spring.expression.compiler.mode"); /**
if (compilerMode != null) { * Create a new {@link SpelParserConfiguration} instance with default settings.
defaultCompilerMode = SpelCompilerMode.valueOf(compilerMode.toLowerCase()); */
// System.out.println("SpelCompiler: switched to "+defaultCompilerMode+" mode"); 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);
} }
/** /**
@ -55,7 +73,7 @@ public class SpelParserConfiguration {
* @see #SpelParserConfiguration(boolean, boolean, int) * @see #SpelParserConfiguration(boolean, boolean, int)
*/ */
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) { 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 * @param maximumAutoGrowSize the maximum size that the collection can auto grow
*/ */
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) { public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
this.autoGrowNullReferences = autoGrowNullReferences; this(null, null, autoGrowNullReferences, autoGrowCollections, maximumAutoGrowSize);
this.autoGrowCollections = autoGrowCollections;
this.maximumAutoGrowSize = maximumAutoGrowSize;
this.compilerMode = defaultCompilerMode;
} }
/** /**
* Create a new {@link SpelParserConfiguration} instance.
* @param compilerMode the compiler mode that parsers using this configuration object should use * @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 void setCompilerMode(SpelCompilerMode compilerMode) { public SpelParserConfiguration(SpelCompilerMode compilerMode, ClassLoader compilerClassLoader,
this.compilerMode = compilerMode; boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode);
this.compilerClassLoader = compilerClassLoader;
this.autoGrowNullReferences = autoGrowNullReferences;
this.autoGrowCollections = autoGrowCollections;
this.maximumAutoGrowSize = maximumAutoGrowSize;
} }
/** /**
* @return the configuration mode for parsers using this configuration object * @return the configuration mode for parsers using this configuration object
*/ */
@ -85,6 +112,13 @@ public class SpelParserConfiguration {
return this.compilerMode; 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 * @return {@code true} if {@code null} references should be automatically grown
*/ */

View File

@ -16,7 +16,7 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; 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; import org.springframework.expression.spel.support.BooleanTypedValue;
/** /**

View File

@ -19,9 +19,9 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.CodeFlow;
/** /**
* Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()' * Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'

View File

@ -33,11 +33,11 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter; import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.common.ExpressionUtils; import org.springframework.expression.common.ExpressionUtils;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.ReflectiveConstructorExecutor; import org.springframework.expression.spel.support.ReflectiveConstructorExecutor;
/** /**

View File

@ -20,8 +20,8 @@ import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; 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 * Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value

View File

@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue; 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. * Expression language AST node that represents a float literal.

View File

@ -25,10 +25,10 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter; import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.ReflectionHelper; import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;

View File

@ -32,10 +32,10 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.PropertyAccessor; import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeConverter; import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
/** /**

View File

@ -17,7 +17,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue; 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. * Expression language AST node that represents an integer literal.

View File

@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue; 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. * Expression language AST node that represents a long integer literal.

View File

@ -32,10 +32,10 @@ import org.springframework.expression.ExpressionInvocationTargetException;
import org.springframework.expression.MethodExecutor; import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver; import org.springframework.expression.MethodResolver;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; 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.ReflectiveMethodExecutor;
import org.springframework.expression.spel.support.ReflectiveMethodResolver; import org.springframework.expression.spel.support.ReflectiveMethodResolver;

View File

@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue; 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. * Expression language AST node that represents null.

View File

@ -20,10 +20,10 @@ import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
/** /**

View File

@ -16,16 +16,15 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation; import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
/** /**

View File

@ -19,8 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.Label; import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
/** /**

View File

@ -15,11 +15,12 @@
*/ */
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;

View File

@ -16,11 +16,12 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;

View File

@ -16,11 +16,12 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;

View File

@ -16,11 +16,12 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;

View File

@ -16,15 +16,14 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation; import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
/** /**

View File

@ -16,15 +16,14 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation; import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
/** /**

View File

@ -19,8 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.Label; import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
/** /**

View File

@ -19,10 +19,10 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.Label; import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
/** /**

View File

@ -16,17 +16,16 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.springframework.asm.MethodVisitor;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation; import org.springframework.expression.Operation;
import org.springframework.expression.TypeConverter; import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;

View File

@ -16,12 +16,11 @@
package org.springframework.expression.spel.ast; 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 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.expression.spel.ExpressionState;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;

View File

@ -20,10 +20,10 @@ import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Type; import org.springframework.asm.Type;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;

View File

@ -19,10 +19,10 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.Label; import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.BooleanTypedValue; import org.springframework.expression.spel.support.BooleanTypedValue;
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.asm.MethodVisitor;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.CompilablePropertyAccessor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.PropertyAccessor; import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue; 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.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
/** /**
@ -331,19 +331,14 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
@Override @Override
public boolean isCompilable() { public boolean isCompilable() {
if (this.cachedReadAccessor == null) { return (this.cachedReadAccessor instanceof CompilablePropertyAccessor &&
return false; ((CompilablePropertyAccessor) this.cachedReadAccessor).isCompilable());
}
if (this.cachedReadAccessor instanceof CompilablePropertyAccessor) {
return ((CompilablePropertyAccessor)this.cachedReadAccessor).isCompilable();
}
return false;
} }
@Override @Override
public void generateCode(MethodVisitor mv,CodeFlow codeflow) { public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
((CompilablePropertyAccessor)this.cachedReadAccessor).generateCode(this, mv, codeflow); ((CompilablePropertyAccessor) this.cachedReadAccessor).generateCode(this.name, mv, codeflow);
codeflow.pushDescriptor(exitTypeDescriptor); codeflow.pushDescriptor(this.exitTypeDescriptor);
} }

View File

@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue; 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. * Expression language AST node that represents a real literal.

View File

@ -21,11 +21,11 @@ import org.springframework.asm.Opcodes;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.common.ExpressionUtils; import org.springframework.expression.common.ExpressionUtils;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.standard.CodeFlow;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -58,6 +58,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
*/ */
protected String exitTypeDescriptor; protected String exitTypeDescriptor;
public SpelNodeImpl(int pos, SpelNodeImpl... operands) { public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
this.pos = pos; this.pos = pos;
// pos combines start and end so can never be zero because tokens cannot be zero length // 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 * 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 * be different but will typically involve checking whether the exit type descriptor
* of the node is known and any relevant child nodes are compilable. * of the node is known and any relevant child nodes are compilable.
*
* @return true if this node can be compiled to bytecode * @return true if this node can be compiled to bytecode
*/ */
public boolean isCompilable() { 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 * the current expression being compiled is available in the codeflow object. For
* example it will include information about the type of the object currently * example it will include information about the type of the object currently
* on the stack. * on the stack.
*
* @param mv the ASM MethodVisitor into which code should be generated * @param mv the ASM MethodVisitor into which code should be generated
* @param codeflow a context object with info about what is on the stack * @param codeflow a context object with info about what is on the stack
*/ */

View File

@ -18,7 +18,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue; 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. * Expression language AST node that represents a string literal.

View File

@ -20,10 +20,10 @@ import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage; import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.standard.CodeFlow;
/** /**
* Represents a ternary expression, for example: "someCheck()?true:false". * Represents a ternary expression, for example: "someCheck()?true:false".

View File

@ -22,8 +22,8 @@ import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Type; import org.springframework.asm.Type;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; 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)" * Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)"

View File

@ -19,9 +19,9 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException; 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* * Represents a variable reference, eg. #someVar. Note this is different to a *local*

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.SpelMessage;
import org.springframework.expression.spel.SpelParseException; import org.springframework.expression.spel.SpelParseException;
import org.springframework.expression.spel.SpelParserConfiguration; 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.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -84,13 +125,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
this.constructedNodes.clear(); this.constructedNodes.clear();
SpelNodeImpl ast = eatExpression(); SpelNodeImpl ast = eatExpression();
if (moreTokens()) { 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()); Assert.isTrue(this.constructedNodes.isEmpty());
return new SpelExpression(expressionString, ast, this.configuration); return new SpelExpression(expressionString, ast, this.configuration);
} }
catch (InternalParseException ipe) { catch (InternalParseException ex) {
throw ipe.getCause(); throw ex.getCause();
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * 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.io.IOException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Collections;
import java.util.Map; 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.ClassWriter;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes; import org.springframework.asm.Opcodes;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.CompiledExpression; import org.springframework.expression.spel.CompiledExpression;
import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.ast.SpelNodeImpl; import org.springframework.expression.spel.ast.SpelNodeImpl;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
/** /**
* A SpelCompiler will take a regular parsed expression and create (and load) a class * 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 * 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. * 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 * <p>The SpelCompiler is not currently handling all expression types but covers many of
* common cases. The framework is extensible to cover more cases in the future. For * 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 * 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 * 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 * 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 * 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 * compiled expression will fail - like a ClassCastException would occur if passing data
* of an unexpected type in a regular Java program. * 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 * 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 * data. Due to these cases the compiler is something that must be selectively turned on
* for an associated SpelExpressionParser (through the {@link SpelParserConfiguration} * for an associated SpelExpressionParser (through the {@link SpelParserConfiguration}
* object), it is not on by default. * object), it is not on by default.
* <p> *
* Individual expressions can be compiled by calling * <p>Individual expressions can be compiled by calling {@code SpelCompiler.compile(expression)}.
* <tt>SpelCompiler.compile(expression)</tt>.
* *
* @author Andy Clement * @author Andy Clement
* @since 4.1 * @since 4.1
*/ */
public class SpelCompiler implements Opcodes { public class SpelCompiler implements Opcodes {
// Default number of times to interpret an expression before compiling it private static final Log logger = LogFactory.getLog(SpelCompiler.class);
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;
// A compiler is created for each classloader, it manages a child class loader of that // 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. // 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 // 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) { private SpelCompiler(ClassLoader classloader) {
this.ccl = new ChildClassLoader(classloader); this.ccl = new ChildClassLoader(classloader);
this.suffixId = 1;
} }
/** /**
* Attempt compilation of the supplied expression. A check is * Attempt compilation of the supplied expression. A check is
* made to see if it is compilable before compilation proceeds. The * 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) { public CompiledExpression compile(SpelNodeImpl expression) {
if (expression.isCompilable()) { if (expression.isCompilable()) {
if (verbose) { if (logger.isDebugEnabled()) {
System.out.println("SpEL: compiling " + expression.toStringAST()); logger.debug("SpEL: compiling " + expression.toStringAST());
} }
Class<? extends CompiledExpression> clazz = createExpressionClass(expression); Class<? extends CompiledExpression> clazz = createExpressionClass(expression);
try { try {
CompiledExpression instance = clazz.newInstance(); return clazz.newInstance();
return instance;
} }
catch (InstantiationException ie) { catch (Exception ex) {
ie.printStackTrace(); throw new IllegalStateException("Failed to instantiate CompiledExpression", ex);
}
catch (IllegalAccessException iae) {
iae.printStackTrace();
} }
} }
else { else {
if (verbose) { if (logger.isDebugEnabled()) {
System.out.println("SpEL: unable to compile " + expression.toStringAST()); logger.debug("SpEL: unable to compile " + expression.toStringAST());
} }
} }
return null; return null;
} }
private synchronized int getNextSuffix() { private int getNextSuffix() {
return suffixId++; return this.suffixId.incrementAndGet();
} }
/** /**
@ -152,7 +128,6 @@ public class SpelCompiler implements Opcodes {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) { private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
// Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression' // Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression'
String clazzName = "spel/Ex" + getNextSuffix(); String clazzName = "spel/Ex" + getNextSuffix();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
@ -179,7 +154,7 @@ public class SpelCompiler implements Opcodes {
expressionToCompile.generateCode(mv,codeflow); expressionToCompile.generateCode(mv,codeflow);
CodeFlow.insertBoxIfNecessary(mv,codeflow.lastDescriptor()); CodeFlow.insertBoxIfNecessary(mv,codeflow.lastDescriptor());
if (codeflow.lastDescriptor() == "V") { if ("V".equals(codeflow.lastDescriptor())) {
mv.visitInsn(ACONST_NULL); mv.visitInsn(ACONST_NULL);
} }
mv.visitInsn(ARETURN); mv.visitInsn(ARETURN);
@ -190,44 +165,79 @@ public class SpelCompiler implements Opcodes {
byte[] data = cw.toByteArray(); byte[] data = cw.toByteArray();
// TODO need to make this conditionally occur based on a debug flag // TODO need to make this conditionally occur based on a debug flag
// dump(expressionToCompile.toStringAST(), clazzName, data); // dump(expressionToCompile.toStringAST(), clazzName, data);
Class<? extends CompiledExpression> clazz = (Class<? extends CompiledExpression>) ccl.defineClass(clazzName.replaceAll("/","."),data); return (Class<? extends CompiledExpression>) ccl.defineClass(clazzName.replaceAll("/","."),data);
return clazz; }
/**
* 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 * Request that an attempt is made to compile the specified expression. It may fail if
* yet hooked in, needs conditionally calling based on a sys prop. * 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 expressionText the text of the expression compiled
* @param name the name of the class being used for the compiled expression * @param name the name of the class being used for the compiled expression
* @param bytecode the bytecode for the generated class * @param bytecode the bytecode for the generated class
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static void dump(String expressionText, String name, byte[] bytecode) { private static void dump(String expressionText, String name, byte[] bytecode) {
name = name.replace('.', '/'); String nameToUse = name.replace('.', '/');
String dir = ""; String dir = (nameToUse.indexOf('/') != -1 ? nameToUse.substring(0, nameToUse.lastIndexOf('/')) : "");
if (name.indexOf('/') != -1) { String dumpLocation = null;
dir = name.substring(0, name.lastIndexOf('/'));
}
String dumplocation = null;
try { try {
File tempfile = null; File tempFile = File.createTempFile("tmp", null);
tempfile = File.createTempFile("tmp", null); dumpLocation = tempFile + File.separator + nameToUse + ".class";
tempfile.delete(); tempFile.delete();
File f = new File(tempfile, dir); File f = new File(tempFile, dir);
f.mkdirs(); f.mkdirs();
dumplocation = tempfile + File.separator + name + ".class"; if (logger.isDebugEnabled()) {
System.out.println("Expression '" + expressionText + "' compiled code dumped to " logger.debug("Expression '" + expressionText + "' compiled code dumped to " + dumpLocation);
+ dumplocation); }
f = new File(dumplocation); f = new File(dumpLocation);
FileOutputStream fos = new FileOutputStream(f); FileOutputStream fos = new FileOutputStream(f);
fos.write(bytecode); fos.write(bytecode);
fos.flush(); fos.flush();
fos.close(); fos.close();
} }
catch (IOException ioe) { catch (IOException ex) {
throw new IllegalStateException("Unexpected problem dumping class " throw new IllegalStateException("Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
+ name + " into " + dumplocation, ioe);
} }
} }
@ -246,32 +256,6 @@ public class SpelCompiler implements Opcodes {
public Class<?> defineClass(String name, byte[] bytes) { public Class<?> defineClass(String name, byte[] bytes) {
return super.defineClass(name, bytes, 0, bytes.length); 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();
}
}
} }

View File

@ -44,25 +44,32 @@ import org.springframework.util.Assert;
*/ */
public class SpelExpression implements Expression { 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; private final String expression;
// Holds the compiled form of the expression (if it has been compiled) private final SpelNodeImpl ast;
private CompiledExpression compiledAst;
private SpelNodeImpl ast;
private final SpelParserConfiguration configuration; private final SpelParserConfiguration configuration;
// the default context is used if no override is supplied by the user // 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 // Count of many times as the expression been interpreted - can trigger compilation
// when certain limit reached // 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 // 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. // 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 // implementing Expression
@Override @Override
public Object getValue() throws EvaluationException { public Object getValue() throws EvaluationException {
Object result = null; Object result;
if (compiledAst != null) { if (this.compiledAst != null) {
try { try {
return this.compiledAst.getValue(null,null); return this.compiledAst.getValue(null,null);
} catch (Throwable t) { }
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted // If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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 @Override
public Object getValue(Object rootObject) throws EvaluationException { public Object getValue(Object rootObject) throws EvaluationException {
Object result = null; Object result;
if (compiledAst!=null) { if (this.compiledAst != null) {
try { try {
return this.compiledAst.getValue(rootObject,null); return this.compiledAst.getValue(rootObject,null);
} catch (Throwable t) { }
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted // If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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") @SuppressWarnings("unchecked")
@Override @Override
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException { public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
if (compiledAst!=null) { if (this.compiledAst != null) {
try { try {
Object result = this.compiledAst.getValue(null,null); Object result = this.compiledAst.getValue(null,null);
if (expectedResultType == null) { if (expectedResultType == null) {
return (T)result; return (T)result;
} else { }
else {
return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType); return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType);
} }
} catch (Throwable t) { }
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted // If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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") @SuppressWarnings("unchecked")
@Override @Override
public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws EvaluationException { public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws EvaluationException {
if (compiledAst!=null) { if (this.compiledAst != null) {
try { try {
Object result = this.compiledAst.getValue(rootObject,null); Object result = this.compiledAst.getValue(rootObject, null);
if (expectedResultType == null) { if (expectedResultType == null) {
return (T)result; return (T)result;
} else { }
else {
return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType); return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType);
} }
} catch (Throwable t) { }
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted // If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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 { try {
Object result = this.compiledAst.getValue(null,context); Object result = this.compiledAst.getValue(null,context);
return result; return result;
} catch (Throwable t) { }
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted // If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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 @Override
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException { public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required"); Assert.notNull(context, "The EvaluationContext is required");
if (compiledAst!=null) { if (this.compiledAst != null) {
try { try {
return this.compiledAst.getValue(rootObject,context); return this.compiledAst.getValue(rootObject,context);
} catch (Throwable t) { }
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted // If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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") @SuppressWarnings("unchecked")
@Override @Override
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException { public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
if (compiledAst!=null) { if (this.compiledAst != null) {
try { try {
Object result = this.compiledAst.getValue(null,context); Object result = this.compiledAst.getValue(null,context);
if (expectedResultType!=null) { if (expectedResultType != null) {
return (T) result;
} else {
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType); 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 running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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") @SuppressWarnings("unchecked")
@Override @Override
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) throws EvaluationException { public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) throws EvaluationException {
if (compiledAst!=null) { if (this.compiledAst != null) {
try { try {
Object result = this.compiledAst.getValue(rootObject,context); Object result = this.compiledAst.getValue(rootObject,context);
if (expectedResultType!=null) { if (expectedResultType != null) {
return (T) result;
} else {
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType); 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 running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.mixed) { if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
interpretedCount = 0; this.interpretedCount = 0;
compiledAst = null; this.compiledAst = null;
} }
else { else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller // 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"); Assert.notNull(context, "The EvaluationContext is required");
ExpressionState eState = new ExpressionState(context, this.configuration); ExpressionState eState = new ExpressionState(context, this.configuration);
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor(); TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor != null ? typeDescriptor.getType() : null; return (typeDescriptor != null ? typeDescriptor.getType() : null);
} }
@Override @Override
public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException { public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration); ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration);
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor(); TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor != null ? typeDescriptor.getType() : null; return (typeDescriptor != null ? typeDescriptor.getType() : null);
} }
@Override @Override
@ -379,6 +418,7 @@ public class SpelExpression implements Expression {
this.ast.setValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration), value); this.ast.setValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration), value);
} }
// impl only // impl only
/** /**
@ -386,17 +426,17 @@ public class SpelExpression implements Expression {
* @param expressionState the expression state used to determine compilation mode * @param expressionState the expression state used to determine compilation mode
*/ */
private void checkCompile(ExpressionState expressionState) { private void checkCompile(ExpressionState expressionState) {
interpretedCount++; this.interpretedCount++;
SpelCompilerMode compilerMode = expressionState.getConfiguration().getCompilerMode(); SpelCompilerMode compilerMode = expressionState.getConfiguration().getCompilerMode();
if (compilerMode != SpelCompilerMode.off) { if (compilerMode != SpelCompilerMode.OFF) {
if (compilerMode == SpelCompilerMode.immediate) { if (compilerMode == SpelCompilerMode.IMMEDIATE) {
if (interpretedCount > 1) { if (this.interpretedCount > 1) {
compileExpression(); compileExpression();
} }
} }
else { else {
// compilerMode = SpelCompilerMode.mixed // compilerMode = SpelCompilerMode.MIXED
if (interpretedCount > SpelCompiler.interpretedCountThreshold) { if (this.interpretedCount > INTERPRETED_COUNT_THRESHOLD) {
compileExpression(); compileExpression();
} }
} }
@ -410,20 +450,20 @@ public class SpelExpression implements Expression {
* no longer considered suitable for compilation. * no longer considered suitable for compilation.
*/ */
public boolean compileExpression() { public boolean compileExpression() {
if (failedAttempts > 100) { if (this.failedAttempts > FAILED_ATTEMPTS_THRESHOLD) {
// Don't try again // Don't try again
return false; return false;
} }
if (this.compiledAst == null) { if (this.compiledAst == null) {
synchronized (expression) { synchronized (this.expression) {
// Possibly compiled by another thread before this thread got into the // Possibly compiled by another thread before this thread got into the sync block
// sync block
if (this.compiledAst != null) { if (this.compiledAst != null) {
return true; 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) { if (this.compiledAst == null) {
failedAttempts++; this.failedAttempts++;
} }
} }
} }
@ -458,25 +498,6 @@ public class SpelExpression implements Expression {
return this.ast.toStringAST(); 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) { private TypedValue toTypedValue(Object object) {
if (object == null) { if (object == null) {
return TypedValue.NULL; return TypedValue.NULL;

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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() { 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 * @param configuration custom configuration options
*/ */
public SpelExpressionParser(SpelParserConfiguration configuration) { 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 @Override
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
} }
public SpelExpression parseRaw(String expressionString) throws ParseException {
return doParseExpression(expressionString, null);
}
} }

View File

@ -35,13 +35,12 @@ import org.springframework.core.convert.Property;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.style.ToStringCreator; import org.springframework.core.style.ToStringCreator;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.CompilablePropertyAccessor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.PropertyAccessor; import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.PropertyOrFieldReference; import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.standard.CodeFlow; import org.springframework.expression.spel.CompilablePropertyAccessor;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -370,10 +369,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
Method[] methods = getSortedClassMethods(clazz); Method[] methods = getSortedClassMethods(clazz);
for (String methodSuffix : methodSuffixes) { for (String methodSuffix : methodSuffixes) {
for (Method method : methods) { for (Method method : methods) {
if (method.getName().equals(prefix + methodSuffix) if (method.getName().equals(prefix + methodSuffix) &&
&& method.getParameterTypes().length == numberOfParams method.getParameterTypes().length == numberOfParams &&
&& (!mustBeStatic || Modifier.isStatic(method.getModifiers())) (!mustBeStatic || Modifier.isStatic(method.getModifiers())) &&
&& (requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) { (requiredReturnTypes.isEmpty() || requiredReturnTypes.contains(method.getReturnType()))) {
return method; return method;
} }
} }
@ -650,16 +649,6 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
throw new AccessException("Neither getter nor field found for property '" + name + "'"); 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 @Override
public boolean canWrite(EvaluationContext context, Object target, String name) { public boolean canWrite(EvaluationContext context, Object target, String name) {
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor"); throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
@ -680,9 +669,18 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
} }
@Override @Override
public void generateCode(PropertyOrFieldReference propertyReference, MethodVisitor mv,CodeFlow codeflow) { public Class<?> getPropertyType() {
boolean isStatic = Modifier.isStatic(member.getModifiers()); 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 descriptor = codeflow.lastDescriptor();
String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.','/'); String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.','/');
if (!isStatic) { if (!isStatic) {
@ -694,9 +692,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
} }
} }
if (member instanceof Field) { if (member instanceof Field) {
mv.visitFieldInsn(isStatic?GETSTATIC:GETFIELD,memberDeclaringClassSlashedDescriptor,member.getName(),CodeFlow.toJVMDescriptor(((Field) member).getType())); mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor,
} else { member.getName(), CodeFlow.toJVMDescriptor(((Field) member).getType()));
mv.visitMethodInsn(isStatic?INVOKESTATIC:INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, member.getName(),CodeFlow.createSignatureDescriptor((Method)member),false); }
else {
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor,
member.getName(), CodeFlow.createSignatureDescriptor((Method) member),false);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 List<MethodResolver> methodResolvers;
private BeanResolver beanResolver;
private ReflectiveMethodResolver reflectiveMethodResolver; private ReflectiveMethodResolver reflectiveMethodResolver;
private List<PropertyAccessor> propertyAccessors; private List<PropertyAccessor> propertyAccessors;
@ -68,15 +70,12 @@ public class StandardEvaluationContext implements EvaluationContext {
private final Map<String, Object> variables = new HashMap<String, Object>(); private final Map<String, Object> variables = new HashMap<String, Object>();
private BeanResolver beanResolver;
public StandardEvaluationContext() { public StandardEvaluationContext() {
setRootObject(null); setRootObject(null);
} }
public StandardEvaluationContext(Object rootObject) { public StandardEvaluationContext(Object rootObject) {
this();
setRootObject(rootObject); setRootObject(rootObject);
} }

View File

@ -25,18 +25,16 @@ import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.junit.Test; import org.junit.Test;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.CompilablePropertyAccessor;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ast.CompoundExpression; import org.springframework.expression.spel.ast.CompoundExpression;
import org.springframework.expression.spel.ast.OpLT; 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.SpelNodeImpl;
import org.springframework.expression.spel.ast.Ternary; 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.SpelCompiler;
import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
@ -2175,6 +2173,35 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertNull(vc); assertNull(vc);
} }
@Test
public void variantGetter() throws Exception {
Payload2Holder holder = new Payload2Holder();
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.addPropertyAccessor(new MyAccessor());
expression = parser.parseExpression("payload2.var1");
Object v = expression.getValue(ctx,holder);
assertEquals("abc",v);
// // time it interpreted
// long stime = System.currentTimeMillis();
// for (int i=0;i<100000;i++) {
// v = expression.getValue(ctx,holder);
// }
// System.out.println((System.currentTimeMillis()-stime));
//
assertCanCompile(expression);
v = expression.getValue(ctx,holder);
assertEquals("abc",v);
//
// // time it compiled
// stime = System.currentTimeMillis();
// for (int i=0;i<100000;i++) {
// v = expression.getValue(ctx,holder);
// }
// System.out.println((System.currentTimeMillis()-stime));
}
static class MyAccessor implements CompilablePropertyAccessor { static class MyAccessor implements CompilablePropertyAccessor {
private Method method; private Method method;
@ -2206,11 +2233,18 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
} }
@Override @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) {
if (method == null) { if (method == null) {
try { try {
method = Payload2.class.getDeclaredMethod("getField", String.class); method = Payload2.class.getDeclaredMethod("getField", String.class);
} catch (Exception e) {} }
catch (Exception e) {
}
} }
String descriptor = codeflow.lastDescriptor(); String descriptor = codeflow.lastDescriptor();
String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/'); String memberDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.','/');
@ -2220,45 +2254,11 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
} }
mv.visitLdcInsn(propertyReference.getName()); mv.visitLdcInsn(propertyName);
mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createSignatureDescriptor(method),false); 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();
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.addPropertyAccessor(new MyAccessor());
expression = parser.parseExpression("payload2.var1");
Object v = expression.getValue(ctx,holder);
assertEquals("abc",v);
// // time it interpreted
// long stime = System.currentTimeMillis();
// for (int i=0;i<100000;i++) {
// v = expression.getValue(ctx,holder);
// }
// System.out.println((System.currentTimeMillis()-stime));
//
assertCanCompile(expression);
v = expression.getValue(ctx,holder);
assertEquals("abc",v);
//
// // time it compiled
// stime = System.currentTimeMillis();
// for (int i=0;i<100000;i++) {
// v = expression.getValue(ctx,holder);
// }
// System.out.println((System.currentTimeMillis()-stime));
}
static class CompilableMapAccessor implements CompilablePropertyAccessor { static class CompilableMapAccessor implements CompilablePropertyAccessor {
@ -2295,40 +2295,23 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
return new Class[] {Map.class}; 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 @Override
public boolean isCompilable() { public boolean isCompilable() {
return true; return true;
} }
@Override @Override
public void generateCode(PropertyOrFieldReference propertyReference, public Class<?> getPropertyType() {
MethodVisitor mv, CodeFlow codeflow) { return Object.class;
}
@Override
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow codeflow) {
String descriptor = codeflow.lastDescriptor(); String descriptor = codeflow.lastDescriptor();
if (descriptor == null) { if (descriptor == null) {
codeflow.loadTarget(mv); codeflow.loadTarget(mv);
} }
mv.visitLdcInsn(propertyReference.getName()); mv.visitLdcInsn(propertyName);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get","(Ljava/lang/Object;)Ljava/lang/Object;",true); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get","(Ljava/lang/Object;)Ljava/lang/Object;",true);
// if (method == null) { // if (method == null) {
@ -2348,12 +2331,27 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
// mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createDescriptor(method)); // mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createDescriptor(method));
// 6: invokeinterface #6, 2; //InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object; // 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 + "'";
}
} }