Community
Participate
Working Groups
Here is the test case: [public class T { public void save() { int a = 3; try { Object warnings = null; try { Object contexts = null; try { System.out.println(warnings); return; } catch (NullPointerException npe) { System.out.println(contexts); return; } } catch (Exception e) { return; } } finally { int b = 4; System.out.println("#save -> " + b + a); } } }] If you compile this code using the Eclipse compiler and then run it using this VM: java version "1.3.0" Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0) Classic VM (build 1.3.0, J2RE 1.3.0 IBM build cn130-20000622 (JIT enabled: jitc)) You will get this error in the console: java.lang.VerifyError: (class: T, method: save signature: ()V) Localvariable 4 contains wrong type at java.lang.Class.forName1(Native Method) at java.lang.Class.forName(Class.java:134) at Test.main(Test.java:5) If you change the code with: [public class T { public void save() { int a = 3; try { Object warnings = null; try { Object contexts = null; System.out.println(contexts); try { System.out.println(warnings); return; } catch (NullPointerException npe) { return; } } catch (Exception e) { return; } } finally { int b = 4; System.out.println("#save -> " + b + a); } } }] The only difference is the location of the line: System.out.println(contexts); then you don't have a problem anymore. The bytecodes generated in the first case are: Method void save() 0 iconst_3 1 istore_1 2 aconst_null 3 astore_3 4 aconst_null 5 astore 4 7 getstatic #20 <Field java.io.PrintStream out> 10 aload_3 11 invokevirtual #26 <Method void println(java.lang.Object)> 14 jsr 44 17 return 18 astore 5 20 getstatic #20 <Field java.io.PrintStream out> 23 aload 4 25 invokevirtual #26 <Method void println(java.lang.Object)> 28 jsr 44 31 return 32 astore 4 34 jsr 44 37 return 38 astore_3 39 jsr 44 42 aload_3 43 athrow 44 astore_2 45 iconst_4 46 istore 4 48 getstatic #20 <Field java.io.PrintStream out> 51 new #28 <Class java.lang.StringBuffer> 54 dup 55 ldc #30 <String "#save -> "> 57 invokespecial #33 <Method java.lang.StringBuffer(java.lang.String)> 60 iload 4 62 invokevirtual #37 <Method java.lang.StringBuffer append(int)> 65 iload_1 66 invokevirtual #37 <Method java.lang.StringBuffer append(int)> 69 invokevirtual #41 <Method java.lang.String toString()> 72 invokevirtual #43 <Method void println(java.lang.String)> 75 ret 2 Exception table: from to target type 7 18 18 <Class java.lang.NullPointerException> 4 32 32 <Class java.lang.Exception> 2 38 38 any In the second case we got: Method void save() 0 iconst_3 1 istore_1 2 aconst_null 3 astore_3 4 aconst_null 5 astore 4 7 getstatic #20 <Field java.io.PrintStream out> 10 aload 4 12 invokevirtual #26 <Method void println(java.lang.Object)> 15 getstatic #20 <Field java.io.PrintStream out> 18 aload_3 19 invokevirtual #26 <Method void println(java.lang.Object)> 22 jsr 44 25 return 26 astore 5 28 jsr 44 31 return 32 astore 4 34 jsr 44 37 return 38 astore_3 39 jsr 44 42 aload_3 43 athrow 44 astore_2 45 iconst_4 46 istore 4 48 getstatic #20 <Field java.io.PrintStream out> 51 new #28 <Class java.lang.StringBuffer> 54 dup 55 ldc #30 <String "#save -> "> 57 invokespecial #33 <Method java.lang.StringBuffer(java.lang.String)> 60 iload 4 62 invokevirtual #37 <Method java.lang.StringBuffer append(int)> 65 iload_1 66 invokevirtual #37 <Method java.lang.StringBuffer append(int)> 69 invokevirtual #41 <Method java.lang.String toString()> 72 invokevirtual #43 <Method void println(java.lang.String)> 75 ret 2 Exception table: from to target type 15 26 26 <Class java.lang.NullPointerException> 4 32 32 <Class java.lang.Exception> 2 38 38 any The only difference I found relevant is that in the first case there is a aload4 bytecode after a jsr. In the second case the local 4 is never used outside the subroutine after the first jsr call at 22. That seems to be the problem. After the first jsr call at 14 in the failing case, there is a load of the local 4. The reason seems to be that the local array used in the subroutine is reused when the subroutine returns. Then the slot 4 is already used by a local of a different type (an int in the subroutine). The Sun VM 1.2.2 (java version "1.2.2" Classic VM (build JDK-1.2.2-001, native threads, symcjit)) has the same VerifyError. NOTES: OT (15/05/2001 3:25:12 PM) One way to fix it would be to use a different slot for the local inside the subroutine. javac 1.2.2 produces the following bytecodes for the first case: Method void save() 0 iconst_3 1 istore_1 2 aconst_null 3 astore 4 5 aconst_null 6 astore 5 8 getstatic #12 <Field java.io.PrintStream out> 11 aload 4 13 invokevirtual #13 <Method void println(java.lang.Object)> 16 jsr 44 19 return 20 pop 21 getstatic #12 <Field java.io.PrintStream out> 24 aload 5 26 invokevirtual #13 <Method void println(java.lang.Object)> 29 jsr 44 32 return 33 pop 34 jsr 44 37 return 38 astore_2 39 jsr 44 42 aload_2 43 athrow 44 astore_3 45 iconst_4 46 istore 4 48 getstatic #12 <Field java.io.PrintStream out> 51 new #7 <Class java.lang.StringBuffer> 54 dup 55 ldc #1 <String "#save -> "> 57 invokespecial #10 <Method java.lang.StringBuffer(java.lang.String)> 60 iload 4 62 invokevirtual #11 <Method java.lang.StringBuffer append(int)> 65 iload_1 66 invokevirtual #11 <Method java.lang.StringBuffer append(int)> 69 invokevirtual #15 <Method java.lang.String toString()> 72 invokevirtual #14 <Method void println(java.lang.String)> 75 ret 3 Exception table: from to target type 8 20 20 <Class java.lang.NullPointerException> 5 33 33 <Class java.lang.Exception> 2 38 38 any You can notice that the local used inside the subroutine is not the same than the one used for the contexts variable (local 5). But if I change the code to be: public class T { public void save() { int a = 3; try { Object warnings = null; try { Object contexts = null; try { System.out.println(warnings); return; } catch (NullPointerException npe) { System.out.println(contexts); System.out.println(warnings); return; } } catch (Exception e) { return; } } finally { int b = 4; System.out.println("#save -> " + b + a); } } } Then they produce: Method void save() 0 iconst_3 1 istore_1 2 aconst_null 3 astore 4 5 aconst_null 6 astore 5 8 getstatic #12 <Field java.io.PrintStream out> 11 aload 4 13 invokevirtual #13 <Method void println(java.lang.Object)> 16 jsr 52 19 return 20 pop 21 getstatic #12 <Field java.io.PrintStream out> 24 aload 5 26 invokevirtual #13 <Method void println(java.lang.Object)> 29 getstatic #12 <Field java.io.PrintStream out> 32 aload 4 34 invokevirtual #13 <Method void println(java.lang.Object)> 37 jsr 52 40 return 41 pop 42 jsr 52 45 return 46 astore_2 47 jsr 52 50 aload_2 51 athrow 52 astore_3 53 iconst_4 54 istore 4 56 getstatic #12 <Field java.io.PrintStream out> 59 new #7 <Class java.lang.StringBuffer> 62 dup 63 ldc #1 <String "#save -> "> 65 invokespecial #10 <Method java.lang.StringBuffer(java.lang.String)> 68 iload 4 70 invokevirtual #11 <Method java.lang.StringBuffer append(int)> 73 iload_1 74 invokevirtual #11 <Method java.lang.StringBuffer append(int)> 77 invokevirtual #15 <Method java.lang.String toString()> 80 invokevirtual #14 <Method void println(java.lang.String)> 83 ret 3 Exception table: from to target type 8 20 20 <Class java.lang.NullPointerException> 5 41 41 <Class java.lang.Exception> 2 46 46 any So they have the same bug, aload 5 is done after the jsr call at 16. Javac (1.3) fixes the problem. They don't use the same local at all. Method void save() 0 iconst_3 1 istore_1 2 aconst_null 3 astore_2 4 aconst_null 5 astore_3 6 getstatic #2 <Field java.io.PrintStream out> 9 aload_2 10 invokevirtual #3 <Method void println(java.lang.Object)> 13 jsr 50 16 return 17 astore 4 19 getstatic #2 <Field java.io.PrintStream out> 22 aload_3 23 invokevirtual #3 <Method void println(java.lang.Object)> 26 getstatic #2 <Field java.io.PrintStream out> 29 aload_2 30 invokevirtual #3 <Method void println(java.lang.Object)> 33 jsr 50 36 return 37 astore_3 38 jsr 50 41 return 42 astore 5 44 jsr 50 47 aload 5 49 athrow 50 astore 6 52 iconst_4 53 istore 7 55 getstatic #2 <Field java.io.PrintStream out> 58 new #6 <Class java.lang.StringBuffer> 61 dup 62 invokespecial #7 <Method java.lang.StringBuffer()> 65 ldc #8 <String "#save -> "> 67 invokevirtual #9 <Method java.lang.StringBuffer append(java.lang.String)> 70 iload 7 72 invokevirtual #10 <Method java.lang.StringBuffer append(int)> 75 iload_1 76 invokevirtual #10 <Method java.lang.StringBuffer append(int)> 79 invokevirtual #11 <Method java.lang.String toString()> 82 invokevirtual #12 <Method void println(java.lang.String)> 85 ret 6 Exception table: from to target type 6 17 17 <Class java.lang.NullPointerException> 4 37 37 <Class java.lang.Exception> 2 42 42 any The local used in the subroutine is in the slot 7 which is not used for the first locals contexts and warnings. OT (15/05/2001 4:24:10 PM) I am not sure this is a bug in the code gen. I think it is a bug in the bytecode verifier, but like this is a bug in one of the Sun VM, we need to find a workaround. The easiest way seems to do like in the javac 1.3 compiler: never reuse a slot for a local inside a subroutine.
Finally block scope is now nested under the try block scope. Note that it does not seem the catch blocks need the same. Added regression tests. Fixed in 204.
This fix was actually not good, and caused 4943 and 4919. New fix is actually to specifically shift return addresses when assigning positions to local variables in scopes. Return addresses are assigned positions at the end of the method. Fix in build > 204. Issued a patch204 for jdtcore.
Fixed now
PRODUCT VERSION: 106