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
This commit is contained in:
Andy Clement
2014-10-01 09:26:05 -07:00
parent b2d67914a8
commit bd7d56ac54
4 changed files with 69 additions and 6 deletions

View File

@@ -446,8 +446,10 @@ public class CodeFlow implements Opcodes {
}
}
else {
// This is chopping off the 'L' to leave us with "java/lang/String"
mv.visitTypeInsn(CHECKCAST, descriptor.substring(1));
if (!descriptor.equals("Ljava/lang/Object")) {
// This is chopping off the 'L' to leave us with "java/lang/String"
mv.visitTypeInsn(CHECKCAST, descriptor.substring(1));
}
}
}
}

View File

@@ -109,6 +109,9 @@ public class Ternary extends SpelNodeImpl {
computeExitTypeDescriptor();
codeflow.enterCompilationScope();
this.children[0].generateCode(mv, codeflow);
if (!CodeFlow.isPrimitive(codeflow.lastDescriptor())) {
CodeFlow.insertUnboxInsns(mv, 'Z', codeflow.lastDescriptor());
}
codeflow.exitCompilationScope();
Label elseTarget = new Label();
Label endOfIf = new Label();

View File

@@ -16,6 +16,8 @@
package org.springframework.expression.spel.ast;
import java.lang.reflect.Modifier;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.TypedValue;
@@ -71,7 +73,17 @@ public class VariableReference extends SpelNodeImpl {
return result;
}
TypedValue result = state.lookupVariable(this.name);
this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(result.getValue());
Object value = result.getValue();
if (value == null || !Modifier.isPublic(value.getClass().getModifiers())) {
// If the type is not public then when generateCode produces a checkcast to it
// then an IllegalAccessError will occur.
// If resorting to Object isn't sufficient, the hierarchy could be traversed for
// the first public type.
this.exitTypeDescriptor ="Ljava/lang/Object";
}
else {
this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(value);
}
// a null value will mean either the value was null or the variable was not found
return result;
}