Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document limitation of SpEL regarding minimum values for numeric literals #20779

Closed
spring-projects-issues opened this issue Nov 25, 2017 · 6 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: documentation A documentation task
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Nov 25, 2017

Tamas Szekeres opened SPR-16232 and commented

The Tokenizer cuts the integer literal -2147483648 (whose value is equal to Integer.MIN_VALUE) into a MINUS token and a LITERAL_INT. The integer token, in itself, falls outside of the integer range, so from that point of view it is understandable that the parsing later fails, but from a usability one, it should work.

public void testParseWhenMinIntegerSetShouldNotBeThrowingExceptionButItDoes() {
    final ExpressionParser parser = new SpelExpressionParser();
    final Expression expression = parser.parseExpression(String.valueOf(Integer.MIN_VALUE));
    expression.getValue();
}
FAILED: testParseWhenMinIntegerSetShouldNotBeThrowingExceptionButItDoes
org.springframework.expression.spel.SpelParseException: EL1035E: The value '2147483648' cannot be parsed as an int
	at org.springframework.expression.spel.ast.Literal.getIntLiteral(Literal.java:80)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.maybeEatLiteral(InternalSpelExpressionParser.java:840)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatStartNode(InternalSpelExpressionParser.java:500)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatPrimaryExpression(InternalSpelExpressionParser.java:343)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatUnaryExpression(InternalSpelExpressionParser.java:337)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatUnaryExpression(InternalSpelExpressionParser.java:316)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatPowerIncDecExpression(InternalSpelExpressionParser.java:293)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatProductExpression(InternalSpelExpressionParser.java:272)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatSumExpression(InternalSpelExpressionParser.java:255)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatRelationalExpression(InternalSpelExpressionParser.java:210)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatLogicalAndExpression(InternalSpelExpressionParser.java:198)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatLogicalOrExpression(InternalSpelExpressionParser.java:186)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatExpression(InternalSpelExpressionParser.java:146)
	at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:127)
	at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:60)
	at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:32)
	at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:73)
	at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:60)
	...
Caused by: java.lang.NumberFormatException: For input string: "2147483648"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:499)
	at org.springframework.expression.spel.ast.Literal.getIntLiteral(Literal.java:76)
	... 44 more

Affects: 4.3.11, 4.3.12

@spring-projects-issues spring-projects-issues added type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.x Backlog milestone Jan 11, 2019
@sbrannen sbrannen changed the title SpelExpressionParser fails to parse Integer.MIN_VALUE [SPR-16232] SpelExpressionParser fails to parse Integer.MIN_VALUE Jan 23, 2024
@sbrannen sbrannen changed the title SpelExpressionParser fails to parse Integer.MIN_VALUE SpelExpressionParser fails to parse Integer.MIN_VALUE as literal integer Jan 23, 2024
@sbrannen sbrannen self-assigned this Jan 23, 2024
@sbrannen
Copy link
Member

First and foremost, thank you for the detailed report and apologies for taking so long to process this issue.

Please note that, depending on your exact use case, you may be able to work around this limitation if you're using the StandardEvaluationContext (and not the SimpleEvaluationContext) by making use of the type operator T() and Integer.valueOf(String) as demonstrated in the following test which passes.

@Test
void parsingIntegerMinValueFromStringLiteral() {
	EvaluationContext context = new StandardEvaluationContext();
	ExpressionParser parser = new SpelExpressionParser();
	Expression expression = parser.parseExpression("T(Integer).valueOf('-2147483648')");
	assertThat(expression.getValue(context)).isEqualTo(Integer.MIN_VALUE);
}

@sbrannen
Copy link
Member

sbrannen commented Jan 23, 2024

An alternative would be to represent the Integer.MIN_VALUE literal value as a Long instead of an Integer as follows, in case that works for your use case.

@Test
void parsingIntegerMinValueFromLongLiteral() {
	EvaluationContext context = new StandardEvaluationContext();
	ExpressionParser parser = new SpelExpressionParser();
	Expression expression = parser.parseExpression("-2147483648L");
	assertThat(expression.getValue(context, Integer.class)).isEqualTo(Integer.MIN_VALUE);
}

Note, however, that the same limitation applies to Long.MIN_VALUE when used as a long literal within a SpEL expression. The following test fails accordingly.

@Test
void parsingLongMinValueFromLongLiteral() {
	EvaluationContext context = new StandardEvaluationContext();
	ExpressionParser parser = new SpelExpressionParser();
	Expression expression = parser.parseExpression("-9223372036854775808L");
	assertThat(expression.getValue(context, Long.class)).isEqualTo(Long.MIN_VALUE);
}

The above test fails with:

org.springframework.expression.spel.SpelParseException: EL1036E: The value '9223372036854775808' cannot be parsed as a long

@sbrannen sbrannen changed the title SpelExpressionParser fails to parse Integer.MIN_VALUE as literal integer Document that SpEL cannot parse Integer.MIN_VALUE or Long.MIN_VALUE as literal Jan 24, 2024
@sbrannen sbrannen added type: documentation A documentation task and removed type: enhancement A general enhancement labels Jan 24, 2024
@sbrannen sbrannen modified the milestones: 6.x Backlog, 6.1.4 Jan 24, 2024
@sbrannen
Copy link
Member

After further investigation, I have determined that the following expressions can be used as workarounds, depending on your exact use case.

Integer.MIN_VALUE (-2147483648)

  • -2147483647 - 1 -- always works (equal to Integer.MIN_VALUE + 1 - 1)
  • -2147483648L -- always works (stores Integer.MIN_VALUE in a Long)
  • 0.MIN_VALUE -- always works, even with SimpleEvaluationContext; 0 can be any integer literal
  • T(Integer).MIN_VALUE -- requires StandardEvaluationContext
  • T(Integer).valueOf('-2147483648') -- requires StandardEvaluationContext

Long.MIN_VALUE (-9223372036854775808)

  • -9223372036854775807 - 1 -- always works (equal to Long.MIN_VALUE + 1 - 1)
  • 0L.MIN_VALUE -- always works, even with SimpleEvaluationContext; 0 can be any long literal
  • T(Long).MIN_VALUE -- requires StandardEvaluationContext
  • T(Long).valueOf('-9223372036854775808') -- requires StandardEvaluationContext

@sbrannen
Copy link
Member

sbrannen commented Jan 24, 2024

After considerable analysis, I have determined that supporting Integer.MIN_VALUE and Long.MIN_VALUE as integer and long literals is out of scope for the Spring Expression Language (SpEL).

The reason is that the internals of the SpEL tokenizer, parser, and AST node implementations rely heavily on the fact that literals are always positive values. For example, when you specify an integer literal such as -2 in an expression, the value is never stored or represented as -2. Instead, the value is stored as 2 in an IntLiteral which is supplied as the sole operand to the OpMinus AST node. So -2 is effectively represented internally as OpMinus(IntLiteral(2)) and calculated on-the-fly as (0 - 2).

Changing the semantics to actually store -2 in an IntLiteral or LongLiteral would break various constructs in SpEL.

In light of that and the aforementioned workarounds, I am repurposing this as a documentation issue to point out this limitation.

@sbrannen sbrannen changed the title Document that SpEL cannot parse Integer.MIN_VALUE or Long.MIN_VALUE as literal Document limitation of SpEL regarding minimum values for numeric literals Jan 28, 2024
@sbrannen
Copy link
Member

sbrannen commented Feb 1, 2024

@firefoxpdm, are you perhaps the same firefoxpdm that created this issue on JIRA?

@sbrannen
Copy link
Member

sbrannen commented Feb 2, 2024

While working on #32187, I realized that the easiest way to express Integer.MIN_VALUE within an expression (when not evaluated in a StandardEvaluationContext) is to simply calculate the valuate using SpEL's exponential power operator (-2^31).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

2 participants