JDK-8026508 : Invokedynamic instructions don't get line number table entries
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2013-10-15
  • Updated: 2015-01-15
  • Resolved: 2013-10-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8 b115Fixed
Related Reports
Blocks :  
Cloners :  
Relates :  
Relates :  
Description
Consider this small program:

1: package scratch;
2: public class MainApp {
3:   public static void main(String[] args) {
4:     Runnable r = () -> {};
5:     System.out.println(r.toString());
6:   }
7: }

When compiled with javac the resulting file looks like this:

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         5: astore_1
         6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: aload_1
        10: invokevirtual #4                  // Method java/lang/Object.toString:()Ljava/lang/String;
        13: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        16: return
      LineNumberTable:
        line 4: 5
        line 5: 6
        line 6: 16

Notice that line 4 begins at bci 5, not on the invokedynamic instruction.

When compiled with the eclipse compiler the file looks like:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: invokedynamic #19,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         5: astore_1
         6: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
         9: aload_1
        10: invokevirtual #26                 // Method java/lang/Object.toString:()Ljava/lang/String;
        13: invokevirtual #30                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        16: return
      LineNumberTable:
        line 4: 0
        line 5: 6
        line 6: 16
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      17     0  args   [Ljava/lang/String;
            6      11     1     r   Ljava/lang/Runnable;

Notice that line 4 begins at the bci 0.

I don't know which variant is right, but they differ. The problem is when debugging and setting a breakpoint on line 4, should the invokedynamic be executed before the breakpoint happens or not?

Comments
The invokedynamic instruction is the implementation of lambda capture, so it needs to be associated with the source code line that performs the lambda capture. Having the invokedynamic instruction associated with *no line* at all is nonsense, since it corresponds to an expression on line 4 (i.e., () -> {}). In general, we should try to associate all bytecodes with source lines, especially those which may perform calls and show up on back-traces. (Invokedynamic performs a call.) Here, Eclipse is right, javac is not right.
31-10-2013

I have accidentally used this bug number when fixing JDK-8026861. I have cloned this bug into JDK-8027142, and JDK-8026861 is fixed by the above commit: http://hg.openjdk.java.net/jdk8/tl/langtools/rev/b05db8c815e8 Sorry for the trouble.
23-10-2013

This bug is fixed and reviewed. We are waiting for the base JVM bug to make it into TL https://bugs.openjdk.java.net/browse/JDK-8026328
23-10-2013

ILW: MMM -> P3
21-10-2013

[~jjg] - if I understand your comment correctly - this issue is specific to lambda's usage of invokedynamic and not generic in invokedynamic and not a regression in 8, correct?
21-10-2013

Vita: the example uses lambda, which is new to 8.
21-10-2013

[~jlahoda] / [~ssides] - is this a regression in JDK 8?
21-10-2013

After some experimentation, here is a case where the difference can be observed in a debugger (using a recent NetBeans daily build - I assume there will be some adjustments to accommodate changes in LineNumberTable done in 8 related to JDK-7024096, so the outcome may be different in future builds): public class Test { public static void main(String[] args) { C c = () -> 1; int i1 = c //debugger will stop here .c() + 2; //and here int i2 = ((C) () -> 1) //but not here .c() + 2; //will only stop here } public interface C { public int c(); } }
18-10-2013

JDK-8026861 found while experimenting with cases around this bug.
18-10-2013

To expand on that: I think it is correct for the invokedynamic instruction (as such) not having an entry in the table (which is what JDK-8010404 states). But, at the same time, I think the local variable should "start" at the beginning of its initializer (so that the invokedynamic is not invoked before the stopping at the breakpoint placed at the variable). That would be consistent with how the LineNumberTable looks like for cases like: int r1 = 0; int r2 = variable; int r3 = methodCall();
16-10-2013

The immediate cause for the current situation appears to be the fix for JDK-8010404.
16-10-2013