Skip to content

Commit

Permalink
Fix SpEL generated code for default method invocation
Browse files Browse the repository at this point in the history
Closes gh-25706
  • Loading branch information
jhoeller committed Sep 5, 2020
1 parent 670b9fd commit c368ce8
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 5 deletions.
@@ -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.
Expand Down Expand Up @@ -347,8 +347,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) {
Expand Down
Expand Up @@ -5159,7 +5159,7 @@ private void assertGetValueFail(Expression expression) {
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);
Expand Down
Expand Up @@ -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.assertj.core.api.Assertions.assertThat;

/**
* Tests for the {@link SpelCompiler}.
*
* @author Sam Brannen
* @author Andy Clement
* @since 5.1.14
*/
class SpelCompilerTests {

@Test // gh-24357
@Test // gh-24357
void expressionCompilesWhenMethodComesFromPublicInterface() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelExpressionParser parser = new SpelExpressionParser(config);
Expand All @@ -47,6 +50,31 @@ void expressionCompilesWhenMethodComesFromPublicInterface() {
IntStream.rangeClosed(1, 5).forEach(i -> assertThat(expression.getValue(component)).isEqualTo(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);
}


static class OrderedComponent implements Ordered {

Expand All @@ -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();
}

}

0 comments on commit c368ce8

Please sign in to comment.