Bug 422015 - Improve handling debugger stack frames containing function objects and lambda implementation methods
Summary: Improve handling debugger stack frames containing function objects and lambda...
Status: ASSIGNED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Debug (show other bugs)
Version: 4.3.1   Edit
Hardware: All All
: P3 enhancement with 1 vote (vote)
Target Milestone: ---   Edit
Assignee: Jesper Moller CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-11-18 19:24 EST by Jesper Moller CLA
Modified: 2015-05-15 08:10 EDT (History)
8 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jesper Moller CLA 2013-11-18 19:24:22 EST
In the debugger, the user should not be too confused about how lambdas and reference expressions are implemented. The LambdaMetafactory will produce a class and an instance for the lambda or reference expression, and we should consider if we need to provide some visual aid to the user that a function object is on the stack and that (s)he cannot debug or step into it.

I have a number of questions, and my preference in brackets. Please vote

1. Should we hide the function object?  [no]
2. Should we consider some sort of graying out the stack frame as to show that it's not something you can step into? [yes]
3. If there are bound variables, should we hide or show them? [show, like we do for inner classes]
4. Should we try to hide/gloss over the lambda implementation method? [no]
5. Should we consider some sort of adornment or hover text explaining the rôle of the lambda implementation method? [yes]

Please chime in, if or when you have an opinion!
Comment 1 Michael Rennie CLA 2013-11-20 17:32:52 EST
(In reply to Jesper Moller from comment #0)
> In the debugger, the user should not be too confused about how lambdas and
> reference expressions are implemented. The LambdaMetafactory will produce a
> class and an instance for the lambda or reference expression, and we should
> consider if we need to provide some visual aid to the user that a function
> object is on the stack and that (s)he cannot debug or step into it.

If the generated class has line number infos in it I would assume we could install a breakpoint and step in the code of the expression, but that does not appear to be the case. Consider the following example:

package lambda;
public class LambdaTest {
   interface Functional {
      String hello(String name);
   }

   public static void main(String[] args) {
	   Functional func = (String name) -> 
	   		{ 
	   			StringBuffer buff = new StringBuffer("Hello");
	   			buff.append(" ").append(name); // BP HERE
	   			return buff.toString();
	   		};
	   System.out.println(func.hello(args[0]));
    }
}

Debugging that will pop up an error stating there is no line infos and cannot suspend in lambda.LambdaTest$$Lambda$1/124313277@66133adc. So in the example there is no frame for the lambda, but it does show up as a standard local variable when the snippet does suspend on the 'System.out' line.

There does not appear to be anything new in JDWP (so far) to aid with asking about lambdas: http://download.java.net/jdk8/docs/platform/jpda/jdwp/jdwp-protocol.html.

> 
> I have a number of questions, and my preference in brackets. Please vote
> 
> 1. Should we hide the function object?  [no]
> 2. Should we consider some sort of graying out the stack frame as to show
> that it's not something you can step into? [yes]

Since we can't seem to debug through the expression (currently), I don't think there is anything to 'grey out', since you will only see the lambda as a variable in the frame it is defined in.

If however, the generated class is synthetic, and the VM supports it, JDWP has ways to ask for this information, so we could tag the variable, etc, with a special adornment that would indicate this, to let users know it cannot be debugged / inspected, etc. 

> 3. If there are bound variables, should we hide or show them? [show, like we
> do for inner classes]

Agreed, we should show the variables (if we can).

> 4. Should we try to hide/gloss over the lambda implementation method? [no]
> 5. Should we consider some sort of adornment or hover text explaining the
> rôle of the lambda implementation method? [yes]

I think that an adornment like I mentioned above would be a good idea, but we should use the standard debug hover - which would show the same adornments as the variables view, etc.

> 
> Please chime in, if or when you have an opinion!

I'm still not that familiar with how the lambda class file is created / linked, so if my assumptions are wrong please say so.
Comment 2 Jesper Moller CLA 2013-11-20 18:30:15 EST
(In reply to comment #1)
> (In reply to Jesper Moller from comment #0)
> > In the debugger, the user should not be too confused about how lambdas and
> > reference expressions are implemented. The LambdaMetafactory will produce a
> > class and an instance for the lambda or reference expression, and we should
> > consider if we need to provide some visual aid to the user that a function
> > object is on the stack and that (s)he cannot debug or step into it.
> 
> If the generated class has line number infos in it I would assume we could
> install a breakpoint and step in the code of the expression, but that does not
> appear to be the case. Consider the following example:

It does and we can -- I just tried your example and it works (first time I tried it with args.length == 0 and it never went inside the 'hello' lambda, but the breakpoint was set (inside the lambda implementation method, the the signature of lambda$0(String)

Try it again, it works (save for the strange warning about the breakpoint as described previously).

When you hit the breakppint, you'll see three stack frames:
1. LambdaTest.lambda$0(String) line: 11
2. 380936215.hello(String) line: not available
3. LambdaTest.main(String[]) line: 14

1. This is the lambda implementation method, i.e. the body of the lambda
2. This is the function object produced by invokedynamic bytecode, of the type produced by resolving the callsite by the LambdaMetafactory. Or perhaps unicorns.
3. Your main which called into the function object.

So no need for new JDWP

> > I have a number of questions, and my preference in brackets. Please vote
> >
> > 1. Should we hide the function object?  [no]
> > 2. Should we consider some sort of graying out the stack frame as to show
> > that it's not something you can step into? [yes]
> 
> Since we can't seem to debug through the expression (currently), I don't think
> there is anything to 'grey out', since you will only see the lambda as a
> variable in the frame it is defined in.
> 
> If however, the generated class is synthetic, and the VM supports it, JDWP has
> ways to ask for this information, so we could tag the variable, etc, with a
> special adornment that would indicate this, to let users know it cannot be
> debugged / inspected, etc.

Any idea for how to decorate?

> > 3. If there are bound variables, should we hide or show them? [show, like we
> > do for inner classes]
> 
> Agreed, we should show the variables (if we can).

Try this one, with a breakpoint at the return statement in the lambda.

package demo;

public class LambdaTest2 {
   interface Functional {
      int mixedSum(int lambdaParameter);
   }

   int fieldValue = 3;
  
   int calcStuff(int methodParameter) {
	   int localValue = Math.max(4, 4);
	   Functional fo = lambdaParameter -> {
		   return 1 + methodParameter + fieldValue + localValue + lambdaParameter;
	   };
	   
	   return fo.mixedSum(5);
   }
   
   public static void main(String[] args) {
	   int result = new LambdaTest2().calcStuff(2);
	   System.out.println("Result should be 1+2+3+4+5 = " + result);
    }
}

I get these stack frames:
1. LambdaTest2.lambda$0(int, int, int) line: 13
2. 142638629.mixedSum(int) line: not available
3. LambdaTest2.calcStuff(int) line: 16
4. LambdaTest2.main(String []) line: 20

Notice how only the formal parameter to the lambda is shown in the 'Variables' view, whereas the signature shows that the two bound variable values from the context of the lambda object creation are in fact passed in. Perhaps we should change this in the code generation of the debug info, and give them fitting synthetic names. In fact, if you click on frame 2, you can see the "missing" two int variables bound in the function object instance, together with as the LambdaTest2 "this" pointer.

> > 4. Should we try to hide/gloss over the lambda implementation method? [no]
> > 5. Should we consider some sort of adornment or hover text explaining the
> > rôle of the lambda implementation method? [yes]
> 
> I think that an adornment like I mentioned above would be a good idea, but we
> should use the standard debug hover - which would show the same adornments as
> the variables view, etc.

I tried hovering over the variables, and they gave a NPE from deep within the internal AST class hierarchy. I'll have to look at that some more. Lambda scopes are tricky/special, since they look like nested scopes but are in fact separate methods.

try javap -v -p on the file, it's instructive if a bit disturbing.
Comment 3 Jesper Moller CLA 2013-11-20 18:38:03 EST
Incidentally, javac also doesn't generate LocalVariableTable entries for the two synthetic parameters.
Comment 4 Jesper Moller CLA 2014-03-27 10:12:37 EDT
I'm leaning towards closing this one as WONTFIX since there seems to be no safe, portable way of reasoning about Function Objects generated by the LambdaMetafactory. 

The solution provided by the current mechanisms show a very raw, but correct view of the objects involved. Anything we could build to gloss it over would be susceptible to breakage, I fear.
Comment 5 Michael Rennie CLA 2014-03-27 10:36:08 EDT
(In reply to Jesper Moller from comment #4)
> I'm leaning towards closing this one as WONTFIX since there seems to be no
> safe, portable way of reasoning about Function Objects generated by the
> LambdaMetafactory. 
> 
> The solution provided by the current mechanisms show a very raw, but correct
> view of the objects involved. Anything we could build to gloss it over would
> be susceptible to breakage, I fear.

Lets leave it open for the time being in case there is something later we could take advantage of, or we do decide to 'gloss it over' for users.