-
Notifications
You must be signed in to change notification settings - Fork 38.4k
VerifyError when trying to compile constructor invocation with SpEL [SPR-12326] #16931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Juergen Hoeller commented Andy, may I assign this to you... The provided test case does fail so the issue is definitely valid - and hopefully easy enough to resolve. Juergen |
Christoph Strobl commented The test below passes if @Test
public void youShouldNotFailSouldYou() {
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, this
.getClass().getClassLoader()));
Person person = new Person(1);
SpelExpression ex = parser.parseRaw("#it?.age?.equals([0])");
StandardEvaluationContext context = new StandardEvaluationContext(new Object[] { person.getAge() });
context.setVariable("it", person);
assertThat(ex.getValue(context, Boolean.class), is(true));
assertThat(ex.getValue(context, Boolean.class), is(true));
}
public class Person {
private int age;
public Person(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
} |
Thomas Darimont commented Hello, I had a quick look at the issue: It seems that in So if I change: Object result = this.compiledAst.getValue(target,context); to be: Object target = context.getRootObject().getValue();
Object result = this.compiledAst.getValue(target,context); We can make sure that the approrpriate target value is passed on. Another thing I found was, that there seems to be a cf.exitCompilationScope();
``` within the loop for each parameter value (to please the bytecode verifier).
So if I change:
```java
...
cf.exitCompilationScope();
}
... to be: ...
mv.visitTypeInsn(CHECKCAST,paramDescriptors[c-1].substring(1)); // it's probably not always needed
cf.exitCompilationScope();
}
... I can run the above SpEL expression from the example:
without any problems in IMMEDIATE mode :) Hope this helps! Cheers, |
Thomas Darimont commented I think Christophs problem is similar to the one mentioned above. In Object result = this.compiledAst.getValue(null,context); to Object target = context.getRootObject().getValue();
Object result = this.compiledAst.getValue(target,context); and load the target variable in if (descriptor == null && !isStaticMethod) {
cf.loadTarget(mv);
} to if (descriptor == null || !isStaticMethod) {
cf.loadTarget(mv);
} I can run Christophs test without any problems. |
Andy Clement commented I already have the fix ready for your problem Thomas - it was simply to change the constructor argument processing to use the new common argument processing code I built for method argument processing (under #16933). This also means constructor references can use varargs constructors as a bonus. I'll commit it in a bit after digesting Christoph's issue. |
Thomas Darimont commented Great! thanks for having a look at tit :) |
Andy Clement commented Fixes all pushed. Multiple fixes, some as Thomas discovered:
I did not make the final change Thomas mentions though. The condition "if (descriptor == null && !isStaticMethod) {" is saying "if there is nothing on the stack but we need something", if I change that to "if (descriptor == null || !isStaticMethod) {" then I think I'll get unnecessary stuff loaded onto the stack and possibly the wrong thing. The problem the testcase was hitting is that there was a primitive on the stack and it wasn't being boxed before equals was being invoked, by adjusting it to '||' we were slapping the root object onto the stack again which just hid the problem with the primitive. A slightly variant of the failing testcase shows the primitive boxing is the right thing to do. |
Christoph Strobl commented Thanks for the fix - though I ran into problems when trying the following. @Test
public void failsWhenSettingContextForExpression() {
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, this
.getClass().getClassLoader()));
Person person = new Person("foo", 1);
SpelExpression expression = parser.parseRaw("#it?.age?.equals([0])");
StandardEvaluationContext context = new StandardEvaluationContext(new Object[] { 1 });
context.setVariable("it", person);
expression.setEvaluationContext(context);
System.out.println(expression.getValue(Boolean.class));
System.out.println(expression.getValue(Boolean.class));
//have to call it 3 time to fail
System.out.println(expression.getValue(Boolean.class));
} the above works fine when I use |
Andy Clement commented I'll take a look, thanks for the testcase. |
Andy Clement commented
I've also made the following changes to improve varargs handling:
The change to ordering and close match selection may affect existing programs out there but these programs are operating with unstable orderings right now that might change between invocations. I think we want stability. |
Christoph Strobl commented Thanks Andy Clement that solved it! |
Thomas Darimont opened SPR-12326 and commented
I tried to speed up dynamic instance creation by compiling constructor invocations with SpEL. Unfortunately I get a VerifyError when I try to evaluate the expression via
getValue();
.I added a test case that demonstrates the issue.
The test case passes when
SpelCompilerMode.OFF
is used.Tested this with (java version "1.8.0_20") and compile target java6.
StackTrace with Spring Framework 4.1:
StackTrace with Spring Framework 4.1.1:
Affects: 4.1 GA, 4.1.1
Issue Links:
0 votes, 6 watchers
The text was updated successfully, but these errors were encountered: