|
|
|
@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
|
|
|
|
|
/* |
|
|
|
|
* Copyright 2002-2018 the original author or authors. |
|
|
|
|
* Copyright 2002-2020 the original author or authors. |
|
|
|
|
* |
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -16,6 +16,8 @@
@@ -16,6 +16,8 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.expression.spel.standard; |
|
|
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger; |
|
|
|
|
|
|
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
|
import org.springframework.expression.EvaluationContext; |
|
|
|
|
import org.springframework.expression.EvaluationException; |
|
|
|
@ -65,15 +67,15 @@ public class SpelExpression implements Expression {
@@ -65,15 +67,15 @@ public class SpelExpression implements Expression {
|
|
|
|
|
|
|
|
|
|
// Holds the compiled form of the expression (if it has been compiled)
|
|
|
|
|
@Nullable |
|
|
|
|
private CompiledExpression compiledAst; |
|
|
|
|
private volatile CompiledExpression compiledAst; |
|
|
|
|
|
|
|
|
|
// Count of many times as the expression been interpreted - can trigger compilation
|
|
|
|
|
// when certain limit reached
|
|
|
|
|
private volatile int interpretedCount = 0; |
|
|
|
|
private final AtomicInteger interpretedCount = new AtomicInteger(0); |
|
|
|
|
|
|
|
|
|
// The number of times compilation was attempted and failed - enables us to eventually
|
|
|
|
|
// give up trying to compile it when it just doesn't seem to be possible.
|
|
|
|
|
private volatile int failedAttempts = 0; |
|
|
|
|
private final AtomicInteger failedAttempts = new AtomicInteger(0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -116,16 +118,17 @@ public class SpelExpression implements Expression {
@@ -116,16 +118,17 @@ public class SpelExpression implements Expression {
|
|
|
|
|
@Override |
|
|
|
|
@Nullable |
|
|
|
|
public Object getValue() throws EvaluationException { |
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
EvaluationContext context = getEvaluationContext(); |
|
|
|
|
return this.compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
return compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -144,10 +147,11 @@ public class SpelExpression implements Expression {
@@ -144,10 +147,11 @@ public class SpelExpression implements Expression {
|
|
|
|
|
@Override |
|
|
|
|
@Nullable |
|
|
|
|
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException { |
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
EvaluationContext context = getEvaluationContext(); |
|
|
|
|
Object result = this.compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
Object result = compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
if (expectedResultType == null) { |
|
|
|
|
return (T) result; |
|
|
|
|
} |
|
|
|
@ -159,8 +163,8 @@ public class SpelExpression implements Expression {
@@ -159,8 +163,8 @@ public class SpelExpression implements Expression {
|
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -179,15 +183,16 @@ public class SpelExpression implements Expression {
@@ -179,15 +183,16 @@ public class SpelExpression implements Expression {
|
|
|
|
|
@Override |
|
|
|
|
@Nullable |
|
|
|
|
public Object getValue(Object rootObject) throws EvaluationException { |
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
return this.compiledAst.getValue(rootObject, getEvaluationContext()); |
|
|
|
|
return compiledAst.getValue(rootObject, getEvaluationContext()); |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -207,9 +212,10 @@ public class SpelExpression implements Expression {
@@ -207,9 +212,10 @@ public class SpelExpression implements Expression {
|
|
|
|
|
@Override |
|
|
|
|
@Nullable |
|
|
|
|
public <T> T getValue(Object rootObject, @Nullable Class<T> expectedResultType) throws EvaluationException { |
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
Object result = this.compiledAst.getValue(rootObject, getEvaluationContext()); |
|
|
|
|
Object result = compiledAst.getValue(rootObject, getEvaluationContext()); |
|
|
|
|
if (expectedResultType == null) { |
|
|
|
|
return (T)result; |
|
|
|
|
} |
|
|
|
@ -221,8 +227,8 @@ public class SpelExpression implements Expression {
@@ -221,8 +227,8 @@ public class SpelExpression implements Expression {
|
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -244,15 +250,16 @@ public class SpelExpression implements Expression {
@@ -244,15 +250,16 @@ public class SpelExpression implements Expression {
|
|
|
|
|
public Object getValue(EvaluationContext context) throws EvaluationException { |
|
|
|
|
Assert.notNull(context, "EvaluationContext is required"); |
|
|
|
|
|
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
return this.compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
return compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -273,9 +280,10 @@ public class SpelExpression implements Expression {
@@ -273,9 +280,10 @@ public class SpelExpression implements Expression {
|
|
|
|
|
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType) throws EvaluationException { |
|
|
|
|
Assert.notNull(context, "EvaluationContext is required"); |
|
|
|
|
|
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
Object result = this.compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
Object result = compiledAst.getValue(context.getRootObject().getValue(), context); |
|
|
|
|
if (expectedResultType != null) { |
|
|
|
|
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType); |
|
|
|
|
} |
|
|
|
@ -286,8 +294,8 @@ public class SpelExpression implements Expression {
@@ -286,8 +294,8 @@ public class SpelExpression implements Expression {
|
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -307,15 +315,16 @@ public class SpelExpression implements Expression {
@@ -307,15 +315,16 @@ public class SpelExpression implements Expression {
|
|
|
|
|
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException { |
|
|
|
|
Assert.notNull(context, "EvaluationContext is required"); |
|
|
|
|
|
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
return this.compiledAst.getValue(rootObject, context); |
|
|
|
|
return compiledAst.getValue(rootObject, context); |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -338,9 +347,10 @@ public class SpelExpression implements Expression {
@@ -338,9 +347,10 @@ public class SpelExpression implements Expression {
|
|
|
|
|
|
|
|
|
|
Assert.notNull(context, "EvaluationContext is required"); |
|
|
|
|
|
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
try { |
|
|
|
|
Object result = this.compiledAst.getValue(rootObject, context); |
|
|
|
|
Object result = compiledAst.getValue(rootObject, context); |
|
|
|
|
if (expectedResultType != null) { |
|
|
|
|
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType); |
|
|
|
|
} |
|
|
|
@ -351,8 +361,8 @@ public class SpelExpression implements Expression {
@@ -351,8 +361,8 @@ public class SpelExpression implements Expression {
|
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
// If running in mixed mode, revert to interpreted
|
|
|
|
|
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) { |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
|
|
|
|
@ -473,48 +483,58 @@ public class SpelExpression implements Expression {
@@ -473,48 +483,58 @@ public class SpelExpression implements Expression {
|
|
|
|
|
* @param expressionState the expression state used to determine compilation mode |
|
|
|
|
*/ |
|
|
|
|
private void checkCompile(ExpressionState expressionState) { |
|
|
|
|
this.interpretedCount++; |
|
|
|
|
this.interpretedCount.incrementAndGet(); |
|
|
|
|
SpelCompilerMode compilerMode = expressionState.getConfiguration().getCompilerMode(); |
|
|
|
|
if (compilerMode != SpelCompilerMode.OFF) { |
|
|
|
|
if (compilerMode == SpelCompilerMode.IMMEDIATE) { |
|
|
|
|
if (this.interpretedCount > 1) { |
|
|
|
|
if (this.interpretedCount.get() > 1) { |
|
|
|
|
compileExpression(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// compilerMode = SpelCompilerMode.MIXED
|
|
|
|
|
if (this.interpretedCount > INTERPRETED_COUNT_THRESHOLD) { |
|
|
|
|
if (this.interpretedCount.get() > INTERPRETED_COUNT_THRESHOLD) { |
|
|
|
|
compileExpression(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Perform expression compilation. This will only succeed once exit descriptors for all nodes have |
|
|
|
|
* been determined. If the compilation fails and has failed more than 100 times the expression is |
|
|
|
|
* no longer considered suitable for compilation. |
|
|
|
|
* Perform expression compilation. This will only succeed once exit descriptors for |
|
|
|
|
* all nodes have been determined. If the compilation fails and has failed more than |
|
|
|
|
* 100 times the expression is no longer considered suitable for compilation. |
|
|
|
|
* @return whether this expression has been successfully compiled |
|
|
|
|
*/ |
|
|
|
|
public boolean compileExpression() { |
|
|
|
|
if (this.failedAttempts > FAILED_ATTEMPTS_THRESHOLD) { |
|
|
|
|
CompiledExpression compiledAst = this.compiledAst; |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
// Previously compiled
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (this.failedAttempts.get() > FAILED_ATTEMPTS_THRESHOLD) { |
|
|
|
|
// Don't try again
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
if (this.compiledAst == null) { |
|
|
|
|
synchronized (this.expression) { |
|
|
|
|
// Possibly compiled by another thread before this thread got into the sync block
|
|
|
|
|
|
|
|
|
|
synchronized (this) { |
|
|
|
|
if (this.compiledAst != null) { |
|
|
|
|
// Compiled by another thread before this thread got into the sync block
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader()); |
|
|
|
|
this.compiledAst = compiler.compile(this.ast); |
|
|
|
|
if (this.compiledAst == null) { |
|
|
|
|
this.failedAttempts++; |
|
|
|
|
compiledAst = compiler.compile(this.ast); |
|
|
|
|
if (compiledAst != null) { |
|
|
|
|
// Successfully compiled
|
|
|
|
|
this.compiledAst = compiledAst; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Failed to compile
|
|
|
|
|
this.failedAttempts.incrementAndGet(); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return (this.compiledAst != null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -524,8 +544,8 @@ public class SpelExpression implements Expression {
@@ -524,8 +544,8 @@ public class SpelExpression implements Expression {
|
|
|
|
|
*/ |
|
|
|
|
public void revertToInterpreted() { |
|
|
|
|
this.compiledAst = null; |
|
|
|
|
this.interpretedCount = 0; |
|
|
|
|
this.failedAttempts = 0; |
|
|
|
|
this.interpretedCount.set(0); |
|
|
|
|
this.failedAttempts.set(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|