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 d70d7d50d622..30a2867cae0f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -351,8 +351,9 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) { } generateCodeForArguments(mv, cf, method, this.children); - mv.visitMethodInsn((isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, method.getName(), - CodeFlow.createSignatureDescriptor(method), method.getDeclaringClass().isInterface()); + mv.visitMethodInsn((isStaticMethod ? INVOKESTATIC : (method.isDefault() ? INVOKEINTERFACE : INVOKEVIRTUAL)), + classDesc, method.getName(), CodeFlow.createSignatureDescriptor(method), + method.getDeclaringClass().isInterface()); cf.pushDescriptor(this.exitTypeDescriptor); if (this.originalPrimitiveExitTypeDescriptor != null) { 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 c08bf4fa99cc..832531e8a814 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 @@ -5183,7 +5183,7 @@ private void assertGetValueFail(Expression expression) { } } - 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 a9439b08a46c..8878669201c7 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,20 +22,23 @@ 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.junit.Assert.assertEquals; +import static org.junit.Assert.*; /** * Tests for the {@link SpelCompiler}. * * @author Sam Brannen + * @author Andy Clement * @since 5.1.14 */ public class SpelCompilerTests { - @Test // gh-24357 + @Test // gh-24357 public void expressionCompilesWhenMethodComesFromPublicInterface() { SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null); SpelExpressionParser parser = new SpelExpressionParser(config); @@ -47,6 +50,31 @@ public void expressionCompilesWhenMethodComesFromPublicInterface() { IntStream.rangeClosed(1, 5).forEach(i -> assertEquals(42, expression.getValue(component))); } + @Test // gh-25706 + public 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()"); + assertFalse(SpelCompiler.compile(expression)); + assertEquals(false, expression.getValue(context)); + assertTrue(SpelCompiler.compile(expression)); + SpelCompilationCoverageTests.assertIsCompiled(expression); + assertEquals(false, expression.getValue(context)); + + context.setVariable("user", new User()); + expression = parser.parseExpression("#root.isEditable(#user)"); + assertFalse(SpelCompiler.compile(expression)); + assertEquals(true, expression.getValue(context)); + assertTrue(SpelCompiler.compile(expression)); + SpelCompilationCoverageTests.assertIsCompiled(expression); + assertEquals(true, expression.getValue(context)); + } + static class OrderedComponent implements Ordered { @@ -56,4 +84,40 @@ public int getOrder() { } } + + 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(); + } + }