Hello
I have been doing quite a bit of analysis of the
verify error that I am seeing with Janino compiled
code. This is on a Win32 system with JDK 1.4 from Sun.
It seems the problem is actually in the Java verifier,
but Sun's javac does not trigger it because it generates
code that differs from the code generated by Janino.
Here is a very small example that triggers the bug.
public class Test {
public static void foo() {
try {
int dummy5 = 5;
int dummy4 = 4;
boolean b = true;
while (b) {
try {
return;
} catch (Exception ex) {}
}
} finally {}
}
public static void main(String[] args) {
int loops = 0;
foo();
if (loops == 0) {
System.out.println("OK");
} else {
System.out.println("ERROR");
}
}
}
This class compiles and runs correctly with javac.
When compiled with Janino, the following error
is generated:
The problem appears to be that a use of the local var slot 2
generating the error. The while loop reads slot 2 when
it tests the boolean "b". It turn out that the generated
code also uses slot 2 is jsr/ret instructions.
One can test this slot 2 thing by commenting out the "dummy1"
and "dummy2" variables. The following example compiles and
does not fail the validate step, the only diff is that the
two local vars are commented out.
public class Test2 {
public static void foo() {
try {
boolean b = true;
while (b) {
try {
return;
} catch (Exception ex) {}
}
} finally {}
}
public static void main(String[] args) {
int loops = 0;
foo();
if (loops == 0) {
System.out.println("OK");
} else {
System.out.println("ERROR");
}
}
}
Taking a peek at the generated code emitted by javac and
janino helps to understand the problem. I have only included
the byte code for the foo() method here.
At first glance, it does not look like anything is wrong
with the code emitted by Janino. In fact, one can compare
the janino emitted code for Test.java to the janino emitted
code for Test2.java:
The janino emitted code for Test.java fails, but the same logic
does not fail for Test2.java. The diff is that Test2.java does
not use local slot 2 for the boolean in the while expr condition.
The boolean "b" in Test2.java uses slot 0 so the use of slot 2
in the jsr/ret instructions does not trigger the validation error.
I see a couple of possible approaches to work around this issue.
Janino could be modified to emit code that worked more like javac,
so that jsr/ret instructions would not be used in these situations.
Another possible approach would be to detect when a loop uses a
register that will also be used in try/catch/finally blocks. The
try/catch/finally blocks could then use some other register to
implement the jsr/ret logic. Any advice as to which approach
would be better?
cheers
Mo DeJong