Limit result size of BigDecimal/BigInteger power operations in SpEL

This commit introduces a configurable limit on the estimated result size
of BigDecimal and BigInteger power operations within SpEL expressions.
The estimated result size in bits is computed as the product of the base
value's bit length and the exponent. If this limit is exceeded, a
SpelEvaluationException is thrown.

The limit defaults to 1,000,000 bits, which is approximately equivalent
to a decimal number with 300,000 digits, and can be configured either
on a per-use-case basis via the new maximumBigPowerBits constructor
argument in SpelParserConfiguration or globally as a JVM system
property or Spring property named `spring.expression.maxBigPowerBits`.
Parsers intended for trusted internal expressions may supply
Integer.MAX_VALUE to remove the limit entirely.

Closes ch-37034
This commit is contained in:
Sam Brannen
2026-08-14 09:19:58 +02:00
committed by Brian Clozel
parent ee1874ac52
commit 8a92c19e4d
8 changed files with 259 additions and 24 deletions
@@ -74,6 +74,12 @@ expressions used in XML bean definitions, `@Value`, etc.
| The mode to use when compiling expressions for the
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
| `spring.expression.maxBigPowerBits`
| The default maximum number of bits permitted in the result of a `BigDecimal` or
`BigInteger` power operation within a
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
expression.
| `spring.expression.maxOperations`
| The default maximum number of operations permitted during
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
@@ -574,6 +574,19 @@ property or Spring property named `spring.expression.maxOperations` to the maxim
of operations required by your application (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
In addition, the result of a `BigDecimal` or `BigInteger` power operation within a SpEL
expression cannot exceed 1,000,000 bits by default approximately equivalent to a
decimal number with 300,000 digits. Power operations involving large base values or large
exponents can be computationally expensive, and this limit ensures that evaluations
remain bounded; however, the `maximumBigPowerBits` value is configurable. If you create a
`SpelExpressionParser` programmatically (the recommended approach), you can specify a
custom `maximumBigPowerBits` value when creating the `SpelParserConfiguration` that you
provide to the `SpelExpressionParser`. To remove this limit entirely, pass
`Integer.MAX_VALUE` as the `maximumBigPowerBits` value. If you are not able to configure
an explicit value for `maximumBigPowerBits` via `SpelParserConfiguration`, you can set a
JVM system property or Spring property named `spring.expression.maxBigPowerBits` to the
maximum result size in bits (see xref:appendix.adoc#appendix-spring-properties[Supported
Spring Properties]).
[[expressions-spel-compilation]]
== SpEL Compilation
@@ -44,6 +44,7 @@ import org.jspecify.annotations.Nullable;
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#STRICT_LOCKING_PROPERTY_NAME
* @see org.springframework.core.env.AbstractEnvironment#IGNORE_GETENV_PROPERTY_NAME
* @see org.springframework.expression.spel.SpelParserConfiguration#SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see org.springframework.expression.spel.SpelParserConfiguration#SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
* @see org.springframework.expression.spel.SpelParserConfiguration#SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see org.springframework.jdbc.core.StatementCreatorUtils#IGNORE_GETPARAMETERTYPE_PROPERTY_NAME
* @see org.springframework.jndi.JndiLocatorDelegate#IGNORE_JNDI_PROPERTY_NAME
@@ -307,7 +307,12 @@ public enum SpelMessage {
/** @since 6.2.19 */
MAX_OPERATIONS_EXCEEDED(Kind.ERROR, 1085,
"SpEL expression evaluation exceeded the threshold of ''{0}'' operations");
"SpEL expression evaluation exceeded the threshold of ''{0}'' operations"),
/** @since 7.0.9 */
MAX_BIG_POWER_RESULT_EXCEEDED(Kind.ERROR, 1086,
"BigDecimal/BigInteger power operation with base bit length ''{0}'' and exponent ''{1}'' " +
"would produce a result exceeding the configured maximum of ''{2}'' bits");
private final Kind kind;
@@ -49,6 +49,16 @@ public class SpelParserConfiguration {
*/
public static final int DEFAULT_MAX_OPERATIONS = 10_000;
/**
* Default maximum number of bits permitted in the result of a
* {@link java.math.BigDecimal} or {@link java.math.BigInteger} power operation
* within a SpEL expression: {@value}.
* <p>Approximately equivalent to a decimal number with 300,000 digits.
* @since 7.0.9
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public static final int DEFAULT_MAX_BIG_POWER_BITS = 1_000_000;
/**
* System property to configure the default compiler mode for SpEL expression parsers: {@value}.
* <p><strong>NOTE</strong>: Instead of relying on a global default, applications
@@ -65,7 +75,7 @@ public class SpelParserConfiguration {
* during SpEL expression evaluation: {@value}.
* <p><strong>NOTE</strong>: Instead of relying on a global default, applications
* and frameworks should ideally set an explicit custom value via the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor which provides complete configuration control and the ability
* to override global defaults per use case.
* <p>Can also be configured via the {@link SpringProperties} mechanism.
@@ -74,6 +84,22 @@ public class SpelParserConfiguration {
*/
public static final String SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME = "spring.expression.maxOperations";
/**
* System property to configure the default maximum number of bits permitted in the
* result of a {@link java.math.BigDecimal} or {@link java.math.BigInteger} power
* operation within a SpEL expression: {@value}.
* <p><strong>NOTE</strong>: Instead of relying on a global default, applications
* and frameworks should ideally set an explicit custom value via the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor which provides complete configuration control and the ability
* to override global defaults per use case.
* <p>Can also be configured via the {@link SpringProperties} mechanism.
* @since 7.0.9
* @see #DEFAULT_MAX_BIG_POWER_BITS
*/
public static final String SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME =
"spring.expression.maxBigPowerBits";
private static final SpelCompilerMode defaultCompilerMode;
@@ -98,15 +124,18 @@ public class SpelParserConfiguration {
private final int maximumOperations;
private final int maximumBigPowerBits;
/**
* Create a new {@code SpelParserConfiguration} instance with default settings.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration() {
this(null, null, false, false, Integer.MAX_VALUE);
@@ -115,7 +144,7 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @param compilerMode the compiler mode that parsers using this configuration
@@ -124,6 +153,7 @@ public class SpelParserConfiguration {
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader) {
this(compilerMode, compilerClassLoader, false, false, Integer.MAX_VALUE);
@@ -132,13 +162,14 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) {
this(null, null, autoGrowNullReferences, autoGrowCollections, Integer.MAX_VALUE);
@@ -147,7 +178,7 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @param autoGrowNullReferences if null references should automatically grow
@@ -155,6 +186,7 @@ public class SpelParserConfiguration {
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
this(null, null, autoGrowNullReferences, autoGrowCollections, maximumAutoGrowSize);
@@ -163,7 +195,7 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @param compilerMode the compiler mode that parsers using this configuration
@@ -175,6 +207,7 @@ public class SpelParserConfiguration {
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
@@ -186,7 +219,7 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int)}
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @param compilerMode the compiler mode that parsers using this configuration
@@ -201,6 +234,7 @@ public class SpelParserConfiguration {
* @since 5.2.25
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength) {
@@ -209,6 +243,34 @@ public class SpelParserConfiguration {
autoGrowCollections, maximumAutoGrowSize, maximumExpressionLength, retrieveMaxOperations());
}
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; must not be {@code null}
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumExpressionLength the maximum length of a SpEL expression;
* must be a positive number
* @param maximumOperations the maximum number of operations permitted during
* SpEL expression evaluation; must be a positive number
* @since 6.2.19
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
*/
public SpelParserConfiguration(SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength,
int maximumOperations) {
this(compilerMode, compilerClassLoader, autoGrowNullReferences, autoGrowCollections,
maximumAutoGrowSize, maximumExpressionLength, maximumOperations, retrieveMaxBigPowerBits());
}
/**
* Create a new {@code SpelParserConfiguration} instance.
* @param compilerMode the compiler mode that parsers using this configuration
@@ -222,15 +284,19 @@ public class SpelParserConfiguration {
* must be a positive number
* @param maximumOperations the maximum number of operations permitted during
* SpEL expression evaluation; must be a positive number
* @since 6.2.19
* @param maximumBigPowerBits the maximum number of bits permitted in the
* result of a {@link java.math.BigDecimal} or {@link java.math.BigInteger} power
* operation; must be a positive number; use {@link Integer#MAX_VALUE} for no limit
* @since 7.0.9
*/
public SpelParserConfiguration(SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength,
int maximumOperations) {
int maximumOperations, int maximumBigPowerBits) {
Assert.notNull(compilerMode, "'compilerMode' must not be null");
Assert.isTrue(maximumExpressionLength > 0, "'maximumExpressionLength' must be a positive number");
Assert.isTrue(maximumOperations > 0, "'maximumOperations' must be a positive number");
Assert.isTrue(maximumBigPowerBits > 0, "'maximumBigPowerBits' must be a positive number");
this.compilerMode = compilerMode;
this.compilerClassLoader = compilerClassLoader;
@@ -239,6 +305,7 @@ public class SpelParserConfiguration {
this.maximumAutoGrowSize = maximumAutoGrowSize;
this.maximumExpressionLength = maximumExpressionLength;
this.maximumOperations = maximumOperations;
this.maximumBigPowerBits = maximumBigPowerBits;
}
@@ -294,6 +361,15 @@ public class SpelParserConfiguration {
return this.maximumOperations;
}
/**
* Return the maximum number of bits permitted in the result of a
* {@link java.math.BigDecimal} or {@link java.math.BigInteger} power operation.
* @since 7.0.9
*/
public int getMaximumBigPowerBits() {
return this.maximumBigPowerBits;
}
private static int retrieveMaxOperations() {
String value = SpringProperties.getProperty(SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME);
@@ -313,4 +389,21 @@ public class SpelParserConfiguration {
}
}
private static int retrieveMaxBigPowerBits() {
String value = SpringProperties.getProperty(SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME);
if (!StringUtils.hasText(value)) {
return DEFAULT_MAX_BIG_POWER_BITS;
}
try {
int maxBits = Integer.parseInt(value.trim());
Assert.isTrue(maxBits > 0, () -> "Value [" + maxBits + "] for system property [" +
SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME + "] must be positive");
return maxBits;
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException("Failed to parse value for system property [" +
SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME + "]: " + ex.getMessage(), ex);
}
}
}
@@ -23,13 +23,15 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.NumberUtils;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
* The power operator.
*
* @author Andy Clement
* @author Giovanni Dall'Oglio Risso
* @author Sam Brannen
* @since 3.0
*/
public class OperatorPower extends Operator {
@@ -41,21 +43,20 @@ public class OperatorPower extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
Object leftOperand = leftOp.getValueInternal(state).getValue();
Object rightOperand = rightOp.getValueInternal(state).getValue();
Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
Object rightOperand = getRightOperand().getValueInternal(state).getValue();
if (leftOperand instanceof Number leftNumber && rightOperand instanceof Number rightNumber) {
state.trackOperation();
if (leftNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
return new TypedValue(leftBigDecimal.pow(rightNumber.intValue()));
if (leftNumber instanceof BigDecimal leftBigDecimal) {
int exponent = rightNumber.intValue();
checkBigNumberPowerBits(state, leftBigDecimal.unscaledValue().bitLength(), exponent);
return new TypedValue(leftBigDecimal.pow(exponent));
}
else if (leftNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
return new TypedValue(leftBigInteger.pow(rightNumber.intValue()));
else if (leftNumber instanceof BigInteger leftBigInteger) {
int exponent = rightNumber.intValue();
checkBigNumberPowerBits(state, leftBigInteger.bitLength(), exponent);
return new TypedValue(leftBigInteger.pow(exponent));
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(Math.pow(leftNumber.doubleValue(), rightNumber.doubleValue()));
@@ -76,4 +77,13 @@ public class OperatorPower extends Operator {
return state.operate(Operation.POWER, leftOperand, rightOperand);
}
private void checkBigNumberPowerBits(ExpressionState state, int baseBitLength, int exponent) {
int maxBigPowerBits = state.getConfiguration().getMaximumBigPowerBits();
long estimatedBigPowerBits = (long) baseBitLength * exponent;
if (estimatedBigPowerBits > maxBigPowerBits) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.MAX_BIG_POWER_RESULT_EXCEEDED,
baseBitLength, exponent, maxBigPowerBits);
}
}
}
@@ -19,6 +19,7 @@ package org.springframework.expression.spel;
import java.util.Arrays;
import java.util.List;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -168,6 +169,23 @@ public abstract class AbstractExpressionTests {
evaluateAndCheckError(this.parser, expression, expectedReturnType, expectedMessage, otherProperties);
}
/**
* Evaluate the specified expression and ensure the expected message comes out.
* The message may have inserts and they will be checked if otherProperties is specified.
* The first entry in otherProperties should always be the position.
* @param evaluationContext the evaluation context to use
* @param expression the expression to evaluate
* @param expectedReturnType ask the expression return value to be of this type if possible
* ({@code null} indicates don't ask for conversion)
* @param expectedMessage the expected message
* @param otherProperties the expected inserts within the message
*/
protected void evaluateAndCheckError(EvaluationContext evaluationContext, String expression,
Class<?> expectedReturnType, SpelMessage expectedMessage, Object... otherProperties) {
evaluateAndCheckError(this.parser, evaluationContext, expression, expectedReturnType, expectedMessage, otherProperties);
}
/**
* Evaluate the specified expression and ensure the expected message comes out.
* The message may have inserts and they will be checked if otherProperties is specified.
@@ -182,14 +200,32 @@ public abstract class AbstractExpressionTests {
protected void evaluateAndCheckError(ExpressionParser parser, String expression, Class<?> expectedReturnType, SpelMessage expectedMessage,
Object... otherProperties) {
evaluateAndCheckError(parser, this.context, expression, expectedReturnType, expectedMessage, otherProperties);
}
/**
* Evaluate the specified expression and ensure the expected message comes out.
* The message may have inserts and they will be checked if otherProperties is specified.
* The first entry in otherProperties should always be the position.
* @param parser the expression parser to use
* @param evaluationContext the evaluation context to use
* @param expression the expression to evaluate
* @param expectedReturnType ask the expression return value to be of this type if possible
* ({@code null} indicates don't ask for conversion)
* @param expectedMessage the expected message
* @param otherProperties the expected inserts within the message
*/
protected void evaluateAndCheckError(ExpressionParser parser, EvaluationContext evaluationContext,
String expression, Class<?> expectedReturnType, SpelMessage expectedMessage, Object... otherProperties) {
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> {
Expression expr = parser.parseExpression(expression);
assertThat(expr).as("expression").isNotNull();
if (expectedReturnType != null) {
expr.getValue(context, expectedReturnType);
expr.getValue(evaluationContext, expectedReturnType);
}
else {
expr.getValue(context);
expr.getValue(evaluationContext);
}
}).satisfies(ex -> {
assertThat(ex.getMessageCode()).isEqualTo(expectedMessage);
@@ -811,6 +811,77 @@ class EvaluationTests extends AbstractExpressionTests {
}
@Nested
class PowerOperatorTests {
private final EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// Use a small limit (16 bits) to verify behavior in tests.
private static final int TEST_MAX_RESULT_BITS = 16;
private final SpelExpressionParser limitedParser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false,
0, 10, 10, TEST_MAX_RESULT_BITS));
@Test
void powerOperatorWithBigDecimal() {
context.setVariable("bd", BigDecimal.valueOf(2.0));
Expression expr = parser.parseExpression("#bd ^ 4");
assertThat(expr.getValue(context, BigDecimal.class)).isEqualByComparingTo("16");
}
@Test
void powerOperatorWithBigDecimalUnderResultLimit() {
// BigDecimal.valueOf(2.0).unscaledValue().bitLength() = 5
// 5 * 3 = 15 bits <= TEST_MAX_RESULT_BITS (16)
context.setVariable("bd", BigDecimal.valueOf(2.0));
Expression expr = limitedParser.parseExpression("#bd ^ 3");
assertThat(expr.getValue(context, BigDecimal.class)).isEqualByComparingTo("8");
}
@Test
void powerOperatorWithBigDecimalExceedingResultLimit() {
// 5 * 4 = 20 bits > TEST_MAX_RESULT_BITS (16)
context.setVariable("bd", BigDecimal.valueOf(2.0));
evaluateAndCheckError(limitedParser, context, "#bd ^ 4", BigDecimal.class,
SpelMessage.MAX_BIG_POWER_RESULT_EXCEEDED,
4, // power operator position
5, // base bit length
4, // exponent
TEST_MAX_RESULT_BITS);
}
@Test
void powerOperatorWithBigInteger() {
context.setVariable("bi", BigInteger.valueOf(2));
Expression expr = parser.parseExpression("#bi ^ 4");
assertThat(expr.getValue(context, BigInteger.class)).isEqualTo(BigInteger.valueOf(16));
}
@Test
void powerOperatorWithBigIntegerUnderResultLimit() {
// BigInteger.valueOf(2).bitLength() = 2
// 2 * 8 = 16 bits == TEST_MAX_RESULT_BITS (16)
context.setVariable("bi", BigInteger.valueOf(2));
Expression expr = limitedParser.parseExpression("#bi ^ 8");
assertThat(expr.getValue(context, BigInteger.class)).isEqualTo(BigInteger.valueOf(256));
}
@Test
void powerOperatorWithBigIntegerExceedingResultLimit() {
// 2 * 9 = 18 bits > TEST_MAX_RESULT_BITS (16)
context.setVariable("bi", BigInteger.valueOf(2));
evaluateAndCheckError(limitedParser, context, "#bi ^ 9", BigInteger.class,
SpelMessage.MAX_BIG_POWER_RESULT_EXCEEDED,
4, // power operator position
2, // base bit length
9, // exponent
TEST_MAX_RESULT_BITS);
}
}
@Nested
class TernaryOperatorTests {