Merge branch '6.1.x'

This commit is contained in:
Sam Brannen 2024-03-22 16:37:55 +01:00
commit b695dbc2bf
4 changed files with 22 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2024 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,7 +24,6 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow; 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.SpelNode;
/** /**
* Represents a DOT separated expression sequence, such as * Represents a DOT separated expression sequence, such as
@ -120,14 +119,13 @@ public class CompoundExpression extends SpelNodeImpl {
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
sb.append(getChild(i).toStringAST()); sb.append(getChild(i).toStringAST());
if (i < getChildCount() - 1) { if (i < getChildCount() - 1) {
SpelNode nextChild = getChild(i + 1); SpelNodeImpl nextChild = this.children[i + 1];
if (nextChild.isNullSafe()) {
sb.append("?.");
}
// Don't append a '.' if the next child is an Indexer. // Don't append a '.' if the next child is an Indexer.
// For example, we want 'myVar[0]' instead of 'myVar.[0]'. // For example, we want 'myVar[0]' instead of 'myVar.[0]'.
if (!(nextChild instanceof Indexer)) { else if (!(nextChild instanceof Indexer)) {
if ((nextChild instanceof MethodReference methodRef && methodRef.isNullSafe()) ||
(nextChild instanceof PropertyOrFieldReference pofRef && pofRef.isNullSafe())) {
sb.append('?');
}
sb.append('.'); sb.append('.');
} }
} }

View File

@ -77,6 +77,7 @@ public class MethodReference extends SpelNodeImpl {
* Does this node represent a null-safe method reference? * Does this node represent a null-safe method reference?
* @since 6.0.13 * @since 6.0.13
*/ */
@Override
public final boolean isNullSafe() { public final boolean isNullSafe() {
return this.nullSafe; return this.nullSafe;
} }

View File

@ -76,6 +76,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
/** /**
* Does this node represent a null-safe property or field reference? * Does this node represent a null-safe property or field reference?
*/ */
@Override
public boolean isNullSafe() { public boolean isNullSafe() {
return this.nullSafe; return this.nullSafe;
} }
@ -181,7 +182,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
throws EvaluationException { throws EvaluationException {
Object targetObject = contextObject.getValue(); Object targetObject = contextObject.getValue();
if (targetObject == null && this.nullSafe) { if (targetObject == null && isNullSafe()) {
return TypedValue.NULL; return TypedValue.NULL;
} }
@ -233,7 +234,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
TypedValue contextObject, EvaluationContext evalContext, String name, @Nullable Object newValue) TypedValue contextObject, EvaluationContext evalContext, String name, @Nullable Object newValue)
throws EvaluationException { throws EvaluationException {
if (contextObject.getValue() == null && this.nullSafe) { if (contextObject.getValue() == null && isNullSafe()) {
return; return;
} }
if (contextObject.getValue() == null) { if (contextObject.getValue() == null) {
@ -353,7 +354,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
} }
Label skipIfNull = null; Label skipIfNull = null;
if (this.nullSafe) { if (isNullSafe()) {
mv.visitInsn(DUP); mv.visitInsn(DUP);
skipIfNull = new Label(); skipIfNull = new Label();
Label continueLabel = new Label(); Label continueLabel = new Label();
@ -381,7 +382,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
// If this property or field access would return a primitive - and yet // If this property or field access would return a primitive - and yet
// it is also marked null safe - then the exit type descriptor must be // it is also marked null safe - then the exit type descriptor must be
// promoted to the box type to allow a null value to be passed on // promoted to the box type to allow a null value to be passed on
if (this.nullSafe && CodeFlow.isPrimitive(descriptor)) { if (isNullSafe() && CodeFlow.isPrimitive(descriptor)) {
this.originalPrimitiveExitTypeDescriptor = descriptor; this.originalPrimitiveExitTypeDescriptor = descriptor;
this.exitTypeDescriptor = CodeFlow.toBoxedDescriptor(descriptor); this.exitTypeDescriptor = CodeFlow.toBoxedDescriptor(descriptor);
} }

View File

@ -181,6 +181,16 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
return this.endPos; return this.endPos;
} }
/**
* Determine if this node is the target of a null-safe navigation operation.
* <p>The default implementation returns {@code false}.
* @return {@code true} if this node is the target of a null-safe operation
* @since 6.1.6
*/
public boolean isNullSafe() {
return false;
}
/** /**
* 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