Bug 3264 - VerifyError related to a local index computation (1GDS7IP)
Summary: VerifyError related to a local index computation (1GDS7IP)
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 2.0   Edit
Hardware: All Windows 2000
: P3 normal (vote)
Target Milestone: 2.0 M1   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2001-10-10 22:52 EDT by Olivier Thomann CLA
Modified: 2002-01-11 09:22 EST (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Olivier Thomann CLA 2001-10-10 22:52:14 EDT
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.
Comment 1 Philipe Mulet CLA 2001-10-12 06:37:03 EDT
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.
Comment 2 Philipe Mulet CLA 2001-10-16 07:37:33 EDT
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.
Comment 3 Philipe Mulet CLA 2001-10-22 13:40:39 EDT
Fixed now
Comment 4 DJ Houghton CLA 2001-10-23 23:53:22 EDT
PRODUCT VERSION:
	106