Skip to content

Commit da02b7a

Browse files
committed
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
1 parent 17117bd commit da02b7a

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@
5555
* @author Andy Clement
5656
* @author Juergen Hoeller
5757
* @author Phillip Webb
58+
* @author Sam Brannen
5859
* @since 3.0
5960
* @see StandardEvaluationContext
6061
* @see SimpleEvaluationContext
@@ -765,8 +766,11 @@ public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
765766
}
766767

767768
if (this.member instanceof Method) {
768-
mv.visitMethodInsn((isStatic ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, this.member.getName(),
769-
CodeFlow.createSignatureDescriptor((Method) this.member), false);
769+
Method method = (Method) this.member;
770+
boolean isInterface = method.getDeclaringClass().isInterface();
771+
int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL);
772+
mv.visitMethodInsn(opcode, classDesc, method.getName(),
773+
CodeFlow.createSignatureDescriptor(method), isInterface);
770774
}
771775
else {
772776
mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, this.member.getName(),
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.expression.spel.standard;
18+
19+
import java.util.stream.IntStream;
20+
21+
import org.junit.Test;
22+
23+
import org.springframework.core.Ordered;
24+
import org.springframework.expression.Expression;
25+
import org.springframework.expression.spel.SpelCompilerMode;
26+
import org.springframework.expression.spel.SpelParserConfiguration;
27+
28+
import static org.junit.Assert.assertEquals;
29+
30+
/**
31+
* Tests for the {@link SpelCompiler}.
32+
*
33+
* @author Sam Brannen
34+
* @since 5.1.14
35+
*/
36+
public class SpelCompilerTests {
37+
38+
@Test // gh-24357
39+
public void expressionCompilesWhenMethodComesFromPublicInterface() {
40+
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
41+
SpelExpressionParser parser = new SpelExpressionParser(config);
42+
43+
OrderedComponent component = new OrderedComponent();
44+
Expression expression = parser.parseExpression("order");
45+
46+
// Evaluate the expression multiple times to ensure that it gets compiled.
47+
IntStream.rangeClosed(1, 5).forEach(i -> assertEquals(42, expression.getValue(component)));
48+
}
49+
50+
51+
static class OrderedComponent implements Ordered {
52+
53+
@Override
54+
public int getOrder() {
55+
return 42;
56+
}
57+
}
58+
59+
}

0 commit comments

Comments
 (0)