Skip to content

Commit beb12cc

Browse files
committed
Improve inline breakpoint discovery for multi line lambda #489
1 parent 7bdf2ef commit beb12cc

File tree

1 file changed

+53
-12
lines changed

1 file changed

+53
-12
lines changed

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.eclipse.jdt.core.dom.IMethodBinding;
5757
import org.eclipse.jdt.core.dom.ITypeBinding;
5858
import org.eclipse.jdt.core.dom.LambdaExpression;
59+
import org.eclipse.jdt.core.dom.SimpleName;
5960
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
6061
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
6162
import org.eclipse.jdt.launching.IVMInstall;
@@ -72,8 +73,8 @@
7273
import com.microsoft.java.debug.core.Configuration;
7374
import com.microsoft.java.debug.core.DebugException;
7475
import com.microsoft.java.debug.core.DebugSettings;
75-
import com.microsoft.java.debug.core.JavaBreakpointLocation;
7676
import com.microsoft.java.debug.core.DebugSettings.Switch;
77+
import com.microsoft.java.debug.core.JavaBreakpointLocation;
7778
import com.microsoft.java.debug.core.adapter.AdapterUtils;
7879
import com.microsoft.java.debug.core.adapter.Constants;
7980
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
@@ -167,11 +168,13 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
167168
return new JavaBreakpointLocation[0];
168169
}
169170

170-
CompilationUnit astUnit = asCompilationUnit(sourceUri);
171+
CompilationUnitResult result = asCompilationUnit(sourceUri);
172+
CompilationUnit astUnit = (result != null) ? result.compilationUnit : null;
171173
JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints)
172174
.map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column))
173175
.toArray(JavaBreakpointLocation[]::new);
174176
if (astUnit != null) {
177+
final String source = result.source;
175178
Map<Integer, BreakpointLocation[]> resolvedLocations = new HashMap<>();
176179
for (JavaBreakpointLocation sourceLocation : sourceLocations) {
177180
int sourceLine = sourceLocation.lineNumber();
@@ -190,7 +193,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
190193
if (resolvedLocations.containsKey(sourceLine)) {
191194
sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine));
192195
} else {
193-
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine);
196+
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine,
197+
source);
194198
sourceLocation.setAvailableBreakpointLocations(inlineLocations);
195199
resolvedLocations.put(sourceLine, inlineLocations);
196200
}
@@ -223,7 +227,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
223227
if (resolvedLocations.containsKey(sourceLine)) {
224228
sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine));
225229
} else {
226-
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine);
230+
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine,
231+
source);
227232
sourceLocation.setAvailableBreakpointLocations(inlineLocations);
228233
resolvedLocations.put(sourceLine, inlineLocations);
229234
}
@@ -238,7 +243,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
238243
return sourceLocations;
239244
}
240245

241-
private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) {
246+
private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine,
247+
String source) {
242248
List<BreakpointLocation> locations = new ArrayList<>();
243249
// The starting position of each line is the default breakpoint location for
244250
// that line.
@@ -255,6 +261,23 @@ public boolean visit(LambdaExpression node) {
255261
int endColumn = astUnit.getColumnNumber(lambdaEnd);
256262
BreakpointLocation location = new BreakpointLocation(startLine, startColumn, endLine, endColumn);
257263
locations.add(location);
264+
} else if (sourceLine < startLine && node.getParent() != null) {
265+
ASTNode parent = node.getParent();
266+
if (astUnit.getLineNumber(parent.getStartPosition()) == sourceLine &&
267+
parent instanceof org.eclipse.jdt.core.dom.MethodInvocation) {
268+
org.eclipse.jdt.core.dom.MethodInvocation methodInvc = (org.eclipse.jdt.core.dom.MethodInvocation) parent;
269+
SimpleName name = methodInvc.getName();
270+
int nameEnd = name.getStartPosition() + name.getLength();
271+
if (hasOnlyWhitespaceInBetween(nameEnd + 1, lambdaStart, source)) {
272+
int lambdaEnd = lambdaStart + node.getLength();
273+
int startColumn = astUnit.getColumnNumber(lambdaStart);
274+
int endLine = astUnit.getLineNumber(lambdaEnd);
275+
int endColumn = astUnit.getColumnNumber(lambdaEnd);
276+
BreakpointLocation location = new BreakpointLocation(startLine, startColumn, endLine,
277+
endColumn);
278+
locations.add(location);
279+
}
280+
}
258281
}
259282
return super.visit(node);
260283
}
@@ -263,12 +286,12 @@ public boolean visit(LambdaExpression node) {
263286
return locations.toArray(BreakpointLocation[]::new);
264287
}
265288

266-
private CompilationUnit asCompilationUnit(String uri) {
289+
private CompilationUnitResult asCompilationUnit(String uri) {
267290
final ASTParser parser = ASTParser.newParser(this.latestASTLevel);
268291
parser.setResolveBindings(true);
269292
parser.setBindingsRecovery(true);
270293
parser.setStatementsRecovery(true);
271-
CompilationUnit astUnit = null;
294+
CompilationUnitResult astUnit = null;
272295
String filePath = AdapterUtils.toPath(uri);
273296
// For file uri, read the file contents directly and pass them to the ast
274297
// parser.
@@ -305,15 +328,15 @@ private CompilationUnit asCompilationUnit(String uri) {
305328
parser.setCompilerOptions(javaOptions);
306329
}
307330
parser.setUnitName(Paths.get(filePath).getFileName().toString());
308-
astUnit = (CompilationUnit) parser.createAST(null);
331+
astUnit = new CompilationUnitResult((CompilationUnit) parser.createAST(null), source);
309332
} else {
310333
// For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class),
311334
// leverage jdt to load the source contents.
312335
IClassFile typeRoot = resolveClassFile(uri);
313336
try {
314337
if (typeRoot != null && typeRoot.getSourceRange() != null) {
315338
parser.setSource(typeRoot);
316-
astUnit = (CompilationUnit) parser.createAST(null);
339+
astUnit = new CompilationUnitResult((CompilationUnit) parser.createAST(null), typeRoot.getSource());
317340
} else if (typeRoot != null && DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON) {
318341
ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager();
319342
try {
@@ -337,7 +360,7 @@ private CompilationUnit asCompilationUnit(String uri) {
337360
}
338361
parser.setUnitName(typeRoot.getElementName());
339362
parser.setSource(contents.toCharArray());
340-
astUnit = (CompilationUnit) parser.createAST(null);
363+
astUnit = new CompilationUnitResult((CompilationUnit) parser.createAST(null), contents);
341364
}
342365
} catch (Exception e) {
343366
JavaLanguageServerPlugin.logException(e.getMessage(), e);
@@ -350,6 +373,13 @@ private CompilationUnit asCompilationUnit(String uri) {
350373
return astUnit;
351374
}
352375

376+
private boolean hasOnlyWhitespaceInBetween(int start, int end, String source) {
377+
if (source != null && start < end && source.length() >= end) {
378+
return source.substring(start, end).isBlank();
379+
}
380+
return false;
381+
}
382+
353383
@Override
354384
public String getSourceFileURI(String fullyQualifiedName, String sourcePath) {
355385
if (sourcePath == null) {
@@ -518,7 +548,7 @@ public List<MethodInvocation> findMethodInvocations(String uri, int line) {
518548
}
519549
}
520550

521-
final CompilationUnit astUnit = useCache ? cachedUnit : asCompilationUnit(uri);
551+
final CompilationUnit astUnit = useCache ? cachedUnit : asCompilationUnit(uri).compilationUnit;
522552
if (astUnit == null) {
523553
return Collections.emptyList();
524554
}
@@ -579,7 +609,8 @@ public int[] getOriginalLineMappings(String uri) {
579609
return null;
580610
}
581611

582-
IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) classFile.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
612+
IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) classFile
613+
.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
583614
if (packageRoot != null && packageRoot.getSourceAttachmentPath() != null) {
584615
return null;
585616
}
@@ -625,4 +656,14 @@ public int[] getDecompiledLineMappings(String uri) {
625656

626657
return null;
627658
}
659+
660+
public static class CompilationUnitResult {
661+
public CompilationUnit compilationUnit;
662+
public String source;
663+
664+
public CompilationUnitResult(CompilationUnit compilationUnit, String source) {
665+
this.compilationUnit = compilationUnit;
666+
this.source = source;
667+
}
668+
}
628669
}

0 commit comments

Comments
 (0)