Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 343785

Summary: [1.7] Incorrect line numbers in stack trace with try with resources
Product: [Eclipse Project] JDT Reporter: Deepak Azad <deepakazad>
Component: CoreAssignee: Srikanth Sankaran <srikanth_sankaran>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: Olivier_Thomann, srikanth_sankaran
Version: 3.7   
Target Milestone: 3.7.1   
Hardware: PC   
OS: Windows XP   
Whiteboard:
Attachments:
Description Flags
Patch under consideration
none
Proposed patch none

Description Deepak Azad CLA 2011-04-25 23:52:35 EDT
--------------------------------------------------------------------
public class TryWithResources1 {
	public static void main(String[] args) throws Throwable {
		try (Test t = new Test()) {
			for (int i = 0; i < 10; i++) {
				int a = 10;
				System.out.println(i);
				a++;
				a *= 10;
				System.out.println(a);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class Test implements AutoCloseable {
	@Override
	public void close() throws Exception {
		System.out.println("closing");
		throw new Exception();
	}
}
------------------------------------------------------------------
When I compile this with Eclipse and then run I get
java.lang.Exception
	at Test.close(TryWithResources1.java:21)
	at TryWithResources1.main(TryWithResources1.java:4)

On the other hand when I compile this with javac and then run I get
java.lang.Exception
        at Test.close(TryWithResources1.java:21)
        at TryWithResources1.main(TryWithResources1.java:11)

Notice the line numbers 4 and 11 in the two stack traces. According to eclipse the exception happened on the for loop, which is wrong (I think it marks the exception at the beginning of the last statement in the try block). With javac the exception happens on the catch, which I think is more correct.
Comment 1 Deepak Azad CLA 2011-04-26 00:11:02 EDT
Another example
----------------------------------------------------------------------
package q;
public class TryWithResources1 {
	public static void main(String[] args) throws Throwable {
		try (Test t1 = new Test(false)) {
			try (Test t2 = new Test(true)) {  //5
			}                                 //6
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
class Test implements AutoCloseable {
	private boolean throwException;
	Test(boolean throwException) {
		this.throwException = throwException;	
	}
	
	@Override
	public void close() throws Exception {
		if (throwException)
			throw new Exception();
	}
}
---------------------------------------------------------------------
Eclipse gives this stack trace
java.lang.Exception
	at q.Test.close(TryWithResources1.java:21)
	at q.TryWithResources1.main(TryWithResources1.java:5) // the line number should be 6
Comment 2 Srikanth Sankaran CLA 2011-04-26 00:52:20 EDT
I remember consciously leaving it as it is since in some
tests it was more reasonable than what javac was reporting.

I'll take a second look.
Comment 3 Srikanth Sankaran CLA 2011-04-26 08:29:28 EDT
(In reply to comment #2)
> I remember consciously leaving it as it is since in some
> tests it was more reasonable than what javac was reporting.

Here is a test case where eclipse's behavior is more intuitive
than javac. We show line 5 as the invocation site of close()
call while javac shows line 10. I think line 5 sheds more light
on what is going on in the program.

public class X {
    public static void main(String[] args) throws Throwable {
    	while (true)
        try (Test t = new Test()) {
            	break;	// line 5




        } // line 10
    }
}

class Test implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("closing");
        throw new Exception();
    }
}

For the test case reported at comment# 0 I agree javac behavior is
more reasonable.

Patch will follow shortly.
Comment 4 Srikanth Sankaran CLA 2011-04-26 08:37:54 EDT
(In reply to comment #0)
> With javac
> the exception happens on the catch, which I think is more correct.

The exception cannot be attributed to the catch. It happens at the end
of the try block. You can easily verify this by inserting a few blank
lines between the } and the catch block.
Comment 5 Deepak Azad CLA 2011-04-26 08:58:03 EDT
(In reply to comment #4)
> (In reply to comment #0)
> > With javac
> > the exception happens on the catch, which I think is more correct.
> 
> The exception cannot be attributed to the catch. It happens at the end
> of the try block. You can easily verify this by inserting a few blank
> lines between the } and the catch block.
Yup, I agree.


(In reply to comment #3)
> Here is a test case where eclipse's behavior is more intuitive
> than javac. We show line 5 as the invocation site of close()
> call while javac shows line 10. I think line 5 sheds more light
> on what is going on in the program.
> 
> public class X {
>     public static void main(String[] args) throws Throwable {
>         while (true)
>         try (Test t = new Test()) {
>                 break;    // line 5
> 
> 
> 
> 
>         } // line 10
>     }
> }
> 
> class Test implements AutoCloseable {
>     @Override
>     public void close() throws Exception {
>         System.out.println("closing");
>         throw new Exception();
>     }
> }
> 
> For the test case reported at comment# 0 I agree javac behavior is
> more reasonable.
> 
> Patch will follow shortly.

I disagree here. The exception does not happen on the 'break', it happens after the try block (in a 'hidden' finally block). Hence showing line 10 is more accurate. In the example from comment 0 I had just made the last statement of the try block span more than 1 line to make the problem stand out a bit more :)
Comment 6 Srikanth Sankaran CLA 2011-04-26 09:00:07 EDT
Created attachment 194048 [details]
Patch under consideration
Comment 7 Srikanth Sankaran CLA 2011-04-26 09:09:55 EDT
(In reply to comment #5)

> I disagree here. The exception does not happen on the 'break', it happens after
> the try block (in a 'hidden' finally block). Hence showing line 10 is more
> accurate. In the example from comment 0 I had just made the last statement of
> the try block span more than 1 line to make the problem stand out a bit more :)

I'll leave this case as it is - the synthetic finally block is inlined into
the break/continue/return site and I feel the stack trace better educates
the user the origins of the exception.  Other cases are fixed by the patch.
Comment 8 Deepak Azad CLA 2011-04-26 09:48:41 EDT
(In reply to comment #7)
> I'll leave this case as it is - the synthetic finally block is inlined into
> the break/continue/return site and I feel the stack trace better educates
> the user the origins of the exception.  Other cases are fixed by the patch.
I see, you want the last statement that was executed in the block to be mentioned in the stack trace and in case of break/continue/return the last statement could be in the middle of the block. 

Hence, in this case the line containing the first 'break' is mentioned
--------------------------------------------------------------------
public class X {
	public static void main(String[] args) throws Throwable {
		int i = 10;
		while (true)
			try (Test t = new Test()) {
				if (i == 10)
					break;
				else
					break;
			}
	}
}

class Test implements AutoCloseable {
	@Override
	public void close() throws Exception {
		System.out.println("closing");
		throw new Exception();
	}
}
--------------------------------------------------------------------

and in this case the line containing the closing brace for the try block is mentioned (because something() throws an exception, hence return statement does not complete execution)
--------------------------------------------------------------------
public class X {
	public static void main(String[] args) throws Throwable {
		foo();
	}

	private static int foo() throws Throwable {
		int i = 10;
		while (true)
			try (Test t = new Test()) {
				if (i == 10)
					return something();
				else
					return something();
			}
	}

	private static int something() throws Throwable {
		throw new IllegalArgumentException();
	}
}

class Test implements AutoCloseable {
	@Override
	public void close() throws Exception {
		System.out.println("closing");
		throw new Exception();
	}
}
--------------------------------------------------------------------

Is this understanding correct ?
Comment 9 Srikanth Sankaran CLA 2011-04-26 10:45:03 EDT
(In reply to comment #8)
> (In reply to comment #7)
> > I'll leave this case as it is - the synthetic finally block is inlined into
> > the break/continue/return site and I feel the stack trace better educates
> > the user the origins of the exception.  Other cases are fixed by the patch.
> I see, you want the last statement that was executed in the block to be
> mentioned in the stack trace and in case of break/continue/return the last
> statement could be in the middle of the block.

The code that needs to be generated for a break/continue/return
is dependent on the context and where that code has to be contain
synthetic constructs that have no source correlation, the compiler
has some wiggle room on how/where the byte code is charged. 

In the current scheme of code generation, the AutoCloseable.close
calls get emitted right at the break/continue/return site without
there being a control transfer of some form to a hidden block.

As a side effect, this has the "beneficial" effect of showing some
more details about the close method came to be invoked.

This does differ from how the stack trace looks for the non-resource
case. Now, I am in two minds whether to leave it as is or to go for
consistency at the expense of arguably better clarify in context.

What do you think ?
Comment 10 Deepak Azad CLA 2011-04-26 11:04:40 EDT
(In reply to comment #9)
> In the current scheme of code generation, the AutoCloseable.close
> calls get emitted right at the break/continue/return site without
> there being a control transfer of some form to a hidden block.

Isn't this an implementation detail? Wouldn't most developers imagine that there is a control transfer to a hidden finally block ?
Comment 11 Srikanth Sankaran CLA 2011-04-26 11:21:55 EDT
Created attachment 194068 [details]
Proposed patch

(In reply to comment #10)
> (In reply to comment #9)
> > In the current scheme of code generation, the AutoCloseable.close
> > calls get emitted right at the break/continue/return site without
> > there being a control transfer of some form to a hidden block.
> 
> Isn't this an implementation detail? Wouldn't most developers imagine that
> there is a control transfer to a hidden finally block ?

May be. This patch fixes even the break/continue/return cases to be consistent
with javac and eclipse non-resources case.
Comment 12 Srikanth Sankaran CLA 2011-04-26 11:25:56 EDT
Released latest patch into BETA_JAVA7 branch. Thanks Deepak. Let
us know if something else is broken.
Comment 13 Olivier Thomann CLA 2011-06-28 09:59:34 EDT
Verified using Java 7 feature patch.