Skip to content

Commit

Permalink
Improve inline breakpoint discovery for multi line lambda #489
Browse files Browse the repository at this point in the history
  • Loading branch information
gayanper committed Nov 25, 2023
1 parent b0c70e3 commit ab47c42
Showing 1 changed file with 53 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.jdt.launching.IVMInstall;
Expand All @@ -72,8 +73,8 @@
import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugException;
import com.microsoft.java.debug.core.DebugSettings;
import com.microsoft.java.debug.core.JavaBreakpointLocation;
import com.microsoft.java.debug.core.DebugSettings.Switch;
import com.microsoft.java.debug.core.JavaBreakpointLocation;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.Constants;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
Expand Down Expand Up @@ -167,11 +168,13 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
return new JavaBreakpointLocation[0];
}

CompilationUnit astUnit = asCompilationUnit(sourceUri);
CompilationUnitResult result = asCompilationUnit(sourceUri);
CompilationUnit astUnit = (result != null) ? result.compilationUnit : null;
JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints)
.map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column))
.toArray(JavaBreakpointLocation[]::new);
if (astUnit != null) {
final String source = result.source;
Map<Integer, BreakpointLocation[]> resolvedLocations = new HashMap<>();
for (JavaBreakpointLocation sourceLocation : sourceLocations) {
int sourceLine = sourceLocation.lineNumber();
Expand All @@ -190,7 +193,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
if (resolvedLocations.containsKey(sourceLine)) {
sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine));
} else {
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine);
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine,
source);
sourceLocation.setAvailableBreakpointLocations(inlineLocations);
resolvedLocations.put(sourceLine, inlineLocations);
}
Expand Down Expand Up @@ -223,7 +227,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
if (resolvedLocations.containsKey(sourceLine)) {
sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine));
} else {
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine);
BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine,
source);
sourceLocation.setAvailableBreakpointLocations(inlineLocations);
resolvedLocations.put(sourceLine, inlineLocations);
}
Expand All @@ -238,7 +243,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB
return sourceLocations;
}

private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) {
private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine,
String source) {
List<BreakpointLocation> locations = new ArrayList<>();
// The starting position of each line is the default breakpoint location for
// that line.
Expand All @@ -255,6 +261,23 @@ public boolean visit(LambdaExpression node) {
int endColumn = astUnit.getColumnNumber(lambdaEnd);
BreakpointLocation location = new BreakpointLocation(startLine, startColumn, endLine, endColumn);
locations.add(location);
} else if (sourceLine < startLine && node.getParent() != null) {
ASTNode parent = node.getParent();
if (astUnit.getLineNumber(parent.getStartPosition()) == sourceLine
&& parent instanceof org.eclipse.jdt.core.dom.MethodInvocation) {
org.eclipse.jdt.core.dom.MethodInvocation methodInvc = (org.eclipse.jdt.core.dom.MethodInvocation) parent;
SimpleName name = methodInvc.getName();
int nameEnd = name.getStartPosition() + name.getLength();
if (hasOnlyWhitespaceInBetween(nameEnd + 1, lambdaStart, source)) {
int lambdaEnd = lambdaStart + node.getLength();
int startColumn = astUnit.getColumnNumber(lambdaStart);
int endLine = astUnit.getLineNumber(lambdaEnd);
int endColumn = astUnit.getColumnNumber(lambdaEnd);
BreakpointLocation location = new BreakpointLocation(startLine, startColumn, endLine,
endColumn);
locations.add(location);
}
}
}
return super.visit(node);
}
Expand All @@ -263,12 +286,12 @@ public boolean visit(LambdaExpression node) {
return locations.toArray(BreakpointLocation[]::new);
}

private CompilationUnit asCompilationUnit(String uri) {
private CompilationUnitResult asCompilationUnit(String uri) {
final ASTParser parser = ASTParser.newParser(this.latestASTLevel);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setStatementsRecovery(true);
CompilationUnit astUnit = null;
CompilationUnitResult astUnit = null;
String filePath = AdapterUtils.toPath(uri);
// For file uri, read the file contents directly and pass them to the ast
// parser.
Expand Down Expand Up @@ -305,15 +328,15 @@ private CompilationUnit asCompilationUnit(String uri) {
parser.setCompilerOptions(javaOptions);
}
parser.setUnitName(Paths.get(filePath).getFileName().toString());
astUnit = (CompilationUnit) parser.createAST(null);
astUnit = new CompilationUnitResult((CompilationUnit) parser.createAST(null), source);
} else {
// For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class),
// leverage jdt to load the source contents.
IClassFile typeRoot = resolveClassFile(uri);
try {
if (typeRoot != null && typeRoot.getSourceRange() != null) {
parser.setSource(typeRoot);
astUnit = (CompilationUnit) parser.createAST(null);
astUnit = new CompilationUnitResult((CompilationUnit) parser.createAST(null), typeRoot.getSource());
} else if (typeRoot != null && DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON) {
ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager();
try {
Expand All @@ -337,7 +360,7 @@ private CompilationUnit asCompilationUnit(String uri) {
}
parser.setUnitName(typeRoot.getElementName());
parser.setSource(contents.toCharArray());
astUnit = (CompilationUnit) parser.createAST(null);
astUnit = new CompilationUnitResult((CompilationUnit) parser.createAST(null), contents);
}
} catch (Exception e) {
JavaLanguageServerPlugin.logException(e.getMessage(), e);
Expand All @@ -350,6 +373,13 @@ private CompilationUnit asCompilationUnit(String uri) {
return astUnit;
}

private boolean hasOnlyWhitespaceInBetween(int start, int end, String source) {
if (source != null && start < end && source.length() >= end) {
return source.substring(start, end).isBlank();
}
return false;
}

@Override
public String getSourceFileURI(String fullyQualifiedName, String sourcePath) {
if (sourcePath == null) {
Expand Down Expand Up @@ -518,7 +548,7 @@ public List<MethodInvocation> findMethodInvocations(String uri, int line) {
}
}

final CompilationUnit astUnit = useCache ? cachedUnit : asCompilationUnit(uri);
final CompilationUnit astUnit = useCache ? cachedUnit : asCompilationUnit(uri).compilationUnit;
if (astUnit == null) {
return Collections.emptyList();
}
Expand Down Expand Up @@ -579,7 +609,8 @@ public int[] getOriginalLineMappings(String uri) {
return null;
}

IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) classFile.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) classFile
.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
if (packageRoot != null && packageRoot.getSourceAttachmentPath() != null) {
return null;
}
Expand Down Expand Up @@ -625,4 +656,14 @@ public int[] getDecompiledLineMappings(String uri) {

return null;
}

public static class CompilationUnitResult {
public CompilationUnit compilationUnit;
public String source;

public CompilationUnitResult(CompilationUnit compilationUnit, String source) {
this.compilationUnit = compilationUnit;
this.source = source;
}
}
}

0 comments on commit ab47c42

Please sign in to comment.