Generated constructor returns from line number of other method
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Oct 22 09:52:56 UTC 2014
Hi Eirik,
this is definitively a javac issue; I isolated the problem to the
following test case:
class Test {
static class Empty { }
}
which generates the following LineNumberTable for the default constructor:
LineNumberTable:
line 1: 0
line 2: 4
However, if the static nested class is commented out, the following
LineNumberTable is obtained:
LineNumberTable:
line 1: 0
The problem seems to be caused by the fact that, during code translation
(Lower), javac replaces nested class declarations with empty blocks
which are then mistakenly confused by a subsequent code generation step
for instance initializers (!!) - this means that the generated code
would look like:
class Test {
{ } // <-------------
Test() {
super();
}
}
And then, after initializer normalization, it becomes:
class Test {
Test() {
super();
{ } <------------
}
}
That's wrong, as the empty block does not correspond to a static
instance initializer, but, rather, to a compile-time artifact and should
therefore be skipped.
I filed this:
https://bugs.openjdk.java.net/browse/JDK-8061778
to keep track of this issue.
Thanks for the report.
Maurizio
On 22/10/14 07:31, Eirik Bjørsnøs wrote:
> If it's even more odd that rather odd, might we even call it a bug? :-)
>
> I noticed that javac also generates a class
> WeirdConstructorLinenumber$1 containing a <clinit> which initialises
> an int[] switch map:
>
> class no.kantega.labs.revoc.demo.WeirdConstructorLinenumber$1 {
> static final int[] $SwitchMap$java$math$RoundingMode;
>
> static {};
> Code:
> 0: invokestatic #1 // Method
> java/math/RoundingMode.values:()[Ljava/math/RoundingMode;
> 3: arraylength
> 4: newarray int
> 6: putstatic #2 // Field
> $SwitchMap$java$math$RoundingMode:[I
> 9: return
> LineNumberTable:
> line 8: 0
> }
>
> Looks like for some reason the line number for the <clinit> "leaks"
> over to the <init> of the enclosing class.
>
> I first noticed this issue when working on a code coverage tool. For
> performance reasons, the tool instruments line number instructions not
> by their actual line numbers, but with the count of the first time a
> line number occurs in the byte code for that class. The odd line
> number in <init> shifted the line count by one which caused line
> counts for a method to spill over to the next method in the class.
>
> I imagine other source-aware tools such as code coverage tools, static
> analysis tools or even debuggers might be affected by this.
>
> Eirik.
>
>
>
> On Wed, Oct 22, 2014 at 12:52 AM, Alex Buckley <alex.buckley at oracle.com> wrote:
>> It's rather odd that an <init> method corresponding to a default constructor has a LineNumberTable attribute in the first place. After all, the default constructor doesn't correspond to any lines of code in the original source file.
>>
>> The fact that <init>'s LineNumberTable attribute changes based on the body of another method is even more odd.
>>
>> Alex
>>
>>
>> On 10/21/2014 3:19 PM, Eirik Bjørsnøs wrote:
>>> Hi,
>>>
>>> Given a class containing a method switching on an enum, like this:
>>>
>>> 1: public class WeirdConstructorLinenumber
>>> 2: {
>>> 3: public void consider(java.math.RoundingMode mode) {
>>> 4: switch ( mode ) {}
>>> 5: }
>>> 6: }
>>>
>>> javac generates a default constructor as expected.
>>>
>>> But the generated constructor returns from line 4 (which really belongs
>>> to the "consider" method).
>>>
>>> I could only make this happen by having my consider method switc on an
>>> enum. If I remove the switch or switch on something different, like an
>>> int, the constructor is generated with a single line and returns at line
>>> 1 as expected.
>>>
>>> Could this be a javac bug? Or is there something I'm just completely
>>> missing?
>>>
>>> $ javac WeirdConstructorLinenumber.java
>>> $ javap -c -l -classpath . WeirdConstructorLinenumber
>>> Compiled from "WeirdConstructorLinenumber.java"
>>> public class WeirdConstructorLinenumber {
>>> public WeirdConstructorLinenumber();
>>> Code:
>>> 0: aload_0
>>> 1: invokespecial #1 // Method
>>> java/lang/Object."<init>":()V
>>> 4: return
>>> LineNumberTable:
>>> line 1: 0
>>> line 4: 4
>>>
>>> [...]
>>> }
>>>
>>>
>>> Thanks,
>>> Eirik Bjørsnøs
>>>
More information about the compiler-dev
mailing list