diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 970060cdf31..68e9c434ca8 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -347,7 +347,8 @@ public class MethodReference extends SpelNodeImpl { } generateCodeForArguments(mv, cf, method, this.children); - mv.visitMethodInsn((isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, method.getName(), + mv.visitMethodInsn((isStaticMethod ? INVOKESTATIC : (method.isDefault() ? INVOKEINTERFACE : INVOKEVIRTUAL)), + classDesc, method.getName(), CodeFlow.createSignatureDescriptor(method), method.getDeclaringClass().isInterface()); cf.pushDescriptor(this.exitTypeDescriptor); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java index 2f5ad40e2a8..b53e8dbb0ba 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java @@ -5159,7 +5159,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests { assertThatExceptionOfType(Exception.class).isThrownBy(expression::getValue); } - private void assertIsCompiled(Expression expression) { + public static void assertIsCompiled(Expression expression) { try { Field field = SpelExpression.class.getDeclaredField("compiledAst"); field.setAccessible(true); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelCompilerTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelCompilerTests.java index dc80b9c90de..5576b595385 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelCompilerTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelCompilerTests.java @@ -22,8 +22,10 @@ import org.junit.jupiter.api.Test; import org.springframework.core.Ordered; import org.springframework.expression.Expression; +import org.springframework.expression.spel.SpelCompilationCoverageTests; import org.springframework.expression.spel.SpelCompilerMode; import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.support.StandardEvaluationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for the {@link SpelCompiler}. * * @author Sam Brannen + * @author Andy Clement * @since 5.1.14 */ class SpelCompilerTests { @@ -55,5 +58,60 @@ class SpelCompilerTests { return 42; } } + + @Test // gh-25706 + void defaultMethodInvocation() { + SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null); + SpelExpressionParser parser = new SpelExpressionParser(config); + + StandardEvaluationContext context = new StandardEvaluationContext(); + Item item = new Item(); + context.setRootObject(item); + + Expression expression = parser.parseExpression("#root.isEditable2()"); + assertThat(SpelCompiler.compile(expression)).isFalse(); + assertThat(expression.getValue(context)).isEqualTo(false); + assertThat(SpelCompiler.compile(expression)).isTrue(); + SpelCompilationCoverageTests.assertIsCompiled(expression); + assertThat(expression.getValue(context)).isEqualTo(false); + + context.setVariable("user", new User()); + expression = parser.parseExpression("#root.isEditable(#user)"); + assertThat(SpelCompiler.compile(expression)).isFalse(); + assertThat(expression.getValue(context)).isEqualTo(true); + assertThat(SpelCompiler.compile(expression)).isTrue(); + SpelCompilationCoverageTests.assertIsCompiled(expression); + assertThat(expression.getValue(context)).isEqualTo(true); + } + + public static class User { + boolean isAdmin() { + return true; + } + } + + public static class Item implements Editable { + // some fields + private String someField = ""; + + // some getters and setters + + @Override + public boolean hasSomeProperty() { + return someField != null; + } + } + + public interface Editable { + default boolean isEditable(User user) { + return user.isAdmin() && hasSomeProperty(); + } + + default boolean isEditable2() { + return false; + } + + boolean hasSomeProperty(); + } }