mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Polish SpEL internals
This commit is contained in:
+30
-20
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.asm.Label;
|
||||
@@ -25,7 +26,6 @@ import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Represents the Elvis operator {@code ?:}.
|
||||
@@ -60,6 +60,7 @@ public class Elvis extends SpelNodeImpl {
|
||||
*/
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue result;
|
||||
TypedValue leftHandTypedValue = this.children[0].getValueInternal(state);
|
||||
Object leftHandValue = leftHandTypedValue.getValue();
|
||||
|
||||
@@ -67,20 +68,21 @@ public class Elvis extends SpelNodeImpl {
|
||||
// Compilation is currently not supported for Optional with the Elvis operator.
|
||||
this.exitTypeDescriptor = null;
|
||||
if (optional.isPresent()) {
|
||||
return new TypedValue(optional.get());
|
||||
result = new TypedValue(optional.get());
|
||||
}
|
||||
else {
|
||||
result = this.children[1].getValueInternal(state);
|
||||
}
|
||||
return this.children[1].getValueInternal(state);
|
||||
}
|
||||
|
||||
// If this check is changed, the generateCode method will need changing too
|
||||
if (leftHandValue != null && !"".equals(leftHandValue)) {
|
||||
return leftHandTypedValue;
|
||||
// If this check is changed, the generateCode() method will need changing too
|
||||
else if (leftHandValue != null && !"".equals(leftHandValue)) {
|
||||
result = leftHandTypedValue;
|
||||
}
|
||||
else {
|
||||
TypedValue result = this.children[1].getValueInternal(state);
|
||||
computeExitTypeDescriptor();
|
||||
return result;
|
||||
result = this.children[1].getValueInternal(state);
|
||||
}
|
||||
computeExitTypeDescriptor();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,22 +94,25 @@ public class Elvis extends SpelNodeImpl {
|
||||
public boolean isCompilable() {
|
||||
SpelNodeImpl condition = this.children[0];
|
||||
SpelNodeImpl ifNullValue = this.children[1];
|
||||
String conditionDescriptor = condition.exitTypeDescriptor;
|
||||
String ifNullValueDescriptor = ifNullValue.exitTypeDescriptor;
|
||||
|
||||
return (condition.isCompilable() && ifNullValue.isCompilable() &&
|
||||
condition.exitTypeDescriptor != null && ifNullValue.exitTypeDescriptor != null);
|
||||
conditionDescriptor != null && ifNullValueDescriptor != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(MethodVisitor mv, CodeFlow cf) {
|
||||
// exit type descriptor can be null if both components are literal expressions
|
||||
computeExitTypeDescriptor();
|
||||
Label elseTarget = new Label();
|
||||
Label endOfIf = new Label();
|
||||
|
||||
cf.enterCompilationScope();
|
||||
this.children[0].generateCode(mv, cf);
|
||||
String lastDesc = cf.lastDescriptor();
|
||||
Assert.state(lastDesc != null, "No last descriptor");
|
||||
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
|
||||
cf.exitCompilationScope();
|
||||
Label elseTarget = new Label();
|
||||
Label endOfIf = new Label();
|
||||
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitJumpInsn(IFNULL, elseTarget);
|
||||
// Also check if empty string, as per the code in the interpreted version
|
||||
@@ -116,6 +121,7 @@ public class Elvis extends SpelNodeImpl {
|
||||
mv.visitInsn(SWAP);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z",false);
|
||||
mv.visitJumpInsn(IFEQ, endOfIf); // if not empty, drop through to elseTarget
|
||||
|
||||
mv.visitLabel(elseTarget);
|
||||
mv.visitInsn(POP);
|
||||
cf.enterCompilationScope();
|
||||
@@ -126,16 +132,20 @@ public class Elvis extends SpelNodeImpl {
|
||||
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
|
||||
}
|
||||
cf.exitCompilationScope();
|
||||
|
||||
mv.visitLabel(endOfIf);
|
||||
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
private void computeExitTypeDescriptor() {
|
||||
if (this.exitTypeDescriptor == null && this.children[0].exitTypeDescriptor != null &&
|
||||
this.children[1].exitTypeDescriptor != null) {
|
||||
String conditionDescriptor = this.children[0].exitTypeDescriptor;
|
||||
String ifNullValueDescriptor = this.children[1].exitTypeDescriptor;
|
||||
if (ObjectUtils.nullSafeEquals(conditionDescriptor, ifNullValueDescriptor)) {
|
||||
SpelNodeImpl condition = this.children[0];
|
||||
SpelNodeImpl ifNullValue = this.children[1];
|
||||
String conditionDescriptor = condition.exitTypeDescriptor;
|
||||
String ifNullValueDescriptor = ifNullValue.exitTypeDescriptor;
|
||||
|
||||
if (this.exitTypeDescriptor == null && conditionDescriptor != null && ifNullValueDescriptor != null) {
|
||||
if (Objects.equals(conditionDescriptor, ifNullValueDescriptor)) {
|
||||
this.exitTypeDescriptor = conditionDescriptor;
|
||||
}
|
||||
else {
|
||||
|
||||
+2
-2
@@ -23,6 +23,7 @@ import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@@ -45,7 +46,6 @@ import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.ReflectiveMethodExecutor;
|
||||
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents a method reference (i.e., a
|
||||
@@ -476,7 +476,7 @@ public class MethodReference extends SpelNodeImpl {
|
||||
|
||||
public boolean isSuitable(Object target, @Nullable TypeDescriptor targetType, List<TypeDescriptor> argumentTypes) {
|
||||
return ((this.staticClass == null || this.staticClass == target) &&
|
||||
ObjectUtils.nullSafeEquals(this.targetType, targetType) && this.argumentTypes.equals(argumentTypes));
|
||||
Objects.equals(this.targetType, targetType) && this.argumentTypes.equals(argumentTypes));
|
||||
}
|
||||
|
||||
public boolean hasProxyTarget() {
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -363,11 +364,11 @@ public abstract class Operator extends SpelNodeImpl {
|
||||
boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
|
||||
|
||||
// If the declared descriptors aren't providing the information, try the actual descriptors
|
||||
if (!leftNumeric && !ObjectUtils.nullSafeEquals(ld, leftActualDescriptor)) {
|
||||
if (!leftNumeric && !Objects.equals(ld, leftActualDescriptor)) {
|
||||
ld = leftActualDescriptor;
|
||||
leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
|
||||
}
|
||||
if (!rightNumeric && !ObjectUtils.nullSafeEquals(rd, rightActualDescriptor)) {
|
||||
if (!rightNumeric && !Objects.equals(rd, rightActualDescriptor)) {
|
||||
rd = rightActualDescriptor;
|
||||
rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
|
||||
}
|
||||
|
||||
-1
@@ -54,5 +54,4 @@ public class RealLiteral extends Literal {
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+25
-19
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
@@ -25,7 +27,6 @@ import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Represents a ternary expression, for example: "someCheck()?true:false".
|
||||
@@ -66,35 +67,26 @@ public class Ternary extends SpelNodeImpl {
|
||||
return "(" + getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST() + ")";
|
||||
}
|
||||
|
||||
private void computeExitTypeDescriptor() {
|
||||
if (this.exitTypeDescriptor == null && this.children[1].exitTypeDescriptor != null &&
|
||||
this.children[2].exitTypeDescriptor != null) {
|
||||
String leftDescriptor = this.children[1].exitTypeDescriptor;
|
||||
String rightDescriptor = this.children[2].exitTypeDescriptor;
|
||||
if (ObjectUtils.nullSafeEquals(leftDescriptor, rightDescriptor)) {
|
||||
this.exitTypeDescriptor = leftDescriptor;
|
||||
}
|
||||
else {
|
||||
// Use the easiest to compute common supertype
|
||||
this.exitTypeDescriptor = "Ljava/lang/Object";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompilable() {
|
||||
SpelNodeImpl condition = this.children[0];
|
||||
SpelNodeImpl left = this.children[1];
|
||||
SpelNodeImpl right = this.children[2];
|
||||
String conditionDescriptor = condition.exitTypeDescriptor;
|
||||
String leftDescriptor = left.exitTypeDescriptor;
|
||||
String rightDescriptor = right.exitTypeDescriptor;
|
||||
|
||||
return (condition.isCompilable() && left.isCompilable() && right.isCompilable() &&
|
||||
CodeFlow.isBooleanCompatible(condition.exitTypeDescriptor) &&
|
||||
left.exitTypeDescriptor != null && right.exitTypeDescriptor != null);
|
||||
CodeFlow.isBooleanCompatible(conditionDescriptor) &&
|
||||
leftDescriptor != null && rightDescriptor != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCode(MethodVisitor mv, CodeFlow cf) {
|
||||
// May reach here without it computed if all elements are literals
|
||||
// May get here without the exit descriptor having been computed, if
|
||||
// all elements are literals.
|
||||
computeExitTypeDescriptor();
|
||||
|
||||
cf.enterCompilationScope();
|
||||
this.children[0].generateCode(mv, cf);
|
||||
String lastDesc = cf.lastDescriptor();
|
||||
@@ -128,4 +120,18 @@ public class Ternary extends SpelNodeImpl {
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
private void computeExitTypeDescriptor() {
|
||||
String leftDescriptor = this.children[1].exitTypeDescriptor;
|
||||
String rightDescriptor = this.children[2].exitTypeDescriptor;
|
||||
if (this.exitTypeDescriptor == null && leftDescriptor != null && rightDescriptor != null) {
|
||||
if (Objects.equals(leftDescriptor, rightDescriptor)) {
|
||||
this.exitTypeDescriptor = leftDescriptor;
|
||||
}
|
||||
else {
|
||||
// Use the easiest to compute common supertype
|
||||
this.exitTypeDescriptor = "Ljava/lang/Object";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user