|
Description
|
Here is the small program
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Vector<Integer> ids = new Vector<Integer>() {
{
addElement(0);
}
};
int id = 0;
// for(int currentId : ids){ // (A)
for(int i = 0; i < ids.size(); i++) { // (B)
int currentId = ids.elementAt(i); // (B)
if(id != currentId){
ids.insertElementAt(new Integer(id), id);
System.out.println("damn");
}
}
}
}
I tried to debug this in Netbeans 6.5. If I step through the code using old-style for loop (B) the stepping works as expected.
If I use the enhanced-for loop (A), then the stepping stops at "damn" statement. Ofcourse it is not executing that statement. I guess it suppose to be hitting the closing brace after "damn".
Looks like the generated code has some wrong line number table, that is why this problem.
I used javap to get the Linenumber table attribute and it really seems to be wrong.
Main.java and Main.javap are attached.
Posted Date : 2009-02-18 22:49:20.0
|
|
Evaluation
|
This is tricky, consider the following code:
while (cond1) {
if (cond2) {
<body>
}
}
this translates to the following meta-bytecode:
:loop
if (cond1) jmp :end //LINE-INFO
if (!cond2) jmp :loop //LINE-INFO
<body> //LINE-INFO
jmp :loop
:end //LINE-INFO
However, if <body> contain a variable declaration, the code gets translated as follows:
:loop
if (cond1) jmp :end //LINE-INFO
if (!cond2) jmp :end-of-scope //LINE_INFO
<body> //LINE-INFO
:end-of-scope //LINE-INFO
jmp :loop
:end //LINE-INFO
That is, the compiler make sure that execution reaches the PC here informally denoted as :end-of-scope. In order to provide proper debugging support javac will also generate a LineNumberTable entry so that PC for :end-of-scope is associated to the close brace of the loop in the original source code.
In order to emit this additional LineNumberTable entry, javac needs the end position of the loop block - if the position is missing, javac does not generate this important debugging info.
What happens if the LineNumberTable entry for :end-of-scope is missing? If that line info is missing, each time the PC hits :end-of-scope, the LineNumberTable will associate that PC with the last LineNumberTable entry defined by <body>. Which means that, even if <body> has been totally skipped during execution (e.g. because cond2 is false), the debugger will still report that a line inside <body> is being executed - and that's because LineNumberTable is missing an entry.
Now back to the original problem: what has all this to do with enhanced for loops?
After some investigation I discovered that only iterable-based enhanced for loops are affected by this problem. That is array-based enhanced for loops generate correct info inside the LineNumberTable. The difference is caused by the fact that an iterable-based for loop gets translated as:
for (init; cond;)
while an array-based for loop gets translated as:
for (init; cond; step)
Because of that additional 'step' part, the compiler emits an additional LineNumberTable entry at the end of the loop - which solves the inconsistency problems we described above.
On the other hand, if 'step' is missing, the for basically gets translated as if it were a while loop - so we are exactly in the problematic scenario presented above. Moreover, it's important to recall that an iterable-based enhanced for loop gets translated as follows:
for (Iterator $it = ... ; it.hasNext();) {
Object elem = $it.next(); //synthetic statement
<body>
}
Which means that we have a variable (namely 'elem') that is declared inside the body of the translated loop. Because of this, javac will try to generate code so that the end of the loop is always reached, regardless of what happens inside body - on the other hand, since the new loop body has been generated during lowering, it is missing the end position info. Because of this, javac will fail to generate the LineNumberTable entry corresponding to the end of the loop body, which, in turn, causes the inconsistencies described in this report when e.g. such code is debugged.
Lower should preserve the end position info of the translated blocks - so that Gen could emit consistent debugging information.
Posted Date : 2009-02-20 15:13:06.0
|