JDK-8010404 : Lambda debugging: redundant LineNumberTable entry for lambda capture
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8,8-repo-lambda
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2013-03-20
  • Updated: 2013-10-31
  • Resolved: 2013-04-08
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 b86Fixed
Related Reports
Relates :  
Relates :  
Description
When debugging a program that uses Lambda feature, then during stepping over an expression with nested Lambdas, there are extra steps in some cases. E.g. when Lambda is in an argument to a method call, like:

44    private static void simpleIterateWithIndex(List<String> list) {
45        eachWithIndex(list,
46            (value, index) -> {
47                String output = String.format("%d -> %s", index, value);
48                System.out.println(output);
49            }
50        );
51    }

which compiles as

  private static void simpleIterateWithIndex(java.util.List<java.lang.String>);
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokedynamic #14,  0             // InvokeDynamic #2:lambda:()Lcom/toy/anagrams/ui/LambdaExpressions$ItemWithIndexVisitor;
         6: invokestatic  #12                 // Method eachWithIndex:(Ljava/util/List;Lcom/toy/anagrams/ui/LambdaExpressions$ItemWithIndexVisitor;)V
         9: return
      LineNumberTable:
        line 45: 0
        line 46: 1
        line 45: 6
        line 51: 9

The reason can be seen in the LineNumberTable. First, program is paused at line 45 as expected. When we do step over, program is paused at line 46, where Lambda is being created. IMHO this pause is not necessary. Further step over then goes again to line 45. I consider this to be confusing. After next step, program is finally paused at line 51.

Is a reason for these extra items in LineNumberTable?

When I compare it to an inner class doing the same work as the Lambda, the stepping is much more smooth:

53    private static void simpleIterateWithIndex2(List<String> list) {
54        eachWithIndex(list,
55            new ItemWithIndexVisitor() {
56                @Override
57                public void visit(Object value, int index) {
58                    String output = String.format("%d -> %s", index, value);
59                    System.out.println(output);
60                }
61            }
62        );
63    }

which compiles to

  private static void simpleIterateWithIndex2(java.util.List<java.lang.String>);
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: new           #15                 // class com/toy/anagrams/ui/LambdaExpressions$1
         4: dup
         5: invokespecial #16                 // Method com/toy/anagrams/ui/LambdaExpressions$1."<init>":()V
         8: invokestatic  #12                 // Method eachWithIndex:(Ljava/util/List;Lcom/toy/anagrams/ui/LambdaExpressions$ItemWithIndexVisitor;)V
        11: return
      LineNumberTable:
        line 54: 0
        line 63: 11

It can be seen, that the step over goes from line 54 straight to line 63, over the method invocation.

Can the LineNumberTable be simplified for Lambdas so that debugger stepping is more smooth?
Comments
In this case the invokedynamic instruction does something without any internal states that the end user cares about, so it is reasonable to omit a line number. But if the invokedynamic instruction is the implementation of a user-visible call, such that an exception or debugger interaction could occur inside the call, then it should have its own line number. Since javac only emits invokedynamic instructions without internal states, it is reasonable to suppress the line number. If later applications of invokedynamic can user-visible code, then javac will have to remember which indy instructions are of which kind and emit line numbers selectively.
31-10-2013

verified in jdk8 b92
06-06-2013