Skip to content

Commit bd7d56a

Browse files
committed
Fix VerifyError for SpEL ternary compilation
The ternary expression node was failing to generate the necessary unboxing bytecode when the condition part of the expression returned a boxed Boolean rather than a primitive boolean. Also fixed here is an IllegalAccessError that was seen in the same expression due to generating a CHECKCAST bytecode for a private type. Issue: SPR-12271
1 parent b2d6791 commit bd7d56a

File tree

4 files changed

+69
-6
lines changed

4 files changed

+69
-6
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,10 @@ public static void insertCheckCast(MethodVisitor mv, String descriptor) {
446446
}
447447
}
448448
else {
449-
// This is chopping off the 'L' to leave us with "java/lang/String"
450-
mv.visitTypeInsn(CHECKCAST, descriptor.substring(1));
449+
if (!descriptor.equals("Ljava/lang/Object")) {
450+
// This is chopping off the 'L' to leave us with "java/lang/String"
451+
mv.visitTypeInsn(CHECKCAST, descriptor.substring(1));
452+
}
451453
}
452454
}
453455
}

spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ public void generateCode(MethodVisitor mv, CodeFlow codeflow) {
109109
computeExitTypeDescriptor();
110110
codeflow.enterCompilationScope();
111111
this.children[0].generateCode(mv, codeflow);
112+
if (!CodeFlow.isPrimitive(codeflow.lastDescriptor())) {
113+
CodeFlow.insertUnboxInsns(mv, 'Z', codeflow.lastDescriptor());
114+
}
112115
codeflow.exitCompilationScope();
113116
Label elseTarget = new Label();
114117
Label endOfIf = new Label();

spring-expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.expression.spel.ast;
1818

19+
import java.lang.reflect.Modifier;
20+
1921
import org.springframework.asm.MethodVisitor;
2022
import org.springframework.expression.EvaluationContext;
2123
import org.springframework.expression.TypedValue;
@@ -71,7 +73,17 @@ public TypedValue getValueInternal(ExpressionState state) throws SpelEvaluationE
7173
return result;
7274
}
7375
TypedValue result = state.lookupVariable(this.name);
74-
this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(result.getValue());
76+
Object value = result.getValue();
77+
if (value == null || !Modifier.isPublic(value.getClass().getModifiers())) {
78+
// If the type is not public then when generateCode produces a checkcast to it
79+
// then an IllegalAccessError will occur.
80+
// If resorting to Object isn't sufficient, the hierarchy could be traversed for
81+
// the first public type.
82+
this.exitTypeDescriptor ="Ljava/lang/Object";
83+
}
84+
else {
85+
this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(value);
86+
}
7587
// a null value will mean either the value was null or the variable was not found
7688
return result;
7789
}

spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.util.StringTokenizer;
2626

2727
import org.junit.Test;
28-
2928
import org.springframework.asm.MethodVisitor;
3029
import org.springframework.expression.AccessException;
3130
import org.springframework.expression.EvaluationContext;
@@ -522,6 +521,19 @@ public void ternary() throws Exception {
522521
assertEquals(1,expression.getValue(root));
523522
}
524523

524+
@Test
525+
public void ternaryWithBooleanReturn() { // SPR-12271
526+
expression = parser.parseExpression("T(Boolean).TRUE?'abc':'def'");
527+
assertEquals("abc",expression.getValue());
528+
assertCanCompile(expression);
529+
assertEquals("abc",expression.getValue());
530+
531+
expression = parser.parseExpression("T(Boolean).FALSE?'abc':'def'");
532+
assertEquals("def",expression.getValue());
533+
assertCanCompile(expression);
534+
assertEquals("def",expression.getValue());
535+
}
536+
525537
@Test
526538
public void elvis() throws Exception {
527539
Expression expression = parser.parseExpression("'a'?:'b'");
@@ -1830,7 +1842,6 @@ public void methodReference_simpleInstanceMethodOneArgReturnPrimitive2() throws
18301842
assertEquals('c',resultC);
18311843
}
18321844

1833-
18341845
@Test
18351846
public void compoundExpression() throws Exception {
18361847
Payload payload = new Payload();
@@ -1914,7 +1925,18 @@ public void propertyReference() throws Exception {
19141925
assertCanCompile(expression);
19151926
assertEquals("value4",expression.getValue(tc));
19161927
}
1917-
1928+
1929+
@Test
1930+
public void propertyReferenceVisibility() { // SPR-12771
1931+
StandardEvaluationContext ctx = new StandardEvaluationContext();
1932+
ctx.setVariable("httpServletRequest", HttpServlet3RequestFactory.getOne());
1933+
// Without a fix compilation was inserting a checkcast to a private type
1934+
expression = parser.parseExpression("#httpServletRequest.servletPath");
1935+
assertEquals("wibble",expression.getValue(ctx));
1936+
assertCanCompile(expression);
1937+
assertEquals("wibble",expression.getValue(ctx));
1938+
}
1939+
19181940
@SuppressWarnings("unchecked")
19191941
@Test
19201942
public void indexer() throws Exception {
@@ -2954,4 +2976,28 @@ private static class TestClass9 {
29542976
public TestClass9(int i) {}
29552977
}
29562978

2979+
// These test classes simulate a pattern of public/private classes seen in Spring Security
2980+
2981+
// final class HttpServlet3RequestFactory implements HttpServletRequestFactory
2982+
static class HttpServlet3RequestFactory {
2983+
2984+
static Servlet3SecurityContextHolderAwareRequestWrapper getOne() {
2985+
HttpServlet3RequestFactory outer = new HttpServlet3RequestFactory();
2986+
return outer.new Servlet3SecurityContextHolderAwareRequestWrapper();
2987+
}
2988+
// private class Servlet3SecurityContextHolderAwareRequestWrapper extends SecurityContextHolderAwareRequestWrapper
2989+
private class Servlet3SecurityContextHolderAwareRequestWrapper extends SecurityContextHolderAwareRequestWrapper {
2990+
}
2991+
}
2992+
2993+
// public class SecurityContextHolderAwareRequestWrapper extends HttpServletRequestWrapper
2994+
static class SecurityContextHolderAwareRequestWrapper extends HttpServletRequestWrapper {
2995+
}
2996+
2997+
public static class HttpServletRequestWrapper {
2998+
public String getServletPath() {
2999+
return "wibble";
3000+
}
3001+
}
3002+
29573003
}

0 commit comments

Comments
 (0)