From a9d9b76d090e912c7cac646df235fddce4353d48 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 12 Feb 2020 15:33:01 +0100 Subject: [PATCH] Support SpEL compilation of interface methods again Spring Framework 5.1.8 introduced a regression for the compilation of SpEL expressions referencing a method declared in an interface. An attempt to compile such an expression resulted in a SpelEvaluationException caused by an IncompatibleClassChangeError. This commit fixes this regression by adding explicit support in ReflectivePropertyAccessor.OptimalPropertyAccessor.generateCode() for methods declared in interfaces. Closes gh-24357 --- .../support/ReflectivePropertyAccessor.java | 10 +++- .../spel/standard/SpelCompilerTests.java | 59 +++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelCompilerTests.java diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index aa3819a3c91..e8cf3dad604 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.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. @@ -55,6 +55,7 @@ import org.springframework.util.StringUtils; * @author Andy Clement * @author Juergen Hoeller * @author Phillip Webb + * @author Sam Brannen * @since 3.0 * @see StandardEvaluationContext * @see SimpleEvaluationContext @@ -765,8 +766,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { } if (this.member instanceof Method) { - mv.visitMethodInsn((isStatic ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, this.member.getName(), - CodeFlow.createSignatureDescriptor((Method) this.member), false); + Method method = (Method) this.member; + boolean isInterface = method.getDeclaringClass().isInterface(); + int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL); + mv.visitMethodInsn(opcode, classDesc, method.getName(), + CodeFlow.createSignatureDescriptor(method), isInterface); } else { mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, this.member.getName(), 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 new file mode 100644 index 00000000000..dc80b9c90de --- /dev/null +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelCompilerTests.java @@ -0,0 +1,59 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.standard; + +import java.util.stream.IntStream; + +import org.junit.jupiter.api.Test; + +import org.springframework.core.Ordered; +import org.springframework.expression.Expression; +import org.springframework.expression.spel.SpelCompilerMode; +import org.springframework.expression.spel.SpelParserConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for the {@link SpelCompiler}. + * + * @author Sam Brannen + * @since 5.1.14 + */ +class SpelCompilerTests { + + @Test // gh-24357 + void expressionCompilesWhenMethodComesFromPublicInterface() { + SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null); + SpelExpressionParser parser = new SpelExpressionParser(config); + + OrderedComponent component = new OrderedComponent(); + Expression expression = parser.parseExpression("order"); + + // Evaluate the expression multiple times to ensure that it gets compiled. + IntStream.rangeClosed(1, 5).forEach(i -> assertThat(expression.getValue(component)).isEqualTo(42)); + } + + + static class OrderedComponent implements Ordered { + + @Override + public int getOrder() { + return 42; + } + } + +}