Breakpoints for Java lambdas.#9285
Conversation
|
@matthiasblaesing, thanks for trying. I guess what you are saying is that:
That way, anyone could go to the settings and specify the lambda index. (+the UI maybe could be slightly better e.g. a combobox showing "Everywhere"/"Main Line Only"/"Lambda 1"/"Lambda 2"/...). Note the breakpoint properties already allow to set the lambda index in this prototype. I can't say I disagree with that. The automagically setup breakpoints where a bit preparation for a (much) better UI (similar to what VS Code is doing), but I don't have the UI, and I don't know if I ever will. So maybe this is simply too complex. And it can be resurrected if we have the better UI. |
|
Yes you understood my point correctly. I'm not opposed to multiple breakpoints in general, but from a UI/UX perspective I think making the options available in the line break point might be more realistic as the UI impact would be smaller. I think it would be good if the "lamdba" "condition" would allow to select multiple lambdas, for this example (in reality I expect lambdas with more interesting content in it 😄): public class Test3 {
public static void main(String[] args) {
List.of("a", "b", "c")
.stream()
.map(s -> s.trim()).filter(s -> !s.isEmpty()).forEach(s -> System.err.println(s));
}
}
it might make sense to break on the first and last lamdba. That way you can observe what goes into the stream pipeline and what reaches the end. |
…he filtering capabilities.
|
FWIW, the lambda index is now a list, and the weird UI is removed. So, e.g. Also, there's a test added, which should (hopefully) ensure the lambda breakpoints work as intended over DAP. I think this is ready for review. |
matthiasblaesing
left a comment
There was a problem hiding this comment.
A manual test with NetBeans IDE was successful for me and showed the expected behavior:
- setting a breakpoint without further config causes breaks outside the lambdas and at each lambda
- setting a lambda index filter causes breaks at the expected places
- it was possible to create multiple breakpoints on the same line, so that I could breakpoints with different behavior (thread stopping, only logging)
I eyeballed the IDE side implementation and in general that made sense to me. I added a few inline comments, but nothing major.
One thing I'm not sure about is 0 vs. 1 based indexes. I.e. entering 0 to get breaks on the first lambda gave me a slight dissonance. On the other hand are java developers accustomed to 0 == first index.
I skipped the LSP parts.
| L_Line_Breakpoint_Lambda_Index=Lambda &Index\: | ||
| ACSD_L_Line_Breakpoint_Lambda_Index=Lambda Index | ||
| ACSD_TF_Line_Breakpoint_Lambda_Index=Lambda Index | ||
| TTT_TF_Line_Breakpoint_Lambda_Index=Lambda Index to stop at |
There was a problem hiding this comment.
I suggest to add a minimal explanation/usage instruction:
| L_Line_Breakpoint_Lambda_Index=Lambda &Index\: | |
| ACSD_L_Line_Breakpoint_Lambda_Index=Lambda Index | |
| ACSD_TF_Line_Breakpoint_Lambda_Index=Lambda Index | |
| TTT_TF_Line_Breakpoint_Lambda_Index=Lambda Index to stop at | |
| L_Line_Breakpoint_Lambda_Index=Lambda &Indexes\: | |
| ACSD_L_Line_Breakpoint_Lambda_Index=Lambda Indexes | |
| ACSD_TF_Line_Breakpoint_Lambda_Index=Lambda Indexes | |
| TTT_TF_Line_Breakpoint_Lambda_Index=Comma seperated lambda indexes to stop at\nempty: all locations\n-1: stop outside of lambda\n0-based index: break in that lambda expression |
| ); | ||
| } | ||
|
|
||
| public int[] getLambdaIndex() { |
There was a problem hiding this comment.
Would it make sense to use correct plural form? I.e. getLambdaIndexes?
| gridBagConstraints.weightx = 1.0; | ||
| gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3); | ||
| pSettings.add(tfLambdaIndex, gridBagConstraints); | ||
| tfLambdaIndex.getAccessibleContext().setAccessibleName("Lambda index"); |
There was a problem hiding this comment.
Should this be localized?
| if (lambdaIndexText.isEmpty()) { | ||
| breakpoint.setLambdaIndex(new int[0]); | ||
| } else { | ||
| breakpoint.setLambdaIndex(Arrays.stream(lambdaIndexText.split(", *")).mapToInt(v -> Integer.parseInt(v)).toArray()); |
There was a problem hiding this comment.
I think we should be prepared for bogus values, so I would suggest this:
| breakpoint.setLambdaIndex(Arrays.stream(lambdaIndexText.split(", *")).mapToInt(v -> Integer.parseInt(v)).toArray()); | |
| int[] indexes = Arrays.stream(lambdaIndexText.split(", *")) | |
| .map(indexString -> { | |
| try { | |
| return Integer.parseInt(indexString); | |
| } catch (NumberFormatException ex) { | |
| // Ignore invalid values | |
| return null; | |
| } | |
| }) | |
| .filter(value -> value != null) | |
| .mapToInt(i -> i) | |
| .toArray(); | |
| breakpoint.setLambdaIndex(indexes); |
| ); | ||
| properties.setArray( | ||
| LineBreakpoint.PROP_LAMBDA_INDEX, | ||
| Arrays.stream(lb.getLambdaIndex()).mapToObj(v -> v).toArray() |
There was a problem hiding this comment.
I assume this is done to get the raw ints boxed?
| List<Location> outsideOfLambda = new ArrayList<>(); | ||
|
|
||
| for (Location l : locations) { | ||
| if (l.method().name().startsWith("lambda$")) { |
There was a problem hiding this comment.
Should here be a comment, that this relies on the lambda naming convention and might break? I only suspect, that this detail is not described in the JLS?



This is something I was looking at long time ago, but was not very happy about the UI. So I was trying to improve the UI, but I didn't make progress on that, and this got stashed. So, now I decided to try to recover this, even with the not-so-nice UI.
I would consider this patch to be a fairly advanced prototype.
Currently, having code like:
putting a breakpoint at the line with
mapwill mean the debugger will stop at the line whenmapis called, and then each time any of the lambdas is called. This is rarely what one wants. It would be better to be able to say that I want the debugger to stop at e.g. the first lambda.This is what this PR is trying to do. There's an ability to specify the breakpoint applies to n-th lambda on the given line. Special values are
-1, which means to stop at the line outside of any lambda, andInteger.MIN_VALUE, which means stop each time this line is reached, regardless of any lambdas. This is the way it works on the backend.Inside the NetBeans UI, it works like this:
The ability is also used in the DAP Java server, for use in DAP clients (e.g. in VS Code). The UI in VS Code is better, but also has some sharp edges.
Inside NB, it can look like this:


^Add meaningful description above
Click to collapse/expand PR instructions
By opening a pull request you confirm that, unless explicitly stated otherwise, the changes -
Please make sure (eg.
git log) that all commits have a valid name and email address for you in the Author field.If you're a first time contributor, see the Contributing guidelines for more information.
If you're a committer, please label the PR before pressing "Create pull request" so that the right test jobs can run.
PR approval and merge checklist:
If this PR targets the delivery branch: don't merge. (full wiki article)