Skip to content

Commit e5f1a0f

Browse files
Looking up the stack frame's associated source file from source containers directly to improve searching perf and bugs (microsoft#127)
* Source lookup from project classpath entries * Remove unused class * Add a workaround to fix jdt sourcelookup bug for java 9 * Fix source lookup support for java 9 * fix review comments * Refine javadoc * Use stream api to optimize code * Use distinct api to remove duplicated projects
1 parent 172dd9f commit e5f1a0f

4 files changed

Lines changed: 224 additions & 72 deletions

File tree

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ public interface ISourceLookUpProvider extends IProvider {
1818

1919
String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) throws DebugException;
2020

21+
/**
22+
* Given a fully qualified class name and source file path, search the associated disk source file.
23+
*
24+
* @param fullyQualifiedName
25+
* the fully qualified class name (e.g. com.microsoft.java.debug.core.adapter.ISourceLookUpProvider).
26+
* @param sourcePath
27+
* the qualified source file path (e.g. com\microsoft\java\debug\core\adapter\ISourceLookupProvider.java).
28+
* @return the associated source file uri.
29+
*/
2130
String getSourceFileURI(String fullyQualifiedName, String sourcePath);
2231

2332
String getSourceContents(String uri);

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package com.microsoft.java.debug.core.adapter.handler;
1313

14+
import java.io.File;
1415
import java.net.URISyntaxException;
1516
import java.util.ArrayList;
1617
import java.util.Arrays;
@@ -123,7 +124,7 @@ private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdap
123124
} catch (AbsentInformationException e) {
124125
String enclosingType = AdapterUtils.parseEnclosingType(fullyQualifiedName);
125126
sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java";
126-
relativeSourcePath = enclosingType.replace('.', '/') + ".java";
127+
relativeSourcePath = enclosingType.replace('.', File.separatorChar) + ".java";
127128
}
128129

129130
final String finalRelativeSourcePath = relativeSourcePath;

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

Lines changed: 31 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,22 @@
2020
import java.nio.charset.Charset;
2121
import java.nio.file.Files;
2222
import java.nio.file.Paths;
23-
import java.util.ArrayList;
2423
import java.util.HashMap;
2524
import java.util.Map;
2625
import java.util.logging.Level;
2726
import java.util.logging.Logger;
2827

2928
import org.eclipse.core.resources.IResource;
29+
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
3030
import org.eclipse.jdt.core.IBuffer;
3131
import org.eclipse.jdt.core.IClassFile;
3232
import org.eclipse.jdt.core.IJavaElement;
33-
import org.eclipse.jdt.core.IJavaProject;
34-
import org.eclipse.jdt.core.IType;
3533
import org.eclipse.jdt.core.ITypeRoot;
3634
import org.eclipse.jdt.core.JavaCore;
3735
import org.eclipse.jdt.core.JavaModelException;
3836
import org.eclipse.jdt.core.dom.AST;
3937
import org.eclipse.jdt.core.dom.ASTParser;
4038
import org.eclipse.jdt.core.dom.CompilationUnit;
41-
import org.eclipse.jdt.core.search.IJavaSearchConstants;
42-
import org.eclipse.jdt.core.search.IJavaSearchScope;
43-
import org.eclipse.jdt.core.search.SearchEngine;
44-
import org.eclipse.jdt.core.search.SearchMatch;
45-
import org.eclipse.jdt.core.search.SearchParticipant;
46-
import org.eclipse.jdt.core.search.SearchPattern;
47-
import org.eclipse.jdt.core.search.SearchRequestor;
4839
import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator;
4940

5041
import com.microsoft.java.debug.core.Configuration;
@@ -58,6 +49,7 @@ public class JdtSourceLookUpProvider implements ISourceLookUpProvider {
5849
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
5950
private static final String JDT_SCHEME = "jdt";
6051
private static final String PATH_SEPARATOR = "/";
52+
private ISourceContainer[] sourceContainers = null;
6153

6254
private HashMap<String, Object> options = new HashMap<String, Object>();
6355

@@ -67,6 +59,10 @@ public void initialize(IDebugSession debugSession, Map<String, Object> props) {
6759
throw new IllegalArgumentException("argument is null");
6860
}
6961
options.putAll(props);
62+
// During initialization, trigger a background job to load the source containers to improve the perf.
63+
new Thread(() -> {
64+
getSourceContainers();
65+
}).start();
7066
}
7167

7268
@Override
@@ -164,17 +160,32 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th
164160

165161
@Override
166162
public String getSourceFileURI(String fullyQualifiedName, String sourcePath) {
167-
if (fullyQualifiedName == null) {
168-
throw new IllegalArgumentException("fullyQualifiedName is null");
163+
if (sourcePath == null) {
164+
return null;
169165
}
170-
// Jdt Search Engine doesn't support searching anonymous class or local type directly.
171-
// But because the inner class and anonymous class have the same java file as the enclosing type,
172-
// search their enclosing type instead.
173-
if (fullyQualifiedName.indexOf("$") >= 0) {
174-
return searchDeclarationFileByFqn(AdapterUtils.parseEnclosingType(fullyQualifiedName));
175-
} else {
176-
return searchDeclarationFileByFqn(fullyQualifiedName);
166+
167+
Object sourceElement = JdtUtils.findSourceElement(sourcePath, getSourceContainers());
168+
if (sourceElement instanceof IResource) {
169+
return getFileURI((IResource) sourceElement);
170+
} else if (sourceElement instanceof IClassFile) {
171+
try {
172+
IClassFile file = (IClassFile) sourceElement;
173+
if (file.getBuffer() != null) {
174+
return getFileURI(file);
175+
}
176+
} catch (JavaModelException e) {
177+
// do nothing.
178+
}
179+
}
180+
return null;
181+
}
182+
183+
private synchronized ISourceContainer[] getSourceContainers() {
184+
if (sourceContainers == null) {
185+
sourceContainers = JdtUtils.getSourceContainers((String) options.get(Constants.PROJECTNAME));
177186
}
187+
188+
return sourceContainers;
178189
}
179190

180191
@Override
@@ -204,50 +215,6 @@ private String getContents(IClassFile cf) {
204215
return source;
205216
}
206217

207-
private String searchDeclarationFileByFqn(String fullyQualifiedName) {
208-
String projectName = (String) options.get(Constants.PROJECTNAME);
209-
IJavaProject project = JdtUtils.getJavaProject(projectName);
210-
IJavaSearchScope searchScope = createSearchScope(project);
211-
SearchPattern pattern = SearchPattern.createPattern(
212-
fullyQualifiedName,
213-
IJavaSearchConstants.TYPE,
214-
IJavaSearchConstants.DECLARATIONS,
215-
SearchPattern.R_EXACT_MATCH);
216-
217-
ArrayList<String> uris = new ArrayList<String>();
218-
219-
SearchRequestor requestor = new SearchRequestor() {
220-
@Override
221-
public void acceptSearchMatch(SearchMatch match) {
222-
Object element = match.getElement();
223-
if (element instanceof IType) {
224-
IType type = (IType) element;
225-
if (type.isBinary()) {
226-
try {
227-
// let the search engine to ignore those class files without attached source.
228-
if (type.getSource() != null) {
229-
uris.add(getFileURI(type.getClassFile()));
230-
}
231-
} catch (JavaModelException e) {
232-
// ignore
233-
}
234-
} else {
235-
uris.add(getFileURI(type.getResource()));
236-
}
237-
}
238-
}
239-
};
240-
SearchEngine searchEngine = new SearchEngine();
241-
try {
242-
searchEngine.search(pattern, new SearchParticipant[] {
243-
SearchEngine.getDefaultSearchParticipant()
244-
}, searchScope, requestor, null /* progress monitor */);
245-
} catch (Exception e) {
246-
logger.log(Level.SEVERE, String.format("Search engine failed: %s", e.toString()), e);
247-
}
248-
return uris.size() == 0 ? null : uris.get(0);
249-
}
250-
251218
private static String getFileURI(IClassFile classFile) {
252219
String packageName = classFile.getParent().getElementName();
253220
String jarName = classFile.getParent().getParent().getElementName();
@@ -288,14 +255,6 @@ private static IClassFile resolveClassFile(String uriString) {
288255
return null;
289256
}
290257

291-
private static IJavaSearchScope createSearchScope(IJavaProject project) {
292-
if (project == null) {
293-
return SearchEngine.createWorkspaceScope();
294-
}
295-
return SearchEngine.createJavaSearchScope(new IJavaProject[] {project},
296-
IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES);
297-
}
298-
299258
private static String readFile(String filePath, Charset cs) {
300259
StringBuilder builder = new StringBuilder();
301260
try (BufferedReader bufferReader =
@@ -314,4 +273,5 @@ private static String readFile(String filePath, Charset cs) {
314273
}
315274
return builder.toString();
316275
}
276+
317277
}

0 commit comments

Comments
 (0)