From 52ff6fd6573828e9fe28544f5b26a64498ec354d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 25 Oct 2017 10:50:07 +0800 Subject: [PATCH 1/8] Source lookup from project classpath entries --- .../META-INF/MANIFEST.MF | 3 +- .../internal/JdtSourceLookUpProvider.java | 61 +++++++++++++++++-- .../plugin/internal/SourceLookupEngine.java | 24 ++++++++ 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 583f1154d..5896faaa1 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -13,7 +13,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.debug.core, org.eclipse.jdt.debug, org.eclipse.jdt.core, - org.eclipse.jdt.ls.core + org.eclipse.jdt.ls.core, + org.eclipse.jdt.launching Bundle-ClassPath: lib/gson-2.7.jar, ., lib/rxjava-2.1.1.jar, diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 002bc2c4f..2ee0ed0fb 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -21,11 +21,14 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; @@ -33,6 +36,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; @@ -52,6 +56,8 @@ import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator; +import org.eclipse.jdt.launching.IRuntimeClasspathEntry; +import org.eclipse.jdt.launching.JavaRuntime; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; @@ -65,6 +71,7 @@ public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final String PATH_SEPARATOR = "/"; private HashMap context = new HashMap(); + private ISourceContainer[] sourceContainer = new ISourceContainer[0]; @Override public void initialize(Map props) { @@ -72,6 +79,7 @@ public void initialize(Map props) { throw new IllegalArgumentException("argument is null"); } context.putAll(props); + sourceContainer = getSourceContainers(); } @Override @@ -172,14 +180,17 @@ public String getSourceFileURI(String fullyQualifiedName, String sourcePath) { if (fullyQualifiedName == null) { throw new IllegalArgumentException("fullyQualifiedName is null"); } + + return this.getSourceElement(fullyQualifiedName, sourcePath); + // Jdt Search Engine doesn't support searching anonymous class or local type directly. // But because the inner class and anonymous class have the same java file as the enclosing type, // search their enclosing type instead. - if (fullyQualifiedName.indexOf("$") >= 0) { - return searchDeclarationFileByFqn(AdapterUtils.parseEnclosingType(fullyQualifiedName)); - } else { - return searchDeclarationFileByFqn(fullyQualifiedName); - } + // if (fullyQualifiedName.indexOf("$") >= 0) { + // return searchDeclarationFileByFqn(AdapterUtils.parseEnclosingType(fullyQualifiedName)); + // } else { + // return searchDeclarationFileByFqn(fullyQualifiedName); + // } } @Override @@ -332,4 +343,44 @@ private static String readFile(String filePath, Charset cs) { } return builder.toString(); } + + private ISourceContainer[] getSourceContainers() { + String projectName = (String) context.get(Constants.PROJECTNAME); + if (StringUtils.isNotBlank(projectName)) { + try { + IJavaProject project = getJavaProjectFromName(projectName); + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + List resolved = new ArrayList<>(); + for (IRuntimeClasspathEntry entry : unresolved) { + resolved.addAll(Arrays.asList(JavaRuntime.resolveRuntimeClasspathEntry(entry, project))); + } + return JavaRuntime.getSourceContainers(resolved.toArray(new IRuntimeClasspathEntry[0])); + } catch (CoreException ex) { + // do nothing. + } + } + return new ISourceContainer[0]; + } + + private String getSourceElement(String fqn, String sourcePath) { + for (ISourceContainer container : sourceContainer) { + try { + Object[] objects = container.findSourceElements(sourcePath); + if (objects.length > 0) { + if (objects[0] instanceof IResource) { + IResource file = (IResource) objects[0]; + return getFileURI(file); + } else if (objects[0] instanceof IClassFile) { + IClassFile file = (IClassFile) objects[0]; + if (file.getBuffer() != null) { + return getFileURI(file); + } + } + } + } catch (CoreException e) { + // do nothing. + } + } + return null; + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java new file mode 100644 index 000000000..2a82d282d --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.plugin.internal; + +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.jdt.core.IJavaProject; + +public class SourceLookupEngine { + private ISourceContainer[] container; + + public SourceLookupEngine(IJavaProject project) { + + } + +} From 4ffd1b8a016b351da97d329c9e0f286e5c37dca2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 5 Dec 2017 17:38:14 +0800 Subject: [PATCH 2/8] Remove unused class --- .../plugin/internal/SourceLookupEngine.java | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java deleted file mode 100644 index 2a82d282d..000000000 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/SourceLookupEngine.java +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Microsoft Corporation - initial API and implementation -*******************************************************************************/ - -package com.microsoft.java.debug.plugin.internal; - -import org.eclipse.debug.core.sourcelookup.ISourceContainer; -import org.eclipse.jdt.core.IJavaProject; - -public class SourceLookupEngine { - private ISourceContainer[] container; - - public SourceLookupEngine(IJavaProject project) { - - } - -} From 22f8881e2b0babcb1fde89e19828326ab1722b81 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 6 Dec 2017 17:08:02 +0800 Subject: [PATCH 3/8] Add a workaround to fix jdt sourcelookup bug for java 9 --- .../internal/JdtSourceLookUpProvider.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 540111687..c5767ca75 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -40,6 +40,7 @@ import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; @@ -58,6 +59,7 @@ import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; +import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; @@ -352,10 +354,23 @@ private synchronized ISourceContainer[] getSourceContainers() { for (IProject project : projects) { IJavaProject javaProject = JdtUtils.getJavaProject(project); if (javaProject != null && project.exists()) { - // Add source containers from the dependencies. + // Add source containers associated with the project's runtime classpath entries. containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); - // Add source containers from the project's source folder. + // Add source containers associated with the project's source folders. containers.add(new JavaProjectSourceContainer(javaProject)); + // Due to a known jdt java 9 support bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=525840, + // it would miss some JRE libraries source containers when the debugger is running on j2se-9 JDK. + // As a workaround, loop all of the package fragment roots contained in this project to complete the missed source containers. + try { + IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); + for (IPackageFragmentRoot root : roots) { + if (root.getKind() == IPackageFragmentRoot.K_BINARY && root.getSourceAttachmentPath() != null) { + containers.add(new PackageFragmentRootSourceContainer(root)); + } + } + } catch (JavaModelException e) { + // ignore. + } } } From 23006da256b2bb4d715556a928c8b4074b43b73d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 7 Dec 2017 17:36:39 +0800 Subject: [PATCH 4/8] Fix source lookup support for java 9 --- .../internal/JdtSourceLookUpProvider.java | 130 +++----------- .../java/debug/plugin/internal/JdtUtils.java | 168 ++++++++++++++++++ 2 files changed, 191 insertions(+), 107 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index c5767ca75..b659fec68 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -21,26 +21,17 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; -import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; @@ -56,10 +47,6 @@ import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator; -import org.eclipse.jdt.launching.IRuntimeClasspathEntry; -import org.eclipse.jdt.launching.JavaRuntime; -import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; -import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; @@ -186,7 +173,29 @@ public String getSourceFileURI(String fullyQualifiedName, String sourcePath) { if (sourcePath == null) { return null; } - return this.findSourceElement(fullyQualifiedName, sourcePath); + + Object sourceElement = JdtUtils.findSourceElement(sourcePath, getSourceContainers()); + if (sourceElement instanceof IResource) { + return getFileURI((IResource) sourceElement); + } else if (sourceElement instanceof IClassFile) { + try { + IClassFile file = (IClassFile) sourceElement; + if (file.getBuffer() != null) { + return getFileURI(file); + } + } catch (JavaModelException e) { + // do nothing. + } + } + return null; + } + + private synchronized ISourceContainer[] getSourceContainers() { + if (sourceContainers == null) { + sourceContainers = JdtUtils.getSourceContainers((String) options.get(Constants.PROJECTNAME)); + } + + return sourceContainers; } @Override @@ -327,97 +336,4 @@ private static String readFile(String filePath, Charset cs) { return builder.toString(); } - /** - * Compute the possible source containers that the debugging project could be associated with. - * And put the source containers parsed from the specified project's classpath entries first, - * then the source containers from the other projects at the same workspace. - */ - private synchronized ISourceContainer[] getSourceContainers() { - if (sourceContainers != null) { - return sourceContainers; - } - Set containers = new LinkedHashSet<>(); - List projects = new ArrayList<>(); - - String projectName = (String) options.get(Constants.PROJECTNAME); - IProject targetProject = JdtUtils.getProject(projectName); - if (targetProject != null) { - projects.add(targetProject); - } - - List workspaceProjects = Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()); - projects.addAll(workspaceProjects.stream().filter((project) -> { - return !project.equals(targetProject); - }).collect(Collectors.toList())); - - Set calculated = new LinkedHashSet<>(); - for (IProject project : projects) { - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null && project.exists()) { - // Add source containers associated with the project's runtime classpath entries. - containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); - // Add source containers associated with the project's source folders. - containers.add(new JavaProjectSourceContainer(javaProject)); - // Due to a known jdt java 9 support bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=525840, - // it would miss some JRE libraries source containers when the debugger is running on j2se-9 JDK. - // As a workaround, loop all of the package fragment roots contained in this project to complete the missed source containers. - try { - IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); - for (IPackageFragmentRoot root : roots) { - if (root.getKind() == IPackageFragmentRoot.K_BINARY && root.getSourceAttachmentPath() != null) { - containers.add(new PackageFragmentRootSourceContainer(root)); - } - } - } catch (JavaModelException e) { - // ignore. - } - } - } - - sourceContainers = containers.toArray(new ISourceContainer[0]); - return sourceContainers; - } - - private ISourceContainer[] getSourceContainers(IJavaProject project, Set calculated) { - if (project != null && project.exists()) { - try { - IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); - List resolved = new ArrayList<>(); - for (IRuntimeClasspathEntry entry : unresolved) { - for (IRuntimeClasspathEntry resolvedEntry : JavaRuntime.resolveRuntimeClasspathEntry(entry, project)) { - if (!calculated.contains(resolvedEntry)) { - calculated.add(resolvedEntry); - resolved.add(resolvedEntry); - } - } - } - return JavaRuntime.getSourceContainers(resolved.toArray(new IRuntimeClasspathEntry[0])); - } catch (CoreException ex) { - // do nothing. - } - } - return new ISourceContainer[0]; - } - - private String findSourceElement(String fqn, String sourcePath) { - for (ISourceContainer container : getSourceContainers()) { - try { - Object[] objects = container.findSourceElements(sourcePath); - if (objects.length > 0) { - if (objects[0] instanceof IResource) { - IResource file = (IResource) objects[0]; - return getFileURI(file); - } else if (objects[0] instanceof IClassFile) { - IClassFile file = (IClassFile) objects[0]; - if (file.getBuffer() != null) { - return getFileURI(file); - } - } - } - } catch (CoreException e) { - // do nothing. - } - } - return null; - } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java index 82259cd0a..e7efce14f 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java @@ -11,14 +11,34 @@ package com.microsoft.java.debug.plugin.internal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; +import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer; + +import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.Location; +import com.sun.jdi.StackFrame; public class JdtUtils { @@ -90,4 +110,152 @@ public static IProject getProject(String projectName) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); return root.getProject(projectName); } + + /** + * Compute the possible source containers that the current project could be associated with. + *

+ * If current project is specified, try to parse the source containers from current project's classpath entries first, + * then the other projects at the same workspace. + *

+ *

+ * Otherwise, loop every projects at the current workspace and combine the parsed source containers directly. + *

+ * @param currentProject + * the current project name. + * @return the possible source container list. + */ + public static ISourceContainer[] getSourceContainers(String currentProject) { + Set containers = new LinkedHashSet<>(); + List projects = new ArrayList<>(); + + IProject targetProject = JdtUtils.getProject(currentProject); + if (targetProject != null) { + projects.add(targetProject); + } + + List workspaceProjects = Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()); + projects.addAll(workspaceProjects.stream().filter((project) -> { + return !project.equals(targetProject); + }).collect(Collectors.toList())); + + Set calculated = new LinkedHashSet<>(); + for (IProject project : projects) { + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null && project.exists()) { + // Add source containers associated with the project's runtime classpath entries. + containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); + // Add source containers associated with the project's source folders. + containers.add(new JavaProjectSourceContainer(javaProject)); + } + } + + return containers.toArray(new ISourceContainer[0]); + } + + private static ISourceContainer[] getSourceContainers(IJavaProject project, Set calculated) { + if (project != null && project.exists()) { + try { + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + List resolved = new ArrayList<>(); + for (IRuntimeClasspathEntry entry : unresolved) { + for (IRuntimeClasspathEntry resolvedEntry : JavaRuntime.resolveRuntimeClasspathEntry(entry, project)) { + if (!calculated.contains(resolvedEntry)) { + calculated.add(resolvedEntry); + resolved.add(resolvedEntry); + } + } + } + Set containers = new LinkedHashSet<>(); + containers.addAll(Arrays.asList( + JavaRuntime.getSourceContainers(resolved.toArray(new IRuntimeClasspathEntry[0])))); + + // Due to a known jdt java 9 support bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=525840, + // it would miss some JRE libraries source containers when the debugger is running on JDK9. + // As a workaround, recompute the possible source containers for JDK9 jrt-fs.jar libraries. + IRuntimeClasspathEntry jrtFs = resolved.stream().filter(entry -> { + return entry.getType() == IRuntimeClasspathEntry.ARCHIVE && entry.getPath().lastSegment().equals("jrt-fs.jar"); + }).findFirst().orElse(null); + if (jrtFs != null && project.isOpen()) { + IPackageFragmentRoot[] allRoots = project.getPackageFragmentRoots(); + for (IPackageFragmentRoot root : allRoots) { + if (root.getPath().equals(jrtFs.getPath()) && isSourceAttachmentEqual(root, jrtFs)) { + containers.add(new PackageFragmentRootSourceContainer(root)); + } + } + } + + return containers.toArray(new ISourceContainer[0]); + } catch (CoreException ex) { + // do nothing. + } + } + return new ISourceContainer[0]; + } + + private static boolean isSourceAttachmentEqual(IPackageFragmentRoot root, IRuntimeClasspathEntry entry) throws JavaModelException { + IPath entryPath = entry.getSourceAttachmentPath(); + if (entryPath == null) { + return true; + } + IPath rootPath = root.getSourceAttachmentPath(); + if (rootPath == null) { + // entry has a source attachment that the package root does not + return false; + } + return rootPath.equals(entryPath); + + } + + /** + * Given a source name info, search the associated source file or class file from the source container list. + * + * @param sourcePath + * the target source name (e.g. com\microsoft\java\debug\xxx.java). + * @param containers + * the source container list. + * @return the associated source file or class file. + */ + public static Object findSourceElement(String sourcePath, ISourceContainer[] containers) { + if (containers == null) { + return null; + } + for (ISourceContainer container : containers) { + try { + Object[] objects = container.findSourceElements(sourcePath); + if (objects.length > 0 && (objects[0] instanceof IResource || objects[0] instanceof IClassFile)) { + return objects[0]; + } + } catch (CoreException e) { + // do nothing. + } + } + return null; + } + + /** + * Given a stack frame, find the target project that the associated source file belongs to. + * + * @param stackFrame + * the stack frame. + * @param containers + * the source container list. + * @return the context project. + */ + public static IProject findProject(StackFrame stackFrame, ISourceContainer[] containers) { + Location location = stackFrame.location(); + try { + Object sourceElement = findSourceElement(location.sourcePath(), containers); + if (sourceElement instanceof IResource) { + return ((IResource) sourceElement).getProject(); + } else if (sourceElement instanceof IClassFile) { + IJavaProject javaProject = ((IClassFile) sourceElement).getJavaProject(); + if (javaProject != null) { + return javaProject.getProject(); + } + } + } catch (AbsentInformationException e) { + // When the compiled .class file doesn't contain debug source information, return null. + } + return null; + } } From 939ff22983d3b64e01c00dcaf2ee63af6232f467 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 8 Dec 2017 10:46:21 +0800 Subject: [PATCH 5/8] fix review comments --- .../internal/JdtSourceLookUpProvider.java | 62 -------------- .../java/debug/plugin/internal/JdtUtils.java | 82 +++++++++---------- 2 files changed, 41 insertions(+), 103 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index b659fec68..fdd372e6f 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -20,7 +20,6 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -31,21 +30,12 @@ import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.search.IJavaSearchConstants; -import org.eclipse.jdt.core.search.IJavaSearchScope; -import org.eclipse.jdt.core.search.SearchEngine; -import org.eclipse.jdt.core.search.SearchMatch; -import org.eclipse.jdt.core.search.SearchParticipant; -import org.eclipse.jdt.core.search.SearchPattern; -import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator; import com.microsoft.java.debug.core.Configuration; @@ -225,50 +215,6 @@ private String getContents(IClassFile cf) { return source; } - private String searchDeclarationFileByFqn(String fullyQualifiedName) { - String projectName = (String) options.get(Constants.PROJECTNAME); - IJavaProject project = JdtUtils.getJavaProject(projectName); - IJavaSearchScope searchScope = createSearchScope(project); - SearchPattern pattern = SearchPattern.createPattern( - fullyQualifiedName, - IJavaSearchConstants.TYPE, - IJavaSearchConstants.DECLARATIONS, - SearchPattern.R_EXACT_MATCH); - - ArrayList uris = new ArrayList(); - - SearchRequestor requestor = new SearchRequestor() { - @Override - public void acceptSearchMatch(SearchMatch match) { - Object element = match.getElement(); - if (element instanceof IType) { - IType type = (IType) element; - if (type.isBinary()) { - try { - // let the search engine to ignore those class files without attached source. - if (type.getSource() != null) { - uris.add(getFileURI(type.getClassFile())); - } - } catch (JavaModelException e) { - // ignore - } - } else { - uris.add(getFileURI(type.getResource())); - } - } - } - }; - SearchEngine searchEngine = new SearchEngine(); - try { - searchEngine.search(pattern, new SearchParticipant[] { - SearchEngine.getDefaultSearchParticipant() - }, searchScope, requestor, null /* progress monitor */); - } catch (Exception e) { - logger.log(Level.SEVERE, String.format("Search engine failed: %s", e.toString()), e); - } - return uris.size() == 0 ? null : uris.get(0); - } - private static String getFileURI(IClassFile classFile) { String packageName = classFile.getParent().getElementName(); String jarName = classFile.getParent().getParent().getElementName(); @@ -309,14 +255,6 @@ private static IClassFile resolveClassFile(String uriString) { return null; } - private static IJavaSearchScope createSearchScope(IJavaProject project) { - if (project == null) { - return SearchEngine.createWorkspaceScope(); - } - return SearchEngine.createJavaSearchScope(new IJavaProject[] {project}, - IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES); - } - private static String readFile(String filePath, Charset cs) { StringBuilder builder = new StringBuilder(); try (BufferedReader bufferReader = diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java index e7efce14f..aa4c88bd9 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java @@ -16,7 +16,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -112,31 +111,29 @@ public static IProject getProject(String projectName) { } /** - * Compute the possible source containers that the current project could be associated with. + * Compute the possible source containers that the specified project could be associated with. *

- * If current project is specified, try to parse the source containers from current project's classpath entries first, - * then the other projects at the same workspace. + * If the project name is specified, it will put the source containers parsed from the specified project's + * classpath entries in the front of the result, then the other projects at the same workspace. *

*

- * Otherwise, loop every projects at the current workspace and combine the parsed source containers directly. + * Otherwise, just loop every projects at the current workspace and combine the parsed source containers directly. *

- * @param currentProject - * the current project name. + * @param projectName + * the project name. * @return the possible source container list. */ - public static ISourceContainer[] getSourceContainers(String currentProject) { + public static ISourceContainer[] getSourceContainers(String projectName) { Set containers = new LinkedHashSet<>(); List projects = new ArrayList<>(); - IProject targetProject = JdtUtils.getProject(currentProject); + IProject targetProject = JdtUtils.getProject(projectName); if (targetProject != null) { projects.add(targetProject); } List workspaceProjects = Arrays.asList(ResourcesPlugin.getWorkspace().getRoot().getProjects()); - projects.addAll(workspaceProjects.stream().filter((project) -> { - return !project.equals(targetProject); - }).collect(Collectors.toList())); + projects.addAll(workspaceProjects); Set calculated = new LinkedHashSet<>(); for (IProject project : projects) { @@ -153,42 +150,45 @@ public static ISourceContainer[] getSourceContainers(String currentProject) { } private static ISourceContainer[] getSourceContainers(IJavaProject project, Set calculated) { - if (project != null && project.exists()) { - try { - IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); - List resolved = new ArrayList<>(); - for (IRuntimeClasspathEntry entry : unresolved) { - for (IRuntimeClasspathEntry resolvedEntry : JavaRuntime.resolveRuntimeClasspathEntry(entry, project)) { - if (!calculated.contains(resolvedEntry)) { - calculated.add(resolvedEntry); - resolved.add(resolvedEntry); - } + if (project == null || !project.exists()) { + return new ISourceContainer[0]; + } + + try { + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + List resolved = new ArrayList<>(); + for (IRuntimeClasspathEntry entry : unresolved) { + for (IRuntimeClasspathEntry resolvedEntry : JavaRuntime.resolveRuntimeClasspathEntry(entry, project)) { + if (!calculated.contains(resolvedEntry)) { + calculated.add(resolvedEntry); + resolved.add(resolvedEntry); } } - Set containers = new LinkedHashSet<>(); - containers.addAll(Arrays.asList( - JavaRuntime.getSourceContainers(resolved.toArray(new IRuntimeClasspathEntry[0])))); + } + Set containers = new LinkedHashSet<>(); + containers.addAll(Arrays.asList( + JavaRuntime.getSourceContainers(resolved.toArray(new IRuntimeClasspathEntry[0])))); - // Due to a known jdt java 9 support bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=525840, - // it would miss some JRE libraries source containers when the debugger is running on JDK9. - // As a workaround, recompute the possible source containers for JDK9 jrt-fs.jar libraries. - IRuntimeClasspathEntry jrtFs = resolved.stream().filter(entry -> { - return entry.getType() == IRuntimeClasspathEntry.ARCHIVE && entry.getPath().lastSegment().equals("jrt-fs.jar"); - }).findFirst().orElse(null); - if (jrtFs != null && project.isOpen()) { - IPackageFragmentRoot[] allRoots = project.getPackageFragmentRoots(); - for (IPackageFragmentRoot root : allRoots) { - if (root.getPath().equals(jrtFs.getPath()) && isSourceAttachmentEqual(root, jrtFs)) { - containers.add(new PackageFragmentRootSourceContainer(root)); - } + // Due to a known jdt java 9 support bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=525840, + // it would miss some JRE libraries source containers when the debugger is running on JDK9. + // As a workaround, recompute the possible source containers for JDK9 jrt-fs.jar libraries. + IRuntimeClasspathEntry jrtFs = resolved.stream().filter(entry -> { + return entry.getType() == IRuntimeClasspathEntry.ARCHIVE && entry.getPath().lastSegment().equals("jrt-fs.jar"); + }).findFirst().orElse(null); + if (jrtFs != null && project.isOpen()) { + IPackageFragmentRoot[] allRoots = project.getPackageFragmentRoots(); + for (IPackageFragmentRoot root : allRoots) { + if (root.getPath().equals(jrtFs.getPath()) && isSourceAttachmentEqual(root, jrtFs)) { + containers.add(new PackageFragmentRootSourceContainer(root)); } } - - return containers.toArray(new ISourceContainer[0]); - } catch (CoreException ex) { - // do nothing. } + + return containers.toArray(new ISourceContainer[0]); + } catch (CoreException ex) { + // do nothing. } + return new ISourceContainer[0]; } From adb3895618498428c12d1affa31087f1b63cc125 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 8 Dec 2017 12:09:39 +0800 Subject: [PATCH 6/8] Refine javadoc --- .../java/debug/core/adapter/ISourceLookUpProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java index f871facac..233d539e3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java @@ -19,12 +19,12 @@ public interface ISourceLookUpProvider extends IProvider { String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) throws DebugException; /** - * Given a source class and source file name, search the associated disk source file. + * Given a fully qualified class name and source file path, search the associated disk source file. * * @param fullyQualifiedName * the fully qualified class name (e.g. com.microsoft.java.debug.core.adapter.ISourceLookUpProvider). * @param sourcePath - * the qualified source file name (e.g. com\microsoft\java\debug\core\adapter\ISourceLookupProvider.java). + * the qualified source file path (e.g. com\microsoft\java\debug\core\adapter\ISourceLookupProvider.java). * @return the associated source file uri. */ String getSourceFileURI(String fullyQualifiedName, String sourcePath); From d086833b4e5c86dd531889cc69a63232fb16eb92 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 11 Dec 2017 10:20:20 +0800 Subject: [PATCH 7/8] Use stream api to optimize code --- .../microsoft/java/debug/plugin/internal/JdtUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java index aa4c88bd9..57da8d0a1 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java @@ -136,15 +136,15 @@ public static ISourceContainer[] getSourceContainers(String projectName) { projects.addAll(workspaceProjects); Set calculated = new LinkedHashSet<>(); - for (IProject project : projects) { - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null && project.exists()) { + + projects.stream().map(project -> JdtUtils.getJavaProject(project)) + .filter(javaProject -> javaProject != null && javaProject.exists()) + .forEach(javaProject -> { // Add source containers associated with the project's runtime classpath entries. containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); // Add source containers associated with the project's source folders. containers.add(new JavaProjectSourceContainer(javaProject)); - } - } + }); return containers.toArray(new ISourceContainer[0]); } From a2b7a2515d6891fdc07dde2802f0391ec3f6926c Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 11 Dec 2017 15:42:08 +0800 Subject: [PATCH 8/8] Use distinct api to remove duplicated projects --- .../com/microsoft/java/debug/plugin/internal/JdtUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java index 57da8d0a1..cb652134c 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java @@ -127,6 +127,8 @@ public static ISourceContainer[] getSourceContainers(String projectName) { Set containers = new LinkedHashSet<>(); List projects = new ArrayList<>(); + // If the project name is specified, firstly compute the source containers from the specified project's + // classpath entries so that they can be placed in the front of the result. IProject targetProject = JdtUtils.getProject(projectName); if (targetProject != null) { projects.add(targetProject); @@ -137,7 +139,7 @@ public static ISourceContainer[] getSourceContainers(String projectName) { Set calculated = new LinkedHashSet<>(); - projects.stream().map(project -> JdtUtils.getJavaProject(project)) + projects.stream().distinct().map(project -> JdtUtils.getJavaProject(project)) .filter(javaProject -> javaProject != null && javaProject.exists()) .forEach(javaProject -> { // Add source containers associated with the project's runtime classpath entries.