Bug 108180 - [compiler] Sanity check error with try/finally block
Summary: [compiler] Sanity check error with try/finally block
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows XP
: P3 major with 2 votes (vote)
Target Milestone: 3.2 RC1   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-08-26 17:31 EDT by Phil Larson CLA
Modified: 2006-04-13 14:18 EDT (History)
1 user (show)

See Also:


Attachments
Proposed fix (1010 bytes, patch)
2005-08-29 13:41 EDT, Olivier Thomann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Phil Larson CLA 2005-08-26 17:31:42 EDT
Build id: I20050627-1435

public class X {
    public static Object sanityCheckBug() {
        Object obj;

        try {
            obj = new Object();
            return obj;
        } finally {
             obj = null;
        }
    }
	
    public static void main(String[] arguments) {
		X.sanityCheckBug();
    }
}

---
Description: SANITY CHECK: Invalid attribute for local variable obj
Severity: Error
On Resource: X.java
Location: line 2
----------
Comment 1 Olivier Thomann CLA 2005-08-26 22:23:54 EDT
Reproduced with compliance 1.5 + debug attributes.
I will investigate.
Comment 2 Olivier Thomann CLA 2005-08-29 13:41:52 EDT
Created attachment 26593 [details]
Proposed fix

The fix might be to closed open initialization ranges before generating the
finally block.
Comment 3 Olivier Thomann CLA 2005-08-29 13:43:39 EDT
Philippe, please review the fix. I will run all tests with this change and
update the pr.
The problem can occur in 1.5 mode when generating debug attributes or in 1.4
mode when generating debug attributes with inlining of the sub routines.
So this might be a candidate for 3.1.1.
Comment 4 Olivier Thomann CLA 2005-08-29 14:23:15 EDT
This fix has a side-effect on this test case:
public class X {
	private void foo(boolean delete) {
		
		String s = bar();
		StringBuffer buffer =new StringBuffer();
		
		try {
			
			String[] datas = new String[] { "" };
			Object[] data= new Object[] { s };
			try {
				buffer.append(datas).append(data);
			} catch (Exception e) {
				if (e != null)
					throw e;
				return;
			}
				
			if (delete)
				buffer.delete(0, buffer.length());
			
		} catch (Exception x) {
		} finally {
			buffer = null;
		}
	}
	
	String bar() {
		return "";
	}
	
	public static void main(String[] args) {
		new X().foo(false);
		System.out.println("SUCCESS");
	}
}

At the return statement in the catch block we used to "show" the local variables
datas and data in the case the subroutine is inlined. With the fix the local
variable ranges for datas and data is split into two pieces.
We now have:
        [pc: 24, pc: 59] local: datas index: 4 type: java.lang.String[]
        [pc: 62, pc: 79] local: datas index: 4 type: java.lang.String[]
        [pc: 34, pc: 59] local: data index: 5 type: java.lang.Object[]
        [pc: 62, pc: 79] local: data index: 5 type: java.lang.Object[]
        [pc: 51, pc: 59] local: e index: 6 type: java.lang.Exception
instead of:
        [pc: 24, pc: 79] local: datas index: 4 type: java.lang.String[]
        [pc: 34, pc: 79] local: data index: 5 type: java.lang.Object[]
        [pc: 51, pc: 62] local: e index: 6 type: java.lang.Exception

I don't think this is wrong if we consider that neither e, datas or data can be
referenced in the subroutine. So technically they are not definitely assigned
when the subroutine is executed. But they are definitely assigned for this
return statement inside the catch block.
Let me know if you believe this is the good fix.
Comment 5 Philipe Mulet CLA 2006-04-10 12:13:36 EDT
Re: comment 3. I don't think the proposed fix is good. Instead of closing extra interval, I rather believe it should extend it. 
Comment 6 Olivier Thomann CLA 2006-04-10 12:23:10 EDT
Not sure.
When entering the finally block, the aconst_null should not be part of the range for obj. The range should start when null is assigned to obj. You can have an exception before you execute obj = new Object(), which means obj should not appear in the locals as long as null has not been assigned to it.
Comment 4 shows a better case where the previous range is split into two ranges. This looks consistent with what the debugger should show fot the value of datas and data.
Comment 7 Philipe Mulet CLA 2006-04-10 12:29:09 EDT
The initial assignment isn't to be included.
This shouldn't be a problem.
Also, in this very case, there would be 2 occurrences of the inlined finally block.


  // Method descriptor #15 ()Ljava/lang/Object;
  // Stack: 2, Locals: 3
  public static Object sanityCheckBug();
     0  new Object [3]
     3  dup
     4  invokespecial Object() [8]
     7  astore_0 [obj]
     8  aload_0 [obj]
     9  astore_2
    10  aconst_null
    11  astore_0 [obj]
    12  aload_2
    13  areturn
    14  astore_1
    15  aconst_null
    16  astore_0 [obj]
    17  aload_1
    18  athrow
      Exception Table:
        [pc: 0, pc: 10] -> 14 when : any
      Line numbers:
        [pc: 0, line: 5]
        [pc: 8, line: 6]
        [pc: 10, line: 8]
        [pc: 12, line: 6]
        [pc: 14, line: 7]
        [pc: 15, line: 8]
        [pc: 17, line: 9]
      Local variable table:
        [pc: 8, pc: 14] local: obj index: 0 type: Object
        [pc: 17, pc: 19] local: obj index: 0 type: Object


Now, I feel concerned about other instances where we do reuse the inlined code, and where the variable may not have been initialized. It feels safer to kill the variable from visible when entering the finally block codegen.
Comment 8 Philipe Mulet CLA 2006-04-10 12:39:26 EDT
Actually, I buy the proposed fix, augmented with code to reuse not closed interval, if this ever occurs.

Would now issue:
  // Method descriptor #15 ()Ljava/lang/Object;
  // Stack: 2, Locals: 3
  public static Object sanityCheckBug();
     0  new Object [3]
     3  dup
     4  invokespecial Object() [8]
     7  astore_0 [obj]
     8  aload_0 [obj]
     9  astore_2
    10  aconst_null
    11  astore_0 [obj]
    12  aload_2
    13  areturn
    14  astore_1
    15  aconst_null
    16  astore_0 [obj]
    17  aload_1
    18  athrow
      Exception Table:
        [pc: 0, pc: 10] -> 14 when : any
      Line numbers:
        [pc: 0, line: 5]
        [pc: 8, line: 6]
        [pc: 10, line: 8]
        [pc: 12, line: 6]
        [pc: 14, line: 7]
        [pc: 15, line: 8]
        [pc: 17, line: 9]
      Local variable table:
        [pc: 8, pc: 10] local: obj index: 0 type: Object
        [pc: 12, pc: 14] local: obj index: 0 type: Object
        [pc: 17, pc: 19] local: obj index: 0 type: Object
Comment 9 Philipe Mulet CLA 2006-04-11 03:05:19 EDT
Tuned tests, added TryStatementTest#test046
Fixed
Comment 10 Frederic Fusier CLA 2006-04-13 14:18:02 EDT
Verified for 3.2 RC1 using build I20060413-0010.