From b31fd4c618bb6edef1ca63c7f6fbf5203ab0b162 Mon Sep 17 00:00:00 2001 From: tom-shan Date: Fri, 14 Jun 2019 14:48:37 +0800 Subject: [PATCH 001/206] Break out of the while loop for better performance (#280) Now it will cost about 5 seconds in the destroyLaunchFiles function, there should have a break statement if no IOException is caught in each loop iteration for better performance. Signed-off-by: tom-shan --- .../core/adapter/handler/AbstractDisconnectRequestHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java index d01fa06f8..3a9653dcd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java @@ -81,6 +81,8 @@ private void destroyLaunchFiles(IDebugAdapterContext context) { Files.deleteIfExists(context.getArgsfile()); context.setArgsfile(null); } + + break; } catch (IOException e) { // do nothing. logger.log(Level.WARNING, "Failed to destory launch files, will retry again."); From e87ae3282453939a635f486671566e77ee08cb55 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 17 Jun 2019 19:20:37 +0800 Subject: [PATCH 002/206] For different project types, use the corresponding class provider to resolve the runtime classpath (#281) Signed-off-by: Jinbo Wang --- .../internal/ResolveClasspathsHandler.java | 146 +++++++++++++----- 1 file changed, 111 insertions(+), 35 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java index 6dd76ac6a..8be73a7a5 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2019 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 @@ -11,7 +11,10 @@ package com.microsoft.java.debug.plugin.internal; +import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; +import java.util.Date; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -19,11 +22,19 @@ import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.internal.core.LaunchConfiguration; +import org.eclipse.debug.internal.core.LaunchConfigurationInfo; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; @@ -34,15 +45,20 @@ 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.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; +import org.w3c.dom.Element; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; import com.microsoft.java.debug.core.Configuration; public class ResolveClasspathsHandler { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - /** * Resolves class path for a java project. * @param arguments a list contains the main class name and project name @@ -160,7 +176,7 @@ private static String[][] computeClassPath(String mainClass, String projectName) project = projects.get(0); } - return computeClassPath(project, isMainClassInTestFolder(project, mainClass)); + return computeClassPath(project, !isMainClassInTestFolder(project, mainClass)); } /** @@ -168,51 +184,39 @@ private static String[][] computeClassPath(String mainClass, String projectName) * * @param javaProject * java project + * @param excludeTestCode whether to exclude the test code and test dependencies. * @return class path * @throws CoreException * CoreException */ - private static String[][] computeClassPath(IJavaProject javaProject, boolean includeTestScope) + private static String[][] computeClassPath(IJavaProject javaProject, boolean excludeTestCode) throws CoreException { if (javaProject == null) { throw new IllegalArgumentException("javaProject is null"); } - String[][] result = new String[2][]; - if (JavaRuntime.isModularProject(javaProject)) { - result[0] = computeDefaultRuntimeClassPath(javaProject, includeTestScope); - result[1] = new String[0]; - } else { - result[0] = new String[0]; - result[1] = computeDefaultRuntimeClassPath(javaProject, includeTestScope); - } - return result; - } - - private static String[] computeDefaultRuntimeClassPath(IJavaProject jproject, boolean includeTestScope) - throws CoreException { - IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(jproject); - Set resolved = new LinkedHashSet(); - for (int i = 0; i < unresolved.length; i++) { - IRuntimeClasspathEntry entry = unresolved[i]; - if (entry.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) { - IRuntimeClasspathEntry[] entries = JavaRuntime.resolveRuntimeClasspathEntry(entry, jproject, - !includeTestScope); - for (int j = 0; j < entries.length; j++) { - if (!includeTestScope && JdtUtils.isTest(entries[j].getClasspathEntry())) { - continue; - } - String location = entries[j].getLocation(); - if (location != null) { - // remove duplicate classpath - resolved.add(location); - } + ILaunchConfiguration launchConfig = new JavaApplicationLaunchConfiguration(javaProject.getProject(), excludeTestCode); + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(launchConfig); + IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspath(unresolved, launchConfig); + Set classpaths = new LinkedHashSet<>(); + Set modulepaths = new LinkedHashSet<>(); + for (IRuntimeClasspathEntry entry : resolved) { + String location = entry.getLocation(); + if (location != null) { + if (entry.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES + || entry.getClasspathProperty() == IRuntimeClasspathEntry.CLASS_PATH) { + classpaths.add(location); + } else if (entry.getClasspathProperty() == IRuntimeClasspathEntry.MODULE_PATH) { + modulepaths.add(location); } } } - return resolved.toArray(new String[resolved.size()]); - } + return new String[][] { + modulepaths.toArray(new String[modulepaths.size()]), + classpaths.toArray(new String[classpaths.size()]) + }; + } /** * Test whether the main class is located in test folders. @@ -254,4 +258,76 @@ public void acceptSearchMatch(SearchMatch match) { } return false; } + + private static class JavaApplicationLaunchConfiguration extends LaunchConfiguration { + public static final String JAVA_APPLICATION_LAUNCH = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + private IProject project; + private boolean excludeTestCode; + private String classpathProvider; + private String sourcepathProvider; + private LaunchConfigurationInfo launchInfo; + + protected JavaApplicationLaunchConfiguration(IProject project, boolean excludeTestCode) throws CoreException { + super(String.valueOf(new Date().getTime()), null, false); + this.project = project; + this.excludeTestCode = excludeTestCode; + if (ProjectUtils.isMavenProject(project)) { + classpathProvider = "org.eclipse.m2e.launchconfig.classpathProvider"; + sourcepathProvider = "org.eclipse.m2e.launchconfig.sourcepathProvider"; + } else if (ProjectUtils.isGradleProject(project)) { + classpathProvider = "org.eclipse.buildship.core.classpathprovider"; + } + this.launchInfo = new JavaLaunchConfigurationInfo(JAVA_APPLICATION_LAUNCH); + } + + @Override + public boolean getAttribute(String attributeName, boolean defaultValue) throws CoreException { + if (IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE.equalsIgnoreCase(attributeName)) { + return excludeTestCode; + } + + return super.getAttribute(attributeName, defaultValue); + } + + @Override + public String getAttribute(String attributeName, String defaultValue) throws CoreException { + if (IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME.equalsIgnoreCase(attributeName)) { + return project.getName(); + } else if (IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER.equalsIgnoreCase(attributeName)) { + return classpathProvider; + } else if (IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER.equalsIgnoreCase(attributeName)) { + return sourcepathProvider; + } + + return super.getAttribute(attributeName, defaultValue); + } + + @Override + protected LaunchConfigurationInfo getInfo() throws CoreException { + return this.launchInfo; + } + } + + private static class JavaLaunchConfigurationInfo extends LaunchConfigurationInfo { + public JavaLaunchConfigurationInfo(String launchXml) { + super(); + try { + DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + parser.setErrorHandler(new DefaultHandler()); + StringReader reader = new StringReader(launchXml); + InputSource source = new InputSource(reader); + Element root = parser.parse(source).getDocumentElement(); + initializeFromXML(root); + } catch (ParserConfigurationException | SAXException | IOException | CoreException e) { + // do nothing + } + } + } } From 9fd08f60a09245a61534a2d1027a91994539dc79 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 28 Jun 2019 11:08:00 +0800 Subject: [PATCH 003/206] Pass the mapped test resources to launch configuration to help m2e to detect test scope or not (#282) Signed-off-by: Jinbo Wang --- .../internal/ResolveClasspathsHandler.java | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java index 8be73a7a5..12b1ddde5 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java @@ -14,6 +14,8 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.LinkedHashSet; import java.util.List; @@ -28,6 +30,7 @@ import org.apache.commons.lang3.StringUtils; 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.core.runtime.IStatus; @@ -176,26 +179,29 @@ private static String[][] computeClassPath(String mainClass, String projectName) project = projects.get(0); } - return computeClassPath(project, !isMainClassInTestFolder(project, mainClass)); + IJavaElement testElement = findMainClassInTestFolders(project, mainClass); + List mappedResources = (testElement != null && testElement.getResource() != null) + ? Arrays.asList(testElement.getResource()) : Collections.EMPTY_LIST; + return computeClassPath(project, testElement == null, mappedResources); } /** * Compute runtime classpath of a java project. * - * @param javaProject - * java project - * @param excludeTestCode whether to exclude the test code and test dependencies. + * @param javaProject java project + * @param excludeTestCode whether to exclude the test code and test dependencies + * @param mappedResources the associated resources with the application * @return class path * @throws CoreException * CoreException */ - private static String[][] computeClassPath(IJavaProject javaProject, boolean excludeTestCode) + private static String[][] computeClassPath(IJavaProject javaProject, boolean excludeTestCode, List mappedResources) throws CoreException { if (javaProject == null) { throw new IllegalArgumentException("javaProject is null"); } - ILaunchConfiguration launchConfig = new JavaApplicationLaunchConfiguration(javaProject.getProject(), excludeTestCode); + ILaunchConfiguration launchConfig = new JavaApplicationLaunchConfiguration(javaProject.getProject(), excludeTestCode, mappedResources); IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(launchConfig); IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspath(unresolved, launchConfig); Set classpaths = new LinkedHashSet<>(); @@ -219,19 +225,24 @@ private static String[][] computeClassPath(IJavaProject javaProject, boolean exc } /** - * Test whether the main class is located in test folders. + * Try to find the associated java element with the main class from the test folders. + * * @param project the java project containing the main class * @param mainClass the main class name - * @return whether the main class is located in test folders + * @return the associated java element */ - private static boolean isMainClassInTestFolder(IJavaProject project, String mainClass) { + private static IJavaElement findMainClassInTestFolders(IJavaProject project, String mainClass) { + if (project == null || StringUtils.isBlank(mainClass)) { + return null; + } + // get a list of test folders and check whether main class is here int constraints = IJavaSearchScope.SOURCES; IJavaElement[] testFolders = JdtUtils.getTestPackageFragmentRoots(project); if (testFolders.length > 0) { try { - List mainClassesInTestFolder = new ArrayList<>(); + List mainClassesInTestFolder = new ArrayList<>(); SearchPattern pattern = SearchPattern.createPattern(mainClass, IJavaSearchConstants.CLASS, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); @@ -242,7 +253,7 @@ private static boolean isMainClassInTestFolder(IJavaProject project, String main public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IJavaElement) { - mainClassesInTestFolder.add(element); + mainClassesInTestFolder.add((IJavaElement) element); } } }; @@ -251,12 +262,15 @@ public void acceptSearchMatch(SearchMatch match) { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, null /* progress monitor */); - return !mainClassesInTestFolder.isEmpty(); + if (!mainClassesInTestFolder.isEmpty()) { + return mainClassesInTestFolder.get(0); + } } catch (Exception e) { logger.log(Level.SEVERE, String.format("Searching the main class failure: %s", e.toString()), e); } } - return false; + + return null; } private static class JavaApplicationLaunchConfiguration extends LaunchConfiguration { @@ -265,19 +279,20 @@ private static class JavaApplicationLaunchConfiguration extends LaunchConfigurat + "\n" + "\n" + "\n" - + "\n" + "\n" + ""; private IProject project; private boolean excludeTestCode; + private List mappedResources; private String classpathProvider; private String sourcepathProvider; private LaunchConfigurationInfo launchInfo; - protected JavaApplicationLaunchConfiguration(IProject project, boolean excludeTestCode) throws CoreException { + protected JavaApplicationLaunchConfiguration(IProject project, boolean excludeTestCode, List mappedResources) throws CoreException { super(String.valueOf(new Date().getTime()), null, false); this.project = project; this.excludeTestCode = excludeTestCode; + this.mappedResources = mappedResources; if (ProjectUtils.isMavenProject(project)) { classpathProvider = "org.eclipse.m2e.launchconfig.classpathProvider"; sourcepathProvider = "org.eclipse.m2e.launchconfig.sourcepathProvider"; @@ -309,6 +324,11 @@ public String getAttribute(String attributeName, String defaultValue) throws Cor return super.getAttribute(attributeName, defaultValue); } + @Override + public IResource[] getMappedResources() throws CoreException { + return mappedResources.toArray(new IResource[0]); + } + @Override protected LaunchConfigurationInfo getInfo() throws CoreException { return this.launchInfo; From 5860855c66a853a3c85a4b19a044b3da4fa548a6 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 28 Jun 2019 11:29:17 +0800 Subject: [PATCH 004/206] Bump version to 0.20.0 (#283) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 7fdda1114..6a78ee43b 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.19.0 + 0.20.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 03edc580d..2d822a34c 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 9aa683c6c..033ebc955 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.19.0 +Bundle-Version: 0.20.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.19.0.jar + lib/com.microsoft.java.debug.core-0.20.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 6a3f1c786..d0a043690 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.19.0 + 0.20.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.19.0 + 0.20.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index ca9812a0d..234d47b68 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 6895c855a..486726b6b 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.19.0 + 0.20.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index d08cd613e..184e34095 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.19.0 + 0.20.0 pom Java Debug Server for Visual Studio Code From 08127a004f8bbd4d055b6b244bdad3b7c83cd45f Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Wed, 7 Aug 2019 10:05:40 +0800 Subject: [PATCH 005/206] Check style only on Travis CI (#287) * Check style only on Travis CI * Update pom.xml * Update pom.xml * update indent --- .travis.yml | 1 + check_style.xml | 4 ++-- pom.xml | 26 +++++++++++++++----------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c448eebf..f331372be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ os: script: - ./mvnw clean verify + - ./mvnw checkstyle:check cache: directories: diff --git a/check_style.xml b/check_style.xml index 5b497d979..360e82d11 100644 --- a/check_style.xml +++ b/check_style.xml @@ -20,7 +20,7 @@ - + @@ -289,4 +289,4 @@ - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 184e34095..de6ecccc5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ Java Debug Server for Visual Studio Code UTF-8 1.2.0 + ${basedir} @@ -113,15 +114,6 @@ org.apache.maven.plugins maven-checkstyle-plugin 2.17 - - - validate - validate - - check - - - com.puppycrawl.tools @@ -135,8 +127,7 @@ - ${project.parent.basedir}/check_style.xml - basedir=${project.parent.basedir} + ${checkstyleDir}/check_style.xml true @@ -148,6 +139,19 @@ + + + activate-in-module + + + ${basedir}/../check_style.xml + + + + ${basedir}/.. + + + photon From 9a5d2fc7d0e6a49c667d936a123d9c418b7dc4ab Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Fri, 9 Aug 2019 14:42:05 +0800 Subject: [PATCH 006/206] Support to use a launcher to run in terminal (#288) --- .../java/debug/core/adapter/handler/LaunchRequestHandler.java | 4 +++- .../java/com/microsoft/java/debug/core/protocol/Requests.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 31a0a0878..991d29197 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -184,8 +184,10 @@ protected void handleTerminatedEvent(IDebugAdapterContext context) { */ public static String[] constructLaunchCommands(LaunchArguments launchArguments, boolean serverMode, String address) { String slash = System.getProperty("file.separator"); - List launchCmds = new ArrayList<>(); + if (launchArguments.launcherScript != null) { + launchCmds.add(launchArguments.launcherScript); + } final String javaHome = StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome) ? DebugSettings.getCurrent().javaHome : System.getProperty("java.home"); launchCmds.add(javaHome + slash + "bin" + slash + "java"); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index ef195219c..9aa1383e6 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -82,8 +82,9 @@ public static class LaunchArguments extends LaunchBaseArguments { public Map env; public boolean stopOnEntry; public boolean noDebug = false; - public CONSOLE console = CONSOLE.internalConsole; + public CONSOLE console = CONSOLE.integratedTerminal; public ShortenApproach shortenCommandLine = ShortenApproach.NONE; + public String launcherScript; } public static class AttachArguments extends LaunchBaseArguments { From 13fa37e9c5401c85efd4d4021e9c5a1fce1371bd Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Thu, 15 Aug 2019 09:31:05 +0800 Subject: [PATCH 007/206] Add script for Maven Central publishing (#289) * Add scripts for deploying artifacts * use native md5sum/sha1sum in Linux * update cases for azure pipelines * wrap passphrase to make terminal happy * add --pinentry-mode=loopback for gpg v2.1+ * update promote script * update * scripts: better naming * poll to see promote status * check args * unify script * address comments --- scripts/publishMaven.js | 273 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 scripts/publishMaven.js diff --git a/scripts/publishMaven.js b/scripts/publishMaven.js new file mode 100644 index 000000000..7c8d3c327 --- /dev/null +++ b/scripts/publishMaven.js @@ -0,0 +1,273 @@ +/** + * Usage: + * node publishMaven.js -task [upload|promote] + * + * upload: Upload artifacts to a nexus staging repo. + * promote: Promote a repo to get it picked up by Maven Central. + */ + +const childProcess = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const artifactFolder = process.env.artifactFolder; +const configs = { + nexus_ossrhuser: process.env.NEXUS_OSSRHUSER, + nexus_ossrhpass: process.env.NEXUS_OSSRHPASS, + nexus_stagingProfileId: process.env.NEXUS_STAGINGPROFILEID, + gpgpass: process.env.GPGPASS, + stagingRepoId: process.env.NEXUS_STAGINGREPOID, + groupId: "com.microsoft.java", + projectName: "java-debug", + releaseVersion: process.env.releaseVersion, + moduleNames: [ + "java-debug-parent", + "com.microsoft.java.debug.core", + "com.microsoft.java.debug.plugin" + ] +}; + +main(configs, artifactFolder); + +function main() { + const argv = process.argv; + const task = argv[argv.indexOf("-task") + 1]; + if (task === "upload") { + uploadToStaging(configs, artifactFolder); + } else if (task === "promote") { + promoteToCentral(configs); + } else { + console.error("Task not specified."); + console.log("Usage: node script.js -task [upload|promote]"); + } +} + +/** + * Task upload: Upload artifacts to a nexus staging repo. + * + * Required binaries: + * - gpg + * - curl + * + * Required Environment Variables: + * - artifactFolder: folder containing *.jar/*.pom files. + * - releaseVersion: version of artifacts. + * - NEXUS_OSSRHUSER: username. + * - NEXUS_OSSRHPASS: password. + * - NEXUS_STAGINGPROFILEID: identifier of the repo to promote. + * - GPGPASS: passphrase of GPG key. + */ +function uploadToStaging(configs, artifactFolder) { + checkPrerequisite(configs); + addChecksumsAndGpgSignature(configs, artifactFolder); + createStagingRepo(configs); + deployToStagingRepo(configs, artifactFolder); + closeStagingRepo(configs); +} + + /** + * Task promote: Promote a repo to get it picked up by Maven Central. + * + * Required binaries: + * - curl + * + * Required Environment Variables: + * - NEXUS_OSSRHUSER: username. + * - NEXUS_OSSRHPASS: password. + * - NEXUS_STAGINGPROFILEID: identifier of the repo to promote. + * - NEXUS_STAGINGREPOID: id of staging repo with artifacts to promote. + */ +function promoteToCentral(configs) { + let message = ""; + console.log("\n========Nexus: Promote======="); + try { + console.log(`Starting to promote staging repository ${configs.stagingRepoId} ...`); + console.log(`curl -i -X POST -d "${configs.stagingRepoId}" -H "Content-Type: application/xml" -u **:** -k https://oss.sonatype.org/service/local/staging/profiles/${configs.nexus_stagingProfileId}/promote`); + message = childProcess.execSync(`curl -i -X POST -d "${configs.stagingRepoId}" -H "Content-Type: application/xml" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k https://oss.sonatype.org/service/local/staging/profiles/${configs.nexus_stagingProfileId}/promote`); + message = message.toString(); + console.log(message); + } catch (ex) { + console.error("\n\n[Failure] Promoting staging repository failed."); + console.error(!message ? ex : message.toString()); + process.exit(1); + } + const success = isReleased(configs); + console.log("Below is the public repository url, you could manually validate it."); + console.log(`https://oss.sonatype.org/content/groups/public/${configs.groupId.replace(/\./g, "/")}`); + console.log("\n\n"); + if (success) { + console.log("\n\n[Success] Nexus: Promote succeeded."); + } else { + console.error("\n\n[Failure] Nexus: Promote failed."); + process.exit(1) + } +} + +function isReleased(configs) { + let pollingCount = 0; + const MAX_POLLINGS = 10; + for (; pollingCount < MAX_POLLINGS; pollingCount++) { + console.log(`\nPolling the release operation finished or not...`); + console.log(`curl -X GET -H "Content-Type:application/xml" -u **:** -k https://oss.sonatype.org/service/local/staging/repository/${configs.stagingRepoId}`); + message = childProcess.execSync(`curl -X GET -H "Content-Type:application/xml" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k https://oss.sonatype.org/service/local/staging/repository/${configs.stagingRepoId}`); + const status = extractStatus(message.toString()); + console.log(status); + if (status !== "closed") { + return true; + } + // use system sleep command to pause the program. + childProcess.execSync(`sleep 6s`); + } + return false; +} + +function checkPrerequisite(configs) { + const props = ["releaseVersion", "artifactFolder", "NEXUS_OSSRHUSER", "NEXUS_OSSRHPASS", "NEXUS_STAGINGPROFILEID", "GPGPASS" ]; + for (const prop of props) { + if (!configs[prop]) { + console.error(`${prop} is not set.`); + process.exit(1); + } + } +} + +function addChecksumsAndGpgSignature(configs, artifactFolder) { + console.log("\n=======Checksum and gpg sign======="); + console.log("Starting to calculate checksum and gpg sign..."); + for (let moduleName of configs.moduleNames) { + const modulePath = path.join(artifactFolder, moduleName); + // remove old md5/sha1/asc files. + fs.readdirSync(modulePath) + .filter(name => name.endsWith(".md5") || name.endsWith(".sha1") || name.endsWith(".asc")) + .forEach(name => fs.unlinkSync(path.join(modulePath, name))); + + const files = fs.readdirSync(modulePath); + for (let file of files) { + // calc md5. + const md5 = childProcess.execSync(`md5sum "${path.join(modulePath, file)}"`); + const md5Match = /([a-z0-9]{32})/.exec(md5.toString()); + fs.writeFileSync(path.join(modulePath, file + ".md5"), md5Match[0]); + + // calc sha1. + const sha1 = childProcess.execSync(`sha1sum "${path.join(modulePath, file)}"`); + const sha1Match = /([a-z0-9]{40})/.exec(sha1.toString()); + fs.writeFileSync(path.join(modulePath, file + ".sha1"), sha1Match[0]); + + // gpg sign. + childProcess.execSync(`gpg --batch --pinentry-mode loopback --passphrase "${configs.gpgpass}" -ab "${path.join(modulePath, file)}"`) + } + } + console.log("\n\n[Success] Checksum and gpg sign finished."); + console.log("\n\n"); +} + +function createStagingRepo(configs) { + let message = ""; + console.log("\n=======Nexus: Create staging repo======="); + console.log("Starting to create staging repository..."); + try { + console.log(`curl -X POST -d "${configs.projectName}-${configs.releaseVersion}" -H "Content-Type: application/xml" -u **:** -k https://oss.sonatype.org/service/local/staging/profiles/${configs.nexus_stagingProfileId}/start`); + message = childProcess.execSync(`curl -X POST -d "${configs.projectName}-${configs.releaseVersion}" -H "Content-Type: application/xml" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k https://oss.sonatype.org/service/local/staging/profiles/${configs.nexus_stagingProfileId}/start`); + message = message.toString(); + const match = /([a-zA-Z0-9-_]+)<\/stagedRepositoryId>/.exec(message); + if (match != null && match.length > 1) { + configs.stagingRepoId = match[1]; + } else { + console.error("\n[Failure] Creating staging repository failed."); + console.error(message); + process.exit(1); + } + } catch (ex) { + console.error("\n[Failure] Creating staging repository failed."); + console.error(!message ? ex : message.toString()); + process.exit(1); + } + console.log("\n\n[Success] Nexus: Creating staging repository completion."); + console.log("staging repository id: " + configs.stagingRepoId); + console.log("\n\n"); +} + +function deployToStagingRepo(configs, artifactFolder) { + console.log("\n========Nexus: Deploy artifacts to staging repo======="); + console.log("Starting to deploy artifacts to staging repository..."); + for (let moduleName of configs.moduleNames) { + const modulePath = path.join(artifactFolder, moduleName); + for (let file of fs.readdirSync(modulePath)) { + const realPath = path.join(modulePath, file); + const url = [ + "https://oss.sonatype.org/service/local/staging/deployByRepositoryId", + configs.stagingRepoId, + configs.groupId.replace(/\./g, "/"), + moduleName, + configs.releaseVersion, + file + ]; + console.log(`curl --upload-file "${realPath}" -u **:** -k ${url.join("/")}`); + message = childProcess.execSync(`curl --upload-file "${realPath}" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k ${url.join("/")}`); + message = message.toString(); + console.log(message); + console.log("Succeeded.\n"); + } + } + console.log("\n\n[Success] Nexus: Deploying completion."); + console.log("\n\n"); +} + +function closeStagingRepo(configs) { + let message = ""; + let pollingCount = 0; + const MAX_POLLINGS = 10; + console.log("\n========Nexus: Verify and Close staging repo======="); + try { + console.log(`Starting to close staging repository ${configs.stagingRepoId} ...`); + console.log(`curl -X POST -d "${configs.stagingRepoId}" -H "Content-Type: application/xml" -u **:** -k https://oss.sonatype.org/service/local/staging/profiles/${configs.nexus_stagingProfileId}/finish`); + message = childProcess.execSync(`curl -X POST -d "${configs.stagingRepoId}" -H "Content-Type: application/xml" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k https://oss.sonatype.org/service/local/staging/profiles/${configs.nexus_stagingProfileId}/finish`); + message = message.toString(); + + for (; pollingCount < MAX_POLLINGS; pollingCount++) { + console.log(`\nPolling the close operation finished or not...`); + console.log(`curl -X GET -H "Content-Type:application/xml" -u **:** -k https://oss.sonatype.org/service/local/staging/repository/${configs.stagingRepoId}`); + message = childProcess.execSync(`curl -X GET -H "Content-Type:application/xml" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k https://oss.sonatype.org/service/local/staging/repository/${configs.stagingRepoId}`); + // console.log(message.toString()); + if (extractStatus(message.toString()) === "closed") { + break; + } + // use system sleep command to pause the program. + childProcess.execSync(`sleep 6s`); + } + + if (pollingCount >= MAX_POLLINGS) { + console.log("\nQuerying the close operation result..."); + message = childProcess.execSync(`curl -X GET -H "Content-Type:application/xml" -u ${configs.nexus_ossrhuser}:${configs.nexus_ossrhpass} -k https://oss.sonatype.org/service/local/staging/repository/${configs.stagingRepoId}/activity`); + // console.log(message.toString()); + const errors = extractErrorMessage(message.toString()); + console.error(`\n\n[Failure] Closing staging repository failed.`); + console.error(`See failure messages:`); + console.error(errors.join("\n\n")); + process.exit(1); + } + } catch (ex) { + console.error("\n\n[Failure] Closing staging repository failed."); + console.error(!message ? ex : message.toString()); + process.exit(1); + } + fs.writeFileSync(".stagingRepoId", configs.stagingRepoId); + console.log("\n\n[Success] Nexus: Staging completion."); + console.log("Below is the staging repository url, you could use it to test deployment."); + console.log(`https://oss.sonatype.org/content/repositories/${configs.stagingRepoId}`); + console.log("\n\n"); +} + +function extractStatus(message) { + const group = /([a-zA-Z0-9-_\.]+)<\/type>/.exec(message); + return group[1]; +} + +function extractErrorMessage(message) { + const errors = []; + const group = message.match(/failureMessage<\/name>[\r?\n ]+(.*)<\/value>/g); + for (let error of group) { + errors.push(error.match(/(.*)<\/value>/)[1]) + } + return errors; +} From a8b36471c60e2b0dddda3a7bf85b9f1d35699c8d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 22 Aug 2019 16:10:51 +0800 Subject: [PATCH 008/206] bump version to 0.21.0 (#291) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 6a78ee43b..6f5388664 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.20.0 + 0.21.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 2d822a34c..69dd86f7a 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 033ebc955..7dc819876 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.20.0 +Bundle-Version: 0.21.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.20.0.jar + lib/com.microsoft.java.debug.core-0.21.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index d0a043690..3f5599746 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.20.0 + 0.21.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.20.0 + 0.21.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 234d47b68..2af8ce536 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 486726b6b..9888293ed 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.20.0 + 0.21.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index de6ecccc5..594b1f468 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.20.0 + 0.21.0 pom Java Debug Server for Visual Studio Code From 399de9a447bafce63d1b5992ca57c92cbc69ad81 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 23 Aug 2019 10:06:50 +0800 Subject: [PATCH 009/206] Update pde target definition (#292) Signed-off-by: Jinbo Wang --- java.debug.target | 29 ++++++++++++++++------------- pom.xml | 11 ++++++++--- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/java.debug.target b/java.debug.target index 38a97e012..530f178af 100644 --- a/java.debug.target +++ b/java.debug.target @@ -1,23 +1,26 @@ - - - + - - - + + + + + + + - - + + + - - + + - - + + - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 594b1f468..7ffa75956 100644 --- a/pom.xml +++ b/pom.xml @@ -154,9 +154,9 @@ - photon + 201906 p2 - http://download.eclipse.org/releases/photon + http://download.eclipse.org/releases/2019-06/ oss.sonatype.org @@ -174,6 +174,11 @@ JBOLL.TOOLS p2 http://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.0-2018-05-16_00-46-30-H11 - + + + orbit + p2 + http://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ + From 4ecdda493dab4ae6d9263a2dee11f6071bd31a29 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Mon, 26 Aug 2019 13:19:53 +0800 Subject: [PATCH 010/206] Fix checkPrerequisite (#293) --- scripts/publishMaven.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/publishMaven.js b/scripts/publishMaven.js index 7c8d3c327..00f5180e3 100644 --- a/scripts/publishMaven.js +++ b/scripts/publishMaven.js @@ -24,7 +24,8 @@ const configs = { "java-debug-parent", "com.microsoft.java.debug.core", "com.microsoft.java.debug.plugin" - ] + ], + artifactFolder: artifactFolder }; main(configs, artifactFolder); From 80d480ae8a361553126bf0f680bcd9cca1bc29c6 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Mon, 26 Aug 2019 13:27:46 +0800 Subject: [PATCH 011/206] Fix checkPrerequisite (#294) --- scripts/publishMaven.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publishMaven.js b/scripts/publishMaven.js index 00f5180e3..1f960766c 100644 --- a/scripts/publishMaven.js +++ b/scripts/publishMaven.js @@ -123,7 +123,7 @@ function isReleased(configs) { } function checkPrerequisite(configs) { - const props = ["releaseVersion", "artifactFolder", "NEXUS_OSSRHUSER", "NEXUS_OSSRHPASS", "NEXUS_STAGINGPROFILEID", "GPGPASS" ]; + const props = ["releaseVersion", "artifactFolder", "nexus_ossrhuser", "nexus_ossrhpass", "nexus_stagingProfileId", "gpgpass" ]; for (const prop of props) { if (!configs[prop]) { console.error(`${prop} is not set.`); From 8aed2d1fe6909c3b34dc51c355e81b6ede5ed434 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 12 Sep 2019 15:20:36 +0800 Subject: [PATCH 012/206] Add command to check if the hovered element is main method (#296) * Add command to check if the hovered element is main method Signed-off-by: Jinbo Wang * Make checkstyle happy Signed-off-by: Jinbo Wang * Address review comments Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.plugin/plugin.xml | 1 + .../JavaDebugDelegateCommandHandler.java | 3 + .../internal/ResolveElementHandler.java | 73 +++++++++++++++++++ .../internal/ResolveMainMethodHandler.java | 5 +- 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveElementHandler.java diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index c1bcaf4fa..3b9c22aa3 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -13,6 +13,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index f80fde1d0..11767e4b4 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -31,6 +31,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String RESOLVE_MAINMETHOD = "vscode.java.resolveMainMethod"; public static final String INFER_LAUNCH_COMMAND_LENGTH = "vscode.java.inferLaunchCommandLength"; public static final String CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings"; + public static final String RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -60,6 +61,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return LaunchCommandHandler.getLaunchCommandLength(JsonUtils.fromJson((String) arguments.get(0), LaunchArguments.class)); case CHECK_PROJECT_SETTINGS: return ProjectSettingsChecker.check(JsonUtils.fromJson((String) arguments.get(0), ProjectSettingsChecker.ProjectSettingsCheckerParams.class)); + case RESOLVE_ELEMENT_AT_SELECTION: + return ResolveElementHandler.resolveElementAtSelection(arguments, progress); default: break; } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveElementHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveElementHandler.java new file mode 100644 index 000000000..2240a2689 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveElementHandler.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2019 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 java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; + +import com.microsoft.java.debug.core.DebugException; + +public class ResolveElementHandler { + + /** + * Resolve the Java element at the selected position. + * @return the resolved Java element information. + */ + public static Object resolveElementAtSelection(List arguments, IProgressMonitor monitor) throws DebugException { + if (arguments == null || arguments.size() < 3) { + return Collections.emptyList(); + } + + String uri = (String) arguments.get(0); + int line = (int) Math.round((double) arguments.get(1)); + int column = (int) Math.round((double) arguments.get(2)); + final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uri); + try { + IJavaElement element = JDTUtils.findElementAtSelection(unit, line, column, + JavaLanguageServerPlugin.getPreferencesManager(), monitor); + if (element instanceof IMethod) { + return new JavaElement(((IMethod) element).getDeclaringType().getFullyQualifiedName(), + element.getJavaProject().getProject().getName(), + ((IMethod) element).isMainMethod()); + } else if (element instanceof IType) { + return new JavaElement(((IType) element).getFullyQualifiedName(), + element.getJavaProject().getProject().getName(), + ResolveMainMethodHandler.getMainMethod((IType) element) != null); + } + } catch (JavaModelException e) { + throw new DebugException("Failed to resolve the selected element information: " + e.getMessage(), e); + } + + return null; + } + + static class JavaElement { + private String declaringType; + private String projectName; + private boolean hasMainMethod; + + public JavaElement(String declaringType, String projectName, boolean hasMainMethod) { + this.declaringType = declaringType; + this.projectName = projectName; + this.hasMainMethod = hasMainMethod; + } + } +} \ No newline at end of file diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index 329a12f90..aaef587c3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -95,7 +95,10 @@ private static List searchMainMethods(ICompilationUnit compilationUnit) return result; } - private static IMethod getMainMethod(IType type) throws JavaModelException { + /** + * Returns the main method defined in the type. + */ + public static IMethod getMainMethod(IType type) throws JavaModelException { for (IMethod method : type.getMethods()) { // Have at most one main method in the member methods of the type. if (method.isMainMethod()) { From 95c6e3ebb01abc8bc708595a10cac40c9395b52c Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 18 Sep 2019 13:23:17 +0800 Subject: [PATCH 013/206] bump version to 0.22.0 (#297) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 6f5388664..99d621703 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.21.0 + 0.22.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 69dd86f7a..3e2b3081f 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 7dc819876..c79373d0a 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.21.0 +Bundle-Version: 0.22.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.21.0.jar + lib/com.microsoft.java.debug.core-0.22.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 3f5599746..9f2a27709 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.21.0 + 0.22.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.21.0 + 0.22.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 2af8ce536..3c3e69fcb 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9888293ed..a183d8bbc 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.21.0 + 0.22.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 7ffa75956..b00a48be3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.21.0 + 0.22.0 pom Java Debug Server for Visual Studio Code From 40a13eed6895df89c8f45d9a6b410b2c23250cf0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 18 Sep 2019 16:47:24 +0800 Subject: [PATCH 014/206] Reuse the temp file name for the same project when shorten the classpath (#298) * Reuse the temp file name for the same project when shorten the classpath Signed-off-by: Jinbo Wang * make checkstyle happy Signed-off-by: Jinbo Wang --- .../java/debug/core/adapter/AdapterUtils.java | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 0c6b754e4..35d99092b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2019 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 @@ -15,6 +15,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -45,6 +46,8 @@ import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; +import sun.security.action.GetPropertyAction; + public class AdapterUtils { private static final String OS_NAME = System.getProperty("os.name", "").toLowerCase(); private static final Pattern ENCLOSING_CLASS_REGEX = Pattern.compile("^([^\\$]*)"); @@ -312,8 +315,9 @@ public static Path generateClasspathJar(String[] classPaths) throws IOException Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); // In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar - attributes.put(Attributes.Name.CLASS_PATH, String.join(" ", classpathUrls)); - Path tempfile = Files.createTempFile("classpath_", ".jar"); + String classpathValue = String.join(" ", classpathUrls); + attributes.put(Attributes.Name.CLASS_PATH, classpathValue); + Path tempfile = createTempFile("cp_" + getMd5(classpathValue), ".jar"); JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest); jar.close(); @@ -338,9 +342,58 @@ public static Path generateArgfile(String[] classPaths, String[] modulePaths) th } argfile = argfile.replace("\\", "\\\\"); - Path tempfile = Files.createTempFile("java_", ".argfile"); + Path tempfile = createTempFile("cp_" + getMd5(argfile), ".argfile"); Files.write(tempfile, argfile.getBytes()); return tempfile; } + + private static Path tmpdir = null; + + private static synchronized Path getTmpDir() throws IOException { + if (tmpdir == null) { + try { + tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + } catch (NullPointerException | InvalidPathException e) { + Path tmpfile = Files.createTempFile("", ".tmp"); + tmpdir = tmpfile.getParent(); + try { + Files.deleteIfExists(tmpfile); + } catch (Exception ex) { + // do nothing + } + } + } + + return tmpdir; + } + + private static Path createTempFile(String baseName, String suffix) throws IOException { + // loop until the temp file can be created + SecurityManager sm = System.getSecurityManager(); + for (int i = 0; ; i++) { + Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); + try { + // delete the old temp file + Files.deleteIfExists(tempFile); + } catch (Exception e) { + // do nothing + } + + if (!Files.exists(tempFile)) { + return Files.createFile(tempFile); + } + } + } + + private static String getMd5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + BigInteger md5 = new BigInteger(1, messageDigest); + return md5.toString(Character.MAX_RADIX); + } catch (NoSuchAlgorithmException e) { + return Integer.toString(input.hashCode(), Character.MAX_RADIX); + } + } } From c90a238126e25e689b51b0396b4b145a47927567 Mon Sep 17 00:00:00 2001 From: Tom Harada <51029057+p10q@users.noreply.github.com> Date: Fri, 20 Sep 2019 01:45:28 -0700 Subject: [PATCH 015/206] Add minor installation note in README.md about mvnw.cmd/mvnw (#299) --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb97a9bed..dd57cfcb3 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,17 @@ The Java Debug Server is the bridge between VSCode and JVM. The implementation i - com.microsoft.java.debug.core - the core logic of the debug server - com.microsoft.java.debug.plugin - wraps the debug server into an Eclipse plugin to work with Eclipse JDT Language Server +## Installation + +### Windows: +``` +mvnw.cmd clean install +``` +### Linux and macOS: +``` +./mvnw clean install +``` + License ------- -EPL 1.0, See [LICENSE](LICENSE.txt) file. \ No newline at end of file +EPL 1.0, See [LICENSE](LICENSE.txt) file. From f4ad19a8c9d40eb83805b6c4ae6e2f952dde1a1b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 23 Sep 2019 10:02:39 +0800 Subject: [PATCH 016/206] Clean up the resources generated by HCR provider (#300) * Clean up the resources generated by HCR provider Signed-off-by: Jinbo Wang * Fix launch.json project name Signed-off-by: Jinbo Wang * clean up hcr provider in noDebug mode Signed-off-by: Jinbo Wang --- .vscode/launch.json | 3 ++- .../microsoft/java/debug/core/adapter/IProvider.java | 6 ++++++ .../handler/AbstractDisconnectRequestHandler.java | 9 +++++++++ .../core/adapter/handler/DisconnectRequestHandler.java | 1 - .../plugin/internal/JavaHotCodeReplaceProvider.java | 10 +++++++++- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c8c1f83c8..6de8b630d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,7 +14,8 @@ "sourcePaths": [ "${workspaceRoot}/com.microsoft.java.debug.core/main/java", "${workspaceRoot}/com.microsoft.java.debug.plugin/src/main/java" - ] + ], + "projectName": "com.microsoft.java.debug.plugin" } ] } \ No newline at end of file diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java index 74b5e17d9..86c30929c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java @@ -24,4 +24,10 @@ public interface IProvider { */ default void initialize(IDebugAdapterContext debugContext, Map options) { } + + /** + * Close the provider and free all associated resources. + */ + default void close() { + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java index 3a9653dcd..888009890 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java @@ -23,6 +23,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider; import com.microsoft.java.debug.core.adapter.LaunchMode; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; @@ -50,6 +51,7 @@ public CompletableFuture handle(Command command, Arguments arguments, * @param context the debug context */ private void destroyResource(IDebugAdapterContext context) { + destroyProviders(context); if (shouldDestroyLaunchFiles(context)) { destroyLaunchFiles(context); } @@ -97,4 +99,11 @@ private void destroyLaunchFiles(IDebugAdapterContext context) { } protected abstract void destroyDebugSession(Command command, Arguments arguments, Response response, IDebugAdapterContext context); + + protected void destroyProviders(IDebugAdapterContext context) { + IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class); + if (hcrProvider != null) { + hcrProvider.close(); + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestHandler.java index 7170e1549..a615e8963 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestHandler.java @@ -32,5 +32,4 @@ public void destroyDebugSession(Command command, Arguments arguments, Response r } } } - } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java index e601bc128..3ea1635d2 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java @@ -265,8 +265,16 @@ public void initialize(IDebugAdapterContext context, Map options } this.context = context; currentDebugSession = context.getDebugSession(); + } + + @Override + public void close() { + if (DebugSettings.getCurrent().hotCodeReplace != DebugSettings.HotCodeReplace.NEVER) { + // Remove the listener. + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + } - // TODO: Change IProvider interface for shutdown event + eventSubject.onComplete(); } @Override From 531b21aaefc7622512693965fbe411c04263cf47 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 11 Oct 2019 09:36:21 +0800 Subject: [PATCH 017/206] Add command to resolve the project build files (#301) * Add command to resolve the project build files Signed-off-by: Jinbo Wang * Fix checkstyle failure Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.plugin/plugin.xml | 1 + .../JavaDebugDelegateCommandHandler.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index 3b9c22aa3..684102199 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -14,6 +14,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 11767e4b4..faa77b78e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -11,10 +11,16 @@ package com.microsoft.java.debug.plugin.internal; +import java.util.ArrayList; import java.util.List; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; +import org.eclipse.jdt.ls.core.internal.ResourceUtils; import com.microsoft.java.debug.core.UsageDataStore; import com.microsoft.java.debug.core.protocol.JsonUtils; @@ -32,6 +38,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String INFER_LAUNCH_COMMAND_LENGTH = "vscode.java.inferLaunchCommandLength"; public static final String CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings"; public static final String RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; + public static final String RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -63,6 +70,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return ProjectSettingsChecker.check(JsonUtils.fromJson((String) arguments.get(0), ProjectSettingsChecker.ProjectSettingsCheckerParams.class)); case RESOLVE_ELEMENT_AT_SELECTION: return ResolveElementHandler.resolveElementAtSelection(arguments, progress); + case RESOLVE_BUILD_FILES: + return getBuildFiles(); default: break; } @@ -70,4 +79,22 @@ public Object executeCommand(String commandId, List arguments, IProgress throw new UnsupportedOperationException(String.format("Java debug plugin doesn't support the command '%s'.", commandId)); } + private List getBuildFiles() { + List result = new ArrayList<>(); + List javaProjects = JdtUtils.listJavaProjects(ResourcesPlugin.getWorkspace().getRoot()); + for (IJavaProject javaProject : javaProjects) { + IFile buildFile = null; + if (ProjectUtils.isMavenProject(javaProject.getProject())) { + buildFile = javaProject.getProject().getFile("pom.xml"); + } else if (ProjectUtils.isGradleProject(javaProject.getProject())) { + buildFile = javaProject.getProject().getFile("build.gradle"); + } + + if (buildFile != null && buildFile.exists() && buildFile.getLocationURI() != null) { + result.add(ResourceUtils.fixURI(buildFile.getLocationURI())); + } + } + + return result; + } } From de511936f2a5b0404a2e2a028277d250e7ce867f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 14 Oct 2019 12:37:30 +0800 Subject: [PATCH 018/206] Check whether java file is on classpath (#302) * Check whether java file is on classpath Signed-off-by: Jinbo Wang * Throw exception for illgeal case Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.plugin/plugin.xml | 1 + .../JavaDebugDelegateCommandHandler.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index 684102199..d71e1c7fd 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -15,6 +15,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index faa77b78e..7a1b55dfc 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -17,11 +17,14 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; +import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.UsageDataStore; import com.microsoft.java.debug.core.protocol.JsonUtils; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; @@ -39,6 +42,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings"; public static final String RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; public static final String RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; + public static final String IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -72,6 +76,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return ResolveElementHandler.resolveElementAtSelection(arguments, progress); case RESOLVE_BUILD_FILES: return getBuildFiles(); + case IS_ON_CLASSPATH: + return isOnClasspath(arguments); default: break; } @@ -97,4 +103,19 @@ private List getBuildFiles() { return result; } + + private boolean isOnClasspath(List arguments) throws DebugException { + if (arguments.size() < 1) { + throw new DebugException("No file uri is specified."); + } + + String uri = (String) arguments.get(0); + final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uri); + if (unit == null || unit.getResource() == null || !unit.getResource().exists()) { + throw new DebugException("The compilation unit " + uri + " doesn't exist."); + } + + IJavaProject javaProject = unit.getJavaProject(); + return javaProject == null || javaProject.isOnClasspath(unit); + } } From ac7d26bcfd5736f2cabd93f2bc065aa8ff302088 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 15 Oct 2019 11:06:43 +0800 Subject: [PATCH 019/206] Use -m prefix only when the main class is contained in a module (#304) * Use -m prefix only when the main class is contained in a module Signed-off-by: Jinbo Wang * Fix -m args Signed-off-by: Jinbo Wang --- .../main/java/com/microsoft/java/debug/core/DebugUtility.java | 2 +- .../java/debug/core/adapter/handler/LaunchRequestHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index 4c3275ab6..3b9174b64 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -148,7 +148,7 @@ public static IDebugSession launch(VirtualMachineManager vmManager, // For java 9 project, should specify "-m $MainClass". String[] mainClasses = mainClass.split("/"); - if (StringUtils.isNotBlank(modulePaths) || mainClasses.length == 2) { + if (mainClasses.length == 2) { mainClass = "-m " + mainClass; } if (StringUtils.isNotBlank(programArguments)) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 991d29197..16ad09cfa 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -207,7 +207,7 @@ public static String[] constructLaunchCommands(LaunchArguments launchArguments, } // For java 9 project, should specify "-m $MainClass". String[] mainClasses = launchArguments.mainClass.split("/"); - if (ArrayUtils.isNotEmpty(launchArguments.modulePaths) || mainClasses.length == 2) { + if (mainClasses.length == 2) { launchCmds.add("-m"); } launchCmds.add(launchArguments.mainClass); From 1af1ed3635878cb707f016fcf4ab61e5f2ea0ba5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 22 Oct 2019 15:49:32 +0800 Subject: [PATCH 020/206] Support searching main classes from the workspace invisible project (#305) * Support searching main classes from the workspace invisible project Signed-off-by: Jinbo Wang * Address review comment Signed-off-by: Jinbo Wang --- .../plugin/internal/ResolveMainClassHandler.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index 187257a9a..9d3b1014e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -12,6 +12,7 @@ package com.microsoft.java.debug.plugin.internal; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -23,6 +24,7 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -37,6 +39,7 @@ 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.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -107,7 +110,8 @@ public void acceptSearchMatch(SearchMatch match) { String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); if (projectName == null || targetProjectPath.isEmpty() - || ResourceUtils.isContainedIn(project.getLocation(), targetProjectPath)) { + || ResourceUtils.isContainedIn(project.getLocation(), targetProjectPath) + || isContainedInInvisibleProject(project, targetProjectPath)) { String filePath = null; if (match.getResource() instanceof IFile) { @@ -141,6 +145,15 @@ public void acceptSearchMatch(SearchMatch match) { return resolutions; } + private boolean isContainedInInvisibleProject(IProject project, Collection rootPaths) { + if (project == null) { + return false; + } + + IFolder workspaceLinkFolder = project.getFolder(ProjectUtils.WORKSPACE_LINK); + return workspaceLinkFolder.exists() && ResourceUtils.isContainedIn(workspaceLinkFolder.getLocation(), rootPaths); + } + private ValidationResponse validateLaunchConfigCore(List arguments) throws CoreException { ValidationResponse response = new ValidationResponse(); From 89808a189c53d33076a1f177ecc36cc4b72033e9 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 24 Oct 2019 10:29:34 +0800 Subject: [PATCH 021/206] bump version to 0.23.0 (#306) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 99d621703..451b767a7 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.22.0 + 0.23.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3e2b3081f..25e75ce7b 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index c79373d0a..044655efd 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.22.0 +Bundle-Version: 0.23.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.22.0.jar + lib/com.microsoft.java.debug.core-0.23.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 9f2a27709..33428752b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.22.0 + 0.23.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.22.0 + 0.23.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 3c3e69fcb..ccf34eb02 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index a183d8bbc..3b213b8d0 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.22.0 + 0.23.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index b00a48be3..2acafccaa 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.22.0 + 0.23.0 pom Java Debug Server for Visual Studio Code From fc45056fa03c853a02ccfe40ddacabd152a42e74 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 13 Nov 2019 16:21:41 +0800 Subject: [PATCH 022/206] Auto append brackets when auto complete function in DEBUG CONSOLE view (#308) Signed-off-by: Jinbo Wang --- .../plugin/internal/CompletionProposalRequestor.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index 6d57801fe..2b32ae3f6 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -21,6 +21,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.lang3.StringUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.CompletionContext; @@ -152,10 +153,20 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, String.valueOf(index)); $.setData(data); this.descriptionProvider.updateDescription(proposal, $); + adjustCompleteItem($); $.setSortText(SortTextHelper.computeSortText(proposal)); return $; } + private void adjustCompleteItem(CompletionItem item) { + if (item.getKind() == CompletionItemKind.Function) { + String text = item.getInsertText(); + if (StringUtils.isNotBlank(text) && !text.endsWith(")")) { + item.setInsertText(text + "()"); + } + } + } + @Override public void acceptContext(CompletionContext context) { super.acceptContext(context); From 27e3c455f8627ac895d49a5633409689e7fff6b4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 15 Nov 2019 10:25:00 +0800 Subject: [PATCH 023/206] Support Data Breakpoint (#307) * Support Data Breakpoint Signed-off-by: Jinbo Wang * Address review comments Signed-off-by: Jinbo Wang * Throw unsupported exception when set log message to data breakpoint Signed-off-by: Jinbo Wang --- .../java/debug/core/DebugSession.java | 5 + .../java/debug/core/IDebugSession.java | 2 + .../debug/core/IEvaluatableBreakpoint.java | 10 +- .../java/debug/core/IWatchpoint.java | 36 +++ .../microsoft/java/debug/core/Watchpoint.java | 252 ++++++++++++++++++ .../debug/core/adapter/BreakpointManager.java | 102 ++++--- .../java/debug/core/adapter/DebugAdapter.java | 4 + .../core/adapter/DebugAdapterContext.java | 6 + .../core/adapter/IBreakpointManager.java | 72 +++++ .../core/adapter/IDebugAdapterContext.java | 2 + .../DataBreakpointInfoRequestHandler.java | 72 +++++ .../handler/InitializeRequestHandler.java | 1 + .../handler/SetBreakpointsRequestHandler.java | 25 +- .../SetDataBreakpointsRequestHandler.java | 165 ++++++++++++ .../java/debug/core/protocol/Requests.java | 21 ++ .../java/debug/core/protocol/Responses.java | 51 ++++ .../java/debug/core/protocol/Types.java | 81 ++++++ 17 files changed, 861 insertions(+), 46 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IWatchpoint.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DataBreakpointInfoRequestHandler.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 289d36c76..a67afd221 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -81,6 +81,11 @@ public IBreakpoint createBreakpoint(String className, int lineNumber, int hitCou return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage); } + @Override + public IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount) { + return new Watchpoint(vm, this.getEventHub(), className, fieldName, accessType, condition, hitCount); + } + @Override public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught) { EventRequestManager manager = vm.eventRequestManager(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 46cdffc66..ec09aff29 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -30,6 +30,8 @@ public interface IDebugSession { // breakpoints IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage); + IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount); + void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught); // TODO: createFunctionBreakpoint diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java index 12150563f..6d465b7c2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java @@ -11,13 +11,21 @@ package com.microsoft.java.debug.core; -public interface IEvaluatableBreakpoint extends IBreakpoint { +public interface IEvaluatableBreakpoint { boolean containsEvaluatableExpression(); boolean containsConditionalExpression(); boolean containsLogpointExpression(); + String getCondition(); + + void setCondition(String condition); + + String getLogMessage(); + + void setLogMessage(String logMessage); + void setCompiledConditionalExpression(Object compiledExpression); Object getCompiledConditionalExpression(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IWatchpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IWatchpoint.java new file mode 100644 index 000000000..a3a29a851 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IWatchpoint.java @@ -0,0 +1,36 @@ +/******************************************************************************* +* Copyright (c) 2019 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.core; + +import java.util.concurrent.CompletableFuture; + +public interface IWatchpoint extends IDebugResource { + String className(); + + String fieldName(); + + String accessType(); + + CompletableFuture install(); + + void putProperty(Object key, Object value); + + Object getProperty(Object key); + + int getHitCount(); + + void setHitCount(int hitCount); + + String getCondition(); + + void setCondition(String condition); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java new file mode 100644 index 000000000..18b4314cf --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java @@ -0,0 +1,252 @@ +/******************************************************************************* +* Copyright (c) 2019 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.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; + +import com.sun.jdi.Field; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.VMDisconnectedException; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ClassPrepareEvent; +import com.sun.jdi.request.ClassPrepareRequest; +import com.sun.jdi.request.EventRequest; +import com.sun.jdi.request.WatchpointRequest; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; + +public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint { + private final VirtualMachine vm; + private final IEventHub eventHub; + private final String className; + private final String fieldName; + private String accessType = null; + private String condition = null; + private int hitCount; + private HashMap propertyMap = new HashMap<>(); + private Object compiledConditionalExpression = null; + + // IDebugResource + private List requests = new ArrayList<>(); + private List subscriptions = new ArrayList<>(); + + Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName) { + this(vm, eventHub, className, fieldName, "write"); + } + + Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType) { + this(vm, eventHub, className, fieldName, accessType, null, 0); + } + + Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, String condition, int hitCount) { + Objects.requireNonNull(vm); + Objects.requireNonNull(eventHub); + Objects.requireNonNull(className); + Objects.requireNonNull(fieldName); + this.vm = vm; + this.eventHub = eventHub; + this.className = className; + this.fieldName = fieldName; + this.accessType = accessType; + this.condition = condition; + this.hitCount = hitCount; + } + + @Override + public List requests() { + return requests; + } + + @Override + public List subscriptions() { + return subscriptions; + } + + @Override + public void close() throws Exception { + try { + vm.eventRequestManager().deleteEventRequests(requests()); + } catch (VMDisconnectedException ex) { + // ignore since removing breakpoints is meaningless when JVM is terminated. + } + subscriptions().forEach(subscription -> { + subscription.dispose(); + }); + requests.clear(); + subscriptions.clear(); + } + + @Override + public String className() { + return className; + } + + @Override + public String fieldName() { + return fieldName; + } + + @Override + public String accessType() { + return accessType; + } + + @Override + public String getCondition() { + return condition; + } + + @Override + public void setCondition(String condition) { + this.condition = condition; + setCompiledConditionalExpression(null); + } + + @Override + public int getHitCount() { + return hitCount; + } + + @Override + public void setHitCount(int hitCount) { + this.hitCount = hitCount; + + Observable.fromIterable(this.requests()) + .filter(request -> request instanceof WatchpointRequest) + .subscribe(request -> { + request.addCountFilter(hitCount); + request.enable(); + }); + } + + @Override + public void putProperty(Object key, Object value) { + propertyMap.put(key, value); + } + + @Override + public Object getProperty(Object key) { + return propertyMap.get(key); + } + + @Override + public CompletableFuture install() { + // It's possible that different class loaders create new class with the same name. + // Here to listen to future class prepare events to handle such case. + ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); + classPrepareRequest.addClassFilter(className); + classPrepareRequest.enable(); + requests.add(classPrepareRequest); + + CompletableFuture future = new CompletableFuture<>(); + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent && (classPrepareRequest.equals(debugEvent.event.request()))) + .subscribe(debugEvent -> { + ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; + List watchpointRequests = createWatchpointRequests(event.referenceType()); + requests.addAll(watchpointRequests); + if (!watchpointRequests.isEmpty() && !future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + }); + subscriptions.add(subscription); + + List watchpointRequests = new ArrayList<>(); + List types = vm.classesByName(className); + for (ReferenceType type : types) { + watchpointRequests.addAll(createWatchpointRequests(type)); + } + + requests.addAll(watchpointRequests); + if (!watchpointRequests.isEmpty() && !future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + + return future; + } + + private List createWatchpointRequests(ReferenceType type) { + List watchpointRequests = new ArrayList<>(); + Field field = type.fieldByName(fieldName); + if (field != null) { + if ("read".equals(accessType)) { + watchpointRequests.add(vm.eventRequestManager().createAccessWatchpointRequest(field)); + } else if ("readWrite".equals(accessType)) { + watchpointRequests.add(vm.eventRequestManager().createAccessWatchpointRequest(field)); + watchpointRequests.add(vm.eventRequestManager().createModificationWatchpointRequest(field)); + } else { + watchpointRequests.add(vm.eventRequestManager().createModificationWatchpointRequest(field)); + } + } + + watchpointRequests.forEach(request -> { + request.setSuspendPolicy(WatchpointRequest.SUSPEND_EVENT_THREAD); + if (hitCount > 0) { + request.addCountFilter(hitCount); + } + request.enable(); + }); + return watchpointRequests; + } + + @Override + public String getLogMessage() { + return null; + } + + @Override + public void setLogMessage(String logMessage) { + throw new UnsupportedOperationException("Log message feature is unsupported for watchpoint."); + } + + @Override + public boolean containsEvaluatableExpression() { + return containsConditionalExpression(); + } + + @Override + public boolean containsConditionalExpression() { + return StringUtils.isNotBlank(getCondition()); + } + + @Override + public boolean containsLogpointExpression() { + return false; + } + + public void setCompiledConditionalExpression(Object compiledExpression) { + this.compiledConditionalExpression = compiledExpression; + } + + public Object getCompiledConditionalExpression() { + return compiledConditionalExpression; + } + + @Override + public void setCompiledLogpointExpression(Object compiledExpression) { + // do nothing + } + + @Override + public Object getCompiledLogpointExpression() { + return null; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java index 59cacd1d0..78898df73 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2019 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 @@ -14,22 +14,27 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.IWatchpoint; -public class BreakpointManager { +public class BreakpointManager implements IBreakpointManager { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); /** * A collection of breakpoints registered with this manager. */ private List breakpoints; - private HashMap> sourceToBreakpoints; + private Map> sourceToBreakpoints; + private Map watchpoints; private AtomicInteger nextBreakpointId = new AtomicInteger(1); /** @@ -38,33 +43,15 @@ public class BreakpointManager { public BreakpointManager() { this.breakpoints = Collections.synchronizedList(new ArrayList<>(5)); this.sourceToBreakpoints = new HashMap<>(); + this.watchpoints = new HashMap<>(); } - /** - * Adds breakpoints to breakpoint manager. - * Deletes all breakpoints that are no longer listed. - * @param source - * source path of breakpoints - * @param breakpoints - * full list of breakpoints that locates in this source file - * @return the full breakpoint list that locates in the source file - */ + @Override public IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints) { return setBreakpoints(source, breakpoints, false); } - /** - * Adds breakpoints to breakpoint manager. - * Deletes all breakpoints that are no longer listed. - * In the case of modified source, delete everything. - * @param source - * source path of breakpoints - * @param breakpoints - * full list of breakpoints that locates in this source file - * @param sourceModified - * the source file are modified or not. - * @return the full breakpoint list that locates in the source file - */ + @Override public IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, boolean sourceModified) { List result = new ArrayList<>(); HashMap breakpointMap = this.sourceToBreakpoints.get(source); @@ -151,13 +138,12 @@ private void removeBreakpointsInternally(String source, IBreakpoint[] breakpoint } } + @Override public IBreakpoint[] getBreakpoints() { return this.breakpoints.toArray(new IBreakpoint[0]); } - /** - * Gets the registered breakpoints at the source file. - */ + @Override public IBreakpoint[] getBreakpoints(String source) { HashMap breakpointMap = this.sourceToBreakpoints.get(source); if (breakpointMap == null) { @@ -166,12 +152,60 @@ public IBreakpoint[] getBreakpoints(String source) { return breakpointMap.values().toArray(new IBreakpoint[0]); } - /** - * Cleanup all breakpoints and reset the breakpoint id counter. - */ - public void reset() { - this.sourceToBreakpoints.clear(); - this.breakpoints.clear(); - this.nextBreakpointId.set(1); + @Override + public IWatchpoint[] setWatchpoints(IWatchpoint[] changedWatchpoints) { + List result = new ArrayList<>(); + List toAdds = new ArrayList<>(); + List toRemoves = new ArrayList<>(); + + Set visitedKeys = new HashSet<>(); + for (IWatchpoint change : changedWatchpoints) { + if (change == null) { + result.add(change); + continue; + } + + String key = getWatchpointKey(change); + IWatchpoint cache = watchpoints.get(key); + if (cache != null && Objects.equals(cache.accessType(), change.accessType())) { + visitedKeys.add(key); + result.add(cache); + } else { + toAdds.add(change); + result.add(change); + } + } + + for (IWatchpoint cache : watchpoints.values()) { + if (!visitedKeys.contains(getWatchpointKey(cache))) { + toRemoves.add(cache); + } + } + + for (IWatchpoint toRemove : toRemoves) { + try { + // Destroy the watch point on the debugee VM. + toRemove.close(); + this.watchpoints.remove(getWatchpointKey(toRemove)); + } catch (Exception e) { + logger.log(Level.SEVERE, String.format("Remove the watch point exception: %s", e.toString()), e); + } + } + + for (IWatchpoint toAdd : toAdds) { + toAdd.putProperty("id", this.nextBreakpointId.getAndIncrement()); + this.watchpoints.put(getWatchpointKey(toAdd), toAdd); + } + + return result.toArray(new IWatchpoint[0]); + } + + private String getWatchpointKey(IWatchpoint watchpoint) { + return watchpoint.className() + "#" + watchpoint.fieldName(); + } + + @Override + public IWatchpoint[] getWatchpoints() { + return this.watchpoints.values().stream().filter(wp -> wp != null).toArray(IWatchpoint[]::new); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 78f7d5b63..798200e97 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -23,6 +23,7 @@ import com.microsoft.java.debug.core.adapter.handler.AttachRequestHandler; import com.microsoft.java.debug.core.adapter.handler.CompletionsHandler; import com.microsoft.java.debug.core.adapter.handler.ConfigurationDoneRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.DataBreakpointInfoRequestHandler; import com.microsoft.java.debug.core.adapter.handler.DisconnectRequestHandler; import com.microsoft.java.debug.core.adapter.handler.DisconnectRequestWithoutDebuggingHandler; import com.microsoft.java.debug.core.adapter.handler.EvaluateRequestHandler; @@ -33,6 +34,7 @@ import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetBreakpointsRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.SetDataBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetExceptionBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetVariableRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SourceRequestHandler; @@ -117,6 +119,8 @@ private void initialize() { registerHandlerForDebug(new RestartFrameHandler()); registerHandlerForDebug(new CompletionsHandler()); registerHandlerForDebug(new ExceptionInfoRequestHandler()); + registerHandlerForDebug(new DataBreakpointInfoRequestHandler()); + registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 646ce01c5..bd29f9946 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -53,6 +53,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private IStackFrameManager stackFrameManager = new StackFrameManager(); private IExceptionManager exceptionManager = new ExceptionManager(); + private IBreakpointManager breakpointManager = new BreakpointManager(); public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) { this.providerContext = providerContext; @@ -291,4 +292,9 @@ public Path getArgsfile() { public IExceptionManager getExceptionManager() { return this.exceptionManager; } + + @Override + public IBreakpointManager getBreakpointManager() { + return breakpointManager; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java new file mode 100644 index 000000000..9ba609221 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java @@ -0,0 +1,72 @@ +/******************************************************************************* +* Copyright (c) 2019 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.core.adapter; + +import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.IWatchpoint; + +public interface IBreakpointManager { + + /** + * Update the breakpoints associated with the source file. + * + * @see #setBreakpoints(String, IBreakpoint[], boolean) + * @param source + * source path of breakpoints + * @param breakpoints + * full list of breakpoints that locates in this source file + * @return the full breakpoint list that locates in the source file + */ + IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints); + + /** + * Update the breakpoints associated with the source file. If the requested breakpoints already registered in the breakpoint manager, + * reuse the cached one. Otherwise register the requested breakpoint as a new breakpoint. Besides, delete those not existed any more. + * + *

If the source file is modified, delete all cached breakpoints associated the file first and re-register the new breakpoints.

+ * + * @param source + * source path of breakpoints + * @param breakpoints + * full list of breakpoints that locates in this source file + * @param sourceModified + * the source file is modified or not. + * @return the full breakpoint list that locates in the source file + */ + IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, boolean sourceModified); + + /** + * Update the watchpoint list. If the requested watchpoint already registered in the breakpoint manager, + * reuse the cached one. Otherwise register the requested watchpoint as a new watchpoint. + * Besides, delete those not existed any more. + * + * @param watchpoints + * the watchpoints requested by client + * @return the full registered watchpoints list + */ + IWatchpoint[] setWatchpoints(IWatchpoint[] watchpoints); + + /** + * Returns all registered breakpoints. + */ + IBreakpoint[] getBreakpoints(); + + /** + * Returns the registered breakpoints at the source file. + */ + IBreakpoint[] getBreakpoints(String source); + + /** + * Returns all registered watchpoints. + */ + IWatchpoint[] getWatchpoints(); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 27837808b..8f7055eee 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -124,4 +124,6 @@ public interface IDebugAdapterContext { Path getArgsfile(); IExceptionManager getExceptionManager(); + + IBreakpointManager getBreakpointManager(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DataBreakpointInfoRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DataBreakpointInfoRequestHandler.java new file mode 100644 index 000000000..2d9b6c30d --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DataBreakpointInfoRequestHandler.java @@ -0,0 +1,72 @@ +/******************************************************************************* +* Copyright (c) 2019 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.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; + +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.adapter.variables.VariableProxy; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.DataBreakpointInfoArguments; +import com.microsoft.java.debug.core.protocol.Responses.DataBreakpointInfoResponseBody; +import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; +import com.sun.jdi.Field; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; + +public class DataBreakpointInfoRequestHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.DATABREAKPOINTINFO); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + DataBreakpointInfoArguments dataBpArgs = (DataBreakpointInfoArguments) arguments; + if (dataBpArgs.variablesReference > 0) { + Object container = context.getRecyclableIdPool().getObjectById(dataBpArgs.variablesReference); + if (container instanceof VariableProxy) { + if (!(((VariableProxy) container).getProxiedVariable() instanceof StackFrameReference)) { + ObjectReference containerObj = (ObjectReference) ((VariableProxy) container).getProxiedVariable(); + ReferenceType type = containerObj.referenceType(); + Field field = type.fieldByName(dataBpArgs.name); + if (field != null) { + String fullyQualifiedName = type.name(); + String dataId = String.format("%s#%s", fullyQualifiedName, dataBpArgs.name); + String description = String.format("%s.%s : %s", getSimpleName(fullyQualifiedName), dataBpArgs.name, getSimpleName(field.typeName())); + response.body = new DataBreakpointInfoResponseBody(dataId, description, + DataBreakpointAccessType.values(), true); + } + } + } + } + return CompletableFuture.completedFuture(response); + } + + private String getSimpleName(String typeName) { + if (StringUtils.isBlank(typeName)) { + return ""; + } + + String[] names = typeName.split("\\."); + return names[names.length - 1]; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index f618e9fc2..ff39bb1c1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -61,6 +61,7 @@ public CompletableFuture handle(Requests.Command command, Req }; caps.exceptionBreakpointFilters = exceptionFilters; caps.supportsExceptionInfoRequest = true; + caps.supportsDataBreakpoints = true; response.body = caps; return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index ca2a002c9..23ed69213 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -27,7 +27,6 @@ import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.IEvaluatableBreakpoint; import com.microsoft.java.debug.core.adapter.AdapterUtils; -import com.microsoft.java.debug.core.adapter.BreakpointManager; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent.EventType; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -56,8 +55,6 @@ public class SetBreakpointsRequestHandler implements IDebugRequestHandler { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - private BreakpointManager manager = new BreakpointManager(); - private boolean registered = false; @Override @@ -127,7 +124,8 @@ public CompletableFuture handle(Command command, Arguments arguments, IBreakpoint[] toAdds = this.convertClientBreakpointsToDebugger(sourcePath, bpArguments.breakpoints, context); // See the VSCode bug https://github.com/Microsoft/vscode/issues/36471. // The source uri sometimes is encoded by VSCode, the debugger will decode it to keep the uri consistent. - IBreakpoint[] added = manager.setBreakpoints(AdapterUtils.decodeURIComponent(sourcePath), toAdds, bpArguments.sourceModified); + IBreakpoint[] added = context.getBreakpointManager() + .setBreakpoints(AdapterUtils.decodeURIComponent(sourcePath), toAdds, bpArguments.sourceModified); for (int i = 0; i < bpArguments.breakpoints.length; i++) { // For newly added breakpoint, should install it to debuggee first. if (toAdds[i] == added[i] && added[i].className() != null) { @@ -161,8 +159,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } } - private IBreakpoint getAssociatedEvaluatableBreakpoint(BreakpointEvent event) { - return Arrays.asList(manager.getBreakpoints()).stream().filter( + private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { + return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream().filter( bp -> { return bp instanceof IEvaluatableBreakpoint && ((IEvaluatableBreakpoint) bp).containsEvaluatableExpression() @@ -187,17 +185,19 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { } // find the breakpoint related to this breakpoint event - IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint((BreakpointEvent) event); + IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint(context, (BreakpointEvent) event); if (expressionBP != null) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> { - boolean resume = handleEvaluationResult(context, bpThread, expressionBP, value, ex); + boolean resume = handleEvaluationResult(context, bpThread, (IEvaluatableBreakpoint) expressionBP, value, ex); // Clear the evaluation environment caused by above evaluation. engine.clearState(bpThread); if (resume) { debugEvent.eventSet.resume(); + } else { + context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); } }); }); @@ -210,7 +210,11 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { } } - private boolean handleEvaluationResult(IDebugAdapterContext context, ThreadReference bpThread, IBreakpoint breakpoint, Value value, Throwable ex) { + /** + * Check whether the condition expression is satisfied, and return a boolean value to determine to resume the thread or not. + */ + public static boolean handleEvaluationResult(IDebugAdapterContext context, ThreadReference bpThread, IEvaluatableBreakpoint breakpoint, + Value value, Throwable ex) { if (StringUtils.isNotBlank(breakpoint.getLogMessage())) { if (ex != null) { logger.log(Level.SEVERE, String.format("[Logpoint]: %s", ex.getMessage() != null ? ex.getMessage() : ex.toString()), ex); @@ -237,7 +241,6 @@ private boolean handleEvaluationResult(IDebugAdapterContext context, ThreadRefer if (resume) { return true; } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); if (ex != null) { logger.log(Level.SEVERE, String.format("[ConditionalBreakpoint]: %s", ex.getMessage() != null ? ex.getMessage() : ex.toString()), ex); context.getProtocolServer().sendEvent(new Events.UserNotificationEvent( @@ -288,7 +291,7 @@ private void reinstallBreakpoints(IDebugAdapterContext context, List typ if (typenames == null || typenames.isEmpty()) { return; } - IBreakpoint[] breakpoints = manager.getBreakpoints(); + IBreakpoint[] breakpoints = context.getBreakpointManager().getBreakpoints(); for (IBreakpoint breakpoint : breakpoints) { if (typenames.contains(breakpoint.className())) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java new file mode 100644 index 000000000..be15852e4 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java @@ -0,0 +1,165 @@ +/******************************************************************************* +* Copyright (c) 2019 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.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; + +import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.IEvaluatableBreakpoint; +import com.microsoft.java.debug.core.IWatchpoint; +import com.microsoft.java.debug.core.Watchpoint; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.protocol.Events; +import com.microsoft.java.debug.core.protocol.Events.BreakpointEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.SetDataBreakpointsArguments; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Types.Breakpoint; +import com.microsoft.java.debug.core.protocol.Types.DataBreakpoint; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.event.Event; +import com.sun.jdi.event.WatchpointEvent; + +public class SetDataBreakpointsRequestHandler implements IDebugRequestHandler { + private boolean registered = false; + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.SETDATABREAKPOINTS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + if (context.getDebugSession() == null) { + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Empty debug session."); + } + + if (!registered) { + registered = true; + registerWatchpointHandler(context); + } + + SetDataBreakpointsArguments dataBpArgs = (SetDataBreakpointsArguments) arguments; + IWatchpoint[] requestedWatchpoints = (dataBpArgs.breakpoints == null) ? new Watchpoint[0] : new Watchpoint[dataBpArgs.breakpoints.length]; + for (int i = 0; i < requestedWatchpoints.length; i++) { + DataBreakpoint dataBreakpoint = dataBpArgs.breakpoints[i]; + if (dataBreakpoint.dataId != null) { + String[] segments = dataBreakpoint.dataId.split("#"); + if (segments.length == 2 && StringUtils.isNotBlank(segments[0]) && StringUtils.isNotBlank(segments[1])) { + int hitCount = 0; + try { + hitCount = Integer.parseInt(dataBreakpoint.hitCondition); + } catch (NumberFormatException e) { + hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. + } + + String accessType = dataBreakpoint.accessType != null ? dataBreakpoint.accessType.label() : null; + requestedWatchpoints[i] = context.getDebugSession().createWatchPoint(segments[0], segments[1], accessType, + dataBreakpoint.condition, hitCount); + } + } + } + + IWatchpoint[] currentWatchpoints = context.getBreakpointManager().setWatchpoints(requestedWatchpoints); + List breakpoints = new ArrayList<>(); + for (int i = 0; i < currentWatchpoints.length; i++) { + if (currentWatchpoints[i] == null) { + breakpoints.add(new Breakpoint(false)); + continue; + } + + // If the requested watchpoint exists in the watchpoint manager, it will reuse the cached watchpoint object. + // Otherwise add the requested watchpoint to the cache. + // So if the returned watchpoint from the manager is same as the requested wantchpoint, this means it's a new watchpoint, need install it. + if (currentWatchpoints[i] == requestedWatchpoints[i]) { + currentWatchpoints[i].install().thenAccept(wp -> { + BreakpointEvent bpEvent = new BreakpointEvent("new", convertDebuggerWatchpointToClient(wp)); + context.getProtocolServer().sendEvent(bpEvent); + }); + } else { + if (currentWatchpoints[i].getHitCount() != requestedWatchpoints[i].getHitCount()) { + currentWatchpoints[i].setHitCount(requestedWatchpoints[i].getHitCount()); + } + + if (!Objects.equals(currentWatchpoints[i].getCondition(), requestedWatchpoints[i].getCondition())) { + currentWatchpoints[i].setCondition(requestedWatchpoints[i].getCondition()); + } + } + + breakpoints.add(convertDebuggerWatchpointToClient(currentWatchpoints[i])); + } + + response.body = new Responses.SetDataBreakpointsResponseBody(breakpoints); + return CompletableFuture.completedFuture(response); + } + + private Breakpoint convertDebuggerWatchpointToClient(IWatchpoint watchpoint) { + return new Breakpoint((int) watchpoint.getProperty("id"), + watchpoint.getProperty("verified") != null && (boolean) watchpoint.getProperty("verified")); + } + + private void registerWatchpointHandler(IDebugAdapterContext context) { + IDebugSession debugSession = context.getDebugSession(); + if (debugSession != null) { + debugSession.getEventHub().events().filter(debugEvent -> debugEvent.event instanceof WatchpointEvent).subscribe(debugEvent -> { + Event event = debugEvent.event; + ThreadReference bpThread = ((WatchpointEvent) event).thread(); + IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); + if (engine.isInEvaluation(bpThread)) { + return; + } + + // Find the watchpoint related to this watchpoint event + IWatchpoint watchpoint = Stream.of(context.getBreakpointManager().getWatchpoints()) + .filter(wp -> { + return wp instanceof IEvaluatableBreakpoint + && ((IEvaluatableBreakpoint) wp).containsEvaluatableExpression() + && wp.requests().contains(event.request()); + }) + .findFirst().orElse(null); + + if (watchpoint != null) { + CompletableFuture.runAsync(() -> { + engine.evaluateForBreakpoint((IEvaluatableBreakpoint) watchpoint, bpThread).whenComplete((value, ex) -> { + boolean resume = SetBreakpointsRequestHandler.handleEvaluationResult( + context, bpThread, (IEvaluatableBreakpoint) watchpoint, value, ex); + // Clear the evaluation environment caused by above evaluation. + engine.clearState(bpThread); + + if (resume) { + debugEvent.eventSet.resume(); + } else { + context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); + } + }); + }); + } else { + context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); + } + debugEvent.shouldResume = false; + }); + } + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 9aa1383e6..c0c6621a9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -15,6 +15,7 @@ import java.util.Map; import com.google.gson.annotations.SerializedName; +import com.microsoft.java.debug.core.protocol.Types.DataBreakpoint; /** * The request arguments types defined by VSCode Debug Protocol. @@ -288,6 +289,24 @@ public static class CompletionsArguments extends Arguments { public int column; } + public static class DataBreakpointInfoArguments extends Arguments { + /** + * Reference to the Variable container if the data breakpoint is requested for a child of the container. + */ + public int variablesReference; + /** + * The name of the Variable's child to obtain data breakpoint information for. If variableReference isn’t provided, this can be an expression. + */ + public String name; + } + + public static class SetDataBreakpointsArguments extends Arguments { + /** + * The contents of this array replaces all existing data breakpoints. An empty array clears all data breakpoints. + */ + public DataBreakpoint[] breakpoints; + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -314,6 +333,8 @@ public static enum Command { RUNINTERMINAL("runInTerminal", RunInTerminalRequestArguments.class), REDEFINECLASSES("redefineClasses", RedefineClassesArguments.class), EXCEPTIONINFO("exceptionInfo", ExceptionInfoArguments.class), + DATABREAKPOINTINFO("dataBreakpointInfo", DataBreakpointInfoArguments.class), + SETDATABREAKPOINTS("setDataBreakpoints", SetDataBreakpointsArguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index ed9b4767d..b1c8d5ae3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -13,6 +13,7 @@ import java.util.List; +import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; @@ -207,6 +208,56 @@ public SetBreakpointsResponseBody(List bpts) { } } + public static class SetDataBreakpointsResponseBody extends SetBreakpointsResponseBody { + public SetDataBreakpointsResponseBody(List bpts) { + super(bpts); + } + } + + public static class DataBreakpointInfoResponseBody extends ResponseBody { + /** + * An identifier for the data on which a data breakpoint can be registered with the setDataBreakpoints request + * or null if no data breakpoint is available. + */ + public String dataId; + /** + * UI string that describes on what data the breakpoint is set on or why a data breakpoint is not available. + */ + public String description; + /** + * Optional attribute listing the available access types for a potential data breakpoint. A UI frontend could surface this information. + */ + public DataBreakpointAccessType[] accessTypes; + /** + * Optional attribute indicating that a potential data breakpoint could be persisted across sessions. + */ + public boolean canPersist; + + public DataBreakpointInfoResponseBody(String dataId) { + this(dataId, null); + } + + public DataBreakpointInfoResponseBody(String dataId, String description) { + this(dataId, description, null); + } + + public DataBreakpointInfoResponseBody(String dataId, String description, + DataBreakpointAccessType[] accessTypes) { + this(dataId, description, accessTypes, false); + } + + /** + * Constructor. + */ + public DataBreakpointInfoResponseBody(String dataId, String description, DataBreakpointAccessType[] accessTypes, + boolean canPersist) { + this.dataId = dataId; + this.description = description; + this.accessTypes = accessTypes; + this.canPersist = canPersist; + } + } + public static class ContinueResponseBody extends ResponseBody { public boolean allThreadsContinued; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 65188273a..0b6fbfa96 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -25,6 +25,7 @@ public static class Message { /** * Constructs a message with the given information. + * * @param id * message id * @param format @@ -45,6 +46,7 @@ public static class StackFrame { /** * Constructs a StackFrame with the given information. + * * @param id * the stack frame id * @param name @@ -146,11 +148,32 @@ public Source(String path, int rf) { } public static class Breakpoint { + /** + * An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints. + */ public int id; + /** + * If true breakpoint could be set (but not necessarily at the desired location). + */ public boolean verified; + /** + * The start line of the actual range covered by the breakpoint. + */ public int line; + /** + * An optional message about the state of the breakpoint. This is shown to the user and can be used to explain why a breakpoint could not be verified. + */ public String message; + public Breakpoint(boolean verified) { + this.verified = verified; + } + + public Breakpoint(int id, boolean verified) { + this.id = id; + this.verified = verified; + } + /** * Constructor. */ @@ -194,6 +217,63 @@ public FunctionBreakpoint(String name) { } } + public static enum DataBreakpointAccessType { + @SerializedName("read") + READ("read"), + @SerializedName("write") + WRITE("write"), + @SerializedName("readWrite") + READWRITE("readWrite"); + + String label; + + DataBreakpointAccessType(String label) { + this.label = label; + } + + public String label() { + return label; + } + } + + public static class DataBreakpoint { + /** + * An id representing the data. This id is returned from the dataBreakpointInfo request. + */ + public String dataId; + /** + * The access type of the data. + */ + public DataBreakpointAccessType accessType; + /** + * An optional expression for conditional breakpoints. + */ + public String condition; + /** + * An optional expression that controls how many hits of the breakpoint are ignored. The backend is expected to interpret the expression as needed. + */ + public String hitCondition; + + public DataBreakpoint(String dataId) { + this.dataId = dataId; + } + + public DataBreakpoint(String dataId, DataBreakpointAccessType accessType) { + this.dataId = dataId; + this.accessType = accessType; + } + + /** + * Constructor. + */ + public DataBreakpoint(String dataId, DataBreakpointAccessType accessType, String condition, String hitCondition) { + this.dataId = dataId; + this.accessType = accessType; + this.condition = condition; + this.hitCondition = hitCondition; + } + } + public static class CompletionItem { public String label; public String text; @@ -265,5 +345,6 @@ public static class Capabilities { public boolean supportsLogPoints; public boolean supportsExceptionInfoRequest; public ExceptionBreakpointFilter[] exceptionBreakpointFilters = new ExceptionBreakpointFilter[0]; + public boolean supportsDataBreakpoints; } } From 14bb785ab2d62ccc31599a1a089ab7be928c45bd Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 18 Nov 2019 13:37:00 +0800 Subject: [PATCH 024/206] Bump version to 0.24.0 (#309) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 451b767a7..e19e994dc 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.23.0 + 0.24.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 25e75ce7b..904deb5c6 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 044655efd..82eab7995 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.23.0 +Bundle-Version: 0.24.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.23.0.jar + lib/com.microsoft.java.debug.core-0.24.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 33428752b..7f5852e85 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.23.0 + 0.24.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.23.0 + 0.24.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index ccf34eb02..62cbcdc1c 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 3b213b8d0..e6d0fbb43 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.23.0 + 0.24.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 2acafccaa..5aa039599 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.23.0 + 0.24.0 pom Java Debug Server for Visual Studio Code From 620842622af1bd1debd04429a9cc6386a32f8ad5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 22 Nov 2019 11:25:12 +0800 Subject: [PATCH 025/206] Fix NPE when searching the source for the exception stack trace (#310) Signed-off-by: Jinbo Wang --- .../java/debug/core/adapter/AdapterUtils.java | 10 ++++++---- .../handler/LaunchWithoutDebuggingDelegate.java | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 35d99092b..e1103a5a3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -68,10 +68,12 @@ public static boolean isWindows() { * @return the absolute file path */ public static String sourceLookup(String[] sourcePaths, String sourceName) { - for (String path : sourcePaths) { - Path fullpath = Paths.get(path, sourceName); - if (Files.isRegularFile(fullpath)) { - return fullpath.toString(); + if (sourcePaths != null) { + for (String path : sourcePaths) { + Path fullpath = Paths.get(path, sourceName); + if (Files.isRegularFile(fullpath)) { + return fullpath.toString(); + } } } return null; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index 804ee58ca..c993dc5f1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -137,6 +137,6 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume @Override public void preLaunch(LaunchArguments launchArguments, IDebugAdapterContext context) { - // TODO Auto-generated method stub + context.setSourcePaths(launchArguments.sourcePaths); } } From 9b3e4d1f60add05a9f3eaf6146eff55e30546d32 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 27 Nov 2019 12:55:54 +0800 Subject: [PATCH 026/206] Clean up the registerd logger handlers when stopping the plugin (#311) Signed-off-by: Jinbo Wang --- .../internal/JavaDebuggerServerPlugin.java | 1 + .../java/debug/plugin/internal/LogUtils.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java index 9f4e723ec..975338371 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java @@ -35,6 +35,7 @@ public void start(BundleContext context) throws Exception { @Override public void stop(BundleContext context) throws Exception { logger.info("Stopping " + PLUGIN_ID); + LogUtils.cleanupHandlers(); } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java index 16c35d859..259631d30 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java @@ -11,6 +11,7 @@ package com.microsoft.java.debug.plugin.internal; +import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; @@ -32,6 +33,21 @@ public static void initialize(Level level) { logger.setLevel(level); } + /** + * Remove the logger handlers registered to the global logger. + */ + public static void cleanupHandlers() { + Handler[] handlers = logger.getHandlers(); + for (Handler handler : handlers) { + logger.removeHandler(handler); + } + + Handler[] usageHandlers = usageDataLogger.getHandlers(); + for (Handler handler : usageHandlers) { + usageDataLogger.removeHandler(handler); + } + } + /** * Configure log level setting for java debugger. * From e81f9c115ba591278198858ec33d3b5c1f58c50b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 10 Jan 2020 00:17:31 -0800 Subject: [PATCH 027/206] Up tycho version to 1.5.1 (#314) Signed-off-by: Jinbo Wang --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5aa039599..e2dbdb0c7 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 1.2.0 + 1.5.1 ${basedir} From adbc59d3e45afbe61bde3e1201963ed2751ce535 Mon Sep 17 00:00:00 2001 From: bhoppeadoy <33680321+bhoppeadoy@users.noreply.github.com> Date: Fri, 10 Jan 2020 02:23:18 -0600 Subject: [PATCH 028/206] Added debugger user setting for setting numeric precision (#313) Co-authored-by: Jinbo Wang --- .../java/com/microsoft/java/debug/core/DebugSettings.java | 1 + .../java/debug/core/adapter/variables/VariableUtils.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index 1c69f88e2..5933517de 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -22,6 +22,7 @@ public final class DebugSettings { private static DebugSettings current = new DebugSettings(); public int maxStringLength = 0; + public int numericPrecision = 0; public boolean showStaticVariables = false; public boolean showQualifiedNames = false; public boolean showHex = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index 6362d0cee..1f95b1154 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -248,6 +248,10 @@ public static void applyFormatterOptions(Map defaultOptions, boo if (DebugSettings.getCurrent().maxStringLength > 0) { options.put(StringObjectFormatter.MAX_STRING_LENGTH_OPTION, DebugSettings.getCurrent().maxStringLength); } + + if (DebugSettings.getCurrent().numericPrecision > 0) { + options.put(NumericFormatter.NUMERIC_PRECISION_OPTION, DebugSettings.getCurrent().numericPrecision); + } } private VariableUtils() { From c2b51f1f3ee364a715b2b66529ea4bd5415c326b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 15 Jan 2020 23:16:39 -0800 Subject: [PATCH 029/206] Provide custom request to support continue/pause all/other threads (#315) * Provide custom request to support continue/pause all/other threads Signed-off-by: Jinbo Wang * Address review comment Signed-off-by: Jinbo Wang --- .../handler/ThreadsRequestHandler.java | 60 ++++++++++++++++++- .../java/debug/core/protocol/Requests.java | 8 +++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index f2612ac9c..5d4ee48f7 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -29,6 +29,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.ContinueArguments; import com.microsoft.java.debug.core.protocol.Requests.PauseArguments; +import com.microsoft.java.debug.core.protocol.Requests.ThreadOperationArguments; import com.microsoft.java.debug.core.protocol.Requests.ThreadsArguments; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; @@ -40,13 +41,16 @@ public class ThreadsRequestHandler implements IDebugRequestHandler { @Override public List getTargetCommands() { - return Arrays.asList(Command.THREADS, Command.PAUSE, Command.CONTINUE); + return Arrays.asList(Command.THREADS, Command.PAUSE, Command.CONTINUE, Command.CONTINUEALL, + Command.CONTINUEOTHERS, Command.PAUSEALL, Command.PAUSEOTHERS); } @Override - public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { if (context.getDebugSession() == null) { - return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Debug Session doesn't exist."); + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, + "Debug Session doesn't exist."); } switch (command) { @@ -56,6 +60,14 @@ public CompletableFuture handle(Command command, Arguments arguments, return this.pause((PauseArguments) arguments, response, context); case CONTINUE: return this.resume((ContinueArguments) arguments, response, context); + case CONTINUEALL: + return this.resumeAll((ThreadOperationArguments) arguments, response, context); + case CONTINUEOTHERS: + return this.resumeOthers((ThreadOperationArguments) arguments, response, context); + case PAUSEALL: + return this.pauseAll((ThreadOperationArguments) arguments, response, context); + case PAUSEOTHERS: + return this.pauseOthers((ThreadOperationArguments) arguments, response, context); default: return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.UNRECOGNIZED_REQUEST_FAILURE, String.format("Unrecognized request: { _request: %s }", command.toString())); @@ -114,6 +126,48 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, return CompletableFuture.completedFuture(response); } + private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { + context.getExceptionManager().removeAllExceptions(); + context.getDebugSession().resume(); + context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); + context.getRecyclableIdPool().removeAllObjects(); + return CompletableFuture.completedFuture(response); + } + + private CompletableFuture resumeOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { + List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + for (ThreadReference thread : threads) { + long threadId = thread.uniqueID(); + if (threadId != arguments.threadId && thread.isSuspended()) { + context.getExceptionManager().removeException(threadId); + DebugUtility.resumeThread(thread); + context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); + checkThreadRunningAndRecycleIds(thread, context); + } + } + + return CompletableFuture.completedFuture(response); + } + + private CompletableFuture pauseAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { + context.getDebugSession().suspend(); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); + return CompletableFuture.completedFuture(response); + } + + private CompletableFuture pauseOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { + List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + for (ThreadReference thread : threads) { + long threadId = thread.uniqueID(); + if (threadId != arguments.threadId && !thread.isCollected() && !thread.isSuspended()) { + thread.suspend(); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + } + } + + return CompletableFuture.completedFuture(response); + } + /** * Recycle the related ids owned by the specified thread. */ diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index c0c6621a9..7fc32452e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -244,6 +244,10 @@ public static class PauseArguments extends Arguments { public long threadId; } + public static class ThreadOperationArguments extends Arguments { + public long threadId; + } + public static class ScopesArguments extends Arguments { public int frameId; } @@ -335,6 +339,10 @@ public static enum Command { EXCEPTIONINFO("exceptionInfo", ExceptionInfoArguments.class), DATABREAKPOINTINFO("dataBreakpointInfo", DataBreakpointInfoArguments.class), SETDATABREAKPOINTS("setDataBreakpoints", SetDataBreakpointsArguments.class), + CONTINUEALL("continueAll", ThreadOperationArguments.class), + CONTINUEOTHERS("continueOthers", ThreadOperationArguments.class), + PAUSEALL("pauseAll", ThreadOperationArguments.class), + PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), UNSUPPORTED("", Arguments.class); private String command; From cf338eee25416d28212346896abe3bc2aefb2be2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 17 Feb 2020 11:28:18 +0800 Subject: [PATCH 030/206] Use project's java runtime to launch the application (#319) * Use project's java runtime to launch the application Signed-off-by: Jinbo Wang * make checkstyle happy Signed-off-by: Jinbo Wang * address review comments Signed-off-by: Jinbo Wang --- .../java/debug/core/DebugUtility.java | 94 +++++++++++++++- .../adapter/handler/LaunchRequestHandler.java | 13 ++- .../handler/LaunchWithDebuggingDelegate.java | 3 +- .../java/debug/core/protocol/Requests.java | 1 + com.microsoft.java.debug.plugin/plugin.xml | 1 + .../JavaDebugDelegateCommandHandler.java | 3 + .../ResolveJavaExecutableHandler.java | 102 ++++++++++++++++++ 7 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index 3b9174b64..bd182ae67 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -17,9 +17,11 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; @@ -57,7 +59,7 @@ public class DebugUtility { /** * Launch a debuggee in suspend mode. - * @see #launch(VirtualMachineManager, String, String, String, String, String) + * @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[]) */ public static IDebugSession launch(VirtualMachineManager vmManager, String mainClass, @@ -78,6 +80,31 @@ public static IDebugSession launch(VirtualMachineManager vmManager, envVars); } + /** + * Launch a debuggee in suspend mode. + * @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[], String) + */ + public static IDebugSession launch(VirtualMachineManager vmManager, + String mainClass, + String programArguments, + String vmArguments, + List modulePaths, + List classPaths, + String cwd, + String[] envVars, + String javaExec) + throws IOException, IllegalConnectorArgumentsException, VMStartException { + return DebugUtility.launch(vmManager, + mainClass, + programArguments, + vmArguments, + String.join(File.pathSeparator, modulePaths), + String.join(File.pathSeparator, classPaths), + cwd, + envVars, + javaExec); + } + /** * Launches a debuggee in suspend mode. * @@ -116,6 +143,50 @@ public static IDebugSession launch(VirtualMachineManager vmManager, String cwd, String[] envVars) throws IOException, IllegalConnectorArgumentsException, VMStartException { + return launch(vmManager, mainClass, programArguments, vmArguments, modulePaths, classPaths, cwd, envVars, null); + } + + /** + * Launches a debuggee in suspend mode. + * + * @param vmManager + * the virtual machine manager. + * @param mainClass + * the main class. + * @param programArguments + * the program arguments. + * @param vmArguments + * the vm arguments. + * @param modulePaths + * the module paths. + * @param classPaths + * the class paths. + * @param cwd + * the working directory of the program. + * @param envVars + * array of strings, each element of which has environment variable settings in the format name=value. + * or null if the subprocess should inherit the environment of the current process. + * @param javaExec + * the java executable path. If not defined, then resolve from java home. + * @return an instance of IDebugSession. + * @throws IOException + * when unable to launch. + * @throws IllegalConnectorArgumentsException + * when one of the arguments is invalid. + * @throws VMStartException + * when the debuggee was successfully launched, but terminated + * with an error before a connection could be established. + */ + public static IDebugSession launch(VirtualMachineManager vmManager, + String mainClass, + String programArguments, + String vmArguments, + String modulePaths, + String classPaths, + String cwd, + String[] envVars, + String javaExec) + throws IOException, IllegalConnectorArgumentsException, VMStartException { List connectors = vmManager.launchingConnectors(); LaunchingConnector connector = connectors.get(0); @@ -164,7 +235,12 @@ public static IDebugSession launch(VirtualMachineManager vmManager, arguments.get(ENV).setValue(encodeArrayArgument(envVars)); } - if (StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome)) { + if (isValidJavaExec(javaExec)) { + String vmExec = new File(javaExec).getName(); + String javaHome = new File(javaExec).getParentFile().getParentFile().getAbsolutePath(); + arguments.get(HOME).setValue(javaHome); + arguments.get(EXEC).setValue(vmExec); + } else if (StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome)) { arguments.get(HOME).setValue(DebugSettings.getCurrent().javaHome); } @@ -179,6 +255,20 @@ public static IDebugSession launch(VirtualMachineManager vmManager, return new DebugSession(vm); } + private static boolean isValidJavaExec(String javaExec) { + if (StringUtils.isBlank(javaExec)) { + return false; + } + + File file = new File(javaExec); + if (!file.exists() || !file.isFile()) { + return false; + } + + return Files.isExecutable(file.toPath()) + && Objects.equals(file.getParentFile().getName(), "bin"); + } + /** * Attach to an existing debuggee VM. * @param vmManager diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 16ad09cfa..26869a394 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -18,6 +18,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -183,14 +184,18 @@ protected void handleTerminatedEvent(IDebugAdapterContext context) { * @return the command arrays */ public static String[] constructLaunchCommands(LaunchArguments launchArguments, boolean serverMode, String address) { - String slash = System.getProperty("file.separator"); List launchCmds = new ArrayList<>(); if (launchArguments.launcherScript != null) { launchCmds.add(launchArguments.launcherScript); } - final String javaHome = StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome) ? DebugSettings.getCurrent().javaHome - : System.getProperty("java.home"); - launchCmds.add(javaHome + slash + "bin" + slash + "java"); + + if (StringUtils.isNotBlank(launchArguments.javaExec)) { + launchCmds.add(launchArguments.javaExec); + } else { + final String javaHome = StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome) ? DebugSettings.getCurrent().javaHome + : System.getProperty("java.home"); + launchCmds.add(Paths.get(javaHome, "bin", "java").toString()); + } if (StringUtils.isNotEmpty(address)) { launchCmds.add(String.format("-agentlib:jdwp=transport=dt_socket,server=%s,suspend=y,address=%s", serverMode ? "y" : "n", address)); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 0f690773a..c4585dd1f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -181,7 +181,8 @@ public Process launch(LaunchArguments launchArguments, IDebugAdapterContext cont Arrays.asList(launchArguments.modulePaths), Arrays.asList(launchArguments.classPaths), launchArguments.cwd, - LaunchRequestHandler.constructEnvironmentVariables(launchArguments)); + LaunchRequestHandler.constructEnvironmentVariables(launchArguments), + launchArguments.javaExec); context.setDebugSession(debugSession); logger.info("Launching debuggee VM succeeded."); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 7fc32452e..1874825e7 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -86,6 +86,7 @@ public static class LaunchArguments extends LaunchBaseArguments { public CONSOLE console = CONSOLE.integratedTerminal; public ShortenApproach shortenCommandLine = ShortenApproach.NONE; public String launcherScript; + public String javaExec; } public static class AttachArguments extends LaunchBaseArguments { diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index d71e1c7fd..5f7634b14 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -16,6 +16,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 7a1b55dfc..8284abc23 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -43,6 +43,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; public static final String RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; public static final String IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; + public static final String RESOLVE_JAVA_EXECUTABLE = "vscode.java.resolveJavaExecutable"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -78,6 +79,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return getBuildFiles(); case IS_ON_CLASSPATH: return isOnClasspath(arguments); + case RESOLVE_JAVA_EXECUTABLE: + return ResolveJavaExecutableHandler.resolveJavaExecutable(arguments); default: break; } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java new file mode 100644 index 000000000..cd368a6d2 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2020 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 java.io.File; +import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.JavaRuntime; + +import com.microsoft.java.debug.core.Configuration; + +public class ResolveJavaExecutableHandler { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private static final String[] javaExecCandidates = { + "java", + "java.exe", + "javaw", + "javaw.exe", + "j9", + "j9.exe", + "j9w", + "j9w.exe" + }; + private static final String[] javaBinCandidates = { + File.separator, + "bin" + File.separatorChar, + "jre" + File.separatorChar + "bin" + File.separatorChar + }; + + /** + * Resolve the java executable path from the project's java runtime. + */ + public static String resolveJavaExecutable(List arguments) throws Exception { + try { + String mainClass = (String) arguments.get(0); + String projectName = (String) arguments.get(1); + IJavaProject targetProject = null; + if (StringUtils.isNotBlank(projectName)) { + targetProject = JdtUtils.getJavaProject(projectName); + } else { + List targetProjects = ResolveClasspathsHandler.getJavaProjectFromType(mainClass); + if (!targetProjects.isEmpty()) { + targetProject = targetProjects.get(0); + } + } + + if (targetProject == null) { + return null; + } + + IVMInstall vmInstall = JavaRuntime.getVMInstall(targetProject); + if (vmInstall == null || vmInstall.getInstallLocation() == null) { + return null; + } + + File exe = findJavaExecutable(vmInstall.getInstallLocation()); + if (exe == null) { + return null; + } + + return exe.getAbsolutePath(); + } catch (CoreException e) { + logger.log(Level.SEVERE, "Failed to resolve java executable: " + e.getMessage(), e); + } + + return null; + } + + private static File findJavaExecutable(File vmInstallLocation) { + boolean isBin = Objects.equals("bin", vmInstallLocation.getName()); + for (int i = 0; i < javaExecCandidates.length; i++) { + for (int j = 0; j < javaBinCandidates.length; j++) { + if (!isBin && j == 0) { + continue; + } + File javaFile = new File(vmInstallLocation, Paths.get(javaBinCandidates[j], javaExecCandidates[i]).toString()); + if (javaFile.isFile()) { + return javaFile; + } + } + } + + return null; + } +} From d73cd852b44f68b26962658f57d10b040912f18c Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 17 Feb 2020 17:35:26 +0800 Subject: [PATCH 031/206] up version to 0.25.0 (#320) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index e19e994dc..ae4d98384 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.24.0 + 0.25.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 904deb5c6..c19e5f920 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 82eab7995..7ab2cd374 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.24.0 +Bundle-Version: 0.25.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.24.0.jar + lib/com.microsoft.java.debug.core-0.25.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 7f5852e85..57d9d2f35 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.24.0 + 0.25.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.24.0 + 0.25.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 62cbcdc1c..9d0253fff 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index e6d0fbb43..a2f8dc079 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.24.0 + 0.25.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index e2dbdb0c7..cc8f2ab1f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.24.0 + 0.25.0 pom Java Debug Server for Visual Studio Code From 58d0277da0850efaeaa0ef215d780ed074b22caa Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 18 Feb 2020 16:06:35 +0800 Subject: [PATCH 032/206] update checkstyle to 8.29 (#321) Signed-off-by: Jinbo Wang --- check_style.xml | 27 +++++++++++++------ .../java/debug/core/DebugUtility.java | 2 -- .../java/debug/core/adapter/AdapterUtils.java | 1 - .../java/debug/plugin/internal/JdtUtils.java | 1 - .../java/debug/plugin/internal/LogUtils.java | 1 - pom.xml | 4 +-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/check_style.xml b/check_style.xml index 360e82d11..3f19228a5 100644 --- a/check_style.xml +++ b/check_style.xml @@ -40,6 +40,23 @@ + + + + + + + + + + + @@ -92,10 +109,6 @@ - - - - @@ -226,6 +239,7 @@ + @@ -249,7 +263,7 @@ - + @@ -257,11 +271,8 @@ - - - diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index bd182ae67..edfb6d4b3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -545,7 +545,6 @@ public static List parseArguments(String cmdStr) { return AdapterUtils.isWindows() ? parseArgumentsWindows(cmdStr) : parseArgumentsNonWindows(cmdStr); } - /** * Parses the given command line into separate arguments for mac/linux platform. * This piece of code is mainly copied from @@ -627,7 +626,6 @@ private static List parseArgumentsNonWindows(String args) { return result; } - /** * Parses the given command line into separate arguments for windows platform. * This piece of code is mainly copied from diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index e1103a5a3..7b6636f7e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -263,7 +263,6 @@ public static DebugException createUserErrorDebugException(String message, Error return new DebugException(message, errorCode.getId(), true); } - /** * Calculate SHA-256 Digest of given string. * @param content 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 c1ae03826..9094a8073 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 @@ -180,7 +180,6 @@ public static boolean isTest(final IClasspathEntry classpathEntry) { return classpathEntry.isTest(); } - /** * Compute the possible source containers that the specified project could be associated with. *

diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java index 259631d30..e4befd735 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/LogUtils.java @@ -21,7 +21,6 @@ public final class LogUtils { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final Logger usageDataLogger = Logger.getLogger(Configuration.USAGE_DATA_LOGGER_NAME); - /** * Initialize logger for logger level and logger handler. * @param level the logger level for java debugger. diff --git a/pom.xml b/pom.xml index cc8f2ab1f..58bc70ea7 100644 --- a/pom.xml +++ b/pom.xml @@ -113,12 +113,12 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.17 + 3.1.0 com.puppycrawl.tools checkstyle - 8.18 + 8.29 com.github.sevntu-checkstyle From fc691fdf8fcad4e4533821caa9b78721bc0a73c7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 19 Feb 2020 15:25:26 +0800 Subject: [PATCH 033/206] Return disconnect response in 500ms (#322) Signed-off-by: Jinbo Wang --- .../core/adapter/handler/AbstractDisconnectRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java index 888009890..c94c1e9d0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java @@ -91,7 +91,7 @@ private void destroyLaunchFiles(IDebugAdapterContext context) { } try { - TimeUnit.SECONDS.sleep(1); + TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { // do nothing. } From 32f3a1fce4e7162d3c38bb35251bae644612a066 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 6 Mar 2020 10:11:20 +0800 Subject: [PATCH 034/206] Update ASTParser to support Java 13 syntax (#323) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- .../plugin/internal/JdtSourceLookUpProvider.java | 10 +++++----- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- java.debug.target | 8 ++++---- pom.xml | 12 ++++++------ 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index ae4d98384..b25d216a3 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.25.0 + 0.25.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c19e5f920..34f0d2046 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 7ab2cd374..83e487e96 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.25.0 +Bundle-Version: 0.25.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.25.0.jar + lib/com.microsoft.java.debug.core-0.25.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 57d9d2f35..54f01eacb 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.25.0 + 0.25.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.25.0 + 0.25.1 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 cc3086eca..99b4b330c 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 @@ -92,8 +92,8 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return new String[0]; } - // Currently the highest version the debugger supports is Java SE 9 Edition (JLS9). - final ASTParser parser = ASTParser.newParser(AST.JLS9); + // Currently the highest version the debugger supports is JavaSE-13 Edition (JLS13). + final ASTParser parser = ASTParser.newParser(AST.JLS13); parser.setResolveBindings(true); parser.setBindingsRecovery(true); parser.setStatementsRecovery(true); @@ -122,9 +122,9 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th * the user need specify the compiler options explicitly. */ Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_9); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_9); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_9); + javaOptions.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_13); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_13); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_13); parser.setCompilerOptions(javaOptions); astUnit = (CompilationUnit) parser.createAST(null); } else { diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 9d0253fff..9344404fb 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index a2f8dc079..553b72c70 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.25.0 + 0.25.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/java.debug.target b/java.debug.target index 530f178af..835c63801 100644 --- a/java.debug.target +++ b/java.debug.target @@ -7,20 +7,20 @@ - + - + - + - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 58bc70ea7..132e28d5e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.25.0 + 0.25.1 pom Java Debug Server for Visual Studio Code @@ -154,9 +154,9 @@ - 201906 + 201912 p2 - http://download.eclipse.org/releases/2019-06/ + https://download.eclipse.org/releases/2019-12/ oss.sonatype.org @@ -168,17 +168,17 @@ JDT.LS p2 - http://download.eclipse.org/jdtls/snapshots/repository/latest/ + https://download.eclipse.org/jdtls/snapshots/repository/latest/ JBOLL.TOOLS p2 - http://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.0-2018-05-16_00-46-30-H11 + https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.0-2018-05-16_00-46-30-H11 orbit p2 - http://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ + https://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ From 020721988021fcc3c01dd5d9006ff272ee010cec Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 10 Apr 2020 15:43:06 +0800 Subject: [PATCH 035/206] add a helper command to get the jdt platform settings (#326) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.plugin/plugin.xml | 1 + .../JavaDebugDelegateCommandHandler.java | 3 ++ .../plugin/internal/PlatformSettings.java | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/PlatformSettings.java diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index 5f7634b14..111e4118f 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -17,6 +17,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 8284abc23..025949a9b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -44,6 +44,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; public static final String IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; public static final String RESOLVE_JAVA_EXECUTABLE = "vscode.java.resolveJavaExecutable"; + public static final String FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -81,6 +82,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return isOnClasspath(arguments); case RESOLVE_JAVA_EXECUTABLE: return ResolveJavaExecutableHandler.resolveJavaExecutable(arguments); + case FETCH_PLATFORM_SETTINGS: + return PlatformSettings.getPlatformSettings(); default: break; } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/PlatformSettings.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/PlatformSettings.java new file mode 100644 index 000000000..9c2764d6a --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/PlatformSettings.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2020 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 java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.core.JavaCore; + +public class PlatformSettings { + + /** + * Resolve the JDT platform settings. + */ + public static Map getPlatformSettings() { + Map result = new HashMap<>(); + result.put("latestSupportedJavaVersion", JavaCore.latestSupportedJavaVersion()); + return result; + } +} From 74f56632831e805022078404ba618c1a01cd4aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Wed, 6 May 2020 10:31:20 +0200 Subject: [PATCH 036/206] Extend readme with basic low level usage instructions (#327) May be helpful for people who try to use java-debug with other clients than Visual Studio Code. --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index dd57cfcb3..4bf15f675 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,36 @@ mvnw.cmd clean install ./mvnw clean install ``` + +## Usage with eclipse.jdt.ls + +To use `java-debug` as a [jdt.ls](https://github.com/eclipse/eclipse.jdt.ls) plugin, an [LSP client](https://langserver.org/) has to launch [jdt.ls](https://github.com/eclipse/eclipse.jdt.ls) with `initializationOptions` that contain the path to the built `java-debug` jar within a `bundles` array: + + +``` +{ + "initializationOptions": { + "bundles": [ + "path/to/microsoft/java-debug/com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin-.jar" + ] + } +} +``` + +Editor extensions like [vscode-java](https://github.com/redhat-developer/vscode-java) take care of this. + + +Once `eclipse.jdt.ls` launched, the client can send a [Command](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#command) to the server to start a debug session: + +``` +{ + "command": "vscode.java.startDebugSession" +} +``` + +The response to this request will contain a port number on which the debug adapter is listening, and to which a client implementing the debug-adapter protocol can connect to. + + License ------- EPL 1.0, See [LICENSE](LICENSE.txt) file. From dacf4403ecd114badee018b07c01b03d0e5f1ab5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 7 May 2020 16:09:45 +0800 Subject: [PATCH 037/206] Fix race competition when the breakpoint needs be evaluated in multi threads (#325) * Fix race competition when the breakpoint needs be evaluated in multi threads Signed-off-by: Jinbo Wang * Use thread-safe map Signed-off-by: Jinbo Wang --- .../debug/core/EvaluatableBreakpoint.java | 48 +++++++++++++++++-- .../debug/core/IEvaluatableBreakpoint.java | 33 +++++++++++++ .../microsoft/java/debug/core/Watchpoint.java | 26 +++++++++- .../AbstractDisconnectRequestHandler.java | 1 + .../handler/SetBreakpointsRequestHandler.java | 15 ++++-- .../internal/eval/JdtEvaluationProvider.java | 20 +++----- 6 files changed, 118 insertions(+), 25 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java index ee1f36e4a..1a0647411 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java @@ -13,26 +13,39 @@ import org.apache.commons.lang3.StringUtils; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import com.sun.jdi.event.ThreadDeathEvent; +import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; +import io.reactivex.disposables.Disposable; + public class EvaluatableBreakpoint extends Breakpoint implements IEvaluatableBreakpoint { + private IEventHub eventHub = null; private Object compiledConditionalExpression = null; private Object compiledLogpointExpression = null; + private Map compiledExpressions = new ConcurrentHashMap<>(); EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { - super(vm, eventHub, className, lineNumber, 0, null); + this(vm, eventHub, className, lineNumber, 0, null); } EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount) { - super(vm, eventHub, className, lineNumber, hitCount, null); + this(vm, eventHub, className, lineNumber, hitCount, null); } - EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) { - super(vm, eventHub, className, lineNumber, hitCount, condition); + EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, + String condition) { + this(vm, eventHub, className, lineNumber, hitCount, condition, null); } - EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) { + EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, + String condition, String logMessage) { super(vm, eventHub, className, lineNumber, hitCount, condition, logMessage); + this.eventHub = eventHub; } @Override @@ -74,11 +87,36 @@ public Object getCompiledLogpointExpression() { public void setCondition(String condition) { super.setCondition(condition); setCompiledConditionalExpression(null); + compiledExpressions.clear(); } @Override public void setLogMessage(String logMessage) { super.setLogMessage(logMessage); setCompiledLogpointExpression(null); + compiledExpressions.clear(); + } + + @Override + public Object getCompiledExpression(long threadId) { + return compiledExpressions.get(threadId); + } + + @Override + public void setCompiledExpression(long threadId, Object compiledExpression) { + compiledExpressions.put(threadId, compiledExpression); + } + + @Override + public CompletableFuture install() { + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ThreadDeathEvent) + .subscribe(debugEvent -> { + ThreadReference deathThread = ((ThreadDeathEvent) debugEvent.event).thread(); + compiledExpressions.remove(deathThread.uniqueID()); + }); + super.subscriptions().add(subscription); + + return super.install(); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java index 6d465b7c2..16d8904c9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java @@ -26,11 +26,44 @@ public interface IEvaluatableBreakpoint { void setLogMessage(String logMessage); + /** + * please use {@link #setCompiledExpression(long, Object)} instead. + */ + @Deprecated void setCompiledConditionalExpression(Object compiledExpression); + /** + * please use {@link #getCompiledExpression(long)} instead. + */ + @Deprecated Object getCompiledConditionalExpression(); + /** + * please use {@link #setCompiledExpression(long, Object)} instead. + */ + @Deprecated void setCompiledLogpointExpression(Object compiledExpression); + /** + * please use {@link #getCompiledExpression(long)} instead. + */ + @Deprecated Object getCompiledLogpointExpression(); + + /** + * Sets the compiled expression for a thread. + * + * @param threadId - thread the breakpoint is hit in + * @param compiledExpression - associated compiled expression + */ + void setCompiledExpression(long threadId, Object compiledExpression); + + /** + * Returns existing compiled expression for the given thread or + * null. + * + * @param threadId thread the breakpoint was hit in + * @return compiled expression or null + */ + Object getCompiledExpression(long threadId); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java index 18b4314cf..3de321ec8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java @@ -14,16 +14,20 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; import com.sun.jdi.Field; import com.sun.jdi.ReferenceType; +import com.sun.jdi.ThreadReference; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; import com.sun.jdi.event.ClassPrepareEvent; +import com.sun.jdi.event.ThreadDeathEvent; import com.sun.jdi.request.ClassPrepareRequest; import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.WatchpointRequest; @@ -41,6 +45,7 @@ public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint { private int hitCount; private HashMap propertyMap = new HashMap<>(); private Object compiledConditionalExpression = null; + private Map compiledExpressions = new ConcurrentHashMap<>(); // IDebugResource private List requests = new ArrayList<>(); @@ -116,6 +121,7 @@ public String getCondition() { public void setCondition(String condition) { this.condition = condition; setCompiledConditionalExpression(null); + compiledExpressions.clear(); } @Override @@ -147,6 +153,14 @@ public Object getProperty(Object key) { @Override public CompletableFuture install() { + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ThreadDeathEvent) + .subscribe(debugEvent -> { + ThreadReference deathThread = ((ThreadDeathEvent) debugEvent.event).thread(); + compiledExpressions.remove(deathThread.uniqueID()); + }); + subscriptions.add(subscription); + // It's possible that different class loaders create new class with the same name. // Here to listen to future class prepare events to handle such case. ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); @@ -155,7 +169,7 @@ public CompletableFuture install() { requests.add(classPrepareRequest); CompletableFuture future = new CompletableFuture<>(); - Disposable subscription = eventHub.events() + subscription = eventHub.events() .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent && (classPrepareRequest.equals(debugEvent.event.request()))) .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; @@ -249,4 +263,14 @@ public void setCompiledLogpointExpression(Object compiledExpression) { public Object getCompiledLogpointExpression() { return null; } + + @Override + public Object getCompiledExpression(long threadId) { + return compiledExpressions.get(threadId); + } + + @Override + public void setCompiledExpression(long threadId, Object compiledExpression) { + compiledExpressions.put(threadId, compiledExpression); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java index c94c1e9d0..fd5c68d6c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java @@ -40,6 +40,7 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + context.setVmTerminated(); destroyDebugSession(command, arguments, response, context); destroyResource(context); return CompletableFuture.completedFuture(response); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 23ed69213..fe246a61e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -47,6 +47,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; +import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.StepEvent; @@ -241,11 +242,15 @@ public static boolean handleEvaluationResult(IDebugAdapterContext context, Threa if (resume) { return true; } else { - if (ex != null) { - logger.log(Level.SEVERE, String.format("[ConditionalBreakpoint]: %s", ex.getMessage() != null ? ex.getMessage() : ex.toString()), ex); - context.getProtocolServer().sendEvent(new Events.UserNotificationEvent( - Events.UserNotificationEvent.NotificationType.ERROR, - String.format("Breakpoint condition '%s' error: %s", breakpoint.getCondition(), ex.getMessage()))); + if (context.isVmTerminated()) { + // do nothing + } else if (ex != null) { + if (!(ex instanceof VMDisconnectedException || ex.getCause() instanceof VMDisconnectedException)) { + logger.log(Level.SEVERE, String.format("[ConditionalBreakpoint]: %s", ex.getMessage() != null ? ex.getMessage() : ex.toString()), ex); + context.getProtocolServer().sendEvent(new Events.UserNotificationEvent( + Events.UserNotificationEvent.NotificationType.ERROR, + String.format("Breakpoint condition '%s' error: %s", breakpoint.getCondition(), ex.getMessage()))); + } } else if (value == null || resultNotBoolean) { context.getProtocolServer().sendEvent(new Events.UserNotificationEvent( Events.UserNotificationEvent.NotificationType.WARNING, diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index ceb93c5cf..21e2dff6e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -148,20 +148,12 @@ private CompletableFuture evaluate(String expression, ThreadReference thr ASTEvaluationEngine engine = new ASTEvaluationEngine(project, debugTarget); boolean newExpression = false; if (breakpoint != null) { - if (StringUtils.isNotBlank(breakpoint.getLogMessage())) { - compiledExpression = (ICompiledExpression) breakpoint.getCompiledLogpointExpression(); - if (compiledExpression == null) { - newExpression = true; - compiledExpression = engine.getCompiledExpression(expression, stackframe); - breakpoint.setCompiledLogpointExpression(compiledExpression); - } - } else { - compiledExpression = (ICompiledExpression) breakpoint.getCompiledConditionalExpression(); - if (compiledExpression == null) { - newExpression = true; - compiledExpression = engine.getCompiledExpression(expression, stackframe); - breakpoint.setCompiledConditionalExpression(compiledExpression); - } + long threadId = thread.uniqueID(); + compiledExpression = (ICompiledExpression) breakpoint.getCompiledExpression(threadId); + if (compiledExpression == null) { + newExpression = true; + compiledExpression = engine.getCompiledExpression(expression, stackframe); + breakpoint.setCompiledExpression(threadId, compiledExpression); } } else { compiledExpression = engine.getCompiledExpression(expression, stackframe); From b7f00aff376d0aed5b1cfb0520d5a46190c210aa Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 7 May 2020 22:10:07 +0800 Subject: [PATCH 038/206] bump version to 0.26.0 (#332) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index b25d216a3..a0a8a7c58 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.25.1 + 0.26.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 34f0d2046..82715a2bd 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 83e487e96..6919caf13 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.25.1 +Bundle-Version: 0.26.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.25.1.jar + lib/com.microsoft.java.debug.core-0.26.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 54f01eacb..a72400647 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.25.1 + 0.26.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.25.1 + 0.26.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 9344404fb..642a82600 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 553b72c70..539da5886 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.25.1 + 0.26.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 132e28d5e..3ba974fef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.25.1 + 0.26.0 pom Java Debug Server for Visual Studio Code From 8de281455774bf79f362cb06e0f6826264831cab Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 11 May 2020 16:50:43 +0800 Subject: [PATCH 039/206] Fix gradle test scope (#333) * Fix gradle test scope Signed-off-by: Jinbo Wang * make checkstyle happy Signed-off-by: Jinbo Wang --- .../plugin/internal/ResolveClasspathsHandler.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java index 12b1ddde5..be37285e3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java @@ -182,7 +182,7 @@ private static String[][] computeClassPath(String mainClass, String projectName) IJavaElement testElement = findMainClassInTestFolders(project, mainClass); List mappedResources = (testElement != null && testElement.getResource() != null) ? Arrays.asList(testElement.getResource()) : Collections.EMPTY_LIST; - return computeClassPath(project, testElement == null, mappedResources); + return computeClassPath(project, mainClass, testElement == null, mappedResources); } /** @@ -195,13 +195,13 @@ private static String[][] computeClassPath(String mainClass, String projectName) * @throws CoreException * CoreException */ - private static String[][] computeClassPath(IJavaProject javaProject, boolean excludeTestCode, List mappedResources) + private static String[][] computeClassPath(IJavaProject javaProject, String mainType, boolean excludeTestCode, List mappedResources) throws CoreException { if (javaProject == null) { throw new IllegalArgumentException("javaProject is null"); } - ILaunchConfiguration launchConfig = new JavaApplicationLaunchConfiguration(javaProject.getProject(), excludeTestCode, mappedResources); + ILaunchConfiguration launchConfig = new JavaApplicationLaunchConfiguration(javaProject.getProject(), mainType, excludeTestCode, mappedResources); IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(launchConfig); IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspath(unresolved, launchConfig); Set classpaths = new LinkedHashSet<>(); @@ -282,15 +282,18 @@ private static class JavaApplicationLaunchConfiguration extends LaunchConfigurat + "\n" + ""; private IProject project; + private String mainType; private boolean excludeTestCode; private List mappedResources; private String classpathProvider; private String sourcepathProvider; private LaunchConfigurationInfo launchInfo; - protected JavaApplicationLaunchConfiguration(IProject project, boolean excludeTestCode, List mappedResources) throws CoreException { + protected JavaApplicationLaunchConfiguration(IProject project, String mainType, boolean excludeTestCode, List mappedResources) + throws CoreException { super(String.valueOf(new Date().getTime()), null, false); this.project = project; + this.mainType = mainType; this.excludeTestCode = excludeTestCode; this.mappedResources = mappedResources; if (ProjectUtils.isMavenProject(project)) { @@ -319,6 +322,8 @@ public String getAttribute(String attributeName, String defaultValue) throws Cor return classpathProvider; } else if (IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER.equalsIgnoreCase(attributeName)) { return sourcepathProvider; + } else if (IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME.equalsIgnoreCase(attributeName)) { + return mainType; } return super.getAttribute(attributeName, defaultValue); From 2e30239ad664b43273d4b1dea3808bab6f5acc3f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Jul 2020 09:16:37 +0800 Subject: [PATCH 040/206] Improve the HCR workflow (#335) * Improve the HCR workflow Signed-off-by: Jinbo Wang * refine the error message Signed-off-by: Jinbo Wang Co-authored-by: Jinbo Wang --- .../java/debug/core/adapter/ErrorCode.java | 3 +- .../handler/HotCodeReplaceHandler.java | 6 ++- .../java/debug/core/protocol/Responses.java | 9 +++++ .../internal/JavaHotCodeReplaceProvider.java | 37 ++++++++++++++----- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java index f39796909..1cdc4f9ce 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java @@ -34,7 +34,8 @@ public enum ErrorCode { COMPLETIONS_FAILURE(1017), EXCEPTION_INFO_FAILURE(1018), EVALUATION_COMPILE_ERROR(2001), - EVALUATE_NOT_SUSPENDED_THREAD(2002); + EVALUATE_NOT_SUSPENDED_THREAD(2002), + HCR_FAILURE(3001); private int id; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/HotCodeReplaceHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/HotCodeReplaceHandler.java index f32d4503e..fa65e978b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/HotCodeReplaceHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/HotCodeReplaceHandler.java @@ -59,8 +59,12 @@ public CompletableFuture handle(Command command, Arguments arguments, IHotCodeReplaceProvider provider = context.getProvider(IHotCodeReplaceProvider.class); - return provider.redefineClasses().thenApply(classNames -> { + return provider.redefineClasses().thenCompose(classNames -> { response.body = new Responses.RedefineClassesResponse(classNames.toArray(new String[0])); + return CompletableFuture.completedFuture(response); + }).exceptionally(ex -> { + String errorMessage = ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage(); + response.body = new Responses.RedefineClassesResponse(new String[0], errorMessage); return response; }); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index b1c8d5ae3..1b4bc7b4a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -299,12 +299,21 @@ public ExceptionInfoResponse(String exceptionId, String description, ExceptionBr public static class RedefineClassesResponse extends ResponseBody { public String[] changedClasses = new String[0]; + public String errorMessage = null; /** * Constructor. */ public RedefineClassesResponse(String[] changedClasses) { + this(changedClasses, null); + } + + /** + * Constructor. + */ + public RedefineClassesResponse(String[] changedClasses, String errorMessage) { this.changedClasses = changedClasses; + this.errorMessage = errorMessage; } } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java index 3ea1635d2..e1e5c2ec3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java @@ -26,8 +26,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.logging.Level; @@ -53,6 +55,7 @@ import org.eclipse.jdt.core.util.IClassFileReader; import org.eclipse.jdt.core.util.ISourceAttribute; import org.eclipse.jdt.internal.core.util.Util; +import org.eclipse.jdt.ls.core.internal.JobHelpers; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; @@ -60,6 +63,8 @@ import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.StackFrameUtility; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider; @@ -96,9 +101,9 @@ public class JavaHotCodeReplaceProvider implements IHotCodeReplaceProvider, IRes private PublishSubject eventSubject = PublishSubject.create(); - private List deltaResources = new ArrayList<>(); + private Set deltaResources = new LinkedHashSet<>(); - private List deltaClassNames = new ArrayList<>(); + private Set deltaClassNames = new LinkedHashSet<>(); /** * Visitor for resource deltas. @@ -300,14 +305,23 @@ public void onClassRedefined(Consumer> consumer) { @Override public CompletableFuture> redefineClasses() { + JobHelpers.waitForBuildJobs(10 * 1000); return CompletableFuture.supplyAsync(() -> { List classNames = new ArrayList<>(); + List resources = new ArrayList<>(); + String errorMessage = null; synchronized (this) { classNames.addAll(deltaClassNames); - doHotCodeReplace(deltaResources, deltaClassNames); + resources.addAll(deltaResources); deltaResources.clear(); deltaClassNames.clear(); + errorMessage = doHotCodeReplace(resources, classNames); } + + if (!classNames.isEmpty() && errorMessage != null) { + throw AdapterUtils.createCompletionException(errorMessage, ErrorCode.HCR_FAILURE); + } + return classNames; }); } @@ -325,27 +339,29 @@ private void publishEvent(HotCodeReplaceEvent.EventType type, String message, Ob eventSubject.onNext(new HotCodeReplaceEvent(type, message, data)); } - private void doHotCodeReplace(List resourcesToReplace, List qualifiedNamesToReplace) { + private String doHotCodeReplace(List resourcesToReplace, List qualifiedNamesToReplace) { if (context == null || currentDebugSession == null) { - return; + return null; } if (resourcesToReplace == null || qualifiedNamesToReplace == null || qualifiedNamesToReplace.isEmpty() || resourcesToReplace.isEmpty()) { - return; + return null; } filterNotLoadedTypes(resourcesToReplace, qualifiedNamesToReplace); if (qualifiedNamesToReplace.isEmpty()) { - return; + return null; // If none of the changed types are loaded, do nothing. } // Not supported scenario: if (!currentDebugSession.getVM().canRedefineClasses()) { - return; + publishEvent(HotCodeReplaceEvent.EventType.ERROR, "JVM doesn't support hot reload classes"); + return "JVM doesn't support hot reload classes"; } + String errorMessage = null; publishEvent(HotCodeReplaceEvent.EventType.STARTING, "Start hot code replacement procedure..."); try { @@ -367,6 +383,7 @@ private void doHotCodeReplace(List resourcesToReplace, List q if (containsObsoleteMethods()) { publishEvent(HotCodeReplaceEvent.EventType.ERROR, "JVM contains obsolete methods"); + errorMessage = "JVM contains obsolete methods"; } if (currentDebugSession.getVM().canPopFrames() && framesPopped) { @@ -376,11 +393,13 @@ private void doHotCodeReplace(List resourcesToReplace, List q } } catch (DebugException e) { logger.log(Level.SEVERE, "Failed to complete hot code replace: " + e.getMessage(), e); + errorMessage = e.getMessage(); } finally { publishEvent(HotCodeReplaceEvent.EventType.END, "Completed hot code replace", qualifiedNamesToReplace); + threadFrameMap.clear(); } - threadFrameMap.clear(); + return errorMessage; } private void filterNotLoadedTypes(List resources, List qualifiedNames) { From 73b448ddf2cb863dca06daaaf1c7735443922313 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Jul 2020 11:51:18 +0800 Subject: [PATCH 041/206] Skip the specified classes when breaking on exception or stepping (#334) * Skip the specified classes when breaking on exception or stepping Signed-off-by: Jinbo Wang * remove unused code Signed-off-by: Jinbo Wang * Address review comments Signed-off-by: Jinbo Wang --- .../java/debug/core/DebugSession.java | 15 + .../java/debug/core/DebugSettings.java | 26 ++ .../java/debug/core/DebugUtility.java | 61 +++- .../java/debug/core/IDebugSession.java | 2 + .../core/adapter/DebugAdapterContext.java | 25 +- .../adapter/handler/RestartFrameHandler.java | 2 +- ...SetExceptionBreakpointsRequestHandler.java | 50 ++- .../adapter/handler/StepRequestHandler.java | 25 +- .../java/debug/core/protocol/Requests.java | 34 +- com.microsoft.java.debug.plugin/plugin.xml | 1 + .../plugin/internal/JavaClassFilter.java | 307 ++++++++++++++++++ .../JavaDebugDelegateCommandHandler.java | 3 + 12 files changed, 518 insertions(+), 33 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaClassFilter.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index a67afd221..a56eaf695 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -88,6 +88,11 @@ public IWatchpoint createWatchPoint(String className, String fieldName, String a @Override public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught) { + setExceptionBreakpoints(notifyCaught, notifyUncaught, null, null); + } + + @Override + public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters) { EventRequestManager manager = vm.eventRequestManager(); ArrayList legacy = new ArrayList<>(manager.exceptionRequests()); manager.deleteEventRequests(legacy); @@ -108,6 +113,16 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught // get only the uncaught exceptions ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (classFilters != null) { + for (String classFilter : classFilters) { + request.addClassFilter(classFilter); + } + } + if (classExclusionFilters != null) { + for (String exclusionFilter : classExclusionFilters) { + request.addClassExclusionFilter(exclusionFilter); + } + } request.enable(); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index 5933517de..f3975fbfe 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -11,14 +11,21 @@ package com.microsoft.java.debug.core; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.JsonUtils; +import com.microsoft.java.debug.core.protocol.Requests.ClassFilters; +import com.microsoft.java.debug.core.protocol.Requests.StepFilters; public final class DebugSettings { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private static Set listeners = + Collections.newSetFromMap(new ConcurrentHashMap()); private static DebugSettings current = new DebugSettings(); public int maxStringLength = 0; @@ -31,6 +38,9 @@ public final class DebugSettings { public String logLevel; public String javaHome; public HotCodeReplace hotCodeReplace = HotCodeReplace.MANUAL; + public StepFilters stepFilters = new StepFilters(); + public ClassFilters exceptionFilters = new ClassFilters(); + public boolean exceptionFiltersUpdated = false; public static DebugSettings getCurrent() { return current; @@ -44,7 +54,11 @@ public static DebugSettings getCurrent() { */ public void updateSettings(String jsonSettings) { try { + DebugSettings oldSettings = current; current = JsonUtils.fromJson(jsonSettings, DebugSettings.class); + for (IDebugSettingChangeListener listener : listeners) { + listener.update(oldSettings, current); + } } catch (JsonSyntaxException ex) { logger.severe(String.format("Invalid json for debugSettings: %s, %s", jsonSettings, ex.getMessage())); } @@ -54,6 +68,14 @@ private DebugSettings() { } + public static boolean addDebugSettingChangeListener(IDebugSettingChangeListener listener) { + return listeners.add(listener); + } + + public static boolean removeDebugSettingChangeListener(IDebugSettingChangeListener listener) { + return listeners.remove(listener); + } + public static enum HotCodeReplace { @SerializedName("manual") MANUAL, @@ -62,4 +84,8 @@ public static enum HotCodeReplace { @SerializedName("never") NEVER } + + public static interface IDebugSettingChangeListener { + public void update(DebugSettings oldSettings, DebugSettings newSettings); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index edfb6d4b3..1202a30f3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -313,7 +313,21 @@ public static IDebugSession attach(VirtualMachineManager vmManager, String hostN * @return the new step request. */ public static StepRequest createStepOverRequest(ThreadReference thread, String[] stepFilters) { - return createStepRequest(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER, stepFilters); + return createStepOverRequest(thread, null, stepFilters); + } + + /** + * Create a step over request on the specified thread. + * @param thread + * the target thread. + * @param classFilters + * restricts the step event to those matching the given class patterns when stepping. + * @param classExclusionFilters + * restricts the step event to those not matching the given class patterns when stepping. + * @return the new step request. + */ + public static StepRequest createStepOverRequest(ThreadReference thread, String[] classFilters, String[] classExclusionFilters) { + return createStepRequest(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER, classFilters, classExclusionFilters); } /** @@ -325,7 +339,21 @@ public static StepRequest createStepOverRequest(ThreadReference thread, String[] * @return the new step request. */ public static StepRequest createStepIntoRequest(ThreadReference thread, String[] stepFilters) { - return createStepRequest(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO, stepFilters); + return createStepIntoRequest(thread, null, stepFilters); + } + + /** + * Create a step into request on the specified thread. + * @param thread + * the target thread. + * @param classFilters + * restricts the step event to those matching the given class patterns when stepping. + * @param classExclusionFilters + * restricts the step event to those not matching the given class patterns when stepping. + * @return the new step request. + */ + public static StepRequest createStepIntoRequest(ThreadReference thread, String[] classFilters, String[] classExclusionFilters) { + return createStepRequest(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO, classFilters, classExclusionFilters); } /** @@ -337,14 +365,33 @@ public static StepRequest createStepIntoRequest(ThreadReference thread, String[] * @return the new step request. */ public static StepRequest createStepOutRequest(ThreadReference thread, String[] stepFilters) { - return createStepRequest(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT, stepFilters); + return createStepOutRequest(thread, null, stepFilters); } - private static StepRequest createStepRequest(ThreadReference thread, int stepSize, int stepDepth, String[] stepFilters) { + /** + * Create a step out request on the specified thread. + * @param thread + * the target thread. + * @param classFilters + * restricts the step event to those matching the given class patterns when stepping. + * @param classExclusionFilters + * restricts the step event to those not matching the given class patterns when stepping. + * @return the new step request. + */ + public static StepRequest createStepOutRequest(ThreadReference thread, String[] classFilters, String[] classExclusionFilters) { + return createStepRequest(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT, classFilters, classExclusionFilters); + } + + private static StepRequest createStepRequest(ThreadReference thread, int stepSize, int stepDepth, String[] classFilters, String[] classExclusionFilters) { StepRequest request = thread.virtualMachine().eventRequestManager().createStepRequest(thread, stepSize, stepDepth); - if (stepFilters != null) { - for (String stepFilter : stepFilters) { - request.addClassExclusionFilter(stepFilter); + if (classFilters != null) { + for (String classFilter : classFilters) { + request.addClassFilter(classFilter); + } + } + if (classExclusionFilters != null) { + for (String exclusionFilter : classExclusionFilters) { + request.addClassExclusionFilter(exclusionFilter); } } request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index ec09aff29..24138fef1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -34,6 +34,8 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught); + void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters); + // TODO: createFunctionBreakpoint Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index bd29f9946..248f0f803 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -13,17 +13,24 @@ import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; +import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; import com.microsoft.java.debug.core.adapter.variables.VariableFormatterFactory; import com.microsoft.java.debug.core.protocol.IProtocolServer; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; +import org.apache.commons.lang3.ArrayUtils; + public class DebugAdapterContext implements IDebugAdapterContext { private static final int MAX_CACHE_ITEMS = 10000; + private final StepFilters defaultFilters = new StepFilters(); private Map sourceMappingCache = Collections.synchronizedMap(new LRUCache<>(MAX_CACHE_ITEMS)); private IProviderContext providerContext; private IProtocolServer server; @@ -235,12 +242,28 @@ public String getMainClass() { @Override public void setStepFilters(StepFilters stepFilters) { + // For backward compatibility, merge the classNameFilters to skipClasses. + if (stepFilters != null && ArrayUtils.isNotEmpty(stepFilters.classNameFilters)) { + Set patterns = new LinkedHashSet<>(); + if (ArrayUtils.isNotEmpty(stepFilters.skipClasses)) { + patterns.addAll(Arrays.asList(stepFilters.skipClasses)); + } + + patterns.addAll(Arrays.asList(stepFilters.classNameFilters)); + stepFilters.skipClasses = patterns.toArray(new String[0]); + } this.stepFilters = stepFilters; } @Override public StepFilters getStepFilters() { - return stepFilters; + if (stepFilters != null) { + return stepFilters; + } else if (DebugSettings.getCurrent().stepFilters != null) { + return DebugSettings.getCurrent().stepFilters; + } + + return defaultFilters; } @Override diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java index 71ed2764e..3479c9a8a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java @@ -102,7 +102,7 @@ private void popStackFrames(IDebugAdapterContext context, ThreadReference thread } private void stepInto(IDebugAdapterContext context, ThreadReference thread) { - StepRequest request = DebugUtility.createStepIntoRequest(thread, context.getStepFilters().classNameFilters); + StepRequest request = DebugUtility.createStepIntoRequest(thread, context.getStepFilters().allowClasses, context.getStepFilters().skipClasses); context.getDebugSession().getEventHub().stepEvents().filter(debugEvent -> request.equals(debugEvent.event.request())).take(1).subscribe(debugEvent -> { debugEvent.shouldResume = false; // Have to send two events to keep the UI sync with the step in operations: diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java index 6584e791b..3a4e642c8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java @@ -17,17 +17,27 @@ import org.apache.commons.lang3.ArrayUtils; +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.DebugSettings.IDebugSettingChangeListener; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.ClassFilters; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.SetExceptionBreakpointsArguments; import com.microsoft.java.debug.core.protocol.Types; +import com.sun.jdi.event.VMDeathEvent; +import com.sun.jdi.event.VMDisconnectEvent; -public class SetExceptionBreakpointsRequestHandler implements IDebugRequestHandler { +public class SetExceptionBreakpointsRequestHandler implements IDebugRequestHandler, IDebugSettingChangeListener { + private IDebugSession debugSession = null; + private boolean isInitialized = false; + private boolean notifyCaught = false; + private boolean notifyUncaught = false; @Override public List getTargetCommands() { @@ -35,17 +45,28 @@ public List getTargetCommands() { } @Override - public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + public synchronized CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { if (context.getDebugSession() == null) { return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Empty debug session."); } + if (!isInitialized) { + isInitialized = true; + debugSession = context.getDebugSession(); + DebugSettings.addDebugSettingChangeListener(this); + debugSession.getEventHub().events().subscribe(debugEvent -> { + if (debugEvent.event instanceof VMDeathEvent + || debugEvent.event instanceof VMDisconnectEvent) { + DebugSettings.removeDebugSettingChangeListener(this); + } + }); + } + String[] filters = ((SetExceptionBreakpointsArguments) arguments).filters; try { - boolean notifyCaught = ArrayUtils.contains(filters, Types.ExceptionBreakpointFilter.CAUGHT_EXCEPTION_FILTER_NAME); - boolean notifyUncaught = ArrayUtils.contains(filters, Types.ExceptionBreakpointFilter.UNCAUGHT_EXCEPTION_FILTER_NAME); - - context.getDebugSession().setExceptionBreakpoints(notifyCaught, notifyUncaught); + this.notifyCaught = ArrayUtils.contains(filters, Types.ExceptionBreakpointFilter.CAUGHT_EXCEPTION_FILTER_NAME); + this.notifyUncaught = ArrayUtils.contains(filters, Types.ExceptionBreakpointFilter.UNCAUGHT_EXCEPTION_FILTER_NAME); + setExceptionBreakpoints(context.getDebugSession(), this.notifyCaught, this.notifyUncaught); return CompletableFuture.completedFuture(response); } catch (Exception ex) { throw AdapterUtils.createCompletionException( @@ -55,4 +76,21 @@ public CompletableFuture handle(Command command, Arguments arguments, } } + private void setExceptionBreakpoints(IDebugSession debugSession, boolean notifyCaught, boolean notifyUncaught) { + ClassFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters; + String[] classFilters = (exceptionFilters == null ? null : exceptionFilters.allowClasses); + String[] classExclusionFilters = (exceptionFilters == null ? null : exceptionFilters.skipClasses); + debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, classFilters, classExclusionFilters); + } + + @Override + public synchronized void update(DebugSettings oldSettings, DebugSettings newSettings) { + try { + if (newSettings != null && newSettings.exceptionFiltersUpdated) { + setExceptionBreakpoints(debugSession, notifyCaught, notifyUncaught); + } + } catch (Exception ex) { + DebugSettings.removeDebugSettingChangeListener(this); + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 2c75462bc..a148ce20b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -76,13 +76,14 @@ public CompletableFuture handle(Command command, Arguments arguments, if (command == Command.STEPIN) { threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().classNameFilters); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, - context.getStepFilters().classNameFilters); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else { - threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, - context.getStepFilters().classNameFilters); + threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } threadState.pendingStepRequest.enable(); DebugUtility.resumeThread(thread); @@ -133,21 +134,14 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.pendingStepType == Command.STEPIN) { int currentStackDepth = thread.frameCount(); Location currentStepLocation = getTopFrame(thread).location(); - // Check if the step into operation stepped through the filtered code and stopped at an un-filtered location. - if (threadState.stackDepth + 1 < thread.frameCount()) { - // Create another stepOut request to return back where we started the step into. - threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, - context.getStepFilters().classNameFilters); - threadState.pendingStepRequest.enable(); - debugEvent.shouldResume = true; - return; - } + // If the ending step location is filtered, or same as the original location where the step into operation is originated, // do another step of the same kind. if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, currentStackDepth, currentStepLocation)) { threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().classNameFilters); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; @@ -169,7 +163,8 @@ private boolean isStepFiltersConfigured(StepFilters filters) { if (filters == null) { return false; } - return ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors + return ArrayUtils.isNotEmpty(filters.allowClasses) || ArrayUtils.isNotEmpty(filters.skipClasses) + || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors || filters.skipStaticInitializers || filters.skipSynthetics; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 1874825e7..f62a241a1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -41,8 +41,36 @@ public static class InitializeArguments extends Arguments { public boolean supportsRunInTerminalRequest; } - public static class StepFilters { - public String[] classNameFilters = new String[0]; + public static class ClassFilters { + /** + * Restricts the events generated by the request to those whose location is + * in a class whose name matches this restricted regular expression. Regular + * expressions are limited to exact matches and patterns that begin with '*' + * or end with '*'; for example, "*.Foo" or "java.*". + * + * This property corresponds to the ClassFilter (include filter). Multiple + * filters are applied with CUT-OFF AND. Only events that satisfied all + * filters are placed in the event queue, that means several include filters + * are handled as "A and B and C", not "A or B or C". + */ + public String[] allowClasses = new String[0]; + + /** + * Restricts the events generated by the request to those whose location is + * in a class whose name does not match this restricted regular expression, e.g. + * "java.*" or "*.Foo". + * + * This property corrsponds to the ClassExclusionFilter (exclude filter). + */ + public String[] skipClasses = new String[0]; + } + + public static class StepFilters extends ClassFilters { + /** + * Deprecated - please use {@link ClassFilters#skipClasses } instead. + */ + @Deprecated + public String[] classNameFilters; public boolean skipSynthetics; public boolean skipStaticInitializers; public boolean skipConstructors; @@ -54,7 +82,7 @@ public static class LaunchBaseArguments extends Arguments { public String request; public String projectName; public String[] sourcePaths = new String[0]; - public StepFilters stepFilters = new StepFilters(); + public StepFilters stepFilters; } public static enum CONSOLE { diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index 111e4118f..cd999ab09 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -18,6 +18,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaClassFilter.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaClassFilter.java new file mode 100644 index 000000000..5d5bc7b0b --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaClassFilter.java @@ -0,0 +1,307 @@ +/******************************************************************************* + * Copyright (c) 2020 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 java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import com.microsoft.java.debug.core.Configuration; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; + +public class JavaClassFilter { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private static final int BLOCK_NONE = 0; + private static final int BLOCK_JDK = 1; + private static final int BLOCK_LIB = 2; + private static final int BLOCK_BIN = 3; + + /** + * Substitute the variables in the exclusion filter list. + * + *

+ * For example, a sample input could be: + * [ + * "$JDK", + * "$Libraries", + * "junit.*", + * "java.lang.ClassLoader" + * ]. + * + * Variable "$JDK" means skipping the classes from the JDK, and variable "$Libraries" + * means skipping the classes from the application libraries. + * + *

+ * This function will resolve the packages belonging to the variable group first, and + * then use a greedy algorithm to generate a list of wildcards to cover these packages. + */ + public static String[] resolveClassFilters(List unresolvedFilters) { + if (unresolvedFilters == null || unresolvedFilters.isEmpty()) { + return new String[0]; + } + + int variableScope = BLOCK_NONE; + Set hardcodePatterns = new LinkedHashSet<>(); + for (Object filter : unresolvedFilters) { + if (Objects.equals("$JDK", filter)) { + variableScope = variableScope | BLOCK_JDK; + } else if (Objects.equals("$Libraries", filter)) { + variableScope = variableScope | BLOCK_LIB; + } else if (filter instanceof String) { + String value = (String) filter; + if (StringUtils.isNotBlank(value)) { + hardcodePatterns.add(value.trim()); + } + } + } + + if (variableScope == BLOCK_NONE) { + return hardcodePatterns.toArray(new String[0]); + } + + Set blackList = new LinkedHashSet<>(); + Set whiteList = new LinkedHashSet<>(); + IJavaProject[] javaProjects = ProjectUtils.getJavaProjects(); + for (IJavaProject javaProject : javaProjects) { + try { + IPackageFragmentRoot[] roots = javaProject.getAllPackageFragmentRoots(); + for (IPackageFragmentRoot root : roots) { + if (isOnBlackList(root, variableScope)) { + collectPackages(root, blackList); + } else { + collectPackages(root, whiteList); + } + } + } catch (JavaModelException e) { + logger.log(Level.SEVERE, String.format("Failed to get the classpath entry for the PackageFragmentRoot: %s", e.toString()), e); + } + } + + return convertToExclusionPatterns(blackList, whiteList, hardcodePatterns); + } + + private static boolean isOnBlackList(IPackageFragmentRoot root, int variableScope) throws JavaModelException { + if (root.isArchive()) { + if (variableScope == BLOCK_BIN) { + return true; + } + + boolean isJDK = isJDKPackageFragmentRoot(root); + return (variableScope == BLOCK_JDK && isJDK) || (variableScope == BLOCK_LIB && !isJDK); + } + + return false; + } + + private static boolean isJDKPackageFragmentRoot(IPackageFragmentRoot root) throws JavaModelException { + if (root.getRawClasspathEntry() != null) { + IPath path = root.getRawClasspathEntry().getPath(); + return path != null && path.segmentCount() > 0 + && Objects.equals(JavaRuntime.JRE_CONTAINER, path.segment(0)); + } + + return false; + } + + private static void collectPackages(IPackageFragmentRoot root, Set result) throws JavaModelException { + for (IJavaElement javaElement : root.getChildren()) { + String elementName = javaElement.getElementName(); + if (javaElement instanceof IPackageFragment + && ((IPackageFragment) javaElement).hasChildren()) { + if (StringUtils.isNotBlank(elementName)) { + result.add(elementName); + } + } + } + } + + private static String[] convertToExclusionPatterns(Collection blackList, + Collection whiteList, Collection hardcodePatterns) { + List hardcodeBlockedPackages = hardcodePatterns.stream() + .filter(pattern -> pattern.endsWith(".*")) + .map(pattern -> pattern.substring(0, pattern.length() - 2)) + .collect(Collectors.toList()); + Trie hardcodeBlackTree = new Trie(hardcodeBlockedPackages); + + // Remove those packages that are on the user hardcode black list. + List newWhiteList = whiteList.stream() + .filter(pattern -> !hardcodeBlackTree.isPrefix(pattern)) + .collect(Collectors.toList()); + + // Superimpose the white tree on the black tree, then gray out the nodes + // with the same name. + Trie whiteTree = new Trie(newWhiteList); + Trie blackTree = new Trie(blackList); + superimpose(whiteTree, blackTree); + + // Generate some wildcard patterns to cover all items in the black list. + List wildcardPatterns = new ArrayList<>(); + traverse(blackTree.root, 0, wildcardPatterns, new ArrayList<>()); + + // Append the hardcode patterns to the result. + for (String name : hardcodePatterns) { + if (!blackTree.wildcardMatch(name)) { + wildcardPatterns.add(name); + } + } + + return wildcardPatterns.toArray(new String[0]); + } + + private static void superimpose(Trie upTree, Trie downTree) { + Queue upQueue = new LinkedList<>(); + Queue downQueue = new LinkedList<>(); + upQueue.offer(upTree.root); + downQueue.offer(downTree.root); + while (!upQueue.isEmpty()) { + TrieNode upNode = upQueue.poll(); + TrieNode downNode = downQueue.poll(); + downNode.isGray = true; + for (Entry entry : upNode.children.entrySet()) { + if (downNode.children.containsKey(entry.getKey())) { + upQueue.offer(entry.getValue()); + downQueue.offer(downNode.children.get(entry.getKey())); + } + } + } + } + + private static void traverse(TrieNode root, int depth, List result, List parent) { + // If the node is gray, that means it also appears in the white list. + // We cannot use it to generate a wildcard pattern. + if (!root.isGray) { + String[] names = new String[depth + 1]; + for (int i = 0; i < depth - 1; i++) { + names[i] = parent.get(i); + } + + names[depth - 1] = root.name; + names[depth] = "*"; + result.add(String.join(".", names)); + return; + } + + if (depth > 0) { + if (parent.size() < depth) { + parent.add(root.name); + } else { + parent.set(depth - 1, root.name); + } + } + + for (TrieNode child : root.children.values()) { + traverse(child, depth + 1, result, parent); + } + } + + private static class Trie { + private TrieNode root = new TrieNode(); + + public Trie(Collection names) { + for (String name : names) { + insert(name); + } + } + + public void insert(String name) { + if (StringUtils.isBlank(name)) { + return; + } + + String[] names = name.split("\\."); + TrieNode currentNode = this.root; + for (int i = 0; i < names.length; i++) { + TrieNode node; + if (currentNode.children.containsKey(names[i])) { + node = currentNode.children.get(names[i]); + } else { + node = new TrieNode(names[i]); + currentNode.children.put(names[i], node); + } + + currentNode = node; + } + + currentNode.isLeaf = true; + } + + public boolean isPrefix(String name) { + String[] names = name.split("\\."); + TrieNode currentNode = this.root; + for (int i = 0; i < names.length; i++) { + TrieNode node = currentNode.children.get(names[i]); + if (node == null) { + break; + } + + currentNode = node; + } + + return currentNode != this.root && currentNode.isLeaf; + } + + /** + * Check whether the name can be covered by the wildcards generated from + * this trie. + */ + public boolean wildcardMatch(String name) { + String[] names = name.split("\\."); + Map children = this.root.children; + for (int i = 0; i < names.length; i++) { + TrieNode node = children.get(names[i]); + if (node == null) { + break; + } else if (!node.isGray) { + return true; + } + + children = node.children; + } + + return false; + } + } + + private static class TrieNode { + private String name; + private Map children = new LinkedHashMap<>(); + private boolean isLeaf = false; + private boolean isGray = false; + + public TrieNode() { + } + + public TrieNode(String name) { + this.name = name; + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 025949a9b..5db924442 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -45,6 +45,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; public static final String RESOLVE_JAVA_EXECUTABLE = "vscode.java.resolveJavaExecutable"; public static final String FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; + public static final String RESOLVE_CLASSFILTERS = "vscode.java.resolveClassFilters"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -84,6 +85,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return ResolveJavaExecutableHandler.resolveJavaExecutable(arguments); case FETCH_PLATFORM_SETTINGS: return PlatformSettings.getPlatformSettings(); + case RESOLVE_CLASSFILTERS: + return JavaClassFilter.resolveClassFilters(arguments); default: break; } From bf689a663e756f739cda175ad9c3fa642617d57e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 6 Jul 2020 15:36:08 +0800 Subject: [PATCH 042/206] Display the method return value when stepping out (#336) * Show the method return value when stepping out Signed-off-by: Jinbo Wang * Clear the cached method results when pause/continue current thread Signed-off-by: Jinbo Wang * Add an icon to method return value Signed-off-by: Jinbo Wang * Refine the key for the return value Signed-off-by: Jinbo Wang * Handle the emoji display support issue for different platform Signed-off-by: Jinbo Wang --- .../java/debug/core/JdiMethodResult.java | 25 +++++++ .../java/debug/core/adapter/AdapterUtils.java | 2 + .../core/adapter/DebugAdapterContext.java | 8 ++- .../core/adapter/IDebugAdapterContext.java | 4 +- .../core/adapter/IStepResultManager.java | 24 +++++++ .../debug/core/adapter/StepResultManager.java | 42 ++++++++++++ .../adapter/handler/StepRequestHandler.java | 67 ++++++++++++++++--- .../handler/ThreadsRequestHandler.java | 4 ++ .../handler/VariablesRequestHandler.java | 11 ++- 9 files changed, 175 insertions(+), 12 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JdiMethodResult.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JdiMethodResult.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JdiMethodResult.java new file mode 100644 index 000000000..715e23622 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JdiMethodResult.java @@ -0,0 +1,25 @@ +/******************************************************************************* +* Copyright (c) 2020 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.core; + +import com.sun.jdi.Method; +import com.sun.jdi.Value; + +public class JdiMethodResult { + public Method method; + public Value value; + + public JdiMethodResult(Method method, Value value) { + this.method = method; + this.value = value; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 7b6636f7e..132d702d9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -51,6 +51,8 @@ public class AdapterUtils { private static final String OS_NAME = System.getProperty("os.name", "").toLowerCase(); private static final Pattern ENCLOSING_CLASS_REGEX = Pattern.compile("^([^\\$]*)"); + public static final boolean isWin = isWindows(); + public static final boolean isMac = OS_NAME.contains("mac") || OS_NAME.contains("darwin"); /** * Check if the OS is windows or not. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 248f0f803..395d39ec6 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2020 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 @@ -61,6 +61,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private IStackFrameManager stackFrameManager = new StackFrameManager(); private IExceptionManager exceptionManager = new ExceptionManager(); private IBreakpointManager breakpointManager = new BreakpointManager(); + private IStepResultManager stepResultManager = new StepResultManager(); public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) { this.providerContext = providerContext; @@ -320,4 +321,9 @@ public IExceptionManager getExceptionManager() { public IBreakpointManager getBreakpointManager() { return breakpointManager; } + + @Override + public IStepResultManager getStepResultManager() { + return stepResultManager; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 8f7055eee..4f843fd09 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -126,4 +126,6 @@ public interface IDebugAdapterContext { IExceptionManager getExceptionManager(); IBreakpointManager getBreakpointManager(); + + IStepResultManager getStepResultManager(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java new file mode 100644 index 000000000..c19c0ecd6 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java @@ -0,0 +1,24 @@ +/******************************************************************************* +* Copyright (c) 2020 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.core.adapter; + +import com.microsoft.java.debug.core.JdiMethodResult; + +public interface IStepResultManager { + JdiMethodResult setMethodResult(long threadId, JdiMethodResult methodResult); + + JdiMethodResult getMethodResult(long threadId); + + JdiMethodResult removeMethodResult(long threadId); + + void removeAllMethodResults(); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java new file mode 100644 index 000000000..dd3937181 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java @@ -0,0 +1,42 @@ +/******************************************************************************* +* Copyright (c) 2020 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.core.adapter; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.microsoft.java.debug.core.JdiMethodResult; + +public class StepResultManager implements IStepResultManager { + private Map methodResults = Collections.synchronizedMap(new HashMap<>()); + + @Override + public JdiMethodResult setMethodResult(long threadId, JdiMethodResult methodResult) { + return this.methodResults.put(threadId, methodResult); + } + + @Override + public JdiMethodResult getMethodResult(long threadId) { + return this.methodResults.get(threadId); + } + + @Override + public JdiMethodResult removeMethodResult(long threadId) { + return this.methodResults.remove(threadId); + } + + @Override + public void removeAllMethodResults() { + this.methodResults.clear(); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index a148ce20b..94e0d3078 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -21,6 +21,7 @@ import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.JdiExceptionReference; +import com.microsoft.java.debug.core.JdiMethodResult; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -34,11 +35,20 @@ import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.Location; import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; +import com.sun.jdi.Value; +import com.sun.jdi.VoidValue; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; +import com.sun.jdi.event.ExceptionEvent; +import com.sun.jdi.event.LocatableEvent; +import com.sun.jdi.event.MethodExitEvent; import com.sun.jdi.event.StepEvent; +import com.sun.jdi.request.EventRequest; +import com.sun.jdi.request.EventRequestManager; +import com.sun.jdi.request.MethodExitRequest; import com.sun.jdi.request.StepRequest; import io.reactivex.disposables.Disposable; @@ -61,6 +71,7 @@ public CompletableFuture handle(Command command, Arguments arguments, ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), threadId); if (thread != null) { JdiExceptionReference exception = context.getExceptionManager().removeException(threadId); + context.getStepResultManager().removeMethodResult(threadId); try { ThreadState threadState = new ThreadState(); threadState.threadId = threadId; @@ -69,7 +80,9 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.stepLocation = getTopFrame(thread).location(); threadState.eventSubscription = context.getDebugSession().getEventHub().events() .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) - || debugEvent.event instanceof BreakpointEvent) + || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) + || debugEvent.event instanceof BreakpointEvent + || debugEvent.event instanceof ExceptionEvent) .subscribe(debugEvent -> { handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); }); @@ -86,6 +99,24 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } threadState.pendingStepRequest.enable(); + + MethodExitRequest methodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); + methodExitRequest.addThreadFilter(thread); + methodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (thread.virtualMachine().canUseInstanceFilters()) { + try { + ObjectReference thisObject = getTopFrame(thread).thisObject(); + if (thisObject != null) { + methodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore + } + } + methodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + threadState.pendingMethodExitRequest = methodExitRequest; + methodExitRequest.enable(); + DebugUtility.resumeThread(thread); ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context); @@ -116,19 +147,18 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, Event event = debugEvent.event; // When a breakpoint occurs, abort any pending step requests from the same thread. - if (event instanceof BreakpointEvent) { - long threadId = ((BreakpointEvent) event).thread().uniqueID(); + if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { + long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { - DebugUtility.deleteEventRequestSafely(debugSession.getVM().eventRequestManager(), threadState.pendingStepRequest); - threadState.pendingStepRequest = null; + threadState.deleteStepRequests(debugSession.getVM().eventRequestManager()); + context.getStepResultManager().removeMethodResult(threadId); if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } } } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); - DebugUtility.deleteEventRequestSafely(thread.virtualMachine().eventRequestManager(), threadState.pendingStepRequest); - threadState.pendingStepRequest = null; + threadState.deleteStepRequests(debugSession.getVM().eventRequestManager()); if (isStepFiltersConfigured(context.getStepFilters())) { try { if (threadState.pendingStepType == Command.STEPIN) { @@ -156,6 +186,19 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID())); debugEvent.shouldResume = false; + } else if (event instanceof MethodExitEvent) { + MethodExitEvent methodExitEvent = (MethodExitEvent) event; + long threadId = methodExitEvent.thread().uniqueID(); + if (threadId == threadState.threadId && methodExitEvent.method().equals(threadState.stepLocation.method())) { + Value returnValue = methodExitEvent.returnValue(); + if (returnValue instanceof VoidValue) { + context.getStepResultManager().removeMethodResult(threadId); + } else { + JdiMethodResult methodResult = new JdiMethodResult(methodExitEvent.method(), returnValue); + context.getStepResultManager().setMethodResult(threadId, methodResult); + } + } + debugEvent.shouldResume = true; } } @@ -232,8 +275,16 @@ class ThreadState { long threadId = -1; Command pendingStepType; StepRequest pendingStepRequest = null; + MethodExitRequest pendingMethodExitRequest = null; int stackDepth = -1; Location stepLocation = null; Disposable eventSubscription = null; + + public void deleteStepRequests(EventRequestManager manager) { + DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest); + DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); + this.pendingMethodExitRequest = null; + this.pendingStepRequest = null; + } } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index 5d4ee48f7..3e4b481d5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -95,9 +95,11 @@ private CompletableFuture threads(Requests.ThreadsArguments arguments, private CompletableFuture pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) { ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); if (thread != null) { + context.getStepResultManager().removeMethodResult(arguments.threadId); thread.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId)); } else { + context.getStepResultManager().removeAllMethodResults(); context.getDebugSession().suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); } @@ -113,11 +115,13 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, * be resumed (through ThreadReference#resume() or VirtualMachine#resume()) the same number of times it has been suspended. */ if (thread != null) { + context.getStepResultManager().removeMethodResult(arguments.threadId); context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; DebugUtility.resumeThread(thread); checkThreadRunningAndRecycleIds(thread, context); } else { + context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); context.getDebugSession().resume(); context.getRecyclableIdPool().removeAllObjects(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 45ed9fc34..37e6466a5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2020 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 @@ -28,6 +28,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.JdiMethodResult; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -106,7 +107,13 @@ public CompletableFuture handle(Command command, Arguments arguments, ErrorCode.GET_VARIABLE_FAILURE); } try { - childrenList = VariableUtils.listLocalVariables(frame); + long threadId = stackFrameReference.getThread().uniqueID(); + JdiMethodResult result = context.getStepResultManager().getMethodResult(threadId); + if (result != null) { + String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->"; + childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value)); + } + childrenList.addAll(VariableUtils.listLocalVariables(frame)); Variable thisVariable = VariableUtils.getThisVariable(frame); if (thisVariable != null) { childrenList.add(thisVariable); From 54b1fed8069e49fb9904dfbfc3ccbc6f8c5d209a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 13 Jul 2020 14:17:11 +0800 Subject: [PATCH 043/206] Fix the call stack matching regex expression for modules (#339) Signed-off-by: Jinbo Wang --- .../java/debug/core/adapter/handler/LaunchRequestHandler.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/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 26869a394..506bce6be 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -258,12 +258,12 @@ protected CompletableFuture launch(LaunchArguments launchArguments, Re return resultFuture; } - private static final Pattern STACKTRACE_PATTERN = Pattern.compile("\\s+at\\s+(([\\w$]+\\.)*[\\w$]+)\\(([\\w-$]+\\.java:\\d+)\\)"); + private static final Pattern STACKTRACE_PATTERN = Pattern.compile("\\s+at\\s+([\\w$\\.]+\\/)?(([\\w$]+\\.)*[\\w$]+)\\(([\\w-$]+\\.java:\\d+)\\)"); private static OutputEvent convertToOutputEvent(String message, Category category, IDebugAdapterContext context) { Matcher matcher = STACKTRACE_PATTERN.matcher(message); if (matcher.find()) { - String methodField = matcher.group(1); + String methodField = matcher.group(2); String locationField = matcher.group(matcher.groupCount()); String fullyQualifiedName = methodField.substring(0, methodField.lastIndexOf(".")); String packageName = fullyQualifiedName.lastIndexOf(".") > -1 ? fullyQualifiedName.substring(0, fullyQualifiedName.lastIndexOf(".")) : ""; From f420492a59b0c4ea66abdbb4424a61f99fa907dc Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 14 Jul 2020 16:11:59 +0800 Subject: [PATCH 044/206] Fix the InvalidStackFrameException when getting variables at the exception breakpoint (#340) Signed-off-by: Jinbo Wang --- .../adapter/handler/ExceptionInfoRequestHandler.java | 9 +++++++++ .../java/debug/core/adapter/variables/VariableUtils.java | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java index 8052ff745..13456029d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java @@ -80,6 +80,15 @@ public CompletableFuture handle(Command command, Arguments arguments, } catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException | InvocationException e) { logger.log(Level.SEVERE, String.format("Failed to get the return value of the method Exception.toString(): %s", e.toString(), e)); + } finally { + try { + // See bug https://github.com/microsoft/vscode-java-debug/issues/767: + // The operation exception.invokeMethod above will resume the thread, that will cause + // the previously cached stack frames for this thread to be invalid. + context.getStackFrameManager().reloadStackFrames(thread); + } catch (Exception e) { + // do nothing. + } } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index 1f95b1154..ffdfc9932 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -149,8 +149,10 @@ public static List listLocalVariables(StackFrame stackFrame) throws Ab return res; } try { - for (LocalVariable localVariable : stackFrame.visibleVariables()) { - Variable var = new Variable(localVariable.name(), stackFrame.getValue(localVariable)); + List localVariables = stackFrame.visibleVariables(); + Map values = stackFrame.getValues(localVariables); + for (LocalVariable localVariable : localVariables) { + Variable var = new Variable(localVariable.name(), values.get(localVariable)); var.local = localVariable; res.add(var); } From f172aa7e31c0b4843b701ffd1015ce6b8dd37635 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 14 Jul 2020 17:08:12 +0800 Subject: [PATCH 045/206] Support copying variable (#338) * #624 "copy value" from variables debugger window not working * Support copy variable to clipboard Signed-off-by: Jinbo Wang Co-authored-by: Michel Moreira --- .../handler/EvaluateRequestHandler.java | 12 +++-- .../handler/InitializeRequestHandler.java | 1 + .../adapter/handler/ScopesRequestHandler.java | 2 +- .../handler/SetVariableRequestHandler.java | 2 +- .../handler/VariablesRequestHandler.java | 40 +++++++++++++-- .../variables/JavaLogicalStructure.java | 50 ++++++++++++++++++- .../JavaLogicalStructureManager.java | 36 ++++++++----- .../core/adapter/variables/Variable.java | 35 +++++++++++++ .../core/adapter/variables/VariableProxy.java | 39 +++++++++++++-- .../core/adapter/variables/VariableUtils.java | 35 ++++++++++++- .../java/debug/core/protocol/Types.java | 1 + 11 files changed, 225 insertions(+), 28 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 30f98d7a8..5da8c24f5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -88,7 +88,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } long threadId = stackFrameReference.getThread().uniqueID(); if (value instanceof ObjectReference) { - VariableProxy varProxy = new VariableProxy(stackFrameReference.getThread(), "eval", value); + VariableProxy varProxy = new VariableProxy(stackFrameReference.getThread(), "eval", value, null, expression); int indexedVariables = -1; Value sizeValue = null; if (value instanceof ArrayReference) { @@ -120,9 +120,13 @@ public CompletableFuture handle(Command command, Arguments arguments, detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); } - response.body = new Responses.EvaluateResponseBody((detailsString == null) ? valueString : valueString + " " + detailsString, - referenceId, variableFormatter.typeToString(value == null ? null : value.type(), options), - Math.max(indexedVariables, 0)); + if ("clipboard".equals(evalArguments.context) && detailsString != null) { + response.body = new Responses.EvaluateResponseBody(detailsString, -1, "String", 0); + } else { + response.body = new Responses.EvaluateResponseBody((detailsString == null) ? valueString : valueString + " " + detailsString, + referenceId, variableFormatter.typeToString(value == null ? null : value.type(), options), + Math.max(indexedVariables, 0)); + } return response; } // for primitive value diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index ff39bb1c1..4733170eb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -62,6 +62,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.exceptionBreakpointFilters = exceptionFilters; caps.supportsExceptionInfoRequest = true; caps.supportsDataBreakpoints = true; + caps.supportsClipboardContext = true; response.body = caps; return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ScopesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ScopesRequestHandler.java index 375752a3b..e7b1a9485 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ScopesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ScopesRequestHandler.java @@ -45,7 +45,7 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } ThreadReference thread = stackFrameReference.getThread(); - VariableProxy localScope = new VariableProxy(thread, "Local", stackFrameReference); + VariableProxy localScope = new VariableProxy(thread, "Local", stackFrameReference, null, null); int localScopeId = context.getRecyclableIdPool().addObject(thread.uniqueID(), localScope); scopes.add(new Types.Scope(localScope.getScope(), localScopeId, false)); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetVariableRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetVariableRequestHandler.java index 568d22c47..c2d3aa1d2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetVariableRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetVariableRequestHandler.java @@ -121,7 +121,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (newValue instanceof ObjectReference && VariableUtils.hasChildren(newValue, showStaticVariables)) { long threadId = ((VariableProxy) container).getThreadId(); String scopeName = ((VariableProxy) container).getScope(); - VariableProxy varProxy = new VariableProxy(((VariableProxy) container).getThread(), scopeName, newValue); + VariableProxy varProxy = new VariableProxy(((VariableProxy) container).getThread(), scopeName, newValue, (VariableProxy) container, name); referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 37e6466a5..b3a9f5a05 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -98,6 +98,8 @@ public CompletableFuture handle(Command command, Arguments arguments, VariableProxy containerNode = (VariableProxy) container; List childrenList = new ArrayList<>(); IStackFrameManager stackFrameManager = context.getStackFrameManager(); + String containerEvaluateName = containerNode.getEvaluateName(); + boolean isUnboundedTypeContainer = containerNode.isUnboundedType(); if (containerNode.getProxiedVariable() instanceof StackFrameReference) { StackFrameReference stackFrameReference = (StackFrameReference) containerNode.getProxiedVariable(); StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); @@ -111,7 +113,7 @@ public CompletableFuture handle(Command command, Arguments arguments, JdiMethodResult result = context.getStepResultManager().getMethodResult(threadId); if (result != null) { String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->"; - childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value)); + childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null)); } childrenList.addAll(VariableUtils.listLocalVariables(frame)); Variable thisVariable = VariableUtils.getThisVariable(frame); @@ -132,11 +134,17 @@ public CompletableFuture handle(Command command, Arguments arguments, ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable(); if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); + if (isUnboundedTypeContainer && logicalStructure != null && containerEvaluateName != null) { + containerEvaluateName = "((" + logicalStructure.getFullyQualifiedName() + ")" + containerEvaluateName + ")"; + isUnboundedTypeContainer = false; + } while (logicalStructure != null) { LogicalStructureExpression valueExpression = logicalStructure.getValueExpression(); LogicalVariable[] logicalVariables = logicalStructure.getVariables(); try { if (valueExpression != null) { + containerEvaluateName = containerEvaluateName == null ? null : containerEvaluateName + "." + valueExpression.evaluateName; + isUnboundedTypeContainer = valueExpression.returnUnboundedType; Value value = logicalStructure.getValue(containerObj, containerNode.getThread(), evaluationEngine); if (value instanceof ObjectReference) { containerObj = (ObjectReference) value; @@ -149,7 +157,9 @@ public CompletableFuture handle(Command command, Arguments arguments, for (LogicalVariable logicalVariable : logicalVariables) { String name = logicalVariable.getName(); Value value = logicalVariable.getValue(containerObj, containerNode.getThread(), evaluationEngine); - childrenList.add(new Variable(name, value)); + Variable variable = new Variable(name, value, logicalVariable.getEvaluateName()); + variable.setUnboundedType(logicalVariable.returnUnboundedType()); + childrenList.add(variable); } } } catch (IllegalArgumentException | CancellationException | InterruptedException | ExecutionException e) { @@ -236,15 +246,37 @@ public CompletableFuture handle(Command command, Arguments arguments, } } + String evaluateName = null; + if (javaVariable.evaluateName == null || (containerEvaluateName == null && containerNode.getProxiedVariable() instanceof ObjectReference)) { + // Disable evaluate on the method return value. + evaluateName = null; + } else if (isUnboundedTypeContainer && !containerNode.isIndexedVariable()) { + // The type name returned by JDI is the binary name, which uses '$' as the separator of + // inner class e.g. Foo$Bar. But the evaluation expression only accepts using '.' as the class + // name separator. + String typeName = ((ObjectReference) containerNode.getProxiedVariable()).referenceType().name(); + // TODO: This replacement will possibly change the $ in the class name itself. + typeName = typeName.replaceAll("\\$", "."); + evaluateName = VariableUtils.getEvaluateName(javaVariable.evaluateName, "((" + typeName + ")" + containerEvaluateName + ")", false); + } else { + if (containerEvaluateName != null && containerEvaluateName.contains("%s")) { + evaluateName = String.format(containerEvaluateName, javaVariable.evaluateName); + } else { + evaluateName = VariableUtils.getEvaluateName(javaVariable.evaluateName, containerEvaluateName, containerNode.isIndexedVariable()); + } + } + int referenceId = 0; if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) { - VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value); + VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName); referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); + varProxy.setIndexedVariable(indexedVariables >= 0); + varProxy.setUnboundedType(javaVariable.isUnboundedType()); } Types.Variable typedVariables = new Types.Variable(name, variableFormatter.valueToString(value, options), variableFormatter.typeToString(value == null ? null : value.type(), options), - referenceId, null); + referenceId, evaluateName); typedVariables.indexedVariables = Math.max(indexedVariables, 0); String detailsValue = null; if (sizeValue != null) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java index c186e0dc9..6bf65a9f8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Microsoft Corporation and others. + * Copyright (c) 2019-2020 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 @@ -26,7 +26,10 @@ import com.sun.jdi.Value; public class JavaLogicalStructure { + // The binary type name. For inner type, the binary name uses '$' as the separator, e.g. java.util.Map$Entry. private final String type; + // The fully qualified name, which uses '.' as the separator, e.g. java.util.Map.Entry. + private final String fullyQualifiedName; private final LogicalStructureExpression valueExpression; private final LogicalStructureExpression sizeExpression; private final LogicalVariable[] variables; @@ -36,8 +39,17 @@ public class JavaLogicalStructure { */ public JavaLogicalStructure(String type, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression, LogicalVariable[] variables) { + this(type, type, valueExpression, sizeExpression, variables); + } + + /** + * Constructor. + */ + public JavaLogicalStructure(String type, String fullyQualifiedName, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression, + LogicalVariable[] variables) { this.valueExpression = valueExpression; this.type = type; + this.fullyQualifiedName = fullyQualifiedName; this.sizeExpression = sizeExpression; this.variables = variables; } @@ -46,6 +58,10 @@ public String getType() { return type; } + public String getFullyQualifiedName() { + return fullyQualifiedName; + } + public LogicalStructureExpression getValueExpression() { return valueExpression; } @@ -152,18 +168,50 @@ public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvalu throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException { return JavaLogicalStructure.getValue(thisObject, valueExpression, thread, evaluationEngine); } + + public String getEvaluateName() { + if (valueExpression == null || valueExpression.evaluateName == null) { + return name; + } + + return valueExpression.evaluateName; + } + + public boolean returnUnboundedType() { + return valueExpression != null && valueExpression.returnUnboundedType; + } } public static class LogicalStructureExpression { public LogicalStructureExpressionType type; public String[] value; + public String evaluateName; + public boolean returnUnboundedType = false; /** * Constructor. */ public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value) { + this(type, value, null); + } + + /** + * Constructor. + */ + public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value, String evaluateName) { + this.type = type; + this.value = value; + this.evaluateName = evaluateName; + } + + /** + * Constructor. + */ + public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value, String evaluateName, boolean returnUnboundedType) { this.type = type; this.value = value; + this.evaluateName = evaluateName; + this.returnUnboundedType = returnUnboundedType; } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java index 45b7a7974..2762173cd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Microsoft Corporation and others. + * Copyright (c) 2019-2020 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 @@ -30,18 +30,30 @@ public class JavaLogicalStructureManager { static { supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map", - new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"entrySet", "()Ljava/util/Set;"}), - new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}), - new LogicalVariable[0])); - supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", null, null, new LogicalVariable[] { - new LogicalVariable("key", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getKey", "()Ljava/lang/Object;"})), - new LogicalVariable("value", - new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getValue", "()Ljava/lang/Object;"})) - })); + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"entrySet", "()Ljava/util/Set;"}, "entrySet()"), + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}), + new LogicalVariable[0] + )); + supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", "java.util.Map.Entry", null, null, + new LogicalVariable[] { + new LogicalVariable("key", + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getKey", "()Ljava/lang/Object;"}, "getKey()", true) + ), + new LogicalVariable("value", + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, + new String[] {"getValue", "()Ljava/lang/Object;"}, "getValue()", true) + )} + )); + supportedLogicalStructures.add(new JavaLogicalStructure("java.util.List", + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}, "get(%s)", true), + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}), + new LogicalVariable[0] + )); supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection", - new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}), - new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}), - new LogicalVariable[0])); + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}, "toArray()", true), + new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}), + new LogicalVariable[0] + )); } /** diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/Variable.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/Variable.java index e2a6efc2a..559e62b29 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/Variable.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/Variable.java @@ -13,6 +13,8 @@ import org.apache.commons.lang3.StringUtils; +import java.util.Objects; + import com.sun.jdi.Field; import com.sun.jdi.LocalVariable; import com.sun.jdi.Type; @@ -61,18 +63,39 @@ public class Variable { */ public int argumentIndex; + /** + * The variable evaluate name for the container context. Defaults to the variable name. + */ + public String evaluateName; + + /** + * Indicates whether this variable's type is determined at runtime. + */ + private boolean isUnboundedType = false; + /** * The constructor of JavaVariable. * @param name the name of this variable. * @param value the JDI value */ public Variable(String name, Value value) { + this(name, value, name); + } + + /** + * The constructor of JavaVariable. + * @param name the name of this variable. + * @param value the JDI value + * @param evaluateName the variable evaluate name for the container context if any + */ + public Variable(String name, Value value, String evaluateName) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException("Name is required for a java variable."); } this.name = name; this.value = value; this.argumentIndex = -1; + this.evaluateName = evaluateName; } /** @@ -86,4 +109,16 @@ public Type getDeclaringType() { } return null; } + + public void setUnboundedType(boolean isUnboundedType) { + this.isUnboundedType = isUnboundedType; + } + + public boolean isUnboundedType() { + if (isUnboundedType) { + return true; + } + + return field != null && Objects.equals(field.signature(), "Ljava/lang/Object;"); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java index 52c4cd535..9f208464c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2020 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 @@ -20,6 +20,10 @@ public class VariableProxy { private final String scopeName; private Object variable; private int hashCode; + // The variable evaluate expression which can be passed to 'EvaluateRequest' to fetch this variable. + private final String evaluateName; + private boolean isIndexedVariable; + private boolean isUnboundedType = false; /** * Create a variable reference. @@ -29,12 +33,19 @@ public class VariableProxy { * the scope name * @param variable * the variable object + * @param container + * the variable container, if any + * @param evaluateName + * the variable evaluate expression which can be passed to 'EvaluateRequest' to + * fetch this variable, if any */ - public VariableProxy(ThreadReference thread, String scopeName, Object variable) { + public VariableProxy(ThreadReference thread, String scopeName, Object variable, VariableProxy container, String evaluateName) { this.thread = thread; this.scopeName = scopeName; this.variable = variable; - hashCode = Objects.hash(scopeName, thread, variable); + this.evaluateName = evaluateName; + + hashCode = Objects.hash(scopeName, thread, variable, evaluateName); } @Override @@ -64,7 +75,7 @@ public boolean equals(Object obj) { } VariableProxy other = (VariableProxy) obj; return Objects.equals(scopeName, other.scopeName) && Objects.equals(getThreadId(), other.getThreadId()) - && Objects.equals(variable, other.variable); + && Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName); } public long getThreadId() { @@ -78,4 +89,24 @@ public String getScope() { public Object getProxiedVariable() { return variable; } + + public String getEvaluateName() { + return evaluateName; + } + + public boolean isIndexedVariable() { + return isIndexedVariable; + } + + public void setIndexedVariable(boolean isIndexedVariable) { + this.isIndexedVariable = isIndexedVariable; + } + + public boolean isUnboundedType() { + return isUnboundedType; + } + + public void setUnboundedType(boolean isUnboundedType) { + this.isUnboundedType = isUnboundedType; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index ffdfc9932..578532bca 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -76,8 +77,10 @@ public static List listFieldVariables(ObjectReference obj, boolean inc Type type = obj.type(); if (type instanceof ArrayType) { int arrayIndex = 0; + boolean isUnboundedArrayType = Objects.equals(type.signature(), "[Ljava/lang/Object;"); for (Value elementValue : ((ArrayReference) obj).getValues()) { Variable ele = new Variable(String.valueOf(arrayIndex++), elementValue); + ele.setUnboundedType(isUnboundedArrayType); res.add(ele); } return res; @@ -126,8 +129,11 @@ public static List listFieldVariables(ObjectReference obj, int start, Type type = obj.type(); if (type instanceof ArrayType) { int arrayIndex = start; + boolean isUnboundedArrayType = Objects.equals(type.signature(), "[Ljava/lang/Object;"); for (Value elementValue : ((ArrayReference) obj).getValues(start, count)) { - res.add(new Variable(String.valueOf(arrayIndex++), elementValue)); + Variable variable = new Variable(String.valueOf(arrayIndex++), elementValue); + variable.setUnboundedType(isUnboundedArrayType); + res.add(variable); } return res; } @@ -256,6 +262,33 @@ public static void applyFormatterOptions(Map defaultOptions, boo } } + /** + * Get the name for evaluation of variable. + * + * @param name the variable name, if any + * @param containerName the container name, if any + * @param isArrayElement is the variable an array element? + */ + public static String getEvaluateName(String name, String containerName, boolean isArrayElement) { + if (name == null) { + return null; + } + + if (isArrayElement) { + if (containerName == null) { + return null; + } + + return String.format("%s[%s]", containerName, name); + } + + if (containerName == null) { + return name; + } + + return String.format("%s.%s", containerName, name); + } + private VariableUtils() { } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 0b6fbfa96..017814a30 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -346,5 +346,6 @@ public static class Capabilities { public boolean supportsExceptionInfoRequest; public ExceptionBreakpointFilter[] exceptionBreakpointFilters = new ExceptionBreakpointFilter[0]; public boolean supportsDataBreakpoints; + public boolean supportsClipboardContext; } } From 7b5c94596e66964c654485805179c5b2fef856f3 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 14 Jul 2020 19:17:29 +0800 Subject: [PATCH 046/206] Bump version to 0.27.0 (#341) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index a0a8a7c58..4726b149a 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.26.0 + 0.27.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 82715a2bd..d87d0f4cc 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 6919caf13..5e92c0bca 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.26.0 +Bundle-Version: 0.27.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.26.0.jar + lib/com.microsoft.java.debug.core-0.27.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index a72400647..517737f92 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.26.0 + 0.27.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.26.0 + 0.27.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 642a82600..2d1fc956c 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 539da5886..771057135 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.26.0 + 0.27.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 3ba974fef..532eac872 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.26.0 + 0.27.0 pom Java Debug Server for Visual Studio Code From c454a6f42ee8fd620c2dc3efbd05f35fd75f01a8 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 21 Jul 2020 16:08:12 +0800 Subject: [PATCH 047/206] Fix HCR not working issue (#342) Signed-off-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- .../debug/plugin/internal/JavaHotCodeReplaceProvider.java | 4 +--- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 12 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 4726b149a..40574f599 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.27.0 + 0.27.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index d87d0f4cc..d2b89cf31 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 5e92c0bca..b3b03e088 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.27.0 +Bundle-Version: 0.27.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.27.0.jar + lib/com.microsoft.java.debug.core-0.27.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 517737f92..fc28388d2 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.27.0 + 0.27.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.27.0 + 0.27.1 diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java index e1e5c2ec3..a80bf1555 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java @@ -308,14 +308,12 @@ public CompletableFuture> redefineClasses() { JobHelpers.waitForBuildJobs(10 * 1000); return CompletableFuture.supplyAsync(() -> { List classNames = new ArrayList<>(); - List resources = new ArrayList<>(); String errorMessage = null; synchronized (this) { classNames.addAll(deltaClassNames); - resources.addAll(deltaResources); + errorMessage = doHotCodeReplace(new ArrayList<>(deltaResources), new ArrayList<>(deltaClassNames)); deltaResources.clear(); deltaClassNames.clear(); - errorMessage = doHotCodeReplace(resources, classNames); } if (!classNames.isEmpty() && errorMessage != null) { diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 2d1fc956c..797d3d808 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 771057135..b79d3af53 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.27.0 + 0.27.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 532eac872..4456333bd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.27.0 + 0.27.1 pom Java Debug Server for Visual Studio Code From 94284b0dbfb989a8145ffb05c32495037dbbf36c Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 3 Aug 2020 13:28:13 +0800 Subject: [PATCH 048/206] Dedup the changed classes before hot reload (#345) * Dedup the changed classes before hot reload Signed-off-by: Jinbo Wang * Make checkstyle happy --- .../internal/JavaHotCodeReplaceProvider.java | 28 ++++++++++++++----- .../java/debug/plugin/internal/JdtUtils.java | 16 +++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java index a80bf1555..2bf905c89 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java @@ -26,10 +26,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.logging.Level; @@ -101,9 +100,9 @@ public class JavaHotCodeReplaceProvider implements IHotCodeReplaceProvider, IRes private PublishSubject eventSubject = PublishSubject.create(); - private Set deltaResources = new LinkedHashSet<>(); + private List deltaResources = new ArrayList<>(); - private Set deltaClassNames = new LinkedHashSet<>(); + private List deltaClassNames = new ArrayList<>(); /** * Visitor for resource deltas. @@ -290,8 +289,23 @@ public void resourceChanged(IResourceChangeEvent event) { List resources = visitor.getChangedClassFiles(); List classNames = visitor.getQualifiedNamesList(); synchronized (this) { - deltaResources.addAll(resources); - deltaClassNames.addAll(classNames); + for (int i = 0; i < classNames.size(); i++) { + String className = classNames.get(i); + IResource resource = resources.get(i); + boolean duplicate = false; + for (int j = 0; j < deltaClassNames.size(); j++) { + if (Objects.equals(deltaClassNames.get(j), className) + && JdtUtils.isSameFile(deltaResources.get(j), resource)) { + duplicate = true; + break; + } + } + + if (!duplicate) { + deltaClassNames.add(className); + deltaResources.add(resource); + } + } } publishEvent(HotCodeReplaceEvent.EventType.BUILD_COMPLETE, "Build completed."); } @@ -311,7 +325,7 @@ public CompletableFuture> redefineClasses() { String errorMessage = null; synchronized (this) { classNames.addAll(deltaClassNames); - errorMessage = doHotCodeReplace(new ArrayList<>(deltaResources), new ArrayList<>(deltaClassNames)); + errorMessage = doHotCodeReplace(deltaResources, deltaClassNames); deltaResources.clear(); deltaClassNames.clear(); } 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 9094a8073..9a918112b 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 @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.StringUtils; @@ -399,4 +400,19 @@ private static String getTypeName(String genericTypeSignature) { } return name.toString(); } + + /** + * Check whether two resources point to the same physical file. + */ + public static boolean isSameFile(IResource resource1, IResource resource2) { + if (resource1 == null || resource2 == null) { + return false; + } + + if (Objects.equals(resource1, resource2)) { + return true; + } + + return Objects.equals(resource1.getLocation(), resource2.getLocation()); + } } From 079a9c935b85e72e43d544957fcf3d1788ec8201 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 12 Aug 2020 10:03:44 +0800 Subject: [PATCH 049/206] Reduce JDWP frequency when resolving variables (#347) --- .../java/debug/core/DebugSettings.java | 3 +- .../handler/EvaluateRequestHandler.java | 18 +++-- .../handler/VariablesRequestHandler.java | 20 +++-- .../variables/JavaLogicalStructure.java | 34 ++++++--- .../core/adapter/variables/VariableUtils.java | 73 +++++++++++++------ 5 files changed, 99 insertions(+), 49 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index f3975fbfe..363f6742c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2019 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -41,6 +41,7 @@ public final class DebugSettings { public StepFilters stepFilters = new StepFilters(); public ClassFilters exceptionFilters = new ClassFilters(); public boolean exceptionFiltersUpdated = false; + public int limitOfVariablesPerJdwpRequest = 100; public static DebugSettings getCurrent() { return current; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 5da8c24f5..2a498608e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2020 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 @@ -32,6 +32,7 @@ import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.IEvaluationProvider; import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; +import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure; import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils; @@ -93,13 +94,14 @@ public CompletableFuture handle(Command command, Arguments arguments, Value sizeValue = null; if (value instanceof ArrayReference) { indexedVariables = ((ArrayReference) value).length(); - } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure - && engine != null - && JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) { + } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && engine != null) { try { - sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, stackFrameReference.getThread(), engine); - if (sizeValue != null && sizeValue instanceof IntegerValue) { - indexedVariables = ((IntegerValue) sizeValue).value(); + JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); + if (structure != null && structure.getSizeExpression() != null) { + sizeValue = structure.getSize((ObjectReference) value, stackFrameReference.getThread(), engine); + if (sizeValue != null && sizeValue instanceof IntegerValue) { + indexedVariables = ((IntegerValue) sizeValue).value(); + } } } catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) { @@ -108,7 +110,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } } int referenceId = 0; - if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) { + if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) { referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index b3a9f5a05..70d5dae31 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -232,13 +232,14 @@ public CompletableFuture handle(Command command, Arguments arguments, Value sizeValue = null; if (value instanceof ArrayReference) { indexedVariables = ((ArrayReference) value).length(); - } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure - && evaluationEngine != null - && JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) { + } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { try { - sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, containerNode.getThread(), evaluationEngine); - if (sizeValue != null && sizeValue instanceof IntegerValue) { - indexedVariables = ((IntegerValue) sizeValue).value(); + JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); + if (structure != null && structure.getSizeExpression() != null) { + sizeValue = structure.getSize((ObjectReference) value, containerNode.getThread(), evaluationEngine); + if (sizeValue != null && sizeValue instanceof IntegerValue) { + indexedVariables = ((IntegerValue) sizeValue).value(); + } } } catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) { logger.log(Level.INFO, @@ -267,7 +268,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } int referenceId = 0; - if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) { + if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) { VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName); referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); varProxy.setIndexedVariable(indexedVariables >= 0); @@ -290,6 +291,11 @@ public CompletableFuture handle(Command command, Arguments arguments, } list.add(typedVariables); } + + if (list.isEmpty() && containerNode.getProxiedVariable() instanceof ObjectReference) { + list.add(new Types.Variable("Class has no fields", "", null, 0, null)); + } + response.body = new Responses.VariablesResponseBody(list); return CompletableFuture.completedFuture(response); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java index 6bf65a9f8..0d065cf51 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java @@ -33,6 +33,8 @@ public class JavaLogicalStructure { private final LogicalStructureExpression valueExpression; private final LogicalStructureExpression sizeExpression; private final LogicalVariable[] variables; + // Indicates whether the specified type is an interface. + private final boolean isInterface; /** * Constructor. @@ -47,9 +49,15 @@ public JavaLogicalStructure(String type, LogicalStructureExpression valueExpress */ public JavaLogicalStructure(String type, String fullyQualifiedName, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression, LogicalVariable[] variables) { + this(type, type, true, valueExpression, sizeExpression, variables); + } + + public JavaLogicalStructure(String type, String fullyQualifiedName, boolean isInterface, LogicalStructureExpression valueExpression, + LogicalStructureExpression sizeExpression, LogicalVariable[] variables) { this.valueExpression = valueExpression; this.type = type; this.fullyQualifiedName = fullyQualifiedName; + this.isInterface = isInterface; this.sizeExpression = sizeExpression; this.variables = variables; } @@ -84,18 +92,24 @@ public boolean providesLogicalStructure(ObjectReference obj) { } ClassType classType = (ClassType) variableType; - while (classType != null) { - if (Objects.equals(type, classType.name())) { - return true; - } - - classType = classType.superclass(); + if (Objects.equals(type, classType.name())) { + return true; } - List interfaceTypes = ((ClassType) variableType).allInterfaces(); - for (InterfaceType interfaceType : interfaceTypes) { - if (Objects.equals(type, interfaceType.name())) { - return true; + if (isInterface) { + List interfaceTypes = ((ClassType) variableType).allInterfaces(); + for (InterfaceType interfaceType : interfaceTypes) { + if (Objects.equals(type, interfaceType.name())) { + return true; + } + } + } else { + while (classType != null) { + if (Objects.equals(type, classType.name())) { + return true; + } + + classType = classType.superclass(); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index 578532bca..d53e2705c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2019 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -55,12 +56,11 @@ public static boolean hasChildren(Value value, boolean includeStatic) { if (value == null || !(value instanceof ObjectReference)) { return false; } - Type type = value.type(); + ReferenceType type = ((ObjectReference) value).referenceType(); if (type instanceof ArrayType) { return ((ArrayReference) value).length() > 0; } - return value.type() instanceof ReferenceType && ((ReferenceType) type).allFields().stream() - .filter(t -> includeStatic || !t.isStatic()).toArray().length > 0; + return type.allFields().stream().anyMatch(t -> includeStatic || !t.isStatic()); } /** @@ -74,7 +74,7 @@ public static boolean hasChildren(Value value, boolean includeStatic) { */ public static List listFieldVariables(ObjectReference obj, boolean includeStatic) throws AbsentInformationException { List res = new ArrayList<>(); - Type type = obj.type(); + ReferenceType type = obj.referenceType(); if (type instanceof ArrayType) { int arrayIndex = 0; boolean isUnboundedArrayType = Objects.equals(type.signature(), "[Ljava/lang/Object;"); @@ -85,7 +85,7 @@ public static List listFieldVariables(ObjectReference obj, boolean inc } return res; } - List fields = obj.referenceType().allFields().stream().filter(t -> includeStatic || !t.isStatic()) + List fields = type.allFields().stream().filter(t -> includeStatic || !t.isStatic()) .sorted((a, b) -> { try { boolean v1isStatic = a.isStatic(); @@ -102,11 +102,16 @@ public static List listFieldVariables(ObjectReference obj, boolean inc return -1; } }).collect(Collectors.toList()); - fields.forEach(f -> { - Variable var = new Variable(f.name(), obj.getValue(f)); - var.field = f; - res.add(var); - }); + + bulkFetchValues(fields, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage -> { + Map fieldValues = obj.getValues(currentPage); + for (Field currentField : currentPage) { + Variable var = new Variable(currentField.name(), fieldValues.get(currentField)); + var.field = currentField; + res.add(var); + } + })); + return res; } @@ -155,13 +160,18 @@ public static List listLocalVariables(StackFrame stackFrame) throws Ab return res; } try { - List localVariables = stackFrame.visibleVariables(); - Map values = stackFrame.getValues(localVariables); - for (LocalVariable localVariable : localVariables) { - Variable var = new Variable(localVariable.name(), values.get(localVariable)); - var.local = localVariable; - res.add(var); - } + List visibleVariables = stackFrame.visibleVariables(); + // When using the API StackFrame.getValues() to batch fetch the variable values, the JDI + // probably throws timeout exception if the variables to be passed at one time are large. + // So use paging to fetch the values in chunks. + bulkFetchValues(visibleVariables, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage -> { + Map values = stackFrame.getValues(currentPage); + for (LocalVariable localVariable : currentPage) { + Variable var = new Variable(localVariable.name(), values.get(localVariable)); + var.local = localVariable; + res.add(var); + } + })); } catch (AbsentInformationException ex) { // avoid listing variable on native methods @@ -228,11 +238,16 @@ public static Variable getThisVariable(StackFrame stackFrame) { public static List listStaticVariables(StackFrame stackFrame) { List res = new ArrayList<>(); ReferenceType type = stackFrame.location().declaringType(); - type.allFields().stream().filter(TypeComponent::isStatic).forEach(field -> { - Variable staticVar = new Variable(field.name(), type.getValue(field)); - staticVar.field = field; - res.add(staticVar); - }); + List fields = type.allFields().stream().filter(TypeComponent::isStatic).collect(Collectors.toList()); + bulkFetchValues(fields, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage -> { + Map fieldValues = type.getValues(currentPage); + for (Field currentField : currentPage) { + Variable var = new Variable(currentField.name(), fieldValues.get(currentField)); + var.field = currentField; + res.add(var); + } + })); + return res; } @@ -289,6 +304,18 @@ public static String getEvaluateName(String name, String containerName, boolean return String.format("%s.%s", containerName, name); } + private static void bulkFetchValues(List elements, int numberPerPage, Consumer> consumer) { + int size = elements.size(); + numberPerPage = numberPerPage < 1 ? 1 : numberPerPage; + int page = size / numberPerPage + Math.min(size % numberPerPage, 1); + for (int i = 0; i < page; i++) { + int pageStart = i * numberPerPage; + int pageEnd = Math.min(pageStart + numberPerPage, size); + List currentPage = elements.subList(pageStart, pageEnd); + consumer.accept(currentPage); + } + } + private VariableUtils() { } From 13eb9c8b44ed9a24b4de5df35eae72539125c9e0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 17 Aug 2020 15:18:26 +0800 Subject: [PATCH 050/206] Allow to update the jdwp request timeout setting (#348) --- .../java/debug/core/DebugSettings.java | 1 + .../core/adapter/IVirtualMachineManager.java | 18 ++++++ .../adapter/handler/AttachRequestHandler.java | 5 +- .../ConfigurationDoneRequestHandler.java | 7 ++- .../handler/LaunchWithDebuggingDelegate.java | 5 ++ .../debug/core/adapter/handler/VMHandler.java | 54 ++++++++++++++++ .../AdvancedVirtualMachineManager.java | 62 +++++++++++++++++-- .../JdtVirtualMachineManagerProvider.java | 5 +- 8 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VMHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index 363f6742c..e3c928332 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -42,6 +42,7 @@ public final class DebugSettings { public ClassFilters exceptionFilters = new ClassFilters(); public boolean exceptionFiltersUpdated = false; public int limitOfVariablesPerJdwpRequest = 100; + public int jdwpRequestTimeout = 3000; public static DebugSettings getCurrent() { return current; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java new file mode 100644 index 000000000..095c070bf --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2020 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.core.adapter; + +public interface IVirtualMachineManager extends com.sun.jdi.VirtualMachineManager { + boolean connectVirtualMachine(com.sun.jdi.VirtualMachine vm); + + boolean disconnectVirtualMachine(com.sun.jdi.VirtualMachine vm); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index d9968c3f5..62b641b88 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2020 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 @@ -42,6 +42,7 @@ public class AttachRequestHandler implements IDebugRequestHandler { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private VMHandler vmHandler = new VMHandler(); @Override public List getTargetCommands() { @@ -57,12 +58,14 @@ public CompletableFuture handle(Command command, Arguments arguments, context.setStepFilters(attachArguments.stepFilters); IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); + vmHandler.setVmProvider(vmProvider); try { logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port)); IDebugSession debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, attachArguments.timeout); context.setDebugSession(debugSession); + vmHandler.connectVirtualMachine(debugSession.getVM()); logger.info("Attaching to debuggee VM succeeded."); // If the debugger and debuggee run at the different JVM platforms, show a warning message. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 5e77eff92..9cd3aa446 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2020 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 @@ -27,6 +27,7 @@ import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.adapter.IVirtualMachineManagerProvider; import com.microsoft.java.debug.core.protocol.Events; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; @@ -43,6 +44,7 @@ public class ConfigurationDoneRequestHandler implements IDebugRequestHandler { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private VMHandler vmHandler = new VMHandler(); @Override public List getTargetCommands() { @@ -52,6 +54,7 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); + vmHandler.setVmProvider(context.getProvider(IVirtualMachineManagerProvider.class)); if (debugSession != null) { // This is a global event handler to handle the JDI Event from Virtual Machine. debugSession.getEventHub().events().subscribe(debugEvent -> { @@ -76,9 +79,11 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, }); } } else if (event instanceof VMDeathEvent) { + vmHandler.disconnectVirtualMachine(event.virtualMachine()); context.setVmTerminated(); context.getProtocolServer().sendEvent(new Events.ExitedEvent(0)); } else if (event instanceof VMDisconnectEvent) { + vmHandler.disconnectVirtualMachine(event.virtualMachine()); if (context.isAttached()) { context.setVmTerminated(); context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index c4585dd1f..138455025 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -58,12 +58,14 @@ public class LaunchWithDebuggingDelegate implements ILaunchDelegate { private static final int ATTACH_TERMINAL_TIMEOUT = 20 * 1000; private static final String TERMINAL_TITLE = "Java Debug Console"; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; + private VMHandler vmHandler = new VMHandler(); @Override public CompletableFuture launchInTerminal(LaunchArguments launchArguments, Response response, IDebugAdapterContext context) { CompletableFuture resultFuture = new CompletableFuture<>(); IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); + vmHandler.setVmProvider(vmProvider); final String launchInTerminalErrorFormat = "Failed to launch debuggee in terminal. Reason: %s"; try { @@ -101,6 +103,7 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume if (runResponse.success) { try { VirtualMachine vm = listenConnector.accept(args); + vmHandler.connectVirtualMachine(vm); context.setDebugSession(new DebugSession(vm)); logger.info("Launching debuggee in terminal console succeeded."); resultFuture.complete(response); @@ -172,6 +175,7 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume public Process launch(LaunchArguments launchArguments, IDebugAdapterContext context) throws IOException, IllegalConnectorArgumentsException, VMStartException { IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); + vmHandler.setVmProvider(vmProvider); IDebugSession debugSession = DebugUtility.launch( vmProvider.getVirtualMachineManager(), @@ -184,6 +188,7 @@ public Process launch(LaunchArguments launchArguments, IDebugAdapterContext cont LaunchRequestHandler.constructEnvironmentVariables(launchArguments), launchArguments.javaExec); context.setDebugSession(debugSession); + vmHandler.connectVirtualMachine(debugSession.getVM()); logger.info("Launching debuggee VM succeeded."); return debugSession.process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VMHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VMHandler.java new file mode 100644 index 000000000..07c57d00b --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VMHandler.java @@ -0,0 +1,54 @@ +/******************************************************************************* +* Copyright (c) 2020 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.core.adapter.handler; + +import com.microsoft.java.debug.core.adapter.IVirtualMachineManager; +import com.microsoft.java.debug.core.adapter.IVirtualMachineManagerProvider; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; + +public class VMHandler { + private IVirtualMachineManagerProvider vmProvider = null; + + public VMHandler() { + } + + public VMHandler(IVirtualMachineManagerProvider vmProvider) { + this.vmProvider = vmProvider; + } + + public IVirtualMachineManagerProvider getVmProvider() { + return vmProvider; + } + + public void setVmProvider(IVirtualMachineManagerProvider vmProvider) { + this.vmProvider = vmProvider; + } + + public void connectVirtualMachine(VirtualMachine vm) { + if (vm != null && vmProvider != null) { + VirtualMachineManager vmManager = vmProvider.getVirtualMachineManager(); + if (vmManager instanceof IVirtualMachineManager) { + ((IVirtualMachineManager) vmManager).connectVirtualMachine(vm); + } + } + } + + public void disconnectVirtualMachine(VirtualMachine vm) { + if (vm != null && vmProvider != null) { + VirtualMachineManager vmManager = vmProvider.getVirtualMachineManager(); + if (vmManager instanceof IVirtualMachineManager) { + ((IVirtualMachineManager) vmManager).disconnectVirtualMachine(vm); + } + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedVirtualMachineManager.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedVirtualMachineManager.java index 57bd4633a..a77bdda62 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedVirtualMachineManager.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedVirtualMachineManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -12,14 +12,31 @@ package com.microsoft.java.debug.plugin.internal; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import org.eclipse.jdi.internal.VirtualMachineManagerImpl; - +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.DebugSettings.IDebugSettingChangeListener; +import com.microsoft.java.debug.core.adapter.IVirtualMachineManager; +import com.sun.jdi.VirtualMachine; import com.sun.jdi.VirtualMachineManager; import com.sun.jdi.connect.LaunchingConnector; -public class AdvancedVirtualMachineManager extends VirtualMachineManagerImpl implements VirtualMachineManager { +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jdi.internal.VirtualMachineManagerImpl; +import org.eclipse.jdt.debug.core.JDIDebugModel; +import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; + +public class AdvancedVirtualMachineManager extends VirtualMachineManagerImpl + implements VirtualMachineManager, IDebugSettingChangeListener, IVirtualMachineManager { + List connectedVMs = Collections.synchronizedList(new ArrayList()); + + public AdvancedVirtualMachineManager() { + super(); + update(DebugSettings.getCurrent(), DebugSettings.getCurrent()); + DebugSettings.addDebugSettingChangeListener(this); + } @Override public List launchingConnectors() { @@ -29,4 +46,41 @@ public List launchingConnectors() { return connectors; } + @Override + public void update(DebugSettings oldSettings, DebugSettings newSettings) { + int currentTimeout = getGlobalRequestTimeout(); + int newTimeout = newSettings.jdwpRequestTimeout; + if (newTimeout != currentTimeout) { + setRequestTimeout(newTimeout); + } + } + + private void setRequestTimeout(int timeout) { + IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(JDIDebugPlugin.getUniqueIdentifier()); + if (prefs != null) { + prefs.putInt(JDIDebugModel.PREF_REQUEST_TIMEOUT, timeout); + } + + if (!connectedVMs.isEmpty()) { + connectedVMs.forEach(vm -> { + if (vm instanceof org.eclipse.jdi.VirtualMachine) { + try { + ((org.eclipse.jdi.VirtualMachine) vm).setRequestTimeout(timeout); + } catch (Exception e) { + // do nothing. + } + } + }); + } + } + + @Override + public boolean connectVirtualMachine(VirtualMachine vm) { + return connectedVMs.add(vm); + } + + @Override + public boolean disconnectVirtualMachine(VirtualMachine vm) { + return connectedVMs.remove(vm); + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtVirtualMachineManagerProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtVirtualMachineManagerProvider.java index 513f49f6b..822251b5c 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtVirtualMachineManagerProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtVirtualMachineManagerProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -11,11 +11,12 @@ package com.microsoft.java.debug.plugin.internal; +import com.microsoft.java.debug.core.adapter.IVirtualMachineManager; import com.microsoft.java.debug.core.adapter.IVirtualMachineManagerProvider; import com.sun.jdi.VirtualMachineManager; public class JdtVirtualMachineManagerProvider implements IVirtualMachineManagerProvider { - private static VirtualMachineManager vmManager = null; + private static IVirtualMachineManager vmManager = null; @Override public synchronized VirtualMachineManager getVirtualMachineManager() { From 885ad7939a0492e9181e6dcda2a33732f9fb5601 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Aug 2020 22:41:41 +0800 Subject: [PATCH 051/206] bump version to 0.28.0 (#349) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 40574f599..35c01bca1 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.27.1 + 0.28.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index d2b89cf31..d021b8868 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index b3b03e088..063190e54 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.27.1 +Bundle-Version: 0.28.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.27.1.jar + lib/com.microsoft.java.debug.core-0.28.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index fc28388d2..eab4e6723 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.27.1 + 0.28.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.27.1 + 0.28.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 797d3d808..213aa359e 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index b79d3af53..6c7384e0e 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.27.1 + 0.28.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4456333bd..99bb72c3c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.27.1 + 0.28.0 pom Java Debug Server for Visual Studio Code From 39a946b9eda96076b6be66e6eff481553405951a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 11 Sep 2020 11:07:30 +0800 Subject: [PATCH 052/206] Allow cancel the resolveMainMethod command (#350) --- .../JavaDebugDelegateCommandHandler.java | 2 +- .../internal/ResolveMainMethodHandler.java | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 5db924442..d3d2bf835 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -70,7 +70,7 @@ public Object executeCommand(String commandId, List arguments, IProgress case VALIDATE_LAUNCHCONFIG: return new ResolveMainClassHandler().validateLaunchConfig(arguments); case RESOLVE_MAINMETHOD: - return ResolveMainMethodHandler.resolveMainMethods(arguments); + return ResolveMainMethodHandler.resolveMainMethods(arguments, progress); case INFER_LAUNCH_COMMAND_LENGTH: return LaunchCommandHandler.getLaunchCommandLength(JsonUtils.fromJson((String) arguments.get(0), LaunchArguments.class)); case CHECK_PROJECT_SETTINGS: diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index aaef587c3..85ff58978 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Microsoft Corporation and others. + * Copyright (c) 2018-2020 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 @@ -18,7 +18,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.Flags; @@ -42,8 +42,8 @@ public class ResolveMainMethodHandler { * Resolve the main methods from the current file. * @return an array of main methods. */ - public static Object resolveMainMethods(List arguments) throws DebugException { - if (arguments == null || arguments.isEmpty()) { + public static Object resolveMainMethods(List arguments, IProgressMonitor monitor) throws DebugException { + if (monitor.isCanceled() || arguments == null || arguments.isEmpty()) { return Collections.emptyList(); } @@ -51,16 +51,20 @@ public static Object resolveMainMethods(List arguments) throws DebugExce // trigger a background job to update the change to the CompilationUnit. Because of race condition, the resolveMainMethods may read // an old CompilationUnit. So add some waiting logic to wait the Document Update to finish first. try { - Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, new NullProgressMonitor()); - } catch (OperationCanceledException ignorable) { - // Do nothing. + Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor); + } catch (OperationCanceledException e) { + return Collections.emptyList(); } catch (InterruptedException e) { // Do nothing. } + if (monitor.isCanceled()) { + return Collections.emptyList(); + } + String uri = (String) arguments.get(0); final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uri); - if (unit == null || unit.getResource() == null || !unit.getResource().exists()) { + if (monitor.isCanceled() || unit == null || unit.getResource() == null || !unit.getResource().exists()) { return Collections.emptyList(); } From e51499091d619a44bfecb4703d5aedcf32b29a0c Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 22 Sep 2020 15:56:23 +0800 Subject: [PATCH 053/206] Fix Java exec exception 'UNC path is missing sharename' (#352) --- .../debug/plugin/internal/ResolveJavaExecutableHandler.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/ResolveJavaExecutableHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java index cd368a6d2..46a9fe428 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java @@ -90,7 +90,9 @@ private static File findJavaExecutable(File vmInstallLocation) { if (!isBin && j == 0) { continue; } - File javaFile = new File(vmInstallLocation, Paths.get(javaBinCandidates[j], javaExecCandidates[i]).toString()); + + String execRelativePath = j == 0 ? javaExecCandidates[i] : Paths.get(javaBinCandidates[j], javaExecCandidates[i]).toString(); + File javaFile = new File(vmInstallLocation, execRelativePath); if (javaFile.isFile()) { return javaFile; } From 24a726070ad51e63a08e5fb8cd6a8f39da3ececd Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 24 Sep 2020 17:26:29 +0800 Subject: [PATCH 054/206] Use the correct runtime to validate the JVM versions between the debugger and debuggee (#353) * Use the correct runtime to validate the JVM versions between the debugger and debuggee * Resolve java version from the system library jar --- .../core/adapter/ISourceLookUpProvider.java | 10 +++ .../adapter/handler/AttachRequestHandler.java | 34 ++++++----- .../internal/JdtSourceLookUpProvider.java | 61 ++++++++++++++++--- .../ResolveJavaExecutableHandler.java | 41 +++++++------ 4 files changed, 104 insertions(+), 42 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 233d539e3..dd3e4dbde 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 @@ -30,4 +30,14 @@ public interface ISourceLookUpProvider extends IProvider { String getSourceFileURI(String fullyQualifiedName, String sourcePath); String getSourceContents(String uri); + + /** + * Returns the Java runtime that the specified project's build path used. + * @param projectName + * the specified project name + * @return the Java runtime version the specified project used. null if projectName is empty or doesn't exist. + */ + default String getJavaRuntimeVersion(String projectName) { + return null; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index 62b641b88..7069bc39e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -40,6 +40,8 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.sun.jdi.connect.IllegalConnectorArgumentsException; +import org.apache.commons.lang3.StringUtils; + public class AttachRequestHandler implements IDebugRequestHandler { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private VMHandler vmHandler = new VMHandler(); @@ -59,28 +61,14 @@ public CompletableFuture handle(Command command, Arguments arguments, IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); - + IDebugSession debugSession = null; try { logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port)); - IDebugSession debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, + debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, attachArguments.timeout); context.setDebugSession(debugSession); vmHandler.connectVirtualMachine(debugSession.getVM()); logger.info("Attaching to debuggee VM succeeded."); - - // If the debugger and debuggee run at the different JVM platforms, show a warning message. - if (debugSession != null) { - String debuggeeVersion = debugSession.getVM().version(); - String debuggerVersion = System.getProperty("java.version"); - if (!debuggerVersion.equals(debuggeeVersion)) { - String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. " - + "You could see wrong source mapping results.\n" - + "Debugger JVM version: %s\n" - + "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion); - logger.warning(warnMessage); - context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); - } - } } catch (IOException | IllegalConnectorArgumentsException e) { throw AdapterUtils.createCompletionException( String.format("Failed to attach to remote debuggee VM. Reason: %s", e.toString()), @@ -96,6 +84,20 @@ public CompletableFuture handle(Command command, Arguments arguments, // TODO: Clean up the initialize mechanism ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); sourceProvider.initialize(context, options); + // If the debugger and debuggee run at the different JVM platforms, show a warning message. + if (debugSession != null) { + String debuggeeVersion = debugSession.getVM().version(); + String debuggerVersion = sourceProvider.getJavaRuntimeVersion(attachArguments.projectName); + if (StringUtils.isNotBlank(debuggerVersion) && !debuggerVersion.equals(debuggeeVersion)) { + String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. " + + "You could see wrong source mapping results.\n" + + "Debugger JVM version: %s\n" + + "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion); + logger.warning(warnMessage); + context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); + } + } + IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); evaluationProvider.initialize(context, options); IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class); 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 99b4b330c..4fbe32b17 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2020 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 @@ -22,28 +22,37 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.Constants; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; + import org.eclipse.core.resources.IResource; +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.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.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator; - -import com.microsoft.java.debug.core.Configuration; -import com.microsoft.java.debug.core.DebugException; -import com.microsoft.java.debug.core.adapter.AdapterUtils; -import com.microsoft.java.debug.core.adapter.Constants; -import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; -import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.LibraryLocation; public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); @@ -181,6 +190,25 @@ public String getSourceFileURI(String fullyQualifiedName, String sourcePath) { return null; } + @Override + public String getJavaRuntimeVersion(String projectName) { + IJavaProject project = JdtUtils.getJavaProject(projectName); + if (project != null) { + try { + IVMInstall vmInstall = JavaRuntime.getVMInstall(project); + if (vmInstall == null || vmInstall.getInstallLocation() == null) { + return null; + } + + return resolveSystemLibraryVersion(project, vmInstall); + } catch (CoreException e) { + logger.log(Level.SEVERE, "Failed to get Java runtime version for project '" + projectName + "': " + e.getMessage(), e); + } + } + + return null; + } + /** * Get the project associated source containers. * @return the initialized source container list @@ -280,4 +308,21 @@ private static String readFile(String filePath, Charset cs) { return builder.toString(); } + private static String resolveSystemLibraryVersion(IJavaProject project, IVMInstall vmInstall) throws JavaModelException { + LibraryLocation[] libraries = JavaRuntime.getLibraryLocations(vmInstall); + if (libraries != null && libraries.length > 0) { + IPackageFragmentRoot root = project.findPackageFragmentRoot(libraries[0].getSystemLibraryPath()); + if (!(root instanceof JarPackageFragmentRoot)) { + return null; + } + Manifest manifest = ((JarPackageFragmentRoot) root).getManifest(); + if (manifest == null) { + return null; + } + Attributes attributes = manifest.getMainAttributes(); + return attributes.getValue("Implementation-Version"); + } + + return null; + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java index 46a9fe428..362532e4f 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveJavaExecutableHandler.java @@ -12,7 +12,6 @@ package com.microsoft.java.debug.plugin.internal; import java.io.File; -import java.nio.file.Paths; import java.util.List; import java.util.Objects; import java.util.logging.Level; @@ -61,21 +60,7 @@ public static String resolveJavaExecutable(List arguments) throws Except } } - if (targetProject == null) { - return null; - } - - IVMInstall vmInstall = JavaRuntime.getVMInstall(targetProject); - if (vmInstall == null || vmInstall.getInstallLocation() == null) { - return null; - } - - File exe = findJavaExecutable(vmInstall.getInstallLocation()); - if (exe == null) { - return null; - } - - return exe.getAbsolutePath(); + return resolveJavaExecutable(targetProject); } catch (CoreException e) { logger.log(Level.SEVERE, "Failed to resolve java executable: " + e.getMessage(), e); } @@ -83,6 +68,27 @@ public static String resolveJavaExecutable(List arguments) throws Except return null; } + /** + * Resolve the Java executable path from the project's Java runtime. + */ + public static String resolveJavaExecutable(IJavaProject javaProject) throws CoreException { + if (javaProject == null) { + return null; + } + + IVMInstall vmInstall = JavaRuntime.getVMInstall(javaProject); + if (vmInstall == null || vmInstall.getInstallLocation() == null) { + return null; + } + + File exe = findJavaExecutable(vmInstall.getInstallLocation()); + if (exe == null) { + return null; + } + + return exe.getAbsolutePath(); + } + private static File findJavaExecutable(File vmInstallLocation) { boolean isBin = Objects.equals("bin", vmInstallLocation.getName()); for (int i = 0; i < javaExecCandidates.length; i++) { @@ -91,8 +97,7 @@ private static File findJavaExecutable(File vmInstallLocation) { continue; } - String execRelativePath = j == 0 ? javaExecCandidates[i] : Paths.get(javaBinCandidates[j], javaExecCandidates[i]).toString(); - File javaFile = new File(vmInstallLocation, execRelativePath); + File javaFile = new File(vmInstallLocation, javaBinCandidates[j] + javaExecCandidates[i]); if (javaFile.isFile()) { return javaFile; } From 211c47aabec6d47d8393ec39b6fdf0cbfcd8dbb0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 12 Oct 2020 13:51:44 +0800 Subject: [PATCH 055/206] Expose a command to resolve the source mapping for the specified stacktrace (#354) --- .../adapter/handler/LaunchRequestHandler.java | 2 +- com.microsoft.java.debug.plugin/plugin.xml | 1 + .../JavaDebugDelegateCommandHandler.java | 3 ++ .../internal/JdtSourceLookUpProvider.java | 9 +--- .../internal/ResolveSourceMappingHandler.java | 53 +++++++++++++++++++ 5 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveSourceMappingHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 506bce6be..5830e7691 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -258,7 +258,7 @@ protected CompletableFuture launch(LaunchArguments launchArguments, Re return resultFuture; } - private static final Pattern STACKTRACE_PATTERN = Pattern.compile("\\s+at\\s+([\\w$\\.]+\\/)?(([\\w$]+\\.)*[\\w$]+)\\(([\\w-$]+\\.java:\\d+)\\)"); + private static final Pattern STACKTRACE_PATTERN = Pattern.compile("\\s+at\\s+([\\w$\\.]+\\/)?(([\\w$]+\\.)+[<\\w$>]+)\\(([\\w-$]+\\.java:\\d+)\\)"); private static OutputEvent convertToOutputEvent(String message, Category category, IDebugAdapterContext context) { Matcher matcher = STACKTRACE_PATTERN.matcher(message); diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index cd999ab09..361cdcbda 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -19,6 +19,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index d3d2bf835..032d0420c 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -46,6 +46,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String RESOLVE_JAVA_EXECUTABLE = "vscode.java.resolveJavaExecutable"; public static final String FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; public static final String RESOLVE_CLASSFILTERS = "vscode.java.resolveClassFilters"; + public static final String RESOLVE_SOURCEURI = "vscode.java.resolveSourceUri"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -87,6 +88,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return PlatformSettings.getPlatformSettings(); case RESOLVE_CLASSFILTERS: return JavaClassFilter.resolveClassFilters(arguments); + case RESOLVE_SOURCEURI: + return ResolveSourceMappingHandler.resolveSourceUri(arguments); default: break; } 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 4fbe32b17..65ba6074a 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 @@ -178,14 +178,7 @@ public String getSourceFileURI(String fullyQualifiedName, String sourcePath) { 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 getFileURI((IClassFile) sourceElement); } return null; } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveSourceMappingHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveSourceMappingHandler.java new file mode 100644 index 000000000..456474141 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveSourceMappingHandler.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2020 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 java.io.File; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; + +public class ResolveSourceMappingHandler { + private static final Pattern SOURCE_PATTERN = Pattern.compile("([\\w$\\.]+\\/)?(([\\w$]+\\.)+[<\\w$>]+)\\(([\\w-$]+\\.java:\\d+)\\)"); + private static final JdtSourceLookUpProvider sourceProvider = new JdtSourceLookUpProvider(); + + public static String resolveSourceUri(List arguments) { + if (arguments == null || arguments.isEmpty()) { + return null; + } + + return resolveSourceUri((String) arguments.get(0)); + } + + public static String resolveSourceUri(String lineText) { + if (lineText == null) { + return null; + } + + Matcher matcher = SOURCE_PATTERN.matcher(lineText); + if (matcher.find()) { + String methodField = matcher.group(2); + String locationField = matcher.group(matcher.groupCount()); + String fullyQualifiedName = methodField.substring(0, methodField.lastIndexOf(".")); + String packageName = fullyQualifiedName.lastIndexOf(".") > -1 ? fullyQualifiedName.substring(0, fullyQualifiedName.lastIndexOf(".")) : ""; + String[] locations = locationField.split(":"); + String sourceName = locations[0]; + String sourcePath = StringUtils.isBlank(packageName) ? sourceName + : packageName.replace('.', File.separatorChar) + File.separatorChar + sourceName; + return sourceProvider.getSourceFileURI(fullyQualifiedName, sourcePath); + } + + return null; + } +} From 66959926ecbddeaa0a3c7dd25b8c4907e13c9c62 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 14 Oct 2020 10:52:07 +0800 Subject: [PATCH 056/206] Fix the wrong completion list in debug console (#356) --- .project | 11 +++++++++++ com.microsoft.java.debug.core/.project | 11 +++++++++++ com.microsoft.java.debug.plugin/.project | 11 +++++++++++ .../debug/plugin/internal/CompletionsProvider.java | 14 +++++++++++++- com.microsoft.java.debug.repository/.project | 11 +++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/.project b/.project index f659e76ef..d34865de3 100644 --- a/.project +++ b/.project @@ -14,4 +14,15 @@ org.eclipse.m2e.core.maven2Nature + + + 1600224298170 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/com.microsoft.java.debug.core/.project b/com.microsoft.java.debug.core/.project index 773c5b793..a7480133e 100644 --- a/com.microsoft.java.debug.core/.project +++ b/com.microsoft.java.debug.core/.project @@ -26,4 +26,15 @@ net.sf.eclipsecs.core.CheckstyleNature org.eclipse.m2e.core.maven2Nature + + + 1599036548523 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/com.microsoft.java.debug.plugin/.project b/com.microsoft.java.debug.plugin/.project index b7a152d24..72ba17ff7 100644 --- a/com.microsoft.java.debug.plugin/.project +++ b/com.microsoft.java.debug.plugin/.project @@ -37,4 +37,15 @@ org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature + + + 1599036548577 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java index bf5131344..3b9680017 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java @@ -69,7 +69,8 @@ public List codeComplete(StackFrame frame, String snippet, int l collector.setAllowsRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF, true); - type.codeComplete(snippet.toCharArray(), offset, snippet.length(), null, null, null, frame.location().method().isStatic(), collector); + int position = getInsertPosition(snippet, line, column); + type.codeComplete(snippet.toCharArray(), offset, position, null, null, null, frame.location().method().isStatic(), collector); List items = collector.getCompletionItems(); @@ -87,6 +88,17 @@ public List codeComplete(StackFrame frame, String snippet, int l return res; } + private int getInsertPosition(String snippet, int line, int column) { + int lineInSnippet = context.isClientLinesStartAt1() ? line - 1 : line; + int offsetInSnippet = -1; + for (int i = 0; i < lineInSnippet; i++) { + offsetInSnippet = snippet.indexOf('\n', offsetInSnippet + 1); + } + + offsetInSnippet++; + return offsetInSnippet + column + (context.isClientColumnsStartAt1() ? -1 : 0); + } + private IType resolveType(StackFrame frame) throws CoreException, DebugException { ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); if (sourceProvider instanceof JdtSourceLookUpProvider) { diff --git a/com.microsoft.java.debug.repository/.project b/com.microsoft.java.debug.repository/.project index 42d872c95..887e4a7a8 100644 --- a/com.microsoft.java.debug.repository/.project +++ b/com.microsoft.java.debug.repository/.project @@ -14,4 +14,15 @@ org.eclipse.m2e.core.maven2Nature + + + 1600224298119 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + From ef441d272b77a33cd708235be62ac4bde2f442a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2020 11:00:10 +0800 Subject: [PATCH 057/206] Bump junit from 4.12 to 4.13.1 in /com.microsoft.java.debug.core (#355) Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jinbo Wang --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 35c01bca1..026eefe77 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -68,7 +68,7 @@ junit junit - 4.12 + 4.13.1 test From 631fb2ab02ecc3fc2282ed675559366743fa68b9 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 14 Oct 2020 21:50:28 +0800 Subject: [PATCH 058/206] Bump version to 0.29.0 (#357) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 026eefe77..24ec1b741 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.28.0 + 0.29.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index d021b8868..3291a02bb 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 063190e54..905946192 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.28.0 +Bundle-Version: 0.29.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.28.0.jar + lib/com.microsoft.java.debug.core-0.29.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index eab4e6723..28901c18d 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.28.0 + 0.29.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.28.0 + 0.29.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 213aa359e..e873f4578 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 6c7384e0e..ce56bf3a5 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.28.0 + 0.29.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 99bb72c3c..994191f7b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.28.0 + 0.29.0 pom Java Debug Server for Visual Studio Code From 59e6079157b269c266a11b9087af6eb2c441c9b6 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 21 Jan 2021 19:38:39 +0800 Subject: [PATCH 059/206] Escape the whitespace if the argfiles path contains space (#358) --- .../java/debug/core/adapter/handler/LaunchRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 5830e7691..54c4127c8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -130,7 +130,7 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { Path tempfile = AdapterUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); - launchArguments.vmArgs += " @" + tempfile.toAbsolutePath().toString(); + launchArguments.vmArgs += " \"@" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; launchArguments.modulePaths = new String[0]; context.setArgsfile(tempfile); From ca7d197c47bc844bf74e4bfb7258344be0e0030c Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 22 Jan 2021 16:46:44 +0800 Subject: [PATCH 060/206] Sort completion by relevance & respect completion filters registered by JLS (#359) * Sort completion by relevance & respect completion filters registered by JLS * Make checkstyle happy --- .../java/debug/core/protocol/Types.java | 4 + .../internal/CompletionProposalRequestor.java | 85 ++++++++++++++++++- .../plugin/internal/CompletionsProvider.java | 5 ++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 017814a30..3ce41869c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -278,6 +278,10 @@ public static class CompletionItem { public String label; public String text; public String type; + /** + * A string that should be used when comparing this item with other items. + */ + public String sortText; public int start; public int number; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index 2b32ae3f6..be7932992 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -22,6 +22,7 @@ import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.CompletionContext; @@ -34,6 +35,7 @@ import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.contentassist.CompletionProposalDescriptionProvider; import org.eclipse.jdt.ls.core.internal.contentassist.GetterSetterCompletionProposal; @@ -109,6 +111,10 @@ private boolean isTestSource(IJavaProject project, ITypeRoot cu) { @Override public void accept(CompletionProposal proposal) { + if (isFiltered(proposal)) { + return; + } + if (!isIgnored(proposal.getKind())) { if (proposal.getKind() == CompletionProposal.POTENTIAL_METHOD_DECLARATION) { acceptPotentialMethodDeclaration(proposal); @@ -265,4 +271,81 @@ public CompletionContext getContext() { return context; } -} \ No newline at end of file + /** + * copied from + * org.eclipse.jdt.ui.text.java.CompletionProposalCollector.isFiltered(CompletionProposal) + */ + private boolean isFiltered(CompletionProposal proposal) { + if (isIgnored(proposal.getKind())) { + return true; + } + + try { + // Only filter types and constructors from completion. + // Methods from already imported types and packages can still be proposed. + // See https://github.com/eclipse/eclipse.jdt.ls/issues/1212 + switch (proposal.getKind()) { + case CompletionProposal.CONSTRUCTOR_INVOCATION: + case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION: + case CompletionProposal.JAVADOC_TYPE_REF: + case CompletionProposal.TYPE_REF: { + char[] declaringType = getDeclaringType(proposal); + return declaringType != null && org.eclipse.jdt.ls.core.internal.contentassist.TypeFilter.isFiltered(declaringType); + } + default: // do nothing + } + } catch (Exception e) { + // do nothing + } + + return false; + } + + /** + * copied from + * org.eclipse.jdt.ui.text.java.CompletionProposalCollector.getDeclaringType(CompletionProposal) + */ + private final char[] getDeclaringType(CompletionProposal proposal) { + switch (proposal.getKind()) { + case CompletionProposal.METHOD_DECLARATION: + case CompletionProposal.METHOD_NAME_REFERENCE: + case CompletionProposal.JAVADOC_METHOD_REF: + case CompletionProposal.METHOD_REF: + case CompletionProposal.CONSTRUCTOR_INVOCATION: + case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION: + case CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER: + case CompletionProposal.ANNOTATION_ATTRIBUTE_REF: + case CompletionProposal.POTENTIAL_METHOD_DECLARATION: + case CompletionProposal.ANONYMOUS_CLASS_DECLARATION: + case CompletionProposal.FIELD_REF: + case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER: + case CompletionProposal.JAVADOC_FIELD_REF: + case CompletionProposal.JAVADOC_VALUE_REF: + char[] declaration = proposal.getDeclarationSignature(); + // special methods may not have a declaring type: methods defined on arrays etc. + // Currently known: class literals don't have a declaring type - use Object + if (declaration == null) { + return "java.lang.Object".toCharArray(); //$NON-NLS-1$ + } + return Signature.toCharArray(declaration); + case CompletionProposal.PACKAGE_REF: + case CompletionProposal.MODULE_REF: + case CompletionProposal.MODULE_DECLARATION: + return proposal.getDeclarationSignature(); + case CompletionProposal.JAVADOC_TYPE_REF: + case CompletionProposal.TYPE_REF: + return Signature.toCharArray(proposal.getSignature()); + case CompletionProposal.LOCAL_VARIABLE_REF: + case CompletionProposal.VARIABLE_DECLARATION: + case CompletionProposal.KEYWORD: + case CompletionProposal.LABEL_REF: + case CompletionProposal.JAVADOC_BLOCK_TAG: + case CompletionProposal.JAVADOC_INLINE_TAG: + case CompletionProposal.JAVADOC_PARAM_REF: + return null; + default: + Assert.isTrue(false); + return null; + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java index 3b9680017..19b808161 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java @@ -115,6 +115,11 @@ private CompletionItem convertFromLsp(org.eclipse.lsp4j.CompletionItem lspItem) if (lspItem.getKind() != null) { item.type = lspItem.getKind().name().toLowerCase(); } + + if (lspItem.getSortText() != null) { + item.sortText = lspItem.getSortText(); + } + return item; } } From 7e8df44cfbde3b8ca64a6cccbcc00ce0e7e3261b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 28 Jan 2021 10:52:38 +0800 Subject: [PATCH 061/206] Avoid using the same temporary launch file when triggering multiple debug sessions in parallel (#360) * Avoid using the same temporary launch file when triggering multiple debug sessions in parallel * Fix checkstyle --- .../java/debug/core/adapter/AdapterUtils.java | 111 +----------- .../adapter/handler/LaunchRequestHandler.java | 8 +- .../core/adapter/handler/LaunchUtils.java | 166 ++++++++++++++++++ 3 files changed, 172 insertions(+), 113 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 132d702d9..74d3fb292 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2021 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 @@ -12,10 +12,7 @@ package com.microsoft.java.debug.core.adapter; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -28,17 +25,11 @@ import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.jar.Attributes; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.DebugException; @@ -46,8 +37,6 @@ import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; -import sun.security.action.GetPropertyAction; - public class AdapterUtils { private static final String OS_NAME = System.getProperty("os.name", "").toLowerCase(); private static final Pattern ENCLOSING_CLASS_REGEX = Pattern.compile("^([^\\$]*)"); @@ -301,102 +290,4 @@ public static String decodeURIComponent(String uri) { return uri; } } - - /** - * Generate the classpath parameters to a temporary classpath.jar. - * @param classPaths - the classpath parameters - * @return the file path of the generate classpath.jar - * @throws IOException Some errors occur during generating the classpath.jar - */ - public static Path generateClasspathJar(String[] classPaths) throws IOException { - List classpathUrls = new ArrayList<>(); - for (String classpath : classPaths) { - classpathUrls.add(AdapterUtils.toUrl(classpath)); - } - - Manifest manifest = new Manifest(); - Attributes attributes = manifest.getMainAttributes(); - attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); - // In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar - String classpathValue = String.join(" ", classpathUrls); - attributes.put(Attributes.Name.CLASS_PATH, classpathValue); - Path tempfile = createTempFile("cp_" + getMd5(classpathValue), ".jar"); - JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest); - jar.close(); - - return tempfile; - } - - /** - * Generate the classpath parameters to a temporary argfile file. - * @param classPaths - the classpath parameters - * @param modulePaths - the modulepath parameters - * @return the file path of the generated argfile - * @throws IOException Some errors occur during generating the argfile - */ - public static Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { - String argfile = ""; - if (ArrayUtils.isNotEmpty(classPaths)) { - argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\""; - } - - if (ArrayUtils.isNotEmpty(modulePaths)) { - argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; - } - - argfile = argfile.replace("\\", "\\\\"); - Path tempfile = createTempFile("cp_" + getMd5(argfile), ".argfile"); - Files.write(tempfile, argfile.getBytes()); - - return tempfile; - } - - private static Path tmpdir = null; - - private static synchronized Path getTmpDir() throws IOException { - if (tmpdir == null) { - try { - tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); - } catch (NullPointerException | InvalidPathException e) { - Path tmpfile = Files.createTempFile("", ".tmp"); - tmpdir = tmpfile.getParent(); - try { - Files.deleteIfExists(tmpfile); - } catch (Exception ex) { - // do nothing - } - } - } - - return tmpdir; - } - - private static Path createTempFile(String baseName, String suffix) throws IOException { - // loop until the temp file can be created - SecurityManager sm = System.getSecurityManager(); - for (int i = 0; ; i++) { - Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); - try { - // delete the old temp file - Files.deleteIfExists(tempFile); - } catch (Exception e) { - // do nothing - } - - if (!Files.exists(tempFile)) { - return Files.createFile(tempFile); - } - } - } - - private static String getMd5(String input) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] messageDigest = md.digest(input.getBytes()); - BigInteger md5 = new BigInteger(1, messageDigest); - return md5.toString(Character.MAX_RADIX); - } catch (NoSuchAlgorithmException e) { - return Integer.toString(input.hashCode(), Character.MAX_RADIX); - } - } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 54c4127c8..7d76e1ffd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2021 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 @@ -115,7 +115,7 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R if (launchArguments.shortenCommandLine == ShortenApproach.JARMANIFEST) { if (ArrayUtils.isNotEmpty(launchArguments.classPaths)) { try { - Path tempfile = AdapterUtils.generateClasspathJar(launchArguments.classPaths); + Path tempfile = LaunchUtils.generateClasspathJar(launchArguments.classPaths); launchArguments.vmArgs += " -cp \"" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; context.setClasspathJar(tempfile); @@ -129,7 +129,7 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { - Path tempfile = AdapterUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); + Path tempfile = LaunchUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); launchArguments.vmArgs += " \"@" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; launchArguments.modulePaths = new String[0]; @@ -140,6 +140,8 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } return launch(launchArguments, response, context).thenCompose(res -> { + LaunchUtils.releaseTempLaunchFile(context.getClasspathJar()); + LaunchUtils.releaseTempLaunchFile(context.getArgsfile()); if (res.success) { activeLaunchHandler.postLaunch(launchArguments, context); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java new file mode 100644 index 000000000..be7f95126 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -0,0 +1,166 @@ +/******************************************************************************* +* Copyright (c) 2021 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.core.adapter.handler; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import com.microsoft.java.debug.core.adapter.AdapterUtils; + +import org.apache.commons.lang3.ArrayUtils; + +import sun.security.action.GetPropertyAction; + +public class LaunchUtils { + private static Set tempFilesInUse = new HashSet<>(); + + /** + * Generate the classpath parameters to a temporary classpath.jar. + * @param classPaths - the classpath parameters + * @return the file path of the generate classpath.jar + * @throws IOException Some errors occur during generating the classpath.jar + */ + public static synchronized Path generateClasspathJar(String[] classPaths) throws IOException { + List classpathUrls = new ArrayList<>(); + for (String classpath : classPaths) { + classpathUrls.add(AdapterUtils.toUrl(classpath)); + } + + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + // In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar + String classpathValue = String.join(" ", classpathUrls); + attributes.put(Attributes.Name.CLASS_PATH, classpathValue); + String baseName = "cp_" + getMd5(classpathValue); + cleanupTempFiles(baseName, ".jar"); + Path tempfile = createTempFile(baseName, ".jar"); + JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest); + jar.close(); + lockTempLaunchFile(tempfile); + + return tempfile; + } + + /** + * Generate the classpath parameters to a temporary argfile file. + * @param classPaths - the classpath parameters + * @param modulePaths - the modulepath parameters + * @return the file path of the generated argfile + * @throws IOException Some errors occur during generating the argfile + */ + public static synchronized Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { + String argfile = ""; + if (ArrayUtils.isNotEmpty(classPaths)) { + argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\""; + } + + if (ArrayUtils.isNotEmpty(modulePaths)) { + argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; + } + + argfile = argfile.replace("\\", "\\\\"); + String baseName = "cp_" + getMd5(argfile); + cleanupTempFiles(baseName, ".argfile"); + Path tempfile = createTempFile(baseName, ".argfile"); + Files.write(tempfile, argfile.getBytes()); + lockTempLaunchFile(tempfile); + + return tempfile; + } + + public static void lockTempLaunchFile(Path tempFile) { + if (tempFile != null) { + tempFilesInUse.add(tempFile); + } + } + + public static void releaseTempLaunchFile(Path tempFile) { + if (tempFile != null) { + tempFilesInUse.remove(tempFile); + } + } + + private static Path tmpdir = null; + + private static synchronized Path getTmpDir() throws IOException { + if (tmpdir == null) { + try { + tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + } catch (NullPointerException | InvalidPathException e) { + Path tmpfile = Files.createTempFile("", ".tmp"); + tmpdir = tmpfile.getParent(); + try { + Files.deleteIfExists(tmpfile); + } catch (Exception ex) { + // do nothing + } + } + } + + return tmpdir; + } + + private static void cleanupTempFiles(String baseName, String suffix) throws IOException { + for (int i = 0; ; i++) { + Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); + if (tempFilesInUse.contains(tempFile)) { + continue; + } else if (!Files.exists(tempFile)) { + break; + } else { + try { + // delete the old temp file + Files.deleteIfExists(tempFile); + } catch (Exception e) { + // do nothing + } + } + } + } + + private static Path createTempFile(String baseName, String suffix) throws IOException { + // loop until the temp file can be created + for (int i = 0; ; i++) { + Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); + if (!Files.exists(tempFile)) { + return Files.createFile(tempFile); + } + } + } + + private static String getMd5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + BigInteger md5 = new BigInteger(1, messageDigest); + return md5.toString(Character.MAX_RADIX); + } catch (NoSuchAlgorithmException e) { + return Integer.toString(input.hashCode(), Character.MAX_RADIX); + } + } +} From 66feca936e8dd0bfbb7f6af3c3f4c4ce9833a95e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 1 Feb 2021 13:19:52 +0800 Subject: [PATCH 062/206] Bump version to 0.30.0 (#361) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 24ec1b741..e29d730d1 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.29.0 + 0.30.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3291a02bb..08383fa97 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 905946192..44401979f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.29.0 +Bundle-Version: 0.30.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.29.0.jar + lib/com.microsoft.java.debug.core-0.30.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 28901c18d..0150d3470 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.29.0 + 0.30.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.29.0 + 0.30.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index e873f4578..c437e34f3 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index ce56bf3a5..09cc52182 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.29.0 + 0.30.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 994191f7b..64805772b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.29.0 + 0.30.0 pom Java Debug Server for Visual Studio Code From 64d42435b9913ad39f49f8700e0c046abdc12dd5 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Tue, 2 Mar 2021 03:58:11 -0800 Subject: [PATCH 063/206] Enable GitHub Actions (#363) Signed-off-by: Sheng Chen --- .github/workflows/build.yml | 91 +++++++++++++++++++++++++++++++++++++ .travis.yml | 13 ------ 2 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..afa1e2185 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,91 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + linux: + name: Linux + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw clean verify + + - name: Checkstyle + run: ./mvnw checkstyle:check + + windows: + name: Windows + runs-on: windows-latest + timeout-minutes: 30 + steps: + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: $HOME/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw.cmd clean verify + + - name: Checkstyle + run: ./mvnw.cmd checkstyle:check + + darwin: + name: macOS + runs-on: macos-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw clean verify + + - name: Checkstyle + run: ./mvnw checkstyle:check diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f331372be..000000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: java - -os: - - linux - - osx - -script: - - ./mvnw clean verify - - ./mvnw checkstyle:check - -cache: - directories: - - $HOME/.m2 From e55f86413b05446f1cb99c30ce2b04c4363e9525 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 23 Mar 2021 12:20:05 +0800 Subject: [PATCH 064/206] Remove the usage of JDK internal API to avoid breaking JDK 16 (#364) --- com.microsoft.java.debug.core/pom.xml | 2 +- .../core/adapter/handler/LaunchUtils.java | 19 ++++++------------- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- .../category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 15 insertions(+), 22 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index e29d730d1..7c20a8aae 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.0 + 0.30.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index be7f95126..17a4f7359 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -16,15 +16,14 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.file.Files; -import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; @@ -33,8 +32,6 @@ import org.apache.commons.lang3.ArrayUtils; -import sun.security.action.GetPropertyAction; - public class LaunchUtils { private static Set tempFilesInUse = new HashSet<>(); @@ -109,16 +106,12 @@ public static void releaseTempLaunchFile(Path tempFile) { private static synchronized Path getTmpDir() throws IOException { if (tmpdir == null) { + Path tmpfile = Files.createTempFile("", UUID.randomUUID().toString()); + tmpdir = tmpfile.getParent(); try { - tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); - } catch (NullPointerException | InvalidPathException e) { - Path tmpfile = Files.createTempFile("", ".tmp"); - tmpdir = tmpfile.getParent(); - try { - Files.deleteIfExists(tmpfile); - } catch (Exception ex) { - // do nothing - } + Files.deleteIfExists(tmpfile); + } catch (Exception ex) { + // do nothing } } diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 08383fa97..e9e2f6c74 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 44401979f..95fc9aeeb 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.30.0 +Bundle-Version: 0.30.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.30.0.jar + lib/com.microsoft.java.debug.core-0.30.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 0150d3470..2a1985e15 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.0 + 0.30.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.30.0 + 0.30.1 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index c437e34f3..d351c135e 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 09cc52182..09bd08f1b 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.30.0 + 0.30.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 64805772b..fe8385811 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.30.0 + 0.30.1 pom Java Debug Server for Visual Studio Code From e521ed1d53faee58fe3a868a3de1fbc21f58b5a0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Apr 2021 12:35:44 +0800 Subject: [PATCH 065/206] Leverage AST and Java Runtime to resolve inline values (#368) * Leverage AST and Java Runtime to resolve inline values --- .../java/debug/core/adapter/DebugAdapter.java | 2 + .../handler/InlineValuesRequestHandler.java | 250 +++++++ .../java/debug/core/protocol/Requests.java | 29 + .../java/debug/core/protocol/Responses.java | 9 + .../java/debug/core/protocol/Types.java | 8 + .../META-INF/MANIFEST.MF | 1 + com.microsoft.java.debug.plugin/plugin.xml | 1 + .../plugin/internal/InlineValueHandler.java | 644 ++++++++++++++++++ .../JavaDebugDelegateCommandHandler.java | 4 + 9 files changed, 948 insertions(+) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 798200e97..db2b28adf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -30,6 +30,7 @@ import com.microsoft.java.debug.core.adapter.handler.ExceptionInfoRequestHandler; import com.microsoft.java.debug.core.adapter.handler.HotCodeReplaceHandler; import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; @@ -121,6 +122,7 @@ private void initialize() { registerHandlerForDebug(new ExceptionInfoRequestHandler()); registerHandlerForDebug(new DataBreakpointInfoRequestHandler()); registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); + registerHandlerForDebug(new InlineValuesRequestHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java new file mode 100644 index 000000000..e9697f899 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java @@ -0,0 +1,250 @@ +/******************************************************************************* +* Copyright (c) 2021 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.core.adapter.handler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.adapter.IStackFrameManager; +import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; +import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure; +import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager; +import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.adapter.variables.Variable; +import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Types; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.InlineVariable; +import com.microsoft.java.debug.core.protocol.Requests.InlineValuesArguments; +import com.sun.jdi.ArrayReference; +import com.sun.jdi.Field; +import com.sun.jdi.IntegerValue; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.StackFrame; +import com.sun.jdi.Value; + +import org.apache.commons.lang3.math.NumberUtils; + +public class InlineValuesRequestHandler implements IDebugRequestHandler { + protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.INLINEVALUES); + } + + /** + * This request only resolves the values for those non-local variables, such as + * field variables and captured variables from outer scope. Because the values + * of local variables in current stackframe are usually expanded by Variables View + * by default, inline values can reuse these values directly. However, for field + * variables and variables captured from external scopes, they are hidden as properties + * of 'this' variable and require additional evaluation to get their values. + */ + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + InlineValuesArguments inlineValuesArgs = (InlineValuesArguments) arguments; + int variableCount = inlineValuesArgs == null || inlineValuesArgs.variables == null ? 0 : inlineValuesArgs.variables.length; + InlineVariable[] inlineVariables = inlineValuesArgs.variables; + StackFrameReference stackFrameReference = (StackFrameReference) context.getRecyclableIdPool().getObjectById(inlineValuesArgs.frameId); + if (stackFrameReference == null) { + logger.log(Level.SEVERE, String.format("InlineValues failed: invalid stackframe id %d.", inlineValuesArgs.frameId)); + response.body = new Responses.InlineValuesResponse(null); + return CompletableFuture.completedFuture(response); + } + + IStackFrameManager stackFrameManager = context.getStackFrameManager(); + StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); + if (frame == null) { + logger.log(Level.SEVERE, String.format("InlineValues failed: stale stackframe id %d.", inlineValuesArgs.frameId)); + response.body = new Responses.InlineValuesResponse(null); + return CompletableFuture.completedFuture(response); + } + + Variable[] values = new Variable[variableCount]; + try { + if (isLambdaFrame(frame)) { + // Lambda expression stores the captured variables from 'outer' scope in a synthetic stackframe below the lambda frame. + StackFrame syntheticLambdaFrame = stackFrameReference.getThread().frame(stackFrameReference.getDepth() + 1); + resolveValuesFromThisVariable(syntheticLambdaFrame.thisObject(), inlineVariables, values, true); + } + + resolveValuesFromThisVariable(frame.thisObject(), inlineVariables, values, false); + } catch (Exception ex) { + // do nothig + } + + Types.Variable[] result = new Types.Variable[variableCount]; + IVariableFormatter variableFormatter = context.getVariableFormatter(); + Map formatterOptions = variableFormatter.getDefaultOptions(); + Map calculatedValues = new HashMap<>(); + IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class); + for (int i = 0; i < variableCount; i++) { + if (values[i] == null) { + continue; + } + + if (calculatedValues.containsKey(inlineVariables[i])) { + result[i] = calculatedValues.get(inlineVariables[i]); + continue; + } + + Value value = values[i].value; + String name = values[i].name; + int indexedVariables = -1; + Value sizeValue = null; + if (value instanceof ArrayReference) { + indexedVariables = ((ArrayReference) value).length(); + } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { + try { + JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); + if (structure != null && structure.getSizeExpression() != null) { + sizeValue = structure.getSize((ObjectReference) value, frame.thread(), evaluationEngine); + if (sizeValue != null && sizeValue instanceof IntegerValue) { + indexedVariables = ((IntegerValue) sizeValue).value(); + } + } + } catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) { + logger.log(Level.INFO, + String.format("Failed to get the logical size for the type %s.", value.type().name()), e); + } + } + + Types.Variable formattedVariable = new Types.Variable(name, variableFormatter.valueToString(value, formatterOptions)); + formattedVariable.indexedVariables = Math.max(indexedVariables, 0); + String detailsValue = null; + if (sizeValue != null) { + detailsValue = "size=" + variableFormatter.valueToString(sizeValue, formatterOptions); + } else if (DebugSettings.getCurrent().showToString) { + detailsValue = VariableDetailUtils.formatDetailsValue(value, frame.thread(), variableFormatter, formatterOptions, evaluationEngine); + } + + if (detailsValue != null) { + formattedVariable.value = formattedVariable.value + " " + detailsValue; + } + + result[i] = formattedVariable; + calculatedValues.put(inlineVariables[i], formattedVariable); + } + + response.body = new Responses.InlineValuesResponse(result); + return CompletableFuture.completedFuture(response); + } + + private static boolean isCapturedLocalVariable(String fieldName, String variableName) { + String capturedVariableName = "val$" + variableName; + return Objects.equals(fieldName, capturedVariableName) + || (fieldName.startsWith(capturedVariableName + "$") && NumberUtils.isDigits(fieldName.substring(capturedVariableName.length() + 1))); + } + + private static boolean isCapturedThisVariable(String fieldName) { + if (fieldName.startsWith("this$")) { + String suffix = fieldName.substring(5).replaceAll("\\$+$", ""); + return NumberUtils.isDigits(suffix); + } + + return false; + } + + private static boolean isLambdaFrame(StackFrame frame) { + Method method = frame.location().method(); + return method.isSynthetic() && method.name().startsWith("lambda$"); + } + + private void resolveValuesFromThisVariable(ObjectReference thisObj, InlineVariable[] unresolvedVariables, Variable[] result, + boolean isSyntheticLambdaFrame) { + if (thisObj == null) { + return; + } + + int unresolved = 0; + for (Variable item : result) { + if (item == null) { + unresolved++; + } + } + + try { + ReferenceType type = thisObj.referenceType(); + String typeName = type.name(); + ObjectReference enclosingInstance = null; + for (Field field : type.allFields()) { + String fieldName = field.name(); + boolean isSyntheticField = field.isSynthetic(); + Value fieldValue = null; + for (int i = 0; i < unresolvedVariables.length; i++) { + if (result[i] != null) { + continue; + } + + InlineVariable inlineVariable = unresolvedVariables[i]; + boolean isInlineFieldVariable = (inlineVariable.declaringClass != null); + boolean isMatch = false; + if (isSyntheticLambdaFrame) { + isMatch = !isInlineFieldVariable && Objects.equals(fieldName, inlineVariable.expression); + } else { + boolean isMatchedField = isInlineFieldVariable + && Objects.equals(fieldName, inlineVariable.expression) + && Objects.equals(typeName, inlineVariable.declaringClass); + boolean isMatchedCapturedVariable = !isInlineFieldVariable + && isSyntheticField + && isCapturedLocalVariable(fieldName, inlineVariable.expression); + isMatch = isMatchedField || isMatchedCapturedVariable; + + if (!isMatch && isSyntheticField && enclosingInstance == null && isCapturedThisVariable(fieldName)) { + Value value = thisObj.getValue(field); + if (value instanceof ObjectReference) { + enclosingInstance = (ObjectReference) value; + break; + } + } + } + + if (isMatch) { + fieldValue = fieldValue == null ? thisObj.getValue(field) : fieldValue; + result[i] = new Variable(inlineVariable.expression, fieldValue); + unresolved--; + } + } + + if (unresolved <= 0) { + break; + } + } + + if (unresolved > 0 && enclosingInstance != null) { + resolveValuesFromThisVariable(enclosingInstance, unresolvedVariables, result, isSyntheticLambdaFrame); + } + } catch (Exception ex) { + // do nothing + } + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index f62a241a1..2afb02bd8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.Map; +import java.util.Objects; import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.Types.DataBreakpoint; @@ -340,6 +341,33 @@ public static class SetDataBreakpointsArguments extends Arguments { public DataBreakpoint[] breakpoints; } + public static class InlineValuesArguments extends Arguments { + public int frameId; + public InlineVariable[] variables; + } + + public static class InlineVariable { + public String expression; + public String declaringClass; + + @Override + public int hashCode() { + return Objects.hash(declaringClass, expression); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof InlineVariable)) { + return false; + } + InlineVariable other = (InlineVariable) obj; + return Objects.equals(declaringClass, other.declaringClass) && Objects.equals(expression, other.expression); + } + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -372,6 +400,7 @@ public static enum Command { CONTINUEOTHERS("continueOthers", ThreadOperationArguments.class), PAUSEALL("pauseAll", ThreadOperationArguments.class), PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), + INLINEVALUES("inlineValues", InlineValuesArguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index 1b4bc7b4a..cc349b6e7 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -16,6 +16,7 @@ import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; +import com.microsoft.java.debug.core.protocol.Types.Variable; /** * The response content types defined by VSCode Debug Protocol. @@ -316,4 +317,12 @@ public RedefineClassesResponse(String[] changedClasses, String errorMessage) { this.errorMessage = errorMessage; } } + + public static class InlineValuesResponse extends ResponseBody { + public Types.Variable[] variables; + + public InlineValuesResponse(Variable[] variables) { + this.variables = variables; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 3ce41869c..0b9a0494a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -101,6 +101,14 @@ public Variable(String name, String val, String type, int rf, String evaluateNam this.variablesReference = rf; this.evaluateName = evaluateName; } + + /** + * Constructor. + */ + public Variable(String name, String value) { + this.name = name; + this.value = value; + } } public static class Thread { diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 95fc9aeeb..40f78351f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.debug.core, org.eclipse.jdt.debug, org.eclipse.jdt.core, + org.eclipse.jdt.core.manipulation, org.eclipse.jdt.ls.core, org.eclipse.jdt.launching, com.google.gson, diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index 361cdcbda..69d76830d 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -20,6 +20,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java new file mode 100644 index 000000000..5c2cf8834 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java @@ -0,0 +1,644 @@ +/******************************************************************************* +* Copyright (c) 2021 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.ISourceRange; +import org.eclipse.jdt.core.ISourceReference; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; +import org.eclipse.jdt.core.dom.Block; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.DoStatement; +import org.eclipse.jdt.core.dom.ForStatement; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.SwitchStatement; +import org.eclipse.jdt.core.dom.TypeDeclarationStatement; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.IfStatement; +import org.eclipse.jdt.core.dom.ImportDeclaration; +import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.ModuleDeclaration; +import org.eclipse.jdt.core.dom.PackageDeclaration; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.QualifiedType; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.handlers.JsonRpcHelpers; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; + +public class InlineValueHandler { + + /** + * Find the valid inline variables belonging to the visible view port. + */ + public static InlineVariable[] resolveInlineVariables(InlineParams params, IProgressMonitor monitor) { + ITypeRoot root = JDTUtils.resolveTypeRoot(params.uri); + try { + if (root == null || root.getBuffer() == null) { + return new InlineVariable[0]; + } + + Position stoppedLocation = params.stoppedLocation.getStart(); + int stoppedOffset = JsonRpcHelpers.toOffset(root.getBuffer(), stoppedLocation.getLine(), stoppedLocation.getCharacter()); + IMethod enclosingMethod = findEnclosingMethod(root, stoppedOffset); + if (enclosingMethod == null) { + return new InlineVariable[0]; + } + + Position startLocation = getPosition(root.getBuffer(), enclosingMethod.getSourceRange().getOffset()); + Range stoppedRange = new Range(startLocation, stoppedLocation); + if (params.viewPort != null + && (params.viewPort.getEnd().getLine() < startLocation.getLine() || params.viewPort.getStart().getLine() > stoppedLocation.getLine())) { + return new InlineVariable[0]; + } + + CompilationUnit astRoot = CoreASTProvider.getInstance().getAST(root, CoreASTProvider.WAIT_YES, monitor); + VariableVisitor visitor = new VariableVisitor(astRoot, stoppedRange, params.viewPort, Flags.isStatic(enclosingMethod.getFlags())); + astRoot.accept(visitor); + InlineVariable[] result = visitor.getInlineVariables(); + return result; + } catch (JavaModelException e) { + return new InlineVariable[0]; + } + } + + private static IMethod findEnclosingMethod(ITypeRoot root, int stoppedOffset) throws JavaModelException { + IType enclosingType = null; + if (root instanceof ICompilationUnit) { + IType[] types = ((ICompilationUnit) root).getAllTypes(); + for (IType type : types) { + if (isEnclosed(type, stoppedOffset)) { + enclosingType = type; + } + } + } else if (root instanceof IClassFile) { + enclosingType = ((IClassFile) root).getType(); + } + + if (enclosingType == null) { + return null; + } + + IMethod enclosingMethod = null; + for (IMethod method : enclosingType.getMethods()) { + if (isEnclosed(method, stoppedOffset)) { + enclosingMethod = method; + break; + } + } + + if (enclosingMethod == null) { + return null; + } + + // Deal with the scenario that the stopped location is inside the local types defined in method. + return findMethodInLocalTypes(enclosingMethod, stoppedOffset); + } + + private static boolean isEnclosed(ISourceReference sourceReference, int offset) throws JavaModelException { + ISourceRange sourceRange = sourceReference.getSourceRange(); + return sourceRange != null && offset >= sourceRange.getOffset() + && offset < sourceRange.getOffset() + sourceRange.getLength(); + } + + private static IMethod findMethodInLocalTypes(IMethod enclosingMethod, int stoppedOffset) throws JavaModelException { + if (enclosingMethod == null) { + return null; + } + + for (IJavaElement element : enclosingMethod.getChildren()) { + if (element instanceof IType) { + if (isEnclosed((IType) element, stoppedOffset)) { + for (IMethod method : ((IType) element).getMethods()) { + if (isEnclosed(method, stoppedOffset)) { + IMethod nearerMethod = findMethodInLocalTypes(method, stoppedOffset); + return nearerMethod == null ? enclosingMethod : nearerMethod; + } + } + + break; + } + } + } + + return enclosingMethod; + } + + /** + * Returns the zero based line and column number. + */ + private static Position getPosition(IBuffer buffer, int offset) { + int[] result = JsonRpcHelpers.toLine(buffer, offset); + if (result == null && result.length < 1) { + return new Position(-1, -1); + } + + return new Position(result[0], result[1]); + } + + static class VariableVisitor extends ASTVisitor { + private CompilationUnit unit = null; + private Range stoppedSourceRange; + private Range viewPort; + private boolean isStoppingAtStaticMethod; + private int baseLine; + private Set[] tokens; + private List localVarDecls = new ArrayList<>(); + private List localVarDeclPositions = new ArrayList<>(); + private boolean isStoppingAtLambda = false; + private Set varDeclsAtLastLine = new HashSet<>(); + private Range visibleInlineRange = null; + + public VariableVisitor(CompilationUnit unit, Range stoppedSourceRange, Range viewPort, boolean stopAtStaticMethod) { + this.unit = unit; + this.stoppedSourceRange = stoppedSourceRange; + this.viewPort = viewPort; + this.isStoppingAtStaticMethod = stopAtStaticMethod; + this.baseLine = stoppedSourceRange.getStart().getLine(); + this.tokens = new Set[stoppedSourceRange.getEnd().getLine() - stoppedSourceRange.getStart().getLine() + 1]; + updateVisibleRange(); + } + + private void updateVisibleRange() { + if (viewPort == null) { + visibleInlineRange = stoppedSourceRange; + } else if (compare(viewPort.getStart(), stoppedSourceRange.getEnd()) > 0 + || compare(viewPort.getEnd(), stoppedSourceRange.getStart()) < 0) { + visibleInlineRange = null; + } else { + Position start = compare(viewPort.getStart(), stoppedSourceRange.getStart()) >= 0 ? viewPort.getStart() : stoppedSourceRange.getStart(); + Position end = compare(viewPort.getEnd(), stoppedSourceRange.getEnd()) <= 0 ? viewPort.getEnd() : stoppedSourceRange.getEnd(); + visibleInlineRange = new Range(start, end); + } + } + + /** + * Handle the variables in the visible source ranges. + */ + @Override + public boolean visit(SimpleName node) { + if (visibleInlineRange == null) { + return false; + } + + Position startPosition = getStartPosition(node); + boolean isAtLastLine = isAtStopLocation(startPosition); + if (isEnclosed(visibleInlineRange, startPosition) || isAtLastLine) { + IBinding binding = node.resolveBinding(); + if (!(binding instanceof IVariableBinding)) { + return false; + } else if (isAtLastLine && this.varDeclsAtLastLine.contains(binding.getKey())) { + return false; + } + + String declaringClass = null; + if (((IVariableBinding) binding).isField()) { + ITypeBinding typeBinding = ((IVariableBinding) binding).getDeclaringClass(); + if (typeBinding == null) { + return false; + } + + declaringClass = typeBinding.getBinaryName(); + } + + Token token = new Token(node.getIdentifier(), startPosition, declaringClass); + int index = startPosition.getLine() - baseLine; + if (tokens[index] == null) { + tokens[index] = new LinkedHashSet<>(); + } + + if (!tokens[index].contains(token)) { + tokens[index].add(token); + } + } + + return false; + } + + /** + * Handle local variable declarations happening in current method. + */ + @Override + public boolean visit(VariableDeclarationFragment node) { + SimpleName name = node.getName(); + Position startPosition = getStartPosition(name); + if (isEnclosed(stoppedSourceRange, startPosition)) { + this.localVarDecls.add(name.getIdentifier()); + this.localVarDeclPositions.add(startPosition); + } + + if (isAtStopLocation(startPosition)) { + IVariableBinding binding = node.resolveBinding(); + if (binding != null) { + this.varDeclsAtLastLine.add(binding.getKey()); + } + } + + return true; + } + + /** + * Handle formal parameter declarations happening in current method. + */ + @Override + public boolean visit(SingleVariableDeclaration node) { + SimpleName name = node.getName(); + Position startPosition = getStartPosition(name); + if (isEnclosed(stoppedSourceRange, startPosition)) { + this.localVarDecls.add(name.getIdentifier()); + this.localVarDeclPositions.add(startPosition); + } + + return false; + } + + /** + * Handle the lambda expression containing the stopped location. + * If the execution instruction stops on a lambda expression, then + * crop the visible source ranges to the lambda expression body. + */ + @Override + public boolean visit(LambdaExpression node) { + Position startPosition = getStartPosition(node); + Position endPosition = getEndPosition(node); + if (compare(startPosition, stoppedSourceRange.getStart()) >= 0 + && isEnclosed(new Range(startPosition, endPosition), stoppedSourceRange.getEnd())) { + stoppedSourceRange.setStart(startPosition); + updateVisibleRange(); + isStoppingAtLambda = true; + localVarDecls.clear(); + localVarDeclPositions.clear(); + return true; + } + + return super.visit(node); + } + + /** + * Handle the method containing the stopped location. + */ + @Override + public boolean visit(MethodDeclaration node) { + Position startPosition = getStartPosition(node); + Position endPosition = getEndPosition(node); + if (compare(startPosition, stoppedSourceRange.getStart()) <= 0 && compare(endPosition, stoppedSourceRange.getEnd()) >= 0) { + return true; + } + + return false; + } + + @Override + public boolean visit(Block node) { + if (isUnreachableNode(node)) { + return false; + } + + return true; + } + + @Override + public boolean visit(DoStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(ForStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(IfStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(SwitchStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(WhileStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + if (isUnreachableNode(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + if (isUnreachableNode(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(TypeDeclarationStatement node) { + if (isUnreachableNode(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(ImportDeclaration node) { + return false; + } + + @Override + public boolean visit(ModuleDeclaration node) { + return false; + } + + @Override + public boolean visit(PackageDeclaration node) { + return false; + } + + @Override + public boolean visit(QualifiedName node) { + return Objects.equals("length", node.getName().getIdentifier()); + } + + @Override + public boolean visit(QualifiedType node) { + return false; + } + + /** + * Return the valid inline variables in the visible source ranges. + * + *

There are four typical kinds of variable: + * - Local variables declared in method body. + * - Formal parameters declared in method declaration. + * - Field variables. + * - Captured variables from outer scope. This includes local type is accessing + * variables of enclosing method, and lambda expression body is accessing to + * variables of enclosing method.

+ * + *

For the first two kinds such as local variables and formal parameters, + * we're going to return them with VariableLookup kind since their values are + * expanded by Variables View by default.

+ * + *

For the last two kinds, we're going to return them with Evaluation kind + * since it requires additional evaluation to get its values.

+ */ + public InlineVariable[] getInlineVariables() { + if (visibleInlineRange == null) { + return new InlineVariable[0]; + } + + // Adding the local variable declarations to the token list. + for (int i = 0; i < localVarDecls.size(); i++) { + String name = localVarDecls.get(i); + Position position = localVarDeclPositions.get(i); + if (isEnclosed(visibleInlineRange, position)) { + int index = position.getLine() - baseLine; + if (tokens[index] == null) { + tokens[index] = new LinkedHashSet<>(); + } + + Token token = new Token(name, position, null); + if (!tokens[index].contains(token)) { + tokens[index].add(token); + } + } + } + + // For lambda expression in non static method, the captured variable 'arg$1' + // points to 'this' object of the enclosing method, and the index of other + // captured variables starts with 2. + int capturedArgIndexInLambda = isStoppingAtStaticMethod ? 1 : 2; + Map capturedVarsInLambda = new HashMap<>(); + List result = new ArrayList<>(); + for (int i = 0; i < tokens.length; i++) { + int line = baseLine + i; + if (tokens[i] == null || line < visibleInlineRange.getStart().getLine()) { + continue; + } + + for (Token token : tokens[i]) { + if (!isEnclosed(visibleInlineRange, token.position) && !isAtLastVisibleLine(token.position)) { + continue; + } + + // Local Variables + if (token.declaringClass == null && localVarDecls.contains(token.name)) { + int declIndex = localVarDecls.lastIndexOf(token.name); + Position declPosition = localVarDeclPositions.get(declIndex); + if (compare(token.position, declPosition) >= 0) { + result.add(new InlineVariable(new Range(token.position, token.position), token.name, InlineKind.VariableLookup)); + continue; + } + } + + InlineVariable value = new InlineVariable( + new Range(token.position, token.position), token.name, InlineKind.Evaluation, token.declaringClass); + // Captured variables by lambda expression + if (isStoppingAtLambda && token.declaringClass == null) { + /** + * When the lambda body accesses variables from its "outer" scope such as + * its enclosing method, these variables will be captured as properties of + * 'this' object of a synthetic lambda instance by Java runtime. However, + * when the compiler parses the lambda expression, it erases the specific + * variable name but keeps the captured variable names with format like + * 'arg$'. In order to evaluate the correct value from Java runtime, + * we have to encode the variable name using the same rule 'arg$' as + * the compiler. + */ + if (capturedVarsInLambda.containsKey(token.name)) { + value.expression = capturedVarsInLambda.get(token.name); + } else { + value.expression = "arg$" + capturedArgIndexInLambda++; + capturedVarsInLambda.put(token.name, value.expression); + } + } + + result.add(value); + } + } + + return result.toArray(new InlineVariable[0]); + } + + private Position getStartPosition(ASTNode node) { + // Line number returned by AST unit is one based, converts it to zero based. + int lineNumber = unit.getLineNumber(node.getStartPosition()) - 1; + int columnNumber = unit.getColumnNumber(node.getStartPosition()); + return new Position(lineNumber, columnNumber); + } + + private Position getEndPosition(ASTNode node) { + // Line number returned by AST unit is one based, converts it to zero based. + int lineNumber = unit.getLineNumber(node.getStartPosition() + node.getLength() - 1) - 1; + int columnNumber = unit.getColumnNumber(node.getStartPosition() + node.getLength() - 1); + return new Position(lineNumber, columnNumber); + } + + private boolean isUnreachableNode(ASTNode node) { + Position startPosition = getStartPosition(node); + Position endPosition = getEndPosition(node); + return compare(startPosition, stoppedSourceRange.getEnd()) > 0 + || compare(endPosition, stoppedSourceRange.getEnd()) < 0; + } + + private boolean isEnclosed(Range range, Position position) { + return compare(range.getStart(), position) <= 0 && compare(range.getEnd(), position) >= 0; + } + + private int compare(Position p1, Position p2) { + if (p1.getLine() < p2.getLine()) { + return -1; + } else if (p1.getLine() == p2.getLine()) { + return p1.getCharacter() - p2.getCharacter(); + } + + return 1; + } + + private boolean isAtStopLocation(Position position) { + return position.getLine() == stoppedSourceRange.getEnd().getLine(); + } + + private boolean isAtStopLocation(ASTNode node) { + Position startPosition = getStartPosition(node); + return isAtStopLocation(startPosition); + } + + private boolean isAtLastVisibleLine(Position position) { + return visibleInlineRange != null && visibleInlineRange.getEnd().getLine() == position.getLine(); + } + } + + static class InlineVariable { + Range range; + String name; + InlineKind kind; + String expression; + String declaringClass; + + public InlineVariable(Range range, String name, InlineKind kind) { + this.range = range; + this.name = name; + this.kind = kind; + } + + public InlineVariable(Range range, String name, InlineKind kind, String declaringClass) { + this.range = range; + this.name = name; + this.kind = kind; + this.declaringClass = declaringClass; + } + } + + static enum InlineKind { + VariableLookup, + Evaluation + } + + static class InlineParams { + String uri; + Range viewPort; + Range stoppedLocation; + } + + static class Token { + String name; + Position position; + String declaringClass = null; + + public Token(String name, Position position) { + this.name = name; + this.position = position; + } + + public Token(String name, Position position, String declaringClass) { + this.name = name; + this.position = position; + this.declaringClass = declaringClass; + } + + @Override + public int hashCode() { + return Objects.hash(declaringClass, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Token)) { + return false; + } + Token other = (Token) obj; + return Objects.equals(declaringClass, other.declaringClass) && Objects.equals(name, other.name); + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 032d0420c..1875d514e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -28,6 +28,7 @@ import com.microsoft.java.debug.core.UsageDataStore; import com.microsoft.java.debug.core.protocol.JsonUtils; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; +import com.microsoft.java.debug.plugin.internal.InlineValueHandler.InlineParams; public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler { public static final String FETCH_USER_DATA = "vscode.java.fetchUsageData"; @@ -47,6 +48,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; public static final String RESOLVE_CLASSFILTERS = "vscode.java.resolveClassFilters"; public static final String RESOLVE_SOURCEURI = "vscode.java.resolveSourceUri"; + public static final String RESOLVE_INLINEVARIABLES = "vscode.java.resolveInlineVariables"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -90,6 +92,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return JavaClassFilter.resolveClassFilters(arguments); case RESOLVE_SOURCEURI: return ResolveSourceMappingHandler.resolveSourceUri(arguments); + case RESOLVE_INLINEVARIABLES: + return InlineValueHandler.resolveInlineVariables(JsonUtils.fromJson((String) arguments.get(0), InlineParams.class), progress); default: break; } From f9aa9649b8c4a33b418a662b4516b2a14c977473 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Apr 2021 17:26:12 +0800 Subject: [PATCH 066/206] Fix bug#973: breakpoints inside record don't work (#370) --- .../internal/JdtSourceLookUpProvider.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 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 65ba6074a..d24d2abdd 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 @@ -61,6 +61,17 @@ public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private ISourceContainer[] sourceContainers = null; private HashMap options = new HashMap(); + private String latestJavaVersion = null; + private int latestASTLevel; + + public JdtSourceLookUpProvider() { + // Get the latest supported Java version by JDT tooling. + this.latestJavaVersion = JavaCore.latestSupportedJavaVersion(); + // Get the mapped AST level for the latest Java version. + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, latestJavaVersion); + this.latestASTLevel = new AST(javaOptions).apiLevel(); + } @Override public void initialize(IDebugAdapterContext context, Map props) { @@ -101,8 +112,7 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return new String[0]; } - // Currently the highest version the debugger supports is JavaSE-13 Edition (JLS13). - final ASTParser parser = ASTParser.newParser(AST.JLS13); + final ASTParser parser = ASTParser.newParser(this.latestASTLevel); parser.setResolveBindings(true); parser.setBindingsRecovery(true); parser.setStatementsRecovery(true); @@ -131,9 +141,10 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th * the user need specify the compiler options explicitly. */ Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_13); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_13); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_13); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); parser.setCompilerOptions(javaOptions); astUnit = (CompilationUnit) parser.createAST(null); } else { From 6fc0528cff69403e470367973dbd93f9929d695e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 22 Apr 2021 15:07:14 +0800 Subject: [PATCH 067/206] Refresh variables with new variable formatters (#369) --- .vscode/settings.json | 1 - .../java/debug/core/adapter/DebugAdapter.java | 2 + .../handler/RefreshVariablesHandler.java | 50 +++++++++++++++++++ .../java/debug/core/protocol/Events.java | 39 +++++++++++++++ .../java/debug/core/protocol/Requests.java | 9 ++++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java diff --git a/.vscode/settings.json b/.vscode/settings.json index fbaf055a0..2c67a2d4b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "java.configuration.updateBuildConfiguration": "automatic", "files.exclude": { "**/.git": true, "**/*.class": true, diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index db2b28adf..4342c48d3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -32,6 +32,7 @@ import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler; import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetBreakpointsRequestHandler; @@ -123,6 +124,7 @@ private void initialize() { registerHandlerForDebug(new DataBreakpointInfoRequestHandler()); registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); registerHandlerForDebug(new InlineValuesRequestHandler()); + registerHandlerForDebug(new RefreshVariablesHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java new file mode 100644 index 000000000..01214b615 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java @@ -0,0 +1,50 @@ +/******************************************************************************* +* Copyright (c) 2021 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.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Events.InvalidatedAreas; +import com.microsoft.java.debug.core.protocol.Events.InvalidatedEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.RefreshVariablesArguments; + +public class RefreshVariablesHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.REFRESHVARIABLES); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + RefreshVariablesArguments refreshArgs = (RefreshVariablesArguments) arguments; + if (refreshArgs != null) { + DebugSettings.getCurrent().showHex = refreshArgs.showHex; + DebugSettings.getCurrent().showQualifiedNames = refreshArgs.showQualifiedNames; + DebugSettings.getCurrent().showStaticVariables = refreshArgs.showStaticVariables; + DebugSettings.getCurrent().showLogicalStructure = refreshArgs.showLogicalStructure; + DebugSettings.getCurrent().showToString = refreshArgs.showToString; + } + + context.getProtocolServer().sendEvent(new InvalidatedEvent(InvalidatedAreas.VARIABLES)); + return CompletableFuture.completedFuture(response); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index ea18f183c..5397c418e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -11,6 +11,7 @@ package com.microsoft.java.debug.core.protocol; +import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.Types.Source; /** @@ -244,4 +245,42 @@ public UserNotificationEvent(NotificationType notifyType, String message) { this.message = message; } } + + public static enum InvalidatedAreas { + @SerializedName("all") + ALL, + @SerializedName("stacks") + STACKS, + @SerializedName("threads") + THREADS, + @SerializedName("variables") + VARIABLES; + } + + public static class InvalidatedEvent extends DebugEvent { + public InvalidatedAreas[] areas; + public long threadId; + public int frameId; + + public InvalidatedEvent() { + super("invalidated"); + } + + public InvalidatedEvent(InvalidatedAreas area) { + super("invalidated"); + this.areas = new InvalidatedAreas[]{area}; + } + + public InvalidatedEvent(InvalidatedAreas area, long threadId) { + super("invalidated"); + this.areas = new InvalidatedAreas[]{area}; + this.threadId = threadId; + } + + public InvalidatedEvent(InvalidatedAreas area, int frameId) { + super("invalidated"); + this.areas = new InvalidatedAreas[]{area}; + this.frameId = frameId; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 2afb02bd8..e31a65261 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -297,6 +297,14 @@ public static class SetVariableArguments extends Arguments { public ValueFormat format; } + public static class RefreshVariablesArguments extends Arguments { + public boolean showStaticVariables = false; + public boolean showQualifiedNames = false; + public boolean showHex = false; + public boolean showLogicalStructure = true; + public boolean showToString = true; + } + public static class SourceArguments extends Arguments { public int sourceReference; } @@ -401,6 +409,7 @@ public static enum Command { PAUSEALL("pauseAll", ThreadOperationArguments.class), PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), INLINEVALUES("inlineValues", InlineValuesArguments.class), + REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), UNSUPPORTED("", Arguments.class); private String command; From a0e31fc8bda7dbe604ef94aabd769bfb0a26bbbc Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 26 Apr 2021 09:46:49 +0800 Subject: [PATCH 068/206] Bump version to 0.31.0 (#371) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 7c20a8aae..04099cf90 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.1 + 0.31.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index e9e2f6c74..3fd9659f9 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 40f78351f..be9168b0c 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.30.1 +Bundle-Version: 0.31.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.30.1.jar + lib/com.microsoft.java.debug.core-0.31.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 2a1985e15..0fefc2fea 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.1 + 0.31.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.30.1 + 0.31.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index d351c135e..9a33da2cf 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 09bd08f1b..1d0b490e9 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.30.1 + 0.31.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index fe8385811..a27138fb9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.30.1 + 0.31.0 pom Java Debug Server for Visual Studio Code From 5bb368e35911e2220e691623de592895b851048e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 29 Apr 2021 16:10:09 +0800 Subject: [PATCH 069/206] Update CI branch (#373) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afa1e2185..58381df99 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: linux: From 71e9ac5a85c9712452f639b5f4708ebb86d7b76a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 30 Apr 2021 15:29:38 +0800 Subject: [PATCH 070/206] Fix the broken due to the changes from the upstream JDI impl class ConnectorImpl$StringArgumentImpl (#374) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 4 +- com.microsoft.java.debug.plugin/pom.xml | 4 +- .../internal/AdvancedLaunchingConnector.java | 80 +++++++++++++++++-- .../category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 83 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 04099cf90..e0260e727 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.0 + 0.31.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3fd9659f9..c00d65ed3 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index be9168b0c..84bd01f8f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.31.0 +Bundle-Version: 0.31.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.31.0.jar + lib/com.microsoft.java.debug.core-0.31.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 0fefc2fea..f9a19badc 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.0 + 0.31.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.31.0 + 0.31.1 diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index d025c9c1d..607d26a4e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2021 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 @@ -44,11 +44,11 @@ public AdvancedLaunchingConnector(VirtualMachineManagerImpl virtualMachineManage public Map defaultArguments() { Map defaultArgs = super.defaultArguments(); - Argument cwdArg = new AdvancedStringArgumentImpl(DebugUtility.CWD, "Current working directory", DebugUtility.CWD, false); + Argument cwdArg = new JDIStringArgumentImpl(DebugUtility.CWD, "Current working directory", DebugUtility.CWD, false); cwdArg.setValue(null); defaultArgs.put(DebugUtility.CWD, cwdArg); - Argument envArg = new AdvancedStringArgumentImpl(DebugUtility.ENV, "Environment variables", DebugUtility.ENV, false); + Argument envArg = new JDIStringArgumentImpl(DebugUtility.ENV, "Environment variables", DebugUtility.ENV, false); envArg.setValue(null); defaultArgs.put(DebugUtility.ENV, envArg); @@ -117,11 +117,79 @@ private static String[] constructLaunchCommand(Map l return DebugUtility.parseArguments(execString.toString()).toArray(new String[0]); } - class AdvancedStringArgumentImpl extends StringArgumentImpl implements StringArgument { - private static final long serialVersionUID = 1L; + abstract class JDIArgumentImpl implements Argument { + private static final long serialVersionUID = 8850533280769854833L; + private String name; + private String description; + private String label; + private boolean mustSpecify; + + protected JDIArgumentImpl(String name, String description, String label, boolean mustSpecify) { + this.name = name; + this.description = description; + this.label = label; + this.mustSpecify = mustSpecify; + } + + @Override + public String name() { + return name; + } + + @Override + public String description() { + return description; + } + + @Override + public String label() { + return label; + } + + @Override + public boolean mustSpecify() { + return mustSpecify; + } + + @Override + public abstract String value(); + + @Override + public abstract void setValue(String value); + + @Override + public abstract boolean isValid(String value); - protected AdvancedStringArgumentImpl(String name, String description, String label, boolean mustSpecify) { + @Override + public abstract String toString(); + } + + class JDIStringArgumentImpl extends JDIArgumentImpl implements StringArgument { + private static final long serialVersionUID = 6009335074727417445L; + private String value; + + protected JDIStringArgumentImpl(String name, String description, String label, boolean mustSpecify) { super(name, description, label, mustSpecify); } + + @Override + public String value() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean isValid(String value) { + return true; + } + + @Override + public String toString() { + return value; + } } } diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 9a33da2cf..3520fec6d 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 1d0b490e9..f3ee87766 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.31.0 + 0.31.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index a27138fb9..4e5d368b8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.31.0 + 0.31.1 pom Java Debug Server for Visual Studio Code From a024806d513674ba4cb1e5dfa2eb381667f0780d Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 21 May 2021 13:39:55 +0800 Subject: [PATCH 071/206] Support customizing the classpaths and modulepaths (#376) --- .../internal/ResolveClasspathsHandler.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java index be37285e3..5e7017128 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java @@ -70,6 +70,9 @@ public class ResolveClasspathsHandler { */ public String[][] resolveClasspaths(List arguments) throws Exception { try { + if (arguments.size() == 3) { + return computeClassPath((String) arguments.get(0), (String) arguments.get(1), (String) arguments.get(2)); + } return computeClassPath((String) arguments.get(0), (String) arguments.get(1)); } catch (CoreException e) { logger.log(Level.SEVERE, "Failed to resolve classpath: " + e.getMessage(), e); @@ -159,6 +162,23 @@ public void acceptSearchMatch(SearchMatch match) { * CoreException */ private static String[][] computeClassPath(String mainClass, String projectName) throws CoreException { + return computeClassPath(mainClass, projectName, null); + } + + /** + * Accord to the project name and the main class, compute runtime classpath. + * + * @param mainClass + * fully qualified class name + * @param projectName + * project name + * @param scope + * scope of the classpath + * @return class path + * @throws CoreException + * CoreException + */ + private static String[][] computeClassPath(String mainClass, String projectName, String scope) throws CoreException { IJavaProject project = null; // if type exists in multiple projects, debug configuration need provide // project name. @@ -179,6 +199,12 @@ private static String[][] computeClassPath(String mainClass, String projectName) project = projects.get(0); } + if ("test".equals(scope)) { + return computeClassPath(project, mainClass, false /*excludeTestCode*/, Collections.EMPTY_LIST); + } else if ("runtime".equals(scope)) { + return computeClassPath(project, mainClass, true /*excludeTestCode*/, Collections.EMPTY_LIST); + } + IJavaElement testElement = findMainClassInTestFolders(project, mainClass); List mappedResources = (testElement != null && testElement.getResource() != null) ? Arrays.asList(testElement.getResource()) : Collections.EMPTY_LIST; @@ -275,7 +301,7 @@ public void acceptSearchMatch(SearchMatch match) { private static class JavaApplicationLaunchConfiguration extends LaunchConfiguration { public static final String JAVA_APPLICATION_LAUNCH = "\n" - + "\n" + + "\n" + "\n" + "\n" + "\n" @@ -302,7 +328,18 @@ protected JavaApplicationLaunchConfiguration(IProject project, String mainType, } else if (ProjectUtils.isGradleProject(project)) { classpathProvider = "org.eclipse.buildship.core.classpathprovider"; } - this.launchInfo = new JavaLaunchConfigurationInfo(JAVA_APPLICATION_LAUNCH); + + // Since MavenRuntimeClasspathProvider will only including test entries when: + // 1. Launch configuration is JUnit/TestNG type + // 2. Mapped resource is in test path. + // That's why we use JUnit launch configuration here to make sure the result is right when excludeTestCode is false. + String launchXml = null; + if (!excludeTestCode && mappedResources.isEmpty()) { + launchXml = String.format(JAVA_APPLICATION_LAUNCH, "org.eclipse.jdt.junit.launchconfig"); + } else { + launchXml = String.format(JAVA_APPLICATION_LAUNCH, "org.eclipse.jdt.launching.localJavaApplication"); + } + this.launchInfo = new JavaLaunchConfigurationInfo(launchXml); } @Override From 79430673710a84a6be59f524368e1fd7ba11c5a2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 21 May 2021 16:16:11 +0800 Subject: [PATCH 072/206] Bump version to 0.32.0 (#377) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index e0260e727..5b6f0c2c3 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.1 + 0.32.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c00d65ed3..8c76f8dd3 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 84bd01f8f..2245a3854 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.31.1 +Bundle-Version: 0.32.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.31.1.jar + lib/com.microsoft.java.debug.core-0.32.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index f9a19badc..f5e0003e3 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.1 + 0.32.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.31.1 + 0.32.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 3520fec6d..88e7c7041 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index f3ee87766..6e9099962 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.31.1 + 0.32.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4e5d368b8..4f7abb14c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.31.1 + 0.32.0 pom Java Debug Server for Visual Studio Code From f15f84bca99b6cb0f4573540a9f9fb3a1579769d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Thu, 17 Jun 2021 09:05:03 +0200 Subject: [PATCH 073/206] Ensure CompletionsResponse contains targets (#362) If a client sent a completion request without a frameId then the server returned a completion response without targets. According to the specification the response of a completions request must always contain a `targets` property. It is not optional. See https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Completions --- .../java/debug/core/adapter/handler/CompletionsHandler.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/handler/CompletionsHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java index 9f660644a..eba7d5153 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java @@ -11,8 +11,8 @@ package com.microsoft.java.debug.core.adapter.handler; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -46,7 +46,7 @@ public CompletableFuture handle(Command command, Arguments arguments, // completions should be illegal when frameId is zero, it is sent when the program is running, while during running we cannot resolve // the completion candidates if (completionsArgs.frameId == 0) { - response.body = new ArrayList<>(); + response.body = new Responses.CompletionsResponseBody(Collections.emptyList()); return CompletableFuture.completedFuture(response); } From 7a2d6debdcfc5b9af95c1ceb6edd82f4b6eab145 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 13 Jul 2021 13:41:39 +0800 Subject: [PATCH 074/206] Fix vulnerabilities (#380) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 5b6f0c2c3..b2b73793d 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -62,7 +62,7 @@ commons-io commons-io - 2.5 + 2.10.0 diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index f5e0003e3..cd27e1bce 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -40,7 +40,7 @@ commons-io commons-io - 2.5 + 2.10.0 com.microsoft.java From 900975382105e900c57bd12eb8c5573c2134bfac Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 7 Sep 2021 20:47:41 +0800 Subject: [PATCH 075/206] Fix the commons-io version issue in project file (#382) --- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 8c76f8dd3..bcc62dabd 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -2,7 +2,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 2245a3854..e6b08d24a 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -21,7 +21,7 @@ Require-Bundle: org.eclipse.core.runtime, org.apache.commons.lang3, org.eclipse.lsp4j, com.google.guava -Bundle-ClassPath: lib/commons-io-2.5.jar, +Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, From 3f50bfc9e994df8f435e02fdf70239f1ef297112 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 13 Sep 2021 13:22:38 +0800 Subject: [PATCH 076/206] Allow mainClass to be configured with unicode characters (#383) --- .../internal/ResolveMainClassHandler.java | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index 9d3b1014e..a554a6e67 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2021 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 @@ -22,6 +22,8 @@ import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.lang.model.SourceVersion; + import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -47,8 +49,10 @@ public class ResolveMainClassHandler { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - // Java command line supports two kinds of main class format: and [/] - private static final String CLASSNAME_REGX = "([$\\w]+\\.)*[$\\w]+(/([$\\w]+\\.)*[$\\w]+)?"; + private static final int CONFIGERROR_INVALID_CLASS_NAME = 1; + private static final int CONFIGERROR_MAIN_CLASS_NOT_EXIST = 2; + private static final int CONFIGERROR_MAIN_CLASS_NOT_UNIQUE = 3; + private static final int CONFIGERROR_INVALID_JAVA_PROJECT = 4; /** * resolve main class and project name. @@ -185,23 +189,41 @@ private ValidationResponse validateLaunchConfigCore(List arguments) thro private ValidationResult validateMainClass(final String mainClass, final String projectName, boolean containsExternalClasspaths) throws CoreException { if (StringUtils.isEmpty(mainClass)) { return new ValidationResult(true); - } else if (!mainClass.matches(CLASSNAME_REGX)) { - return new ValidationResult(false, String.format("ConfigError: '%s' is not a valid class name.", mainClass)); + } else if (!isValidMainClassName(mainClass)) { + return new ValidationResult(false, String.format("ConfigError: '%s' is not a valid class name.", mainClass), + CONFIGERROR_INVALID_CLASS_NAME); } if (!containsExternalClasspaths && StringUtils.isEmpty(projectName)) { List javaProjects = searchClassInProjectClasspaths(mainClass); if (javaProjects.size() == 0) { - return new ValidationResult(false, String.format("ConfigError: Main class '%s' doesn't exist in the workspace.", mainClass)); + return new ValidationResult(false, String.format("ConfigError: Main class '%s' doesn't exist in the workspace.", mainClass), + CONFIGERROR_MAIN_CLASS_NOT_EXIST); } if (javaProjects.size() > 1) { - return new ValidationResult(false, String.format("ConfigError: Main class '%s' isn't unique in the workspace.", mainClass)); + return new ValidationResult(false, String.format("ConfigError: Main class '%s' isn't unique in the workspace.", mainClass), + CONFIGERROR_MAIN_CLASS_NOT_UNIQUE); } } return new ValidationResult(true); } + // Java command line supports two kinds of main class format: and [/] + private boolean isValidMainClassName(String mainClass) { + if (StringUtils.isEmpty(mainClass)) { + return true; + } + + int index = mainClass.indexOf('/'); + if (index == -1) { + return SourceVersion.isName(mainClass); + } + + return SourceVersion.isName(mainClass.substring(0, index)) + && SourceVersion.isName(mainClass.substring(index + 1)); + } + private List searchClassInProjectClasspaths(String fullyQualifiedClassName) throws CoreException { return ResolveClasspathsHandler.getJavaProjectFromType(fullyQualifiedClassName); } @@ -212,7 +234,8 @@ private ValidationResult validateProjectName(final String mainClass, final Strin } if (JdtUtils.getJavaProject(projectName) == null) { - return new ValidationResult(false, String.format("ConfigError: The project '%s' is not a valid java project.", projectName)); + return new ValidationResult(false, String.format("ConfigError: The project '%s' is not a valid java project.", projectName), + CONFIGERROR_INVALID_JAVA_PROJECT); } return new ValidationResult(true); @@ -302,14 +325,16 @@ class ValidationResponse { class ValidationResult { boolean isValid; String message; + int kind; ValidationResult(boolean isValid) { this.isValid = isValid; } - ValidationResult(boolean isValid, String message) { + ValidationResult(boolean isValid, String message, int kind) { this.isValid = isValid; this.message = message; + this.kind = kind; } } } From 64b54e8c781e96e6e8fe1572a2e014bc091247d6 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 13 Sep 2021 15:30:35 +0800 Subject: [PATCH 077/206] Fix: classpath is not generated into argfile when using shortCommanLine=argfile (#384) --- .../java/debug/core/adapter/handler/LaunchUtils.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/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 17a4f7359..d0d99af40 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -73,11 +73,11 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws public static synchronized Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { String argfile = ""; if (ArrayUtils.isNotEmpty(classPaths)) { - argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\""; + argfile = "-cp \"" + String.join(File.pathSeparator, classPaths) + "\""; } if (ArrayUtils.isNotEmpty(modulePaths)) { - argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; + argfile += " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; } argfile = argfile.replace("\\", "\\\\"); From 9a84e7c5733db35e4b60bf1c64335870bf0192e4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 23 Sep 2021 12:27:09 +0800 Subject: [PATCH 078/206] if listing the variable value of a large object throws OOM or timeout exception, just display its basic info (#385) --- .../handler/EvaluateRequestHandler.java | 48 ++++++++++---- .../handler/VariablesRequestHandler.java | 64 ++++++++++++++----- 2 files changed, 84 insertions(+), 28 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 2a498608e..9cf99742d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2021 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 @@ -14,7 +14,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; @@ -103,10 +102,8 @@ public CompletableFuture handle(Command command, Arguments arguments, indexedVariables = ((IntegerValue) sizeValue).value(); } } - } catch (CancellationException | IllegalArgumentException | InterruptedException - | ExecutionException | UnsupportedOperationException e) { - logger.log(Level.INFO, - String.format("Failed to get the logical size for the type %s.", value.type().name()), e); + } catch (Exception e) { + logger.log(Level.INFO, "Failed to get the logical size of the variable", e); } } int referenceId = 0; @@ -114,20 +111,49 @@ public CompletableFuture handle(Command command, Arguments arguments, referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy); } - String valueString = variableFormatter.valueToString(value, options); + boolean hasErrors = false; + String valueString = null; + try { + valueString = variableFormatter.valueToString(value, options); + } catch (OutOfMemoryError e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to convert the value of a large object to a string", e); + valueString = ""; + } catch (Exception e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to resolve the variable value", e); + valueString = ""; + } + String detailsString = null; - if (sizeValue != null) { + if (hasErrors) { + // If failed to resolve the variable value, skip the details info as well. + } else if (sizeValue != null) { detailsString = "size=" + variableFormatter.valueToString(sizeValue, options); } else if (DebugSettings.getCurrent().showToString) { - detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); + try { + detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); + } catch (OutOfMemoryError e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); + detailsString = ""; + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value", e); + detailsString = ""; + } } if ("clipboard".equals(evalArguments.context) && detailsString != null) { response.body = new Responses.EvaluateResponseBody(detailsString, -1, "String", 0); } else { + String typeString = ""; + try { + typeString = variableFormatter.typeToString(value == null ? null : value.type(), options); + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to resolve the variable type", e); + typeString = ""; + } response.body = new Responses.EvaluateResponseBody((detailsString == null) ? valueString : valueString + " " + detailsString, - referenceId, variableFormatter.typeToString(value == null ? null : value.type(), options), - Math.max(indexedVariables, 0)); + referenceId, typeString, Math.max(indexedVariables, 0)); } return response; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 70d5dae31..26a0f7124 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2021 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 @@ -19,9 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -133,7 +131,12 @@ public CompletableFuture handle(Command command, Arguments arguments, try { ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable(); if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { - JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); + JavaLogicalStructure logicalStructure = null; + try { + logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to get the logical structure for the variable, fall back to the Object view.", e); + } if (isUnboundedTypeContainer && logicalStructure != null && containerEvaluateName != null) { containerEvaluateName = "((" + logicalStructure.getFullyQualifiedName() + ")" + containerEvaluateName + ")"; isUnboundedTypeContainer = false; @@ -162,11 +165,8 @@ public CompletableFuture handle(Command command, Arguments arguments, childrenList.add(variable); } } - } catch (IllegalArgumentException | CancellationException | InterruptedException | ExecutionException e) { - logger.log(Level.WARNING, - String.format("Failed to get the logical structure for the type %s, fall back to the Object view.", - containerObj.type().name()), - e); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to get the logical structure for the variable, fall back to the Object view.", e); } logicalStructure = null; @@ -241,9 +241,8 @@ public CompletableFuture handle(Command command, Arguments arguments, indexedVariables = ((IntegerValue) sizeValue).value(); } } - } catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) { - logger.log(Level.INFO, - String.format("Failed to get the logical size for the type %s.", value.type().name()), e); + } catch (Exception e) { + logger.log(Level.INFO, "Failed to get the logical size of the variable", e); } } @@ -275,15 +274,46 @@ public CompletableFuture handle(Command command, Arguments arguments, varProxy.setUnboundedType(javaVariable.isUnboundedType()); } - Types.Variable typedVariables = new Types.Variable(name, variableFormatter.valueToString(value, options), - variableFormatter.typeToString(value == null ? null : value.type(), options), - referenceId, evaluateName); + boolean hasErrors = false; + String valueString = null; + try { + valueString = variableFormatter.valueToString(value, options); + } catch (OutOfMemoryError e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to convert the value of a large object to a string", e); + valueString = ""; + } catch (Exception e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to resolve the variable value", e); + valueString = ""; + } + + String typeString = ""; + try { + typeString = variableFormatter.typeToString(value == null ? null : value.type(), options); + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to resolve the variable type", e); + typeString = ""; + } + + Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); typedVariables.indexedVariables = Math.max(indexedVariables, 0); + String detailsValue = null; - if (sizeValue != null) { + if (hasErrors) { + // If failed to resolve the variable value, skip the details info as well. + } else if (sizeValue != null) { detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options); } else if (DebugSettings.getCurrent().showToString) { - detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); + try { + detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); + } catch (OutOfMemoryError e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); + detailsValue = ""; + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value", e); + detailsValue = ""; + } } if (detailsValue != null) { From 83fe25d3d6257e72848466b5a7a37365c1d1d512 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 23 Sep 2021 12:56:29 +0800 Subject: [PATCH 079/206] Bump version to 0.33.0 (#386) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index b2b73793d..c9610d0a5 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.32.0 + 0.33.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index bcc62dabd..47284619a 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index e6b08d24a..96e3a626d 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.32.0 +Bundle-Version: 0.33.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.32.0.jar + lib/com.microsoft.java.debug.core-0.33.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index cd27e1bce..84dcc6f50 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.32.0 + 0.33.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.32.0 + 0.33.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 88e7c7041..0fffc8012 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 6e9099962..9426a408a 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.32.0 + 0.33.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4f7abb14c..b4db6d530 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.32.0 + 0.33.0 pom Java Debug Server for Visual Studio Code From a5b88da1ec7af905293421437f1e0d3e6c331dff Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Tue, 12 Oct 2021 09:20:31 +0200 Subject: [PATCH 080/206] Fix step filter: keep method exit request (#387) --- .../core/adapter/handler/StepRequestHandler.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 94e0d3078..72d14eb5d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -145,12 +145,14 @@ public CompletableFuture handle(Command command, Arguments arguments, private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; + EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); // When a breakpoint occurs, abort any pending step requests from the same thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { - threadState.deleteStepRequests(debugSession.getVM().eventRequestManager()); + threadState.deleteStepRequest(eventRequestManager); + threadState.deleteMethodExitRequest(eventRequestManager); context.getStepResultManager().removeMethodResult(threadId); if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); @@ -158,7 +160,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); - threadState.deleteStepRequests(debugSession.getVM().eventRequestManager()); + threadState.deleteStepRequest(eventRequestManager); if (isStepFiltersConfigured(context.getStepFilters())) { try { if (threadState.pendingStepType == Command.STEPIN) { @@ -181,6 +183,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // ignore. } } + threadState.deleteMethodExitRequest(eventRequestManager); if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } @@ -280,10 +283,13 @@ class ThreadState { Location stepLocation = null; Disposable eventSubscription = null; - public void deleteStepRequests(EventRequestManager manager) { - DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest); + public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); this.pendingMethodExitRequest = null; + } + + public void deleteStepRequest(EventRequestManager manager) { + DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest); this.pendingStepRequest = null; } } From da61636dcbc4970e73fa6adcb124f75c7269208e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 19 Oct 2021 10:55:12 +0800 Subject: [PATCH 081/206] Add ThirdPartyNotices.txt (#389) Signed-off-by: Jinbo Wang --- ThirdPartyNotices.txt | 437 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 ThirdPartyNotices.txt diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt new file mode 100644 index 000000000..b9fc26420 --- /dev/null +++ b/ThirdPartyNotices.txt @@ -0,0 +1,437 @@ +java-debug + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. + +1. ReactiveX/RxJava (https://github.com/ReactiveX/RxJava) +2. reactive-streams/reactive-streams-jvm (https://github.com/reactive-streams/reactive-streams-jvm) +3. apache/commons-io (https://github.com/apache/commons-io) + + +%% ReactiveX/RxJava NOTICES AND INFORMATION BEGIN HERE +========================================= + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF ReactiveX/RxJava NOTICES AND INFORMATION + +%% reactive-streams/reactive-streams-jvm NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF reactive-streams/reactive-streams-jvm NOTICES AND INFORMATION + +%% apache/commons-io NOTICES AND INFORMATION BEGIN HERE +========================================= + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF apache/commons-io NOTICES AND INFORMATION \ No newline at end of file From d5fc1fd33528876f2732cec94adc82902c8ae83e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 15 Nov 2021 10:52:28 +0800 Subject: [PATCH 082/206] Update to gson@2.8.9 (#390) --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index c9610d0a5..9fac8bde1 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -47,7 +47,7 @@ com.google.code.gson gson - 2.7 + 2.8.9 io.reactivex.rxjava2 From 934d6cb0b8ffb3fb2ee9db4702c494ea02b2df03 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 19 Nov 2021 15:32:13 +0800 Subject: [PATCH 083/206] Make DAP implementation to be encoding-neutral (#391) --- .../java/debug/core/adapter/ProcessConsole.java | 2 +- .../adapter/handler/LaunchRequestHandler.java | 17 +++++++---------- .../internal/JdtSourceLookUpProvider.java | 13 ++++--------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java index 6b384c628..3d823df91 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java @@ -129,7 +129,7 @@ public void stop() { } private void monitor(InputStream input, PublishSubject subject) { - BufferedReader reader = new BufferedReader(new InputStreamReader(input, encoding)); + BufferedReader reader = new BufferedReader(encoding == null ? new InputStreamReader(input) : new InputStreamReader(input, encoding)); final int BUFFERSIZE = 4096; char[] buffer = new char[BUFFERSIZE]; while (true) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 7d76e1ffd..031a4826b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -16,7 +16,6 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -90,23 +89,21 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R "Failed to launch debuggee VM. Missing mainClass or modulePaths/classPaths options in launch configuration.", ErrorCode.ARGUMENT_MISSING); } - if (StringUtils.isBlank(launchArguments.encoding)) { - context.setDebuggeeEncoding(StandardCharsets.UTF_8); - } else { + if (StringUtils.isNotBlank(launchArguments.encoding)) { if (!Charset.isSupported(launchArguments.encoding)) { throw AdapterUtils.createCompletionException( "Failed to launch debuggee VM. 'encoding' options in the launch configuration is not recognized.", ErrorCode.INVALID_ENCODING); } context.setDebuggeeEncoding(Charset.forName(launchArguments.encoding)); + if (StringUtils.isBlank(launchArguments.vmArgs)) { + launchArguments.vmArgs = String.format("-Dfile.encoding=%s", context.getDebuggeeEncoding().name()); + } else { + // if vmArgs already has the file.encoding settings, duplicate options for jvm will not cause an error, the right most value wins + launchArguments.vmArgs = String.format("%s -Dfile.encoding=%s", launchArguments.vmArgs, context.getDebuggeeEncoding().name()); + } } - if (StringUtils.isBlank(launchArguments.vmArgs)) { - launchArguments.vmArgs = String.format("-Dfile.encoding=%s", context.getDebuggeeEncoding().name()); - } else { - // if vmArgs already has the file.encoding settings, duplicate options for jvm will not cause an error, the right most value wins - launchArguments.vmArgs = String.format("%s -Dfile.encoding=%s", launchArguments.vmArgs, context.getDebuggeeEncoding().name()); - } context.setLaunchMode(launchArguments.noDebug ? LaunchMode.NO_DEBUG : LaunchMode.DEBUG); activeLaunchHandler.preLaunch(launchArguments, context); 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 d24d2abdd..efc7ce20c 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2021 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 @@ -17,7 +17,6 @@ import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; @@ -120,11 +119,7 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th String filePath = AdapterUtils.toPath(uri); // For file uri, read the file contents directly and pass them to the ast parser. if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { - Charset cs = (Charset) this.options.get(Constants.DEBUGGEE_ENCODING); - if (cs == null) { - cs = Charset.defaultCharset(); - } - String source = readFile(filePath, cs); + String source = readFile(filePath); parser.setSource(source.toCharArray()); /** * See the java doc for { @link ASTParser#setResolveBindings(boolean) }. @@ -293,10 +288,10 @@ private static IClassFile resolveClassFile(String uriString) { return null; } - private static String readFile(String filePath, Charset cs) { + private static String readFile(String filePath) { StringBuilder builder = new StringBuilder(); try (BufferedReader bufferReader = - new BufferedReader(new InputStreamReader(new FileInputStream(filePath), cs))) { + new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) { final int BUFFER_SIZE = 4096; char[] buffer = new char[BUFFER_SIZE]; while (true) { From 719eb8c3efd3098b75e91c5b5f866c3b1c984ce2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 22 Nov 2021 13:19:59 +0800 Subject: [PATCH 084/206] Bump version to 0.34.0 (#392) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 9fac8bde1..4009824a5 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.33.0 + 0.34.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 47284619a..51b0645e0 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 96e3a626d..ae117b544 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.33.0 +Bundle-Version: 0.34.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.33.0.jar + lib/com.microsoft.java.debug.core-0.34.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 84dcc6f50..d7cd7c6a4 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.33.0 + 0.34.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.33.0 + 0.34.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 0fffc8012..88c8461b3 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9426a408a..87692aa25 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.33.0 + 0.34.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index b4db6d530..a0f7c9b9d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.33.0 + 0.34.0 pom Java Debug Server for Visual Studio Code From f652c8d109c782f22fddec153e47bd49acee0410 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 23 Dec 2021 10:40:19 +0800 Subject: [PATCH 085/206] Migrate to Java 11 (#393) * Migrate to Java 11 --- com.microsoft.java.debug.core/.classpath | 2 +- com.microsoft.java.debug.core/pom.xml | 21 ++----------------- com.microsoft.java.debug.plugin/.classpath | 7 ++++++- .../META-INF/MANIFEST.MF | 2 +- com.microsoft.java.debug.plugin/pom.xml | 11 ++++++++++ .../internal/ResolveMainMethodHandler.java | 5 +++-- java.debug.target | 15 ++++++++++--- pom.xml | 6 +++--- 8 files changed, 39 insertions(+), 30 deletions(-) diff --git a/com.microsoft.java.debug.core/.classpath b/com.microsoft.java.debug.core/.classpath index f0257c5a5..9ba41a249 100644 --- a/com.microsoft.java.debug.core/.classpath +++ b/com.microsoft.java.debug.core/.classpath @@ -13,7 +13,7 @@ - + diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 4009824a5..09cb040af 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -28,8 +28,8 @@ maven-compiler-plugin 3.7.0 - 1.8 - 1.8 + 11 + 11 @@ -78,21 +78,4 @@ test - - - default-tools.jar - - (,9) - - - - com.sun - tools - 1.8 - system - ${java.home}/../lib/tools.jar - - - - diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 51b0645e0..a305d1f0b 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -1,6 +1,11 @@ - + + + + + + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index ae117b544..27f1ad0c0 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true Bundle-Version: 0.34.0 -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin Bundle-Vendor: Microsoft diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index d7cd7c6a4..273f12875 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -17,6 +17,17 @@ tycho-maven-plugin ${tycho-version} true + + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho-version} + + + --limit-modules + java.base,java.logging,java.xml,jdk.jdi,java.compiler + + org.apache.maven.plugins diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index 85ff58978..a3c61e814 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018-2020 Microsoft Corporation and others. + * Copyright (c) 2018-2021 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 @@ -26,6 +26,7 @@ import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.IType; @@ -167,7 +168,7 @@ private static MainMethod extractMainMethodInfo(ICompilationUnit typeRoot, IMeth private static Range getRange(ICompilationUnit typeRoot, IJavaElement element) throws JavaModelException { ISourceRange r = ((ISourceReference) element).getNameRange(); - return JDTUtils.toRange(typeRoot, r.getOffset(), r.getLength()); + return JDTUtils.toRange((IOpenable) typeRoot, r.getOffset(), r.getLength()); } static class MainMethod { diff --git a/java.debug.target b/java.debug.target index 835c63801..dcb0b8c5e 100644 --- a/java.debug.target +++ b/java.debug.target @@ -6,17 +6,26 @@ - + + + + + + + + + + - + - + diff --git a/pom.xml b/pom.xml index a0f7c9b9d..de00b34b0 100644 --- a/pom.xml +++ b/pom.xml @@ -154,9 +154,9 @@ - 201912 + 202112 p2 - https://download.eclipse.org/releases/2019-12/ + https://download.eclipse.org/releases/2021-12/202112081000/ oss.sonatype.org @@ -173,7 +173,7 @@ JBOLL.TOOLS p2 - https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.0-2018-05-16_00-46-30-H11 + https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.3-2019-11-08_11-04-22-H22/ orbit From 810d955d329e853a6e949d949e0f0f52028241a5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Jan 2022 10:20:38 +0800 Subject: [PATCH 086/206] Limit search scope of main class to source files and the specified project or paths (#395) * Limit search scope of main class to source files and the specified project or paths --- .../internal/ResolveMainClassHandler.java | 96 ++++++++++++++++--- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index a554a6e67..f70d0c044 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2021 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -12,6 +12,7 @@ package com.microsoft.java.debug.plugin.internal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -21,6 +22,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.lang.model.SourceVersion; @@ -79,15 +81,26 @@ public Object validateLaunchConfig(List arguments) throws Exception { } private List resolveMainClassCore(List arguments) { - IPath rootPath = null; if (arguments != null && arguments.size() > 0 && arguments.get(0) != null) { - rootPath = ResourceUtils.filePathFromURI((String) arguments.get(0)); - } - final ArrayList targetProjectPath = new ArrayList<>(); - if (rootPath != null) { - targetProjectPath.add(rootPath); + String argument = (String) arguments.get(0); + IProject[] projects = ProjectUtils.getAllProjects(); + if (Stream.of(projects).anyMatch(project -> Objects.equals(project.getName(), argument))) { + return resolveMainClassUnderProject(argument); + } + + IPath rootPath = ResourceUtils.filePathFromURI(argument); + if (rootPath != null) { + return resolveMainClassUnderPaths(Arrays.asList(rootPath)); + } } - IJavaSearchScope searchScope = SearchEngine.createWorkspaceScope(); + + return resolveMainClassUnderPaths(Collections.emptyList()); + } + + private List resolveMainClassUnderPaths(List parentPaths) { + // Limit to search main method from source code only. + IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), + IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); final List res = new ArrayList<>(); @@ -112,10 +125,9 @@ public void acceptSearchMatch(SearchMatch match) { } } String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); - if (projectName == null - || targetProjectPath.isEmpty() - || ResourceUtils.isContainedIn(project.getLocation(), targetProjectPath) - || isContainedInInvisibleProject(project, targetProjectPath)) { + if (parentPaths.isEmpty() + || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) + || isContainedInInvisibleProject(project, parentPaths)) { String filePath = null; if (match.getResource() instanceof IFile) { @@ -149,6 +161,66 @@ public void acceptSearchMatch(SearchMatch match) { return resolutions; } + private List resolveMainClassUnderProject(final String projectName) { + // Limit to search main method from source code only. + IJavaProject javaProject = ProjectUtils.getJavaProject(projectName); + IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(javaProject == null ? new IJavaProject[0] : new IJavaProject[] {javaProject}, + IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); + SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + final List res = new ArrayList<>(); + SearchRequestor requestor = new SearchRequestor() { + @Override + public void acceptSearchMatch(SearchMatch match) { + Object element = match.getElement(); + if (element instanceof IMethod) { + IMethod method = (IMethod) element; + try { + if (method.isMainMethod()) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; + } + } + + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore + } + } + res.add(new ResolutionItem(mainClass, projectName, filePath)); + } + } + } + } catch (JavaModelException e) { + // ignore + } + } + } + }; + 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("Searching the main class failure: %s", e.toString()), e); + } + + List resolutions = res.stream().distinct().collect(Collectors.toList()); + Collections.sort(resolutions); + return resolutions; + } + private boolean isContainedInInvisibleProject(IProject project, Collection rootPaths) { if (project == null) { return false; From e6655ead412ceed5afa37b3781ed84e0b8ba425a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Jan 2022 11:08:30 +0800 Subject: [PATCH 087/206] Bump version to 0.35.0 (#396) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 09cb040af..f081491ae 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.34.0 + 0.35.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index a305d1f0b..de236212a 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 27f1ad0c0..1c0eaf78c 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.34.0 +Bundle-Version: 0.35.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.34.0.jar + lib/com.microsoft.java.debug.core-0.35.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 273f12875..ffc3ded41 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.34.0 + 0.35.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.34.0 + 0.35.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 88c8461b3..782edf279 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 87692aa25..b21f4ba96 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.34.0 + 0.35.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index de00b34b0..007cf78be 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.34.0 + 0.35.0 pom Java Debug Server for Visual Studio Code From fca1d23dd000ef6e5885047f82097b76edf8fd3f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 16 Mar 2022 19:45:53 +0800 Subject: [PATCH 088/206] Bump java deps version (#403) --- com.microsoft.java.debug.core/pom.xml | 6 +++--- com.microsoft.java.debug.plugin/.classpath | 6 +++--- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 6 +++--- com.microsoft.java.debug.plugin/pom.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index f081491ae..811a32a64 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -52,17 +52,17 @@ io.reactivex.rxjava2 rxjava - 2.1.1 + 2.2.21 org.reactivestreams reactive-streams - 1.0.0 + 1.0.3 commons-io commons-io - 2.10.0 + 2.11.0 diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index de236212a..0362be354 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -7,10 +7,10 @@ - + - - + + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 1c0eaf78c..925c853aa 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -21,8 +21,8 @@ Require-Bundle: org.eclipse.core.runtime, org.apache.commons.lang3, org.eclipse.lsp4j, com.google.guava -Bundle-ClassPath: lib/commons-io-2.10.0.jar, +Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., - lib/rxjava-2.1.1.jar, - lib/reactive-streams-1.0.0.jar, + lib/rxjava-2.2.21.jar, + lib/reactive-streams-1.0.3.jar, lib/com.microsoft.java.debug.core-0.35.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index ffc3ded41..44eecc5fb 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -41,17 +41,17 @@ io.reactivex.rxjava2 rxjava - 2.1.1 + 2.2.21 org.reactivestreams reactive-streams - 1.0.0 + 1.0.3 commons-io commons-io - 2.10.0 + 2.11.0 com.microsoft.java From 7f6f76be4fab5c94b6bc5b090f2d8a85b96bfb80 Mon Sep 17 00:00:00 2001 From: Karl von Randow Date: Thu, 17 Mar 2022 21:36:39 +1300 Subject: [PATCH 089/206] Report more detail in VM start errors (#398) * Report more detail in VM start errors Co-authored-by: Jinbo Wang --- .../java/debug/core/LaunchException.java | 51 +++++++++ .../adapter/handler/LaunchRequestHandler.java | 17 +++ .../internal/AdvancedLaunchingConnector.java | 104 ++++++++++++++++-- 3 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java new file mode 100644 index 000000000..6c1e1a513 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2018-2021 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.core; + +import com.sun.jdi.connect.VMStartException; + +/** + * Extends {@link VMStartException} to provide more detail about the failed process + * from before it is destroyed. + */ +public class LaunchException extends VMStartException { + + boolean exited; + int exitStatus; + String stdout; + String stderr; + + public LaunchException(String message, Process process, boolean exited, int exitStatus, String stdout, String stderr) { + super(message, process); + this.exited = exited; + this.exitStatus = exitStatus; + this.stdout = stdout; + this.stderr = stderr; + } + + public boolean isExited() { + return exited; + } + + public int getExitStatus() { + return exitStatus; + } + + public String getStdout() { + return stdout; + } + + public String getStderr() { + return stderr; + } + +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 031a4826b..37182e9fd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -41,6 +41,7 @@ import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.LaunchException; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -245,6 +246,22 @@ protected CompletableFuture launch(LaunchArguments launchArguments, Re .subscribe((event) -> context.getProtocolServer().sendEvent(event)); debuggeeConsole.start(); resultFuture.complete(response); + } catch (LaunchException e) { + if (StringUtils.isNotBlank(e.getStdout())) { + OutputEvent event = convertToOutputEvent(e.getStdout(), Category.stdout, context); + context.getProtocolServer().sendEvent(event); + } + if (StringUtils.isNotBlank(e.getStderr())) { + OutputEvent event = convertToOutputEvent(e.getStderr(), Category.stderr, context); + context.getProtocolServer().sendEvent(event); + } + + resultFuture.completeExceptionally( + new DebugException( + String.format("Failed to launch debuggee VM. Reason: %s", e.getMessage()), + ErrorCode.LAUNCH_FAILURE.getId() + ) + ); } catch (IOException | IllegalConnectorArgumentsException | VMStartException e) { resultFuture.completeExceptionally( new DebugException( diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index 607d26a4e..3551ab2e6 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -13,16 +13,22 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import org.apache.commons.io.IOUtils; import org.eclipse.jdi.internal.VirtualMachineImpl; import org.eclipse.jdi.internal.VirtualMachineManagerImpl; import org.eclipse.jdi.internal.connect.SocketLaunchingConnectorImpl; import org.eclipse.jdi.internal.connect.SocketListeningConnectorImpl; import com.microsoft.java.debug.core.DebugUtility; +import com.microsoft.java.debug.core.LaunchException; import com.sun.jdi.VirtualMachine; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.IllegalConnectorArgumentsException; @@ -83,18 +89,100 @@ public VirtualMachine launch(Map connectionArgs) String address = listenConnector.startListening(args); String[] cmds = constructLaunchCommand(connectionArgs, address); - Process process = Runtime.getRuntime().exec(cmds, envVars, workingDir); - VirtualMachineImpl vm; + /* Launch the Java process */ + final Process process = Runtime.getRuntime().exec(cmds, envVars, workingDir); + + /* A Future that will be completed if we successfully connect to the launched process, or + will fail with an Exception if we do not. + */ + final CompletableFuture result = new CompletableFuture<>(); + + /* Listen for the debug connection from the Java process */ + CompletableFuture.runAsync(() -> { + try { + VirtualMachineImpl vm = (VirtualMachineImpl) listenConnector.accept(args); + vm.setLaunchedProcess(process); + result.complete(vm); + } catch (IllegalConnectorArgumentsException e) { + result.completeExceptionally(e); + } catch (IOException e) { + if (result.isDone()) { + /* The result Future has already been completed by the Process onExit hook */ + return; + } + + final String stdout = streamToString(process.getInputStream()); + final String stderr = streamToString(process.getErrorStream()); + + process.destroy(); + + result.completeExceptionally(new LaunchException( + String.format("VM did not connect within given time: %d ms", ACCEPT_TIMEOUT), + process, + false, + -1, + stdout, + stderr + )); + } catch (RuntimeException e) { + result.completeExceptionally(e); + } + }); + + /* Wait for the Java process to exit; if it exits before the debug connection is made, report it as an error. */ + process.onExit().thenAcceptAsync(theProcess -> { + if (result.isDone()) { + /* The result Future has already been completed by successfully connecting to the debug connection */ + return; + } + + final int exitStatus = theProcess.exitValue(); + final String stdout = streamToString(process.getInputStream()); + final String stderr = streamToString(process.getErrorStream()); + + result.completeExceptionally(new LaunchException( + String.format("VM exited with status %d", exitStatus), + theProcess, + true, + exitStatus, + stdout, + stderr + )); + + /* Stop the debug connection attempt */ + try { + listenConnector.stopListening(args); + } catch (IOException e) { + /* Ignore */ + } + }); + try { - vm = (VirtualMachineImpl) listenConnector.accept(args); - } catch (IOException | IllegalConnectorArgumentsException e) { - process.destroy(); - throw new VMStartException(String.format("VM did not connect within given time: %d ms", ACCEPT_TIMEOUT), process); + return result.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } else if (e.getCause() instanceof IllegalConnectorArgumentsException) { + throw (IllegalConnectorArgumentsException) e.getCause(); + } else if (e.getCause() instanceof VMStartException) { + throw (VMStartException) e.getCause(); + } else if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } else { + throw new IllegalStateException("Unexpected exception thrown when launching VM", e.getCause()); + } + } catch (InterruptedException e) { + throw new VMStartException("VM start interrupted", process); } + } - vm.setLaunchedProcess(process); - return vm; + private String streamToString(final InputStream inputStream) { + try { + return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } catch (IOException ioe) { + return null; + } } private static String[] constructLaunchCommand(Map launchingOptions, String address) { From 798d259d5078ccb81c9d233df67c70ca1553c259 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 22 Mar 2022 22:59:28 -0400 Subject: [PATCH 090/206] Send logpoint messages to the debug console (#402) * Send logpoint messages as protocol events Instead of printing them to `System.out` of the process being debugged. This should be paired with a change in vscode-java-debug to receive the message and send it to the vscode debug console. --- .../core/adapter/handler/SetBreakpointsRequestHandler.java | 7 +++++++ .../debug/plugin/internal/eval/JdtEvaluationProvider.java | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index fe246a61e..2bc2b5f05 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -44,6 +44,7 @@ import com.sun.jdi.BooleanValue; import com.sun.jdi.Field; import com.sun.jdi.ObjectReference; +import com.sun.jdi.StringReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; @@ -222,6 +223,12 @@ public static boolean handleEvaluationResult(IDebugAdapterContext context, Threa context.getProtocolServer().sendEvent(new Events.UserNotificationEvent( Events.UserNotificationEvent.NotificationType.ERROR, String.format("[Logpoint] Log message '%s' error: %s", breakpoint.getLogMessage(), ex.getMessage()))); + } else if (value != null) { + if (value instanceof StringReference) { + String message = ((StringReference) value).value(); + context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput( + message + System.lineSeparator())); + } } return true; } else { diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index 21e2dff6e..e014552a8 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -226,9 +226,9 @@ private String logMessageToExpression(String logMessage) { } if (arguments.size() > 0) { - return "System.out.println(String.format(\"" + format + "\"," + String.join(",", arguments) + "))"; + return "String.format(\"" + format + "\"," + String.join(",", arguments) + ")"; } else { - return "System.out.println(\"" + format + "\")"; + return "\"" + format + "\""; } } From f704199702097475e268a7b7aa25c84d046c5b38 Mon Sep 17 00:00:00 2001 From: Shi Chen Date: Thu, 24 Mar 2022 19:12:42 +0800 Subject: [PATCH 091/206] Support lazy loading object values from toString() (#401) --- .../handler/VariablesRequestHandler.java | 68 +++++++++++++++---- .../variables/VariableDetailUtils.java | 14 ++++ .../core/adapter/variables/VariableProxy.java | 13 +++- .../java/debug/core/protocol/Types.java | 9 +++ 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 26a0f7124..8f37ef574 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -47,6 +47,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.VariablesArguments; +import com.microsoft.java.debug.core.protocol.Types.VariablePresentationHint; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.AbsentInformationException; @@ -94,6 +95,15 @@ public CompletableFuture handle(Command command, Arguments arguments, } VariableProxy containerNode = (VariableProxy) container; + + if (containerNode.isLazyVariable() && DebugSettings.getCurrent().showToString) { + Types.Variable typedVariable = this.resolveLazyVariable(context, containerNode, variableFormatter, options, evaluationEngine); + if (typedVariable != null) { + list.add(typedVariable); + response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); + } + } List childrenList = new ArrayList<>(); IStackFrameManager stackFrameManager = context.getStackFrameManager(); String containerEvaluateName = containerNode.getEvaluateName(); @@ -266,10 +276,9 @@ public CompletableFuture handle(Command command, Arguments arguments, } } - int referenceId = 0; + VariableProxy varProxy = null; if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) { - VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName); - referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); + varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName); varProxy.setIndexedVariable(indexedVariables >= 0); varProxy.setUnboundedType(javaVariable.isUnboundedType()); } @@ -296,26 +305,38 @@ public CompletableFuture handle(Command command, Arguments arguments, typeString = ""; } - Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); - typedVariables.indexedVariables = Math.max(indexedVariables, 0); - String detailsValue = null; if (hasErrors) { // If failed to resolve the variable value, skip the details info as well. } else if (sizeValue != null) { detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options); } else if (DebugSettings.getCurrent().showToString) { - try { - detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); - } catch (OutOfMemoryError e) { - logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); - detailsValue = ""; - } catch (Exception e) { - logger.log(Level.SEVERE, "Failed to compute the toString() value", e); - detailsValue = ""; + if (VariableDetailUtils.isLazyLoadingSupported(value) && varProxy != null) { + varProxy.setLazyVariable(true); + } else { + try { + detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); + } catch (OutOfMemoryError e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); + detailsValue = ""; + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value", e); + detailsValue = ""; + } } } + int referenceId = 0; + if (varProxy != null) { + referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); + } + + Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); + typedVariables.indexedVariables = Math.max(indexedVariables, 0); + if (varProxy != null && varProxy.isLazyVariable()) { + typedVariables.presentationHint = new VariablePresentationHint(true); + } + if (detailsValue != null) { typedVariables.value = typedVariables.value + " " + detailsValue; } @@ -331,6 +352,25 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter, + Map options, IEvaluationProvider evaluationEngine) { + VariableProxy valueReferenceProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), + containerNode.getProxiedVariable(), null /** container */, containerNode.getEvaluateName()); + valueReferenceProxy.setIndexedVariable(containerNode.isIndexedVariable()); + valueReferenceProxy.setUnboundedType(containerNode.isUnboundedType()); + int referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), valueReferenceProxy); + // this proxiedVariable is intermediate object, see https://github.com/microsoft/vscode/issues/135147#issuecomment-1076240074 + Object proxiedVariable = containerNode.getProxiedVariable(); + if (proxiedVariable instanceof ObjectReference) { + ObjectReference variable = (ObjectReference) proxiedVariable; + String valueString = variableFormatter.valueToString(variable, options); + String detailString = VariableDetailUtils.formatDetailsValue(variable, containerNode.getThread(), variableFormatter, options, + evaluationEngine); + return new Types.Variable("", valueString + " " + detailString, "", referenceId, containerNode.getEvaluateName()); + } + return null; + } + private Set getDuplicateNames(Collection list) { Set result = new HashSet<>(); Set set = new HashSet<>(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java index e82ab79f9..c6d0d38e0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java @@ -154,4 +154,18 @@ private static boolean isClassType(Value value, String typeName) { return Objects.equals(((ObjectReference) value).type().name(), typeName); } + + public static boolean isLazyLoadingSupported(Value value) { + if (isClassType(value, STRING_TYPE)) { + return false; + } + if (!(value instanceof ObjectReference)) { + return false; + } + String inheritedType = findInheritedType(value, COLLECTION_TYPES); + if (inheritedType == null && !containsToStringMethod((ObjectReference) value)) { + return false; + } + return true; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java index 9f208464c..5ebf93735 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java @@ -24,6 +24,7 @@ public class VariableProxy { private final String evaluateName; private boolean isIndexedVariable; private boolean isUnboundedType = false; + private boolean isLazyVariable = false; /** * Create a variable reference. @@ -75,7 +76,8 @@ public boolean equals(Object obj) { } VariableProxy other = (VariableProxy) obj; return Objects.equals(scopeName, other.scopeName) && Objects.equals(getThreadId(), other.getThreadId()) - && Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName); + && Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName) + && Objects.equals(isLazyVariable, other.isLazyVariable); } public long getThreadId() { @@ -109,4 +111,13 @@ public boolean isUnboundedType() { public void setUnboundedType(boolean isUnboundedType) { this.isUnboundedType = isUnboundedType; } + + public boolean isLazyVariable() { + return isLazyVariable; + } + + public void setLazyVariable(boolean isLazyVariable) { + this.isLazyVariable = isLazyVariable; + } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 0b9a0494a..f0a91ce88 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -90,6 +90,7 @@ public static class Variable { public int namedVariables; public int indexedVariables; public String evaluateName; + public VariablePresentationHint presentationHint; /** * Constructor. @@ -343,6 +344,14 @@ public static class ExceptionDetails { public ExceptionDetails[] innerException; } + public static class VariablePresentationHint { + public boolean lazy; + + public VariablePresentationHint(boolean lazy) { + this.lazy = lazy; + } + } + public static class Capabilities { public boolean supportsConfigurationDoneRequest; public boolean supportsHitConditionalBreakpoints; From 8a1c9752e0df8ef241ba27fba1dedad8796691fa Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Mar 2022 12:48:22 +0800 Subject: [PATCH 092/206] customRequest 'processId' to get the debugging Java process id (#399) * customRequest 'processId' to get Java process id * send a custom notification when processId/shellProcessId is ready --- .../java/debug/core/adapter/DebugAdapter.java | 4 +- .../core/adapter/DebugAdapterContext.java | 23 ++++++++++ .../core/adapter/IDebugAdapterContext.java | 8 ++++ .../adapter/handler/LaunchRequestHandler.java | 11 +++++ .../handler/LaunchWithDebuggingDelegate.java | 10 +++++ .../LaunchWithoutDebuggingDelegate.java | 17 +++++++- .../adapter/handler/ProcessIdHandler.java | 43 +++++++++++++++++++ .../java/debug/core/protocol/Events.java | 22 ++++++++++ .../java/debug/core/protocol/Requests.java | 1 + .../java/debug/core/protocol/Responses.java | 29 +++++++++++-- 10 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 4342c48d3..afdb5759c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -32,6 +32,7 @@ import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler; import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.ProcessIdHandler; import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; @@ -125,10 +126,11 @@ private void initialize() { registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); registerHandlerForDebug(new InlineValuesRequestHandler()); registerHandlerForDebug(new RefreshVariablesHandler()); + registerHandlerForDebug(new ProcessIdHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); - + registerHandlerForNoDebug(new ProcessIdHandler()); } private void registerHandlerForDebug(IDebugRequestHandler handler) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 395d39ec6..30fb12200 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -54,6 +54,9 @@ public class DebugAdapterContext implements IDebugAdapterContext { private Path classpathJar = null; private Path argsfile = null; + private long shellProcessId = -1; + private long processId = -1; + private IdCollection sourceReferences = new IdCollection<>(); private RecyclableObjectPool recyclableIdPool = new RecyclableObjectPool<>(); private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter(); @@ -326,4 +329,24 @@ public IBreakpointManager getBreakpointManager() { public IStepResultManager getStepResultManager() { return stepResultManager; } + + @Override + public long getProcessId() { + return this.processId; + } + + @Override + public long getShellProcessId() { + return this.shellProcessId; + } + + @Override + public void setProcessId(long processId) { + this.processId = processId; + } + + @Override + public void setShellProcessId(long shellProcessId) { + this.shellProcessId = shellProcessId; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 4f843fd09..a1b21ecee 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -128,4 +128,12 @@ public interface IDebugAdapterContext { IBreakpointManager getBreakpointManager(); IStepResultManager getStepResultManager(); + + void setShellProcessId(long shellProcessId); + + long getShellProcessId(); + + void setProcessId(long processId); + + long getProcessId(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 37182e9fd..fec967b84 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -138,6 +138,17 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } return launch(launchArguments, response, context).thenCompose(res -> { + long processId = context.getProcessId(); + long shellProcessId = context.getShellProcessId(); + if (context.getDebuggeeProcess() != null) { + processId = context.getDebuggeeProcess().pid(); + } + + // If processId or shellProcessId exist, send a notification to client. + if (processId > 0 || shellProcessId > 0) { + context.getProtocolServer().sendEvent(new Events.ProcessIdNotification(processId, shellProcessId)); + } + LaunchUtils.releaseTempLaunchFile(context.getClasspathJar()); LaunchUtils.releaseTempLaunchFile(context.getArgsfile()); if (res.success) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 138455025..11935fcbe 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.SystemUtils; import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.DebugSession; @@ -45,6 +46,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; import com.microsoft.java.debug.core.protocol.Requests.RunInTerminalRequestArguments; +import com.microsoft.java.debug.core.protocol.Responses.RunInTerminalResponseBody; import com.sun.jdi.VirtualMachine; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.IllegalConnectorArgumentsException; @@ -102,6 +104,14 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume if (runResponse != null) { if (runResponse.success) { try { + try { + RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson( + JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class); + context.setProcessId(terminalResponse.processId); + context.setShellProcessId(terminalResponse.shellProcessId); + } catch (JsonSyntaxException e) { + logger.severe("Failed to resolve runInTerminal response: " + e.toString()); + } VirtualMachine vm = listenConnector.accept(args); vmHandler.connectVirtualMachine(vm); context.setDebugSession(new DebugSession(vm)); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index c993dc5f1..7b9a5be10 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -21,6 +21,7 @@ import java.util.logging.Logger; import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.adapter.ErrorCode; @@ -33,6 +34,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; import com.microsoft.java.debug.core.protocol.Requests.RunInTerminalRequestArguments; +import com.microsoft.java.debug.core.protocol.Responses.RunInTerminalResponseBody; import com.sun.jdi.connect.IllegalConnectorArgumentsException; import com.sun.jdi.connect.VMStartException; @@ -112,8 +114,19 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume context.getProtocolServer().sendRequest(request, RUNINTERMINAL_TIMEOUT).whenComplete((runResponse, ex) -> { if (runResponse != null) { if (runResponse.success) { - // Without knowing the pid, debugger has lost control of the process. - // So simply send `terminated` event to end the session. + try { + RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson( + JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class); + context.setProcessId(terminalResponse.processId); + context.setShellProcessId(terminalResponse.shellProcessId); + } catch (JsonSyntaxException e) { + logger.severe("Failed to resolve runInTerminal response: " + e.toString()); + } + + // TODO: Since the RunInTerminal request will return the pid or parent shell + // pid now, the debugger is able to use this pid to monitor the lifecycle + // of the running Java process. There is no need to terminate the debug + // session early here. context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); resultFuture.complete(response); } else { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java new file mode 100644 index 000000000..d3eb5ad13 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2022 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.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Responses.ProcessIdResponseBody; + +public class ProcessIdHandler implements IDebugRequestHandler { + @Override + public List getTargetCommands() { + return Arrays.asList(Command.PROCESSID); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + long processId = context.getProcessId(); + long shellProcessId = context.getShellProcessId(); + if (context.getDebuggeeProcess() != null) { + processId = context.getDebuggeeProcess().pid(); + } + + response.body = new ProcessIdResponseBody(processId, shellProcessId); + return CompletableFuture.completedFuture(response); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index 5397c418e..681ec543d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -283,4 +283,26 @@ public InvalidatedEvent(InvalidatedAreas area, int frameId) { this.frameId = frameId; } } + + public static class ProcessIdNotification extends DebugEvent { + /** + * The process ID. + */ + public long processId = -1; + /** + * The process ID of the terminal shell if the process is running in a terminal shell. + */ + public long shellProcessId = -1; + + public ProcessIdNotification(long processId) { + super("processid"); + this.processId = processId; + } + + public ProcessIdNotification(long processId, long shellProcessId) { + super("processid"); + this.processId = processId; + this.shellProcessId = shellProcessId; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index e31a65261..9b444155c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -410,6 +410,7 @@ public static enum Command { PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), INLINEVALUES("inlineValues", InlineValuesArguments.class), REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), + PROCESSID("processId", Arguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index cc349b6e7..2210c8e93 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -39,11 +39,34 @@ public InitializeResponseBody(Types.Capabilities capabilities) { } } - public static class RunInTerminalResponseBody extends ResponseBody { - public int processId; + public static class ProcessIdResponseBody extends ResponseBody { + /** + * The process ID. + */ + public long processId = -1; + /** + * The process ID of the terminal shell if the process is running in a terminal shell. + */ + public long shellProcessId = -1; + + public ProcessIdResponseBody(long processId) { + this.processId = processId; + } - public RunInTerminalResponseBody(int processId) { + public ProcessIdResponseBody(long processId, long shellProcessId) { this.processId = processId; + this.shellProcessId = shellProcessId; + } + } + + public static class RunInTerminalResponseBody extends ProcessIdResponseBody { + + public RunInTerminalResponseBody(long processId) { + super(processId); + } + + public RunInTerminalResponseBody(long processId, long shellProcessId) { + super(processId, shellProcessId); } } From 625fe84cef4825a4b6b476d04383f5168e81708d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Mar 2022 15:06:44 +0800 Subject: [PATCH 093/206] Up version to 0.36.0 (#406) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 811a32a64..4c67c37f4 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.35.0 + 0.36.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 0362be354..94b396327 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 925c853aa..0f59e7eb9 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.35.0 +Bundle-Version: 0.36.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.3.jar, - lib/com.microsoft.java.debug.core-0.35.0.jar + lib/com.microsoft.java.debug.core-0.36.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 44eecc5fb..1e8e8c5d0 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.35.0 + 0.36.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.35.0 + 0.36.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 782edf279..ab60e9b37 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index b21f4ba96..9e02c6804 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.35.0 + 0.36.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 007cf78be..b60bc4cf5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.35.0 + 0.36.0 pom Java Debug Server for Visual Studio Code From e3cf474244163561d74c9a2dec3047fa0ed025ab Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 24 May 2022 14:41:36 +0800 Subject: [PATCH 094/206] Capture Java process pid and display debug toolbar when running Java in terminal (#413) * Capture Java process pid and display debug toolbar when running Java in terminal --- ...connectRequestWithoutDebuggingHandler.java | 9 +- .../core/adapter/handler/LaunchUtils.java | 190 +++++++++++++++++- .../handler/LaunchWithDebuggingDelegate.java | 8 +- .../LaunchWithoutDebuggingDelegate.java | 24 ++- 4 files changed, 222 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java index bb56d4052..59d750bcd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2022 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 @@ -11,6 +11,8 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.util.Optional; + import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; @@ -25,6 +27,11 @@ public void destroyDebugSession(Command command, Arguments arguments, Response r Process debuggeeProcess = context.getDebuggeeProcess(); if (debuggeeProcess != null && disconnectArguments.terminateDebuggee) { debuggeeProcess.destroy(); + } else if (context.getProcessId() > 0 && disconnectArguments.terminateDebuggee) { + Optional debuggeeHandle = ProcessHandle.of(context.getProcessId()); + if (debuggeeHandle.isPresent()) { + debuggeeHandle.get().destroy(); + } } } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index d0d99af40..fc89bfa7c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2021 Microsoft Corporation and others. +* Copyright (c) 2021-2022 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 @@ -11,28 +11,39 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.adapter.AdapterUtils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; public class LaunchUtils { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static Set tempFilesInUse = new HashSet<>(); /** @@ -102,6 +113,171 @@ public static void releaseTempLaunchFile(Path tempFile) { } } + public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String javaCommand, int timeout/*ms*/) { + ProcessHandle shellProcess = ProcessHandle.of(shellPid).orElse(null); + if (shellProcess != null) { + int retry = 0; + final int INTERVAL = 100; + final int maxRetries = timeout / INTERVAL; + final boolean isCygwinShell = isCygwinShell(shellProcess.info().command().orElse(null)); + while (retry <= maxRetries) { + Optional subProcessHandle = shellProcess.descendants().filter(proc -> { + String command = proc.info().command().orElse(""); + return Objects.equals(command, javaCommand) || command.endsWith("\\java.exe") || command.endsWith("/java"); + }).findFirst(); + + if (subProcessHandle.isPresent()) { + logger.info("shellPid: " + shellPid + ", javaPid: " + subProcessHandle.get().pid()); + return subProcessHandle.get(); + } else if (isCygwinShell) { + long javaPid = findJavaProcessByCygwinPsCommand(shellProcess, javaCommand); + if (javaPid > 0) { + logger.info("[Cygwin Shell] shellPid: " + shellPid + ", javaPid: " + javaPid); + return ProcessHandle.of(javaPid).orElse(null); + } + } + + retry++; + if (retry > maxRetries) { + break; + } + + try { + Thread.sleep(INTERVAL); + } catch (InterruptedException e) { + // do nothing + } + logger.info("Retry to find Java subProcess of shell pid " + shellPid); + } + } + + return null; + } + + private static long findJavaProcessByCygwinPsCommand(ProcessHandle shellProcess, String javaCommand) { + String psCommand = detectPsCommandPath(shellProcess.info().command().orElse(null)); + if (psCommand == null) { + return -1; + } + + BufferedReader psReader = null; + List psProcs = new ArrayList<>(); + List javaCandidates = new ArrayList<>(); + try { + String[] headers = null; + int pidIndex = -1; + int ppidIndex = -1; + int winpidIndex = -1; + String line; + String javaExeName = Paths.get(javaCommand).toFile().getName().replaceFirst("\\.exe$", ""); + + Process p = Runtime.getRuntime().exec(new String[] {psCommand, "-l"}); + psReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + /** + * Here is a sample output when running ps command in Cygwin/MINGW64 shell. + * PID PPID PGID WINPID TTY UID STIME COMMAND + * 1869 1 1869 7852 cons2 4096 15:29:27 /usr/bin/bash + * 2271 1 2271 30820 cons4 4096 19:38:30 /usr/bin/bash + * 1812 1 1812 21540 cons1 4096 15:05:03 /usr/bin/bash + * 2216 1 2216 11328 cons3 4096 19:38:18 /usr/bin/bash + * 1720 1 1720 5404 cons0 4096 13:46:42 /usr/bin/bash + * 2269 2216 2269 6676 cons3 4096 19:38:21 /c/Program Files/Microsoft/jdk-11.0.14.9-hotspot/bin/java + * 1911 1869 1869 29708 cons2 4096 15:29:31 /c/Program Files/nodejs/node + * 2315 2271 2315 18064 cons4 4096 19:38:34 /usr/bin/ps + */ + while ((line = psReader.readLine()) != null) { + String[] cols = line.strip().split("\\s+"); + if (headers == null) { + headers = cols; + pidIndex = ArrayUtils.indexOf(headers, "PID"); + ppidIndex = ArrayUtils.indexOf(headers, "PPID"); + winpidIndex = ArrayUtils.indexOf(headers, "WINPID"); + if (pidIndex < 0 || ppidIndex < 0 || winpidIndex < 0) { + logger.warning("Failed to find Java process because ps command is not the standard Cygwin ps command."); + return -1; + } + } else if (cols.length >= headers.length) { + long pid = Long.parseLong(cols[pidIndex]); + long ppid = Long.parseLong(cols[ppidIndex]); + long winpid = Long.parseLong(cols[winpidIndex]); + PsProcess process = new PsProcess(pid, ppid, winpid); + psProcs.add(process); + if (cols[cols.length - 1].endsWith("/" + javaExeName) || cols[cols.length - 1].endsWith("/java")) { + javaCandidates.add(process); + } + } + } + } catch (Exception err) { + logger.log(Level.WARNING, "Failed to find Java process by Cygwin ps command.", err); + } finally { + if (psReader != null) { + try { + psReader.close(); + } catch (IOException e) { + // ignore + } + } + } + + if (!javaCandidates.isEmpty()) { + Set descendantWinpids = shellProcess.descendants().map(proc -> proc.pid()).collect(Collectors.toSet()); + long shellWinpid = shellProcess.pid(); + for (PsProcess javaCandidate: javaCandidates) { + if (descendantWinpids.contains(javaCandidate.winpid)) { + return javaCandidate.winpid; + } + + for (PsProcess psProc : psProcs) { + if (javaCandidate.ppid != psProc.pid) { + continue; + } + + if (descendantWinpids.contains(psProc.winpid) || psProc.winpid == shellWinpid) { + return javaCandidate.winpid; + } + + break; + } + } + } + + return -1; + } + + private static boolean isCygwinShell(String shellPath) { + if (!SystemUtils.IS_OS_WINDOWS || shellPath == null) { + return false; + } + + String lowerShellPath = shellPath.toLowerCase(); + return lowerShellPath.endsWith("git\\bin\\bash.exe") + || lowerShellPath.endsWith("git\\usr\\bin\\bash.exe") + || lowerShellPath.endsWith("mintty.exe") + || lowerShellPath.endsWith("cygwin64\\bin\\bash.exe") + || (lowerShellPath.endsWith("bash.exe") && detectPsCommandPath(shellPath) != null) + || (lowerShellPath.endsWith("sh.exe") && detectPsCommandPath(shellPath) != null); + } + + private static String detectPsCommandPath(String shellPath) { + if (shellPath == null) { + return null; + } + + Path psPath = Paths.get(shellPath, "..\\ps.exe"); + if (!Files.exists(psPath)) { + psPath = Paths.get(shellPath, "..\\..\\usr\\bin\\ps.exe"); + if (!Files.exists(psPath)) { + psPath = null; + } + } + + if (psPath == null) { + return null; + } + + return psPath.normalize().toString(); + } + private static Path tmpdir = null; private static synchronized Path getTmpDir() throws IOException { @@ -156,4 +332,16 @@ private static String getMd5(String input) { return Integer.toString(input.hashCode(), Character.MAX_RADIX); } } + + private static class PsProcess { + long pid; + long ppid; + long winpid; + + public PsProcess(long pid, long ppid, long winpid) { + this.pid = pid; + this.ppid = ppid; + this.winpid = winpid; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 11935fcbe..6506e93f0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -116,6 +116,12 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume vmHandler.connectVirtualMachine(vm); context.setDebugSession(new DebugSession(vm)); logger.info("Launching debuggee in terminal console succeeded."); + if (context.getShellProcessId() > 0) { + ProcessHandle debuggeeProcess = LaunchUtils.findJavaProcessInTerminalShell(context.getShellProcessId(), cmds[0], 0); + if (debuggeeProcess != null) { + context.setProcessId(debuggeeProcess.pid()); + } + } resultFuture.complete(response); } catch (TransportTimeoutException e) { int commandLength = StringUtils.length(launchArguments.cwd) + 1; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index 7b9a5be10..1718e6a36 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2022 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 @@ -114,20 +114,32 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume context.getProtocolServer().sendRequest(request, RUNINTERMINAL_TIMEOUT).whenComplete((runResponse, ex) -> { if (runResponse != null) { if (runResponse.success) { + ProcessHandle debuggeeProcess = null; try { RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson( JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class); context.setProcessId(terminalResponse.processId); context.setShellProcessId(terminalResponse.shellProcessId); + + if (terminalResponse.processId > 0) { + debuggeeProcess = ProcessHandle.of(terminalResponse.processId).orElse(null); + } else if (terminalResponse.shellProcessId > 0) { + debuggeeProcess = LaunchUtils.findJavaProcessInTerminalShell(terminalResponse.shellProcessId, cmds[0], 3000); + } + + if (debuggeeProcess != null) { + context.setProcessId(debuggeeProcess.pid()); + debuggeeProcess.onExit().thenAcceptAsync(proc -> { + context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); + }); + } } catch (JsonSyntaxException e) { logger.severe("Failed to resolve runInTerminal response: " + e.toString()); } - // TODO: Since the RunInTerminal request will return the pid or parent shell - // pid now, the debugger is able to use this pid to monitor the lifecycle - // of the running Java process. There is no need to terminate the debug - // session early here. - context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); + if (debuggeeProcess == null || !debuggeeProcess.isAlive()) { + context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); + } resultFuture.complete(response); } else { resultFuture.completeExceptionally( From 9e017f3382b5319dc8ba918d60df01736af4afd5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 24 May 2022 16:10:52 +0800 Subject: [PATCH 095/206] Naming Java terminal with mainClass name (#414) * Naming Java terminal with mainClass name --- .../core/adapter/handler/LaunchWithDebuggingDelegate.java | 7 ++++--- .../adapter/handler/LaunchWithoutDebuggingDelegate.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 6506e93f0..2962294a0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -58,7 +58,6 @@ public class LaunchWithDebuggingDelegate implements ILaunchDelegate { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final int ATTACH_TERMINAL_TIMEOUT = 20 * 1000; - private static final String TERMINAL_TITLE = "Java Debug Console"; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; private VMHandler vmHandler = new VMHandler(); @@ -77,6 +76,8 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume ((Connector.IntegerArgument) args.get("timeout")).setValue(ATTACH_TERMINAL_TIMEOUT); String address = listenConnector.startListening(args); + final String[] names = launchArguments.mainClass.split("[/\\.]"); + final String terminalName = "Debug: " + names[names.length - 1]; String[] cmds = LaunchRequestHandler.constructLaunchCommands(launchArguments, false, address); RunInTerminalRequestArguments requestArgs = null; if (launchArguments.console == CONSOLE.integratedTerminal) { @@ -84,13 +85,13 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume cmds, launchArguments.cwd, launchArguments.env, - TERMINAL_TITLE); + terminalName); } else { requestArgs = RunInTerminalRequestArguments.createExternalTerminal( cmds, launchArguments.cwd, launchArguments.env, - TERMINAL_TITLE); + terminalName); } Request request = new Request(Command.RUNINTERMINAL.getName(), (JsonObject) JsonUtils.toJsonTree(requestArgs, RunInTerminalRequestArguments.class)); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index 1718e6a36..82a481718 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -40,7 +40,6 @@ public class LaunchWithoutDebuggingDelegate implements ILaunchDelegate { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - protected static final String TERMINAL_TITLE = "Java Process Console"; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; private Consumer terminateHandler; @@ -92,14 +91,16 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume final String launchInTerminalErrorFormat = "Failed to launch debuggee in terminal. Reason: %s"; + final String[] names = launchArguments.mainClass.split("[/\\.]"); + final String terminalName = "Run: " + names[names.length - 1]; String[] cmds = LaunchRequestHandler.constructLaunchCommands(launchArguments, false, null); RunInTerminalRequestArguments requestArgs = null; if (launchArguments.console == CONSOLE.integratedTerminal) { requestArgs = RunInTerminalRequestArguments.createIntegratedTerminal(cmds, launchArguments.cwd, - launchArguments.env, TERMINAL_TITLE); + launchArguments.env, terminalName); } else { requestArgs = RunInTerminalRequestArguments.createExternalTerminal(cmds, launchArguments.cwd, - launchArguments.env, TERMINAL_TITLE); + launchArguments.env, terminalName); } Request request = new Request(Command.RUNINTERMINAL.getName(), (JsonObject) JsonUtils.toJsonTree(requestArgs, RunInTerminalRequestArguments.class)); From 4dacc167891c0a60e6db0644f67513afeb26ae7b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 30 May 2022 12:59:05 +0800 Subject: [PATCH 096/206] bump version to 0.37.0 (#415) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 4c67c37f4..25f51106c 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.36.0 + 0.37.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 94b396327..c8b5582ff 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 0f59e7eb9..e28ec94a2 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.36.0 +Bundle-Version: 0.37.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.3.jar, - lib/com.microsoft.java.debug.core-0.36.0.jar + lib/com.microsoft.java.debug.core-0.37.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 1e8e8c5d0..5f73e6b7b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.36.0 + 0.37.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.36.0 + 0.37.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index ab60e9b37..1fc613e25 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9e02c6804..d7770f079 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.36.0 + 0.37.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index b60bc4cf5..3a37847b8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.36.0 + 0.37.0 pom Java Debug Server for Visual Studio Code From f8f34858446e8d7cd34aad31ac8f5cd80ba525fc Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 31 May 2022 15:17:57 +0800 Subject: [PATCH 097/206] Increase polling frequency to ensure that Java processes are detected (#416) * Increase polling frequency to ensure that Java processes are detected --- .../java/debug/core/adapter/handler/LaunchUtils.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index fc89bfa7c..ca7d69a99 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -117,7 +117,7 @@ public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String ProcessHandle shellProcess = ProcessHandle.of(shellPid).orElse(null); if (shellProcess != null) { int retry = 0; - final int INTERVAL = 100; + final int INTERVAL = 20/*ms*/; final int maxRetries = timeout / INTERVAL; final boolean isCygwinShell = isCygwinShell(shellProcess.info().command().orElse(null)); while (retry <= maxRetries) { @@ -127,11 +127,17 @@ public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String }).findFirst(); if (subProcessHandle.isPresent()) { + if (retry > 0) { + logger.info("Retried " + retry + " times to find Java subProcess."); + } logger.info("shellPid: " + shellPid + ", javaPid: " + subProcessHandle.get().pid()); return subProcessHandle.get(); } else if (isCygwinShell) { long javaPid = findJavaProcessByCygwinPsCommand(shellProcess, javaCommand); if (javaPid > 0) { + if (retry > 0) { + logger.info("Retried " + retry + " times to find Java subProcess."); + } logger.info("[Cygwin Shell] shellPid: " + shellPid + ", javaPid: " + javaPid); return ProcessHandle.of(javaPid).orElse(null); } @@ -147,8 +153,9 @@ public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String } catch (InterruptedException e) { // do nothing } - logger.info("Retry to find Java subProcess of shell pid " + shellPid); } + + logger.info("Retried " + retry + " times but failed to find Java subProcess of shell pid " + shellPid); } return null; From f47241fcad3ebf0f1e8a1e32b6a0c880eee97238 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 7 Jun 2022 17:13:43 +0800 Subject: [PATCH 098/206] Update Java deps (#417) * Update Java deps --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 2 +- com.microsoft.java.debug.plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 25f51106c..275da47c4 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -57,7 +57,7 @@ org.reactivestreams reactive-streams - 1.0.3 + 1.0.4 commons-io diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c8b5582ff..837ca9ac0 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -10,7 +10,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index e28ec94a2..9a8f02795 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -24,5 +24,5 @@ Require-Bundle: org.eclipse.core.runtime, Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, - lib/reactive-streams-1.0.3.jar, + lib/reactive-streams-1.0.4.jar, lib/com.microsoft.java.debug.core-0.37.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 5f73e6b7b..944690f7e 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -46,7 +46,7 @@ org.reactivestreams reactive-streams - 1.0.3 + 1.0.4 commons-io From d1f2da599ac2e033618d28afab61a475784a5289 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 8 Jun 2022 17:44:06 +0800 Subject: [PATCH 099/206] Fix CI to support JDK 17 as build JDK (#420) --- .github/workflows/build.yml | 12 ++++++------ pom.xml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58381df99..41c85cb62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Cache local Maven repository uses: actions/cache@v2 @@ -45,10 +45,10 @@ jobs: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Cache local Maven repository uses: actions/cache@v2 @@ -71,10 +71,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Cache local Maven repository uses: actions/cache@v2 diff --git a/pom.xml b/pom.xml index 3a37847b8..e8ff76899 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 1.5.1 + 2.7.3 ${basedir} From 57a98e267c94e9354c51efbc6c9b378215dd1eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Wed, 8 Jun 2022 17:29:02 +0200 Subject: [PATCH 100/206] Mark native frames and unavailable methods as subtle (#409) This causes some clients to display the frames in a different way, e.g by graying them out. --- .../core/adapter/handler/StackTraceRequestHandler.java | 4 +++- .../com/microsoft/java/debug/core/protocol/Types.java | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 089a55b9d..021bae609 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -95,7 +95,9 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame String methodName = formatMethodName(method, true, true); int lineNumber = AdapterUtils.convertLineNumber(location.lineNumber(), context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); // Line number returns -1 if the information is not available; specifically, always returns -1 for native methods. + String presentationHint = null; if (lineNumber < 0) { + presentationHint = "subtle"; if (method.isNative()) { // For native method, display a tip text "native method" in the Call Stack View. methodName += "[native method]"; @@ -105,7 +107,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame clientSource = null; } } - return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0); + return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); } private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdapterContext context) throws URISyntaxException { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index f0a91ce88..e59f65f74 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -43,6 +43,8 @@ public static class StackFrame { public int line; public int column; public String name; + public String presentationHint; + /** * Constructs a StackFrame with the given information. @@ -57,13 +59,17 @@ public static class StackFrame { * line number of the stack frame * @param col * column number of the stack frame + * @param presentationHint + * An optional hint for how to present this frame in the UI. + * Values: 'normal', 'label', 'subtle' */ - public StackFrame(int id, String name, Source src, int ln, int col) { + public StackFrame(int id, String name, Source src, int ln, int col, String presentationHint) { this.id = id; this.name = name; this.source = src; this.line = ln; this.column = col; + this.presentationHint = presentationHint; } } From 9bb983200e3176beb579238f68662fa4317c76b5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 28 Jun 2022 17:32:01 +0800 Subject: [PATCH 101/206] Support method breakpoints (#424) * Add support for method breakpoints * Handle the duplicated MethodEntryEvent better Co-authored-by: Gayan Perera --- .../java/debug/core/DebugSession.java | 6 + .../java/debug/core/IDebugSession.java | 2 +- .../java/debug/core/IMethodBreakpoint.java | 33 +++ .../java/debug/core/MethodBreakpoint.java | 258 ++++++++++++++++++ .../debug/core/adapter/BreakpointManager.java | 60 ++++ .../java/debug/core/adapter/DebugAdapter.java | 3 +- .../core/adapter/IBreakpointManager.java | 20 ++ .../handler/InitializeRequestHandler.java | 1 + .../SetFunctionBreakpointsRequestHandler.java | 189 +++++++++++++ .../java/debug/core/protocol/Types.java | 1 + 10 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index a56eaf695..ab4341f51 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -146,4 +146,10 @@ public IEventHub getEventHub() { public VirtualMachine getVM() { return vm; } + + @Override + public IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, + int hitCount) { + return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 24138fef1..03780c2d9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -36,7 +36,7 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters); - // TODO: createFunctionBreakpoint + IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount); Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java new file mode 100644 index 000000000..668884567 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.core; + +import java.util.concurrent.CompletableFuture; + +public interface IMethodBreakpoint extends IDebugResource { + String methodName(); + + String className(); + + int getHitCount(); + + String getCondition(); + + void setHitCount(int hitCount); + + void setCondition(String condition); + + CompletableFuture install(); + + Object getProperty(Object key); + + void putProperty(Object key, Object value); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java new file mode 100644 index 000000000..82c5b75e2 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java @@ -0,0 +1,258 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.lang3.StringUtils; + +import com.sun.jdi.ReferenceType; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.VMDisconnectedException; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ClassPrepareEvent; +import com.sun.jdi.event.ThreadDeathEvent; +import com.sun.jdi.request.ClassPrepareRequest; +import com.sun.jdi.request.EventRequest; +import com.sun.jdi.request.MethodEntryRequest; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; + +public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoint { + + private VirtualMachine vm; + private IEventHub eventHub; + private String className; + private String functionName; + private String condition; + private int hitCount; + + private HashMap propertyMap = new HashMap<>(); + private Object compiledConditionalExpression = null; + private Map compiledExpressions = new ConcurrentHashMap<>(); + + private List requests = new ArrayList<>(); + private List subscriptions = new ArrayList<>(); + + public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, String functionName, + String condition, int hitCount) { + Objects.requireNonNull(vm); + Objects.requireNonNull(eventHub); + Objects.requireNonNull(className); + Objects.requireNonNull(functionName); + this.vm = vm; + this.eventHub = eventHub; + this.className = className; + this.functionName = functionName; + this.condition = condition; + this.hitCount = hitCount; + } + + @Override + public List requests() { + return requests; + } + + @Override + public List subscriptions() { + return subscriptions; + } + + @Override + public void close() throws Exception { + try { + vm.eventRequestManager().deleteEventRequests(requests()); + } catch (VMDisconnectedException ex) { + // ignore since removing breakpoints is meaningless when JVM is terminated. + } + subscriptions().forEach(Disposable::dispose); + requests.clear(); + subscriptions.clear(); + } + + @Override + public boolean containsEvaluatableExpression() { + return containsConditionalExpression() || containsLogpointExpression(); + } + + @Override + public boolean containsConditionalExpression() { + return StringUtils.isNotBlank(getCondition()); + } + + @Override + public boolean containsLogpointExpression() { + return false; + } + + @Override + public String getCondition() { + return condition; + } + + @Override + public void setCondition(String condition) { + this.condition = condition; + setCompiledConditionalExpression(null); + compiledExpressions.clear(); + } + + @Override + public String getLogMessage() { + return null; + } + + @Override + public void setLogMessage(String logMessage) { + // for future implementation + } + + @Override + public void setCompiledConditionalExpression(Object compiledExpression) { + this.compiledConditionalExpression = compiledExpression; + } + + @Override + public Object getCompiledConditionalExpression() { + return compiledConditionalExpression; + } + + @Override + public void setCompiledLogpointExpression(Object compiledExpression) { + // for future implementation + } + + @Override + public Object getCompiledLogpointExpression() { + return null; + } + + @Override + public void setCompiledExpression(long threadId, Object compiledExpression) { + compiledExpressions.put(threadId, compiledExpression); + } + + @Override + public Object getCompiledExpression(long threadId) { + return compiledExpressions.get(threadId); + } + + @Override + public int getHitCount() { + return hitCount; + } + + @Override + public void setHitCount(int hitCount) { + this.hitCount = hitCount; + Observable.fromIterable(this.requests()) + .filter(request -> request instanceof MethodEntryRequest) + .subscribe(request -> { + request.addCountFilter(hitCount); + request.enable(); + }); + } + + @Override + public CompletableFuture install() { + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ThreadDeathEvent) + .subscribe(debugEvent -> { + ThreadReference deathThread = ((ThreadDeathEvent) debugEvent.event).thread(); + compiledExpressions.remove(deathThread.uniqueID()); + }); + + subscriptions.add(subscription); + + // It's possible that different class loaders create new class with the same + // name. + // Here to listen to future class prepare events to handle such case. + ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); + classPrepareRequest.addClassFilter(className); + classPrepareRequest.enable(); + requests.add(classPrepareRequest); + + CompletableFuture future = new CompletableFuture<>(); + subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent + && (classPrepareRequest.equals(debugEvent.event.request()))) + .subscribe(debugEvent -> { + ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; + Optional createdRequest = createMethodEntryRequest(event.referenceType()); + if (createdRequest.isPresent()) { + MethodEntryRequest methodEntryRequest = createdRequest.get(); + requests.add(methodEntryRequest); + if (!future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + } + }); + subscriptions.add(subscription); + + List types = vm.classesByName(className); + for (ReferenceType type : types) { + Optional createdRequest = createMethodEntryRequest(type); + if (createdRequest.isPresent()) { + MethodEntryRequest methodEntryRequest = createdRequest.get(); + requests.add(methodEntryRequest); + if (!future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + } + } + return future; + } + + private Optional createMethodEntryRequest(ReferenceType type) { + return type.methodsByName(functionName).stream().findFirst().map(method -> { + MethodEntryRequest request = vm.eventRequestManager().createMethodEntryRequest(); + + request.addClassFilter(type); + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (hitCount > 0) { + request.addCountFilter(hitCount); + } + request.enable(); + return request; + }); + } + + @Override + public Object getProperty(Object key) { + return propertyMap.get(key); + } + + @Override + public void putProperty(Object key, Object value) { + propertyMap.put(key, value); + } + + @Override + public String methodName() { + return functionName; + } + + @Override + public String className() { + return className; + } + +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java index 78898df73..4b0a7ae89 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java @@ -25,6 +25,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.IMethodBreakpoint; import com.microsoft.java.debug.core.IWatchpoint; public class BreakpointManager implements IBreakpointManager { @@ -35,6 +36,7 @@ public class BreakpointManager implements IBreakpointManager { private List breakpoints; private Map> sourceToBreakpoints; private Map watchpoints; + private Map methodBreakpoints; private AtomicInteger nextBreakpointId = new AtomicInteger(1); /** @@ -44,6 +46,7 @@ public BreakpointManager() { this.breakpoints = Collections.synchronizedList(new ArrayList<>(5)); this.sourceToBreakpoints = new HashMap<>(); this.watchpoints = new HashMap<>(); + this.methodBreakpoints = new HashMap<>(); } @Override @@ -208,4 +211,61 @@ private String getWatchpointKey(IWatchpoint watchpoint) { public IWatchpoint[] getWatchpoints() { return this.watchpoints.values().stream().filter(wp -> wp != null).toArray(IWatchpoint[]::new); } + + @Override + public IMethodBreakpoint[] getMethodBreakpoints() { + return this.methodBreakpoints.values().stream().filter(Objects::nonNull).toArray(IMethodBreakpoint[]::new); + } + + @Override + public IMethodBreakpoint[] setMethodBreakpoints(IMethodBreakpoint[] breakpoints) { + List result = new ArrayList<>(); + List toAdds = new ArrayList<>(); + List toRemoves = new ArrayList<>(); + + Set visitedKeys = new HashSet<>(); + for (IMethodBreakpoint change : breakpoints) { + if (change == null) { + result.add(change); + continue; + } + + String key = getMethodBreakpointKey(change); + IMethodBreakpoint cache = methodBreakpoints.get(key); + if (cache != null) { + visitedKeys.add(key); + result.add(cache); + } else { + toAdds.add(change); + result.add(change); + } + } + + for (IMethodBreakpoint cache : methodBreakpoints.values()) { + if (!visitedKeys.contains(getMethodBreakpointKey(cache))) { + toRemoves.add(cache); + } + } + + for (IMethodBreakpoint toRemove : toRemoves) { + try { + // Destroy the method breakpoint on the debugee VM. + toRemove.close(); + this.methodBreakpoints.remove(getMethodBreakpointKey(toRemove)); + } catch (Exception e) { + logger.log(Level.SEVERE, String.format("Remove the method breakpoint exception: %s", e.toString()), e); + } + } + + for (IMethodBreakpoint toAdd : toAdds) { + toAdd.putProperty("id", this.nextBreakpointId.getAndIncrement()); + this.methodBreakpoints.put(getMethodBreakpointKey(toAdd), toAdd); + } + + return result.toArray(new IMethodBreakpoint[0]); + } + + private String getMethodBreakpointKey(IMethodBreakpoint breakpoint) { + return breakpoint.className() + "#" + breakpoint.methodName(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index afdb5759c..0bf5d2683 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -39,6 +39,7 @@ import com.microsoft.java.debug.core.adapter.handler.SetBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetDataBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetExceptionBreakpointsRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.SetFunctionBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetVariableRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SourceRequestHandler; import com.microsoft.java.debug.core.adapter.handler.StackTraceRequestHandler; @@ -127,7 +128,7 @@ private void initialize() { registerHandlerForDebug(new InlineValuesRequestHandler()); registerHandlerForDebug(new RefreshVariablesHandler()); registerHandlerForDebug(new ProcessIdHandler()); - + registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); registerHandlerForNoDebug(new ProcessIdHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java index 9ba609221..196714d52 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java @@ -12,6 +12,7 @@ package com.microsoft.java.debug.core.adapter; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.IMethodBreakpoint; import com.microsoft.java.debug.core.IWatchpoint; public interface IBreakpointManager { @@ -69,4 +70,23 @@ public interface IBreakpointManager { * Returns all registered watchpoints. */ IWatchpoint[] getWatchpoints(); + + /** + * Returns all the registered method breakpoints. + */ + IMethodBreakpoint[] getMethodBreakpoints(); + + /** + * Update the method breakpoints list. If the requested method breakpoints + * already registered in the breakpoint + * manager, reuse the cached one. Otherwise register the requested method + * breakpoints as a new method breakpoints. + * Besides, delete those not existed any more. + * + * @param methodBreakpoints + * the method breakpoints requested by client + * @return the full registered method breakpoints list + */ + IMethodBreakpoint[] setMethodBreakpoints(IMethodBreakpoint[] methodBreakpoints); + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index 4733170eb..ae92f356f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -62,6 +62,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.exceptionBreakpointFilters = exceptionFilters; caps.supportsExceptionInfoRequest = true; caps.supportsDataBreakpoints = true; + caps.supportsFunctionBreakpoints = true; caps.supportsClipboardContext = true; response.body = caps; return CompletableFuture.completedFuture(response); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java new file mode 100644 index 000000000..fa4d23388 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -0,0 +1,189 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; + +import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.IEvaluatableBreakpoint; +import com.microsoft.java.debug.core.IMethodBreakpoint; +import com.microsoft.java.debug.core.MethodBreakpoint; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.protocol.Events; +import com.microsoft.java.debug.core.protocol.Events.BreakpointEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.SetFunctionBreakpointsArguments; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Types.Breakpoint; +import com.microsoft.java.debug.core.protocol.Types.FunctionBreakpoint; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.event.MethodEntryEvent; + +public class SetFunctionBreakpointsRequestHandler implements IDebugRequestHandler { + private boolean registered = false; + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.SETFUNCTIONBREAKPOINTS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + if (context.getDebugSession() == null) { + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, + "Empty debug session."); + } + + if (!registered) { + registered = true; + registerMethodBreakpointHandler(context); + } + + SetFunctionBreakpointsArguments funcBpArgs = (SetFunctionBreakpointsArguments) arguments; + IMethodBreakpoint[] requestedMethodBreakpoints = (funcBpArgs.breakpoints == null) ? new IMethodBreakpoint[0] + : new MethodBreakpoint[funcBpArgs.breakpoints.length]; + for (int i = 0; i < requestedMethodBreakpoints.length; i++) { + FunctionBreakpoint funcBreakpoint = funcBpArgs.breakpoints[i]; + if (funcBreakpoint.name != null) { + String[] segments = funcBreakpoint.name.split("#"); + if (segments.length == 2 && StringUtils.isNotBlank(segments[0]) + && StringUtils.isNotBlank(segments[1])) { + int hitCount = 0; + try { + hitCount = Integer.parseInt(funcBreakpoint.hitCondition); + } catch (NumberFormatException e) { + hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. + } + requestedMethodBreakpoints[i] = context.getDebugSession().createFunctionBreakpoint(segments[0], + segments[1], + funcBreakpoint.condition, hitCount); + } + } + } + + IMethodBreakpoint[] currentMethodBreakpoints = context.getBreakpointManager() + .setMethodBreakpoints(requestedMethodBreakpoints); + List breakpoints = new ArrayList<>(); + for (int i = 0; i < currentMethodBreakpoints.length; i++) { + if (currentMethodBreakpoints[i] == null) { + breakpoints.add(new Breakpoint(false)); + continue; + } + + // If the requested method breakpoint exists in the manager, it will reuse + // the cached breakpoint exists object. + // Otherwise add the requested method breakpoint to the cache. + // So if the returned method breakpoint from the manager is same as the + // requested method breakpoint, this means it's a new method breakpoint, need + // install it. + if (currentMethodBreakpoints[i] == requestedMethodBreakpoints[i]) { + currentMethodBreakpoints[i].install().thenAccept(wp -> { + BreakpointEvent bpEvent = new BreakpointEvent("changed", convertDebuggerMethodToClient(wp)); + context.getProtocolServer().sendEvent(bpEvent); + }); + } else { + if (currentMethodBreakpoints[i].getHitCount() != requestedMethodBreakpoints[i].getHitCount()) { + currentMethodBreakpoints[i].setHitCount(requestedMethodBreakpoints[i].getHitCount()); + } + + if (!Objects.equals(currentMethodBreakpoints[i].getCondition(), + requestedMethodBreakpoints[i].getCondition())) { + currentMethodBreakpoints[i].setCondition(requestedMethodBreakpoints[i].getCondition()); + } + } + + breakpoints.add(convertDebuggerMethodToClient(currentMethodBreakpoints[i])); + } + + response.body = new Responses.SetDataBreakpointsResponseBody(breakpoints); + return CompletableFuture.completedFuture(response); + } + + private Breakpoint convertDebuggerMethodToClient(IMethodBreakpoint methodBreakpoint) { + return new Breakpoint((int) methodBreakpoint.getProperty("id"), + methodBreakpoint.getProperty("verified") != null && (boolean) methodBreakpoint.getProperty("verified")); + } + + private void registerMethodBreakpointHandler(IDebugAdapterContext context) { + IDebugSession debugSession = context.getDebugSession(); + if (debugSession != null) { + debugSession.getEventHub().events().filter(debugEvent -> debugEvent.event instanceof MethodEntryEvent) + .subscribe(debugEvent -> { + MethodEntryEvent methodEntryEvent = (MethodEntryEvent) debugEvent.event; + ThreadReference bpThread = methodEntryEvent.thread(); + IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); + + // Find the method breakpoint related to this method entry event + IMethodBreakpoint methodBreakpoint = Stream + .of(context.getBreakpointManager().getMethodBreakpoints()) + .filter(mp -> { + return mp.requests().contains(methodEntryEvent.request()) + && matches(methodEntryEvent, mp); + }) + .findFirst().orElse(null); + + if (methodBreakpoint != null) { + if (methodBreakpoint instanceof IEvaluatableBreakpoint + && ((IEvaluatableBreakpoint) methodBreakpoint).containsConditionalExpression()) { + if (engine.isInEvaluation(bpThread)) { + return; + } + CompletableFuture.runAsync(() -> { + engine.evaluateForBreakpoint((IEvaluatableBreakpoint) methodBreakpoint, bpThread) + .whenComplete((value, ex) -> { + boolean resume = SetBreakpointsRequestHandler.handleEvaluationResult( + context, bpThread, (IEvaluatableBreakpoint) methodBreakpoint, + value, + ex); + // Clear the evaluation environment caused by above evaluation. + engine.clearState(bpThread); + + if (resume) { + debugEvent.eventSet.resume(); + } else { + context.getProtocolServer().sendEvent(new Events.StoppedEvent( + "function breakpoint", bpThread.uniqueID())); + } + }); + }); + + } else { + context.getProtocolServer() + .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); + } + + debugEvent.shouldResume = false; + } + }); + } + } + + private boolean matches(MethodEntryEvent methodEntryEvent, IMethodBreakpoint breakpoint) { + return breakpoint.className().equals(methodEntryEvent.location().declaringType().name()) + && breakpoint.methodName().equals(methodEntryEvent.method().name()); + } + +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index e59f65f74..cd20faf4f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -374,5 +374,6 @@ public static class Capabilities { public ExceptionBreakpointFilter[] exceptionBreakpointFilters = new ExceptionBreakpointFilter[0]; public boolean supportsDataBreakpoints; public boolean supportsClipboardContext; + public boolean supportsFunctionBreakpoints; } } From 2461b037178f16aa0801386d78e3a9400f6fdfbe Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 29 Jun 2022 09:39:55 +0800 Subject: [PATCH 102/206] Up version to 0.38.0 (#425) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 275da47c4..b866771cc 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.37.0 + 0.38.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 837ca9ac0..94fa67e10 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 9a8f02795..a082aef62 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.37.0 +Bundle-Version: 0.38.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.37.0.jar + lib/com.microsoft.java.debug.core-0.38.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 944690f7e..8a65029aa 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.37.0 + 0.38.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.37.0 + 0.38.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 1fc613e25..2b3229b2d 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index d7770f079..5e3c1ed1d 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.37.0 + 0.38.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index e8ff76899..0e0c0ffee 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.37.0 + 0.38.0 pom Java Debug Server for Visual Studio Code From 1fcbe706dbdea171b0b1ab1348d46e5f9a6a1a31 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 19 Jul 2022 04:23:08 +0200 Subject: [PATCH 103/206] Improve support for method breakpoints (#426) Now method breakpoints can added by adding a line breakpoint at method header. --- .../microsoft/java/debug/core/Breakpoint.java | 35 ++++++- .../java/debug/core/IBreakpoint.java | 3 + .../handler/SetBreakpointsRequestHandler.java | 17 +++- .../java/debug/BreakpointLocationLocator.java | 91 +++++++++++++++++++ .../internal/JdtSourceLookUpProvider.java | 24 +++-- 5 files changed, 153 insertions(+), 17 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 790eff801..3a8d88316 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -18,6 +18,7 @@ import java.util.concurrent.CompletableFuture; import com.sun.jdi.Location; +import com.sun.jdi.Method; import com.sun.jdi.ReferenceType; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; @@ -38,6 +39,7 @@ public class Breakpoint implements IBreakpoint { private String condition = null; private String logMessage = null; private HashMap propertyMap = new HashMap<>(); + private String methodSignature = null; Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { this(vm, eventHub, className, lineNumber, 0, null); @@ -50,7 +52,12 @@ public class Breakpoint implements IBreakpoint { Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) { this.vm = vm; this.eventHub = eventHub; - this.className = className; + if (className != null && className.contains("#")) { + this.className = className.substring(0, className.indexOf("#")); + this.methodSignature = className.substring(className.indexOf("#") + 1); + } else { + this.className = className; + } this.lineNumber = lineNumber; this.hitCount = hitCount; this.condition = condition; @@ -234,6 +241,24 @@ private static List collectLocations(List refTypes, int return locations; } + private static List collectLocations(List refTypes, String nameAndSignature) { + List locations = new ArrayList<>(); + String[] segments = nameAndSignature.split("#"); + + for (ReferenceType refType : refTypes) { + List methods = refType.methods(); + for (Method method : methods) { + if (!method.isAbstract() && !method.isNative() + && segments[0].equals(method.name()) + && (segments[1].equals(method.genericSignature()) || segments[1].equals(method.signature()))) { + locations.add(method.location()); + break; + } + } + } + return locations; + } + private List createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount, boolean includeNestedTypes) { List refTypes = new ArrayList<>(); @@ -243,7 +268,12 @@ private List createBreakpointRequests(ReferenceType refType, private List createBreakpointRequests(List refTypes, int lineNumber, int hitCount, boolean includeNestedTypes) { - List locations = collectLocations(refTypes, lineNumber, includeNestedTypes); + List locations; + if (this.methodSignature != null) { + locations = collectLocations(refTypes, this.methodSignature); + } else { + locations = collectLocations(refTypes, lineNumber, includeNestedTypes); + } // find out the existing breakpoint locations List existingLocations = new ArrayList<>(requests.size()); @@ -268,6 +298,7 @@ private List createBreakpointRequests(List ref request.addCountFilter(hitCount); } request.enable(); + request.putProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL, Boolean.valueOf(this.methodSignature != null)); newRequests.add(request); } catch (VMDisconnectedException ex) { // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index bb9549635..485a13739 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -14,6 +14,9 @@ import java.util.concurrent.CompletableFuture; public interface IBreakpoint extends IDebugResource { + + String REQUEST_TYPE_FUNCTIONAL = "functional"; + String className(); int getLineNumber(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 2bc2b5f05..2e7dbf35a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -44,11 +44,11 @@ import com.sun.jdi.BooleanValue; import com.sun.jdi.Field; import com.sun.jdi.ObjectReference; -import com.sun.jdi.StringReference; import com.sun.jdi.ReferenceType; +import com.sun.jdi.StringReference; import com.sun.jdi.ThreadReference; -import com.sun.jdi.Value; import com.sun.jdi.VMDisconnectedException; +import com.sun.jdi.Value; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.StepEvent; @@ -171,6 +171,11 @@ private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext cont ).findFirst().orElse(null); } + private IBreakpoint getAssociatedBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { + return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream() + .filter(bp -> bp.requests().contains(event.request())).findFirst().orElse(null); + } + private void registerBreakpointHandler(IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); if (debugSession != null) { @@ -188,7 +193,7 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { // find the breakpoint related to this breakpoint event IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint(context, (BreakpointEvent) event); - + boolean functional = (boolean) event.request().getProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL); if (expressionBP != null) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> { @@ -199,12 +204,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); + context.getProtocolServer().sendEvent(new Events.StoppedEvent( + functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); } }); }); } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); + context.getProtocolServer().sendEvent(new Events.StoppedEvent( + functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); } debugEvent.shouldResume = false; } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java new file mode 100644 index 000000000..198b1eb67 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -0,0 +1,91 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera (gayanper@gmail.com) - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug; + +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; + +@SuppressWarnings("restriction") +public class BreakpointLocationLocator + extends org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator { + + private IMethodBinding methodBinding; + + public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, + boolean bestMatch) { + super(compilationUnit, lineNumber, bindingsResolved, bestMatch); + } + + @Override + public boolean visit(MethodDeclaration node) { + boolean result = super.visit(node); + if (methodBinding == null && getLocationType() == LOCATION_METHOD) { + this.methodBinding = node.resolveBinding(); + } + return result; + } + + /** + * Returns the signature of method found if the + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#getLocationType()} + * is + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#LOCATION_METHOD}. + * Otherwise return null. + */ + public String getMethodSignature() { + if (this.methodBinding == null) { + return null; + } + return toSignature(this.methodBinding); + } + + /** + * Returns the name of method found if the + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#getLocationType()} + * is + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#LOCATION_METHOD}. + * Otherwise return null. + */ + public String getMethodName() { + if (this.methodBinding == null) { + return null; + } + return this.methodBinding.getName(); + } + + @Override + public String getFullyQualifiedTypeName() { + if (this.methodBinding != null) { + return this.methodBinding.getDeclaringClass().getQualifiedName(); + } + return super.getFullyQualifiedTypeName(); + } + + private String toSignature(IMethodBinding binding) { + // use key for now until JDT core provides a public API for this. + // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" + // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" + String signatureString = binding.getKey(); + if (signatureString != null) { + String name = binding.getName(); + int index = signatureString.indexOf(name); + if (index > -1) { + int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); + if (exceptionIndex > -1) { + return signatureString.substring(index + name.length(), exceptionIndex); + } + return signatureString.substring(index + name.length()); + } + } + return null; + } +} 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 efc7ce20c..eb51faaeb 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 @@ -26,13 +26,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.microsoft.java.debug.core.Configuration; -import com.microsoft.java.debug.core.DebugException; -import com.microsoft.java.debug.core.adapter.AdapterUtils; -import com.microsoft.java.debug.core.adapter.Constants; -import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; -import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; - import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.sourcelookup.ISourceContainer; @@ -48,11 +41,18 @@ import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; -import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import com.microsoft.java.debug.BreakpointLocationLocator; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.Constants; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; + public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; @@ -162,12 +162,16 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th // In current stage, we don't support to move the invalid breakpoint down to the next valid location, and just // mark it as "unverified". // In future, we could consider supporting to update the breakpoint to a valid location. - ValidBreakpointLocationLocator locator = new ValidBreakpointLocationLocator(astUnit, lines[i], true, true); + BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, true); astUnit.accept(locator); // When the final valid line location is same as the original line, that represents it's a valid breakpoint. // Add location type check to avoid breakpoint on method/field which will never be hit in current implementation. - if (lines[i] == locator.getLineLocation() && locator.getLocationType() == ValidBreakpointLocationLocator.LOCATION_LINE) { + if (lines[i] == locator.getLineLocation() + && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { fqns[i] = locator.getFullyQualifiedTypeName(); + } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { + fqns[i] = locator.getFullyQualifiedTypeName().concat("#").concat(locator.getMethodName()) + .concat("#").concat(locator.getMethodSignature()); } } } From 93963b9582887fb3f62bd4bf05fc3ad2a526a122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Fri, 22 Jul 2022 06:51:55 +0200 Subject: [PATCH 104/206] Show target VM exceptions as result in evaluate requests (#428) This changes the result of an expression like `Long.parseLong("foo")` sent to the evaluate handler from: org.eclipse.debug.core.DebugException: com.sun.jdi.InvocationException: Exception occurred in target VM occurred invoking method. to the actual exception: NumberFormatException@76 "java.lang.NumberFormatException: For input string: "foo"" backtrace: Object[6]@82 cause: NumberFormatException@76 depth: 46 detailMessage: "For input string: "foo"" stackTrace: StackTraceElement[0]@84 suppressedExceptions: Collections$EmptyList@85 size=0 --- .../internal/eval/JdtEvaluationProvider.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index e014552a8..d4fac9054 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -29,6 +29,7 @@ import org.apache.commons.lang3.reflect.FieldUtils; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; @@ -328,9 +329,22 @@ private void internalEvaluate(ASTEvaluationEngine engine, ICompiledExpression co try { engine.evaluateExpression(compiledExpression, stackframe, evaluateResult -> { if (evaluateResult == null || evaluateResult.hasErrors()) { - Exception ex = evaluateResult.getException() != null ? evaluateResult.getException() - : new RuntimeException(StringUtils.join(evaluateResult.getErrorMessages())); - completableFuture.completeExceptionally(ex); + DebugException debugException = evaluateResult.getException(); + if (debugException == null) { + completableFuture.completeExceptionally( + new RuntimeException(String.join(" ", evaluateResult.getErrorMessages()))); + return; + } + IStatus status = debugException.getStatus(); + if (status.getCode() == DebugException.TARGET_REQUEST_FAILED) { + Throwable innerException = status.getException(); + if (innerException instanceof com.sun.jdi.InvocationException) { + ObjectReference objectReference = ((com.sun.jdi.InvocationException) innerException).exception(); + completableFuture.complete(objectReference); + return; + } + } + completableFuture.completeExceptionally(debugException); return; } try { From 8c0e87defedc5bc2a17a9e2980824f8573c08386 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 25 Jul 2022 15:15:44 +0800 Subject: [PATCH 105/206] Bump version to 0.39.0 (#430) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index b866771cc..287c599fe 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.38.0 + 0.39.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 94fa67e10..c3d2cffbb 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index a082aef62..c39f5a9ac 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.38.0 +Bundle-Version: 0.39.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.38.0.jar + lib/com.microsoft.java.debug.core-0.39.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 8a65029aa..7e86528b5 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.38.0 + 0.39.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.38.0 + 0.39.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 2b3229b2d..d4f44d697 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 5e3c1ed1d..4b76e45fb 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.38.0 + 0.39.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 0e0c0ffee..30c6ed253 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.38.0 + 0.39.0 pom Java Debug Server for Visual Studio Code From fe77989c0c5b286121dd2fac9d6a2f334da2e54f Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Thu, 4 Aug 2022 04:02:25 +0200 Subject: [PATCH 106/206] Add support for lambda breakpoints (#427) * Add support for lambda breakpoints The support for method header breakpoints was extended to support lambda breakpoints using the vscode inline breakpoint feature. * Optimize lambda search Only search for lambda when the breakpoing is an inline breakpoint. Add support for any column position within lambda expression. * Improve for multi line and multi lambdas * Improve lambda breakpoints - Only support for expressions. - The inline breakpoint should be before the expression start. * Remove the unnecessary nodeType check Co-authored-by: Jinbo Wang --- .../microsoft/java/debug/core/Breakpoint.java | 27 ++++- .../debug/core/EvaluatableBreakpoint.java | 6 +- .../java/debug/core/IBreakpoint.java | 8 +- .../java/debug/core/adapter/AdapterUtils.java | 18 +++ .../debug/core/adapter/BreakpointManager.java | 12 +- .../handler/SetBreakpointsRequestHandler.java | 26 ++++- .../java/debug/core/protocol/Types.java | 11 ++ .../java/debug/BreakpointLocationLocator.java | 8 +- .../java/debug/LambdaExpressionLocator.java | 105 ++++++++++++++++++ .../internal/JdtSourceLookUpProvider.java | 41 +++++-- 10 files changed, 231 insertions(+), 31 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 3a8d88316..a81968ad3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -115,12 +115,19 @@ public String getCondition() { @Override public boolean equals(Object obj) { - if (!(obj instanceof IBreakpoint)) { + if (!(obj instanceof Breakpoint)) { return super.equals(obj); } - IBreakpoint breakpoint = (IBreakpoint) obj; - return Objects.equals(this.className(), breakpoint.className()) && this.getLineNumber() == breakpoint.getLineNumber(); + Breakpoint breakpoint = (Breakpoint) obj; + return Objects.equals(this.className(), breakpoint.className()) + && this.getLineNumber() == breakpoint.getLineNumber() + && Objects.equals(this.methodSignature, breakpoint.methodSignature); + } + + @Override + public int hashCode() { + return Objects.hash(this.className, this.lineNumber, this.methodSignature); } @Override @@ -298,7 +305,7 @@ private List createBreakpointRequests(List ref request.addCountFilter(hitCount); } request.enable(); - request.putProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL, Boolean.valueOf(this.methodSignature != null)); + request.putProperty(IBreakpoint.REQUEST_TYPE, computeRequestType()); newRequests.add(request); } catch (VMDisconnectedException ex) { // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be @@ -310,6 +317,18 @@ private List createBreakpointRequests(List ref return newRequests; } + private Object computeRequestType() { + if (this.methodSignature == null) { + return IBreakpoint.REQUEST_TYPE_LINE; + } + + if (this.methodSignature.startsWith("lambda$")) { + return IBreakpoint.REQUEST_TYPE_LAMBDA; + } else { + return IBreakpoint.REQUEST_TYPE_METHOD; + } + } + @Override public void putProperty(Object key, Object value) { propertyMap.put(key, value); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java index 1a0647411..9b3fdb2dd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java @@ -11,15 +11,15 @@ package com.microsoft.java.debug.core; -import org.apache.commons.lang3.StringUtils; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import com.sun.jdi.event.ThreadDeathEvent; +import org.apache.commons.lang3.StringUtils; + import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ThreadDeathEvent; import io.reactivex.disposables.Disposable; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 485a13739..3db16b577 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -15,7 +15,13 @@ public interface IBreakpoint extends IDebugResource { - String REQUEST_TYPE_FUNCTIONAL = "functional"; + String REQUEST_TYPE = "request_type"; + + int REQUEST_TYPE_LINE = 0; + + int REQUEST_TYPE_METHOD = 1; + + int REQUEST_TYPE_LAMBDA = 2; String className(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 74d3fb292..5c1272102 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -111,6 +111,24 @@ public static int convertLineNumber(int line, boolean sourceLinesStartAt1, boole } } + /** + * Convert the source platform's column number to the target platform's column + * number. + * + * @param column + * the column number from the source platform + * @param sourceColumnsStartAt1 + * the source platform's column starts at 1 or not + * @return the new column number + */ + public static int convertColumnNumber(int column, boolean sourceColumnsStartAt1) { + if (sourceColumnsStartAt1) { + return column - 1; + } else { + return column; + } + } + /** * Convert the source platform's path format to the target platform's path format. * diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java index 4b0a7ae89..eaf1bb56f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java @@ -79,12 +79,12 @@ public IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, bo // Compute the breakpoints that are newly added. List toAdd = new ArrayList<>(); - List visitedLineNumbers = new ArrayList<>(); + List visitedBreakpoints = new ArrayList<>(); for (IBreakpoint breakpoint : breakpoints) { - IBreakpoint existed = breakpointMap.get(String.valueOf(breakpoint.getLineNumber())); + IBreakpoint existed = breakpointMap.get(String.valueOf(breakpoint.hashCode())); if (existed != null) { result.add(existed); - visitedLineNumbers.add(existed.getLineNumber()); + visitedBreakpoints.add(existed.hashCode()); continue; } else { result.add(breakpoint); @@ -95,7 +95,7 @@ public IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, bo // Compute the breakpoints that are no longer listed. List toRemove = new ArrayList<>(); for (IBreakpoint breakpoint : breakpointMap.values()) { - if (!visitedLineNumbers.contains(breakpoint.getLineNumber())) { + if (!visitedBreakpoints.contains(breakpoint.hashCode())) { toRemove.add(breakpoint); } } @@ -113,7 +113,7 @@ private void addBreakpointsInternally(String source, IBreakpoint[] breakpoints) for (IBreakpoint breakpoint : breakpoints) { breakpoint.putProperty("id", this.nextBreakpointId.getAndIncrement()); this.breakpoints.add(breakpoint); - breakpointMap.put(String.valueOf(breakpoint.getLineNumber()), breakpoint); + breakpointMap.put(String.valueOf(breakpoint.hashCode()), breakpoint); } } } @@ -133,7 +133,7 @@ private void removeBreakpointsInternally(String source, IBreakpoint[] breakpoint // Destroy the breakpoint on the debugee VM. breakpoint.close(); this.breakpoints.remove(breakpoint); - breakpointMap.remove(String.valueOf(breakpoint.getLineNumber())); + breakpointMap.remove(String.valueOf(breakpoint.hashCode())); } catch (Exception e) { logger.log(Level.SEVERE, String.format("Remove breakpoint exception: %s", e.toString()), e); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 2e7dbf35a..6dc74a0bd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -52,6 +52,7 @@ import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.StepEvent; +import com.sun.jdi.request.EventRequest; public class SetBreakpointsRequestHandler implements IDebugRequestHandler { @@ -193,7 +194,8 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { // find the breakpoint related to this breakpoint event IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint(context, (BreakpointEvent) event); - boolean functional = (boolean) event.request().getProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL); + String breakpointName = computeBreakpointName(event.request()); + if (expressionBP != null) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> { @@ -205,13 +207,13 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { debugEvent.eventSet.resume(); } else { context.getProtocolServer().sendEvent(new Events.StoppedEvent( - functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); + breakpointName, bpThread.uniqueID())); } }); }); } else { context.getProtocolServer().sendEvent(new Events.StoppedEvent( - functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); + breakpointName, bpThread.uniqueID())); } debugEvent.shouldResume = false; } @@ -219,6 +221,17 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { } } + private String computeBreakpointName(EventRequest request) { + switch ((int) request.getProperty(IBreakpoint.REQUEST_TYPE)) { + case IBreakpoint.REQUEST_TYPE_LAMBDA: + return "lambda breakpoint"; + case IBreakpoint.REQUEST_TYPE_METHOD: + return "function breakpoint"; + default: + return "breakpoint"; + } + } + /** * Check whether the condition expression is satisfied, and return a boolean value to determine to resume the thread or not. */ @@ -287,8 +300,13 @@ private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Type int[] lines = Arrays.asList(sourceBreakpoints).stream().map(sourceBreakpoint -> { return AdapterUtils.convertLineNumber(sourceBreakpoint.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); }).mapToInt(line -> line).toArray(); + + int[] columns = Arrays.asList(sourceBreakpoints).stream().map(b -> { + return AdapterUtils.convertColumnNumber(b.column, context.isClientColumnsStartAt1()); + }).mapToInt(b -> b).toArray(); + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); - String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, null); + String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, columns); IBreakpoint[] breakpoints = new IBreakpoint[lines.length]; for (int i = 0; i < lines.length; i++) { int hitCount = 0; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index cd20faf4f..979c7d632 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -202,6 +202,7 @@ public Breakpoint(int id, boolean verified, int line, String message) { public static class SourceBreakpoint { public int line; + public int column; public String hitCondition; public String condition; public String logMessage; @@ -217,6 +218,16 @@ public SourceBreakpoint(int line, String condition, String hitCondition) { this.condition = condition; this.hitCondition = hitCondition; } + + /** + * Constructor. + */ + public SourceBreakpoint(int line, String condition, String hitCondition, int column) { + this.line = line; + this.column = column; + this.condition = condition; + this.hitCondition = hitCondition; + } } public static class FunctionBreakpoint { diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index 198b1eb67..00cffb766 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -20,7 +20,8 @@ public class BreakpointLocationLocator private IMethodBinding methodBinding; - public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, + public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, + boolean bindingsResolved, boolean bestMatch) { super(compilationUnit, lineNumber, bindingsResolved, bestMatch); } @@ -45,7 +46,7 @@ public String getMethodSignature() { if (this.methodBinding == null) { return null; } - return toSignature(this.methodBinding); + return toSignature(this.methodBinding, getMethodName()); } /** @@ -70,13 +71,12 @@ public String getFullyQualifiedTypeName() { return super.getFullyQualifiedTypeName(); } - private String toSignature(IMethodBinding binding) { + static String toSignature(IMethodBinding binding, String name) { // use key for now until JDT core provides a public API for this. // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" String signatureString = binding.getKey(); if (signatureString != null) { - String name = binding.getName(); int index = signatureString.indexOf(name); if (index > -1) { int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java new file mode 100644 index 000000000..351be8cb3 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -0,0 +1,105 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera (gayanper@gmail.com) - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.LambdaExpression; + +public class LambdaExpressionLocator extends ASTVisitor { + private CompilationUnit compilationUnit; + private int line; + private int column; + private boolean found; + + private IMethodBinding lambdaMethodBinding; + private LambdaExpression lambdaExpression; + + public LambdaExpressionLocator(CompilationUnit compilationUnit, int line, int column) { + this.compilationUnit = compilationUnit; + this.line = line; + this.column = column; + } + + @Override + public boolean visit(LambdaExpression node) { + // we only support inline breakpoints which are added before the expression part of the + // lambda. And we don't support lambda blocks since they can be debugged using line + // breakpoints. + if (column > -1) { + int startPosition = node.getStartPosition(); + int endPosition = node.getBody().getStartPosition(); + int offset = this.compilationUnit.getPosition(line, column); + // lambda on same line: + // list.stream().map(i -> i + 1); + // + // lambda on multiple lines: + // list.stream().map(user + // -> user.isSystem() ? new SystemUser(user) : new EndUser(user)); + + if (offset >= startPosition && offset <= endPosition) { + this.lambdaMethodBinding = node.resolveMethodBinding(); + this.found = true; + this.lambdaExpression = node; + return false; + } + } + return super.visit(node); + } + + /** + * Returns true if a lambda is found at given location. + */ + public boolean isFound() { + return found; + } + + /** + * Returns the signature of lambda method otherwise return null. + */ + public String getMethodSignature() { + if (!this.found) { + return null; + } + return BreakpointLocationLocator.toSignature(this.lambdaMethodBinding, getMethodName()); + } + + /** + * Returns the name of lambda method otherwise return null. + */ + public String getMethodName() { + if (!this.found) { + return null; + } + String key = this.lambdaMethodBinding.getKey(); + return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + } + + /** + * Returns the name of the type which the lambda method is found. + */ + public String getFullyQualifiedTypeName() { + if (this.found) { + ASTNode parent = lambdaExpression.getParent(); + while (parent != null) { + if (parent instanceof AbstractTypeDeclaration) { + AbstractTypeDeclaration declaration = (AbstractTypeDeclaration) parent; + return declaration.resolveBinding().getBinaryName(); + } + parent = parent.getParent(); + } + } + return null; + } +} 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 eb51faaeb..1dc1c0e64 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 @@ -46,6 +46,7 @@ import org.eclipse.jdt.launching.LibraryLocation; import com.microsoft.java.debug.BreakpointLocationLocator; +import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.adapter.AdapterUtils; @@ -155,22 +156,44 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th String[] fqns = new String[lines.length]; if (astUnit != null) { for (int i = 0; i < lines.length; i++) { + if (columns[i] > -1) { + // if we have a column, try to find the lambda expression at that column + LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, lines[i], + columns[i]); + astUnit.accept(lambdaExpressionLocator); + if (lambdaExpressionLocator.isFound()) { + fqns[i] = lambdaExpressionLocator.getFullyQualifiedTypeName().concat("#") + .concat(lambdaExpressionLocator.getMethodName()) + .concat("#").concat(lambdaExpressionLocator.getMethodSignature()); + continue; + } + } + // TODO - // The ValidBreakpointLocationLocator will verify if the current line is a valid location or not. - // If so, it will return the fully qualified name of the class type that contains the current line. - // Otherwise, it will try to find a valid location from the next lines and return it's fully qualified name. - // In current stage, we don't support to move the invalid breakpoint down to the next valid location, and just + // The ValidBreakpointLocationLocator will verify if the current line is a valid + // location or not. + // If so, it will return the fully qualified name of the class type that + // contains the current line. + // Otherwise, it will try to find a valid location from the next lines and + // return it's fully qualified name. + // In current stage, we don't support to move the invalid breakpoint down to the + // next valid location, and just // mark it as "unverified". - // In future, we could consider supporting to update the breakpoint to a valid location. - BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, true); + // In future, we could consider supporting to update the breakpoint to a valid + // location. + BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, + true); astUnit.accept(locator); - // When the final valid line location is same as the original line, that represents it's a valid breakpoint. - // Add location type check to avoid breakpoint on method/field which will never be hit in current implementation. + // When the final valid line location is same as the original line, that + // represents it's a valid breakpoint. + // Add location type check to avoid breakpoint on method/field which will never + // be hit in current implementation. if (lines[i] == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { fqns[i] = locator.getFullyQualifiedTypeName(); } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - fqns[i] = locator.getFullyQualifiedTypeName().concat("#").concat(locator.getMethodName()) + fqns[i] = locator.getFullyQualifiedTypeName().concat("#") + .concat(locator.getMethodName()) .concat("#").concat(locator.getMethodSignature()); } } From 0723ee0b69bf42390310964cd6bdadc53298298b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 29 Aug 2022 16:55:04 +0800 Subject: [PATCH 107/206] Provide an option to dump the DAP perf (#435) * Provide an option to dump the DAP perf --- .../java/debug/core/UsageDataSession.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java index e54727b66..e292026c8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -12,6 +12,7 @@ package com.microsoft.java.debug.core; import java.util.ArrayList; +import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -32,6 +33,7 @@ public class UsageDataSession { private static final Logger usageDataLogger = Logger.getLogger(Configuration.USAGE_DATA_LOGGER_NAME); private static final long RESPONSE_MAX_DELAY_MS = 1000; private static final ThreadLocal threadLocal = new InheritableThreadLocal<>(); + private static final boolean TRACE_DAP_PERF = Boolean.getBoolean("debug.dap.perf"); private final String sessionGuid = UUID.randomUUID().toString(); private boolean jdiEventSequenceEnabled = false; @@ -43,6 +45,7 @@ public class UsageDataSession { private Map userErrorCount = new HashMap<>(); private Map commandPerfCountMap = new HashMap<>(); private List eventList = new ArrayList<>(); + private List dapPerf = new ArrayList<>(); public static String getSessionGuid() { return threadLocal.get() == null ? "" : threadLocal.get().sessionGuid; @@ -117,6 +120,12 @@ public void recordResponse(Response response) { long duration = responseMillis - requestMillis; commandPerfCountMap.compute(command, (k, v) -> (v == null ? 0 : v.intValue()) + (int) duration); + if (TRACE_DAP_PERF) { + synchronized (dapPerf) { + dapPerf.add(new String[]{command, String.valueOf(duration)}); + } + } + if (!response.success || duration > RESPONSE_MAX_DELAY_MS) { Map props = new HashMap<>(); props.put("duration", duration); @@ -148,6 +157,18 @@ public void submitUsageData() { } } usageDataLogger.log(Level.INFO, "session usage data summary", props); + + if (TRACE_DAP_PERF) { + Formatter fmt = new Formatter(); + fmt.format("\nDAP Performance Metrics:\n"); + fmt.format("%30s %10s(ms)\n", "Request", "Duration"); + synchronized (dapPerf) { + dapPerf.forEach((event) -> { + fmt.format("%30s %14s\n", event[0], event[1]); + }); + } + logger.info(String.valueOf(fmt)); + } } /** From 9ce0aef47c71de9263ad6f3f179e1b80e7b83678 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 31 Aug 2022 12:15:39 +0800 Subject: [PATCH 108/206] Perf: support sending JDWP commands asynchronously (#436) * Perf: support sending JDWP commands asynchronously --- .../java/debug/core/AsyncJdwpUtils.java | 143 +++++++++++ .../microsoft/java/debug/core/Breakpoint.java | 239 ++++++++++++------ .../java/debug/core/DebugSession.java | 9 +- .../java/debug/core/DebugSettings.java | 12 +- .../java/debug/core/DebugUtility.java | 23 +- .../java/debug/core/IBreakpoint.java | 7 + .../java/debug/core/IMethodBreakpoint.java | 7 + .../java/debug/core/MethodBreakpoint.java | 62 ++++- .../java/debug/core/UsageDataSession.java | 6 + .../core/adapter/DebugAdapterContext.java | 29 ++- .../core/adapter/IDebugAdapterContext.java | 10 + .../core/adapter/IStackFrameManager.java | 10 + .../debug/core/adapter/StackFrameManager.java | 37 ++- .../java/debug/core/adapter/ThreadCache.java | 72 ++++++ .../adapter/handler/AttachRequestHandler.java | 20 ++ .../ConfigurationDoneRequestHandler.java | 2 + .../handler/EvaluateRequestHandler.java | 5 + .../handler/InlineValuesRequestHandler.java | 8 +- .../adapter/handler/RestartFrameHandler.java | 26 +- .../handler/SetBreakpointsRequestHandler.java | 3 +- .../SetFunctionBreakpointsRequestHandler.java | 1 + .../handler/StackTraceRequestHandler.java | 159 +++++++++--- .../adapter/handler/StepRequestHandler.java | 105 ++++++-- .../handler/ThreadsRequestHandler.java | 158 +++++++++--- .../handler/VariablesRequestHandler.java | 110 +++++++- .../variables/StringReferenceProxy.java | 121 +++++++++ .../core/adapter/variables/VariableUtils.java | 152 ++++++++++- 27 files changed, 1315 insertions(+), 221 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java new file mode 100644 index 000000000..fad2ac223 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java @@ -0,0 +1,143 @@ +/******************************************************************************* +* Copyright (c) 2022 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.core; + +import static java.util.concurrent.CompletableFuture.allOf; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Supplier; + +public class AsyncJdwpUtils { + /** + * Create a the thread pool to process JDWP tasks. + * JDWP tasks are IO-bounded, so use a relatively large thread pool for JDWP tasks. + */ + public static ExecutorService jdwpThreadPool = Executors.newWorkStealingPool(100); + // public static ExecutorService jdwpThreadPool = Executors.newCachedThreadPool(); + + public static CompletableFuture runAsync(List tasks) { + return runAsync(jdwpThreadPool, tasks.toArray(new Runnable[0])); + } + + public static CompletableFuture runAsync(Runnable... tasks) { + return runAsync(jdwpThreadPool, tasks); + } + + public static CompletableFuture runAsync(Executor executor, List tasks) { + return runAsync(executor, tasks.toArray(new Runnable[0])); + } + + public static CompletableFuture runAsync(Executor executor, Runnable... tasks) { + List> promises = new ArrayList<>(); + for (Runnable task : tasks) { + if (task == null) { + continue; + } + + promises.add(CompletableFuture.runAsync(task, executor)); + } + + return CompletableFuture.allOf(promises.toArray(new CompletableFuture[0])); + } + + public static CompletableFuture supplyAsync(Supplier supplier) { + return supplyAsync(jdwpThreadPool, supplier); + } + + public static CompletableFuture supplyAsync(Executor executor, Supplier supplier) { + return CompletableFuture.supplyAsync(supplier, executor); + } + + public static U await(CompletableFuture future) { + try { + return future.join(); + } catch (CompletionException ex) { + if (ex.getCause() instanceof RuntimeException) { + throw (RuntimeException) ex.getCause(); + } + + throw ex; + } + } + + public static List await(CompletableFuture[] futures) { + List results = new ArrayList<>(); + try { + allOf(futures).join(); + for (CompletableFuture future : futures) { + results.add(await(future)); + } + } catch (CompletionException ex) { + if (ex.getCause() instanceof RuntimeException) { + throw (RuntimeException) ex.getCause(); + } + + throw ex; + } + + return results; + } + + public static List await(List> futures) { + return await((CompletableFuture[]) futures.toArray(new CompletableFuture[0])); + } + + public static CompletableFuture> all(CompletableFuture... futures) { + return allOf(futures).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture future : futures) { + results.add(future.join()); + } + + return results; + }); + } + + public static CompletableFuture> all(List> futures) { + return allOf(futures.toArray(new CompletableFuture[0])).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture future : futures) { + results.add(future.join()); + } + + return results; + }); + } + + public static CompletableFuture> flatAll(CompletableFuture>... futures) { + return allOf(futures).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture> future : futures) { + results.addAll(future.join()); + } + + return results; + }); + } + + public static CompletableFuture> flatAll(List>> futures) { + return allOf(futures.toArray(new CompletableFuture[0])).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture> future : futures) { + results.addAll(future.join()); + } + + return results; + }); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index a81968ad3..8b865d44f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -12,11 +12,15 @@ package com.microsoft.java.debug.core; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import com.sun.jdi.AbsentInformationException; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ReferenceType; @@ -41,6 +45,8 @@ public class Breakpoint implements IBreakpoint { private HashMap propertyMap = new HashMap<>(); private String methodSignature = null; + private boolean async = false; + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { this(vm, eventHub, className, lineNumber, 0, null); } @@ -69,7 +75,7 @@ public class Breakpoint implements IBreakpoint { } // IDebugResource - private List requests = new ArrayList<>(); + private List requests = Collections.synchronizedList(new ArrayList<>()); private List subscriptions = new ArrayList<>(); @Override @@ -162,6 +168,16 @@ public String getLogMessage() { return this.logMessage; } + @Override + public boolean async() { + return this.async; + } + + @Override + public void setAsync(boolean async) { + this.async = async; + } + @Override public CompletableFuture install() { // It's possible that different class loaders create new class with the same name. @@ -185,8 +201,9 @@ public CompletableFuture install() { || localClassPrepareRequest.equals(debugEvent.event.request()))) .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; - List newRequests = createBreakpointRequests(event.referenceType(), lineNumber, - hitCount, false); + List newRequests = AsyncJdwpUtils.await( + createBreakpointRequests(event.referenceType(), lineNumber, hitCount, false) + ); requests.addAll(newRequests); if (!newRequests.isEmpty() && !future.isDone()) { this.putProperty("verified", true); @@ -195,126 +212,180 @@ public CompletableFuture install() { }); subscriptions.add(subscription); - List refTypes = vm.classesByName(className); - List newRequests = createBreakpointRequests(refTypes, lineNumber, hitCount, true); - requests.addAll(newRequests); + Runnable resolveRequestsFromExistingClasses = () -> { + List refTypes = vm.classesByName(className); + createBreakpointRequests(refTypes, lineNumber, hitCount, true) + .whenComplete((newRequests, ex) -> { + if (ex != null) { + return; + } + + requests.addAll(newRequests); + if (!newRequests.isEmpty() && !future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + }); + }; - if (!newRequests.isEmpty() && !future.isDone()) { - this.putProperty("verified", true); - future.complete(this); + if (async()) { + AsyncJdwpUtils.runAsync(resolveRequestsFromExistingClasses); + } else { + resolveRequestsFromExistingClasses.run(); } return future; } - private static List collectLocations(ReferenceType refType, int lineNumber) { - List locations = new ArrayList<>(); - - try { - locations.addAll(refType.locationsOfLine(lineNumber)); - } catch (Exception e) { - // could be AbsentInformationException or ClassNotPreparedException - // but both are expected so no need to further handle + private CompletableFuture> collectLocations(ReferenceType refType, int lineNumber) { + List>> futures = new ArrayList<>(); + Iterator iter = refType.methods().iterator(); + while (iter.hasNext()) { + Method method = iter.next(); + if (async()) { + futures.add(AsyncJdwpUtils.supplyAsync(() -> findLocaitonsOfLine(method, lineNumber))); + } else { + futures.add(CompletableFuture.completedFuture(findLocaitonsOfLine(method, lineNumber))); + } } - return locations; + return AsyncJdwpUtils.flatAll(futures); } - private static List collectLocations(List refTypes, int lineNumber, boolean includeNestedTypes) { - List locations = new ArrayList<>(); - try { - refTypes.forEach(refType -> { - List newLocations = collectLocations(refType, lineNumber); - if (!newLocations.isEmpty()) { - locations.addAll(newLocations); - } else if (includeNestedTypes) { - // ReferenceType.nestedTypes() will invoke vm.allClasses() to list all loaded classes, - // should avoid using nestedTypes for performance. - for (ReferenceType nestedType : refType.nestedTypes()) { - List nestedLocations = collectLocations(nestedType, lineNumber); - if (!nestedLocations.isEmpty()) { - locations.addAll(nestedLocations); - break; - } + private CompletableFuture> collectLocations(List refTypes, int lineNumber, boolean includeNestedTypes) { + List>> futures = new ArrayList<>(); + refTypes.forEach(refType -> { + futures.add(collectLocations(refType, lineNumber, includeNestedTypes)); + }); + + return AsyncJdwpUtils.flatAll(futures); + } + + private CompletableFuture> collectLocations(ReferenceType refType, int lineNumber, boolean includeNestedTypes) { + return collectLocations(refType, lineNumber).thenCompose((newLocations) -> { + if (!newLocations.isEmpty()) { + return CompletableFuture.completedFuture(newLocations); + } else if (includeNestedTypes) { + // ReferenceType.nestedTypes() will invoke vm.allClasses() to list all loaded classes, + // should avoid using nestedTypes for performance. + for (ReferenceType nestedType : refType.nestedTypes()) { + CompletableFuture> nestedLocationsFuture = collectLocations(nestedType, lineNumber); + List nestedLocations = nestedLocationsFuture.join(); + if (!nestedLocations.isEmpty()) { + return CompletableFuture.completedFuture(nestedLocations); } } - }); - } catch (VMDisconnectedException ex) { - // collect locations operation may be executing while JVM is terminating, thus the VMDisconnectedException may be - // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid - // response in vscode, causing no error log in trace. - } + } - return locations; + return CompletableFuture.completedFuture(Collections.emptyList()); + }); } - private static List collectLocations(List refTypes, String nameAndSignature) { - List locations = new ArrayList<>(); + private CompletableFuture> collectLocations(List refTypes, String nameAndSignature) { String[] segments = nameAndSignature.split("#"); - + List> futures = new ArrayList<>(); for (ReferenceType refType : refTypes) { - List methods = refType.methods(); - for (Method method : methods) { - if (!method.isAbstract() && !method.isNative() - && segments[0].equals(method.name()) - && (segments[1].equals(method.genericSignature()) || segments[1].equals(method.signature()))) { - locations.add(method.location()); - break; - } + if (async()) { + futures.add(AsyncJdwpUtils.supplyAsync(() -> findMethodLocaiton(refType, segments[0], segments[1]))); + } else { + futures.add(CompletableFuture.completedFuture(findMethodLocaiton(refType, segments[0], segments[1]))); } } - return locations; + + return AsyncJdwpUtils.all(futures); } - private List createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount, + private Location findMethodLocaiton(ReferenceType refType, String methodName, String methodSiguature) { + List methods = refType.methods(); + Location location = null; + for (Method method : methods) { + if (!method.isAbstract() && !method.isNative() + && methodName.equals(method.name()) + && (methodSiguature.equals(method.genericSignature()) || methodSiguature.equals(method.signature()))) { + location = method.location(); + break; + } + } + + return location; + } + + private List findLocaitonsOfLine(Method method, int lineNumber) { + try { + return method.locationsOfLine(lineNumber); + } catch (AbsentInformationException e) { + // could be AbsentInformationException or ClassNotPreparedException + // but both are expected so no need to further handle + } + + return Collections.emptyList(); + } + + private CompletableFuture> createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount, boolean includeNestedTypes) { - List refTypes = new ArrayList<>(); - refTypes.add(refType); - return createBreakpointRequests(refTypes, lineNumber, hitCount, includeNestedTypes); + return createBreakpointRequests(Arrays.asList(refType), lineNumber, hitCount, includeNestedTypes); } - private List createBreakpointRequests(List refTypes, int lineNumber, + private CompletableFuture> createBreakpointRequests(List refTypes, int lineNumber, int hitCount, boolean includeNestedTypes) { - List locations; + CompletableFuture> locationsFuture; if (this.methodSignature != null) { - locations = collectLocations(refTypes, this.methodSignature); + locationsFuture = collectLocations(refTypes, this.methodSignature); } else { - locations = collectLocations(refTypes, lineNumber, includeNestedTypes); + locationsFuture = collectLocations(refTypes, lineNumber, includeNestedTypes); } - // find out the existing breakpoint locations - List existingLocations = new ArrayList<>(requests.size()); - Observable.fromIterable(requests).filter(request -> request instanceof BreakpointRequest) - .map(request -> ((BreakpointRequest) request).location()).toList().subscribe(list -> { - existingLocations.addAll(list); - }); - - // remove duplicated locations - List newLocations = new ArrayList<>(locations.size()); - Observable.fromIterable(locations).filter(location -> !existingLocations.contains(location)).toList().subscribe(list -> { - newLocations.addAll(list); - }); + return locationsFuture.thenCompose((locations) -> { + // find out the existing breakpoint locations + List existingLocations = new ArrayList<>(requests.size()); + Observable.fromIterable(requests).filter(request -> request instanceof BreakpointRequest) + .map(request -> ((BreakpointRequest) request).location()).toList().subscribe(list -> { + existingLocations.addAll(list); + }); + + // remove duplicated locations + List newLocations = new ArrayList<>(locations.size()); + Observable.fromIterable(locations).filter(location -> !existingLocations.contains(location)).toList().subscribe(list -> { + newLocations.addAll(list); + }); - List newRequests = new ArrayList<>(newLocations.size()); + List newRequests = new ArrayList<>(newLocations.size()); - newLocations.forEach(location -> { - try { + newLocations.forEach(location -> { BreakpointRequest request = vm.eventRequestManager().createBreakpointRequest(location); request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD); if (hitCount > 0) { request.addCountFilter(hitCount); } - request.enable(); request.putProperty(IBreakpoint.REQUEST_TYPE, computeRequestType()); newRequests.add(request); - } catch (VMDisconnectedException ex) { - // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be - // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid - // response in vscode, causing no error log in trace. + }); + + List> futures = new ArrayList<>(); + for (BreakpointRequest request : newRequests) { + if (async()) { + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + request.enable(); + } catch (VMDisconnectedException ex) { + // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be + // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid + // response in vscode, causing no error log in trace. + } + })); + } else { + try { + request.enable(); + } catch (VMDisconnectedException ex) { + // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be + // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid + // response in vscode, causing no error log in trace. + } + } } - }); - return newRequests; + return AsyncJdwpUtils.all(futures).thenApply((res) -> newRequests); + }); } private Object computeRequestType() { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index ab4341f51..220688693 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.List; +import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; import com.sun.jdi.request.EventRequest; @@ -57,8 +58,12 @@ public void resume() { * all threads fully. */ for (ThreadReference tr : DebugUtility.getAllThreadsSafely(this)) { - while (!tr.isCollected() && tr.suspendCount() > 1) { - tr.resume(); + try { + while (tr.suspendCount() > 1) { + tr.resume(); + } + } catch (ObjectCollectedException ex) { + // Skip it if the thread is garbage collected. } } vm.resume(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index e3c928332..f59f10043 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -43,6 +43,7 @@ public final class DebugSettings { public boolean exceptionFiltersUpdated = false; public int limitOfVariablesPerJdwpRequest = 100; public int jdwpRequestTimeout = 3000; + public AsyncMode asyncJDWP = AsyncMode.OFF; public static DebugSettings getCurrent() { return current; @@ -87,6 +88,15 @@ public static enum HotCodeReplace { NEVER } + public static enum AsyncMode { + @SerializedName("auto") + AUTO, + @SerializedName("on") + ON, + @SerializedName("off") + OFF + } + public static interface IDebugSettingChangeListener { public void update(DebugSettings oldSettings, DebugSettings newSettings); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index 1202a30f3..4a2a49e9b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -444,7 +444,7 @@ public static CompletableFuture stopOnEntry(IDebugSession debugSession, St */ public static ThreadReference getThread(IDebugSession debugSession, long threadId) { for (ThreadReference thread : getAllThreadsSafely(debugSession)) { - if (thread.uniqueID() == threadId && !thread.isCollected()) { + if (thread.uniqueID() == threadId) { return thread; } } @@ -477,7 +477,7 @@ public static List getAllThreadsSafely(IDebugSession debugSessi */ public static void resumeThread(ThreadReference thread) { // if thread is not found or is garbage collected, do nothing - if (thread == null || thread.isCollected()) { + if (thread == null) { return; } try { @@ -495,6 +495,25 @@ public static void resumeThread(ThreadReference thread) { } } + public static void resumeThread(ThreadReference thread, int resumeCount) { + if (thread == null) { + return; + } + + try { + for (int i = 0; i < resumeCount; i++) { + /** + * Invoking this method will decrement the count of pending suspends on this thread. + * If it is decremented to 0, the thread will continue to execute. + */ + thread.resume(); + } + } catch (ObjectCollectedException ex) { + // ObjectCollectionException can be thrown if the thread has already completed (exited) in the VM when calling suspendCount, + // the resume operation to this thread is meanness. + } + } + /** * Remove the event request from the vm. If the vm has terminated, do nothing. * @param eventManager diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 3db16b577..04fbf5005 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -44,4 +44,11 @@ public interface IBreakpoint extends IDebugResource { String getLogMessage(); void setLogMessage(String logMessage); + + default void setAsync(boolean async) { + } + + default boolean async() { + return false; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java index 668884567..68f9539c8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java @@ -30,4 +30,11 @@ public interface IMethodBreakpoint extends IDebugResource { Object getProperty(Object key); void putProperty(Object key, Object value); + + default void setAsync(boolean async) { + } + + default boolean async() { + return false; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java index 82c5b75e2..7a6e74c6b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java @@ -11,6 +11,7 @@ package com.microsoft.java.debug.core; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,12 +43,13 @@ public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoi private String functionName; private String condition; private int hitCount; + private boolean async = false; private HashMap propertyMap = new HashMap<>(); private Object compiledConditionalExpression = null; private Map compiledExpressions = new ConcurrentHashMap<>(); - private List requests = new ArrayList<>(); + private List requests = Collections.synchronizedList(new ArrayList<>()); private List subscriptions = new ArrayList<>(); public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, String functionName, @@ -169,6 +171,16 @@ public void setHitCount(int hitCount) { }); } + @Override + public boolean async() { + return this.async; + } + + @Override + public void setAsync(boolean async) { + this.async = async; + } + @Override public CompletableFuture install() { Disposable subscription = eventHub.events() @@ -194,7 +206,9 @@ public CompletableFuture install() { && (classPrepareRequest.equals(debugEvent.event.request()))) .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; - Optional createdRequest = createMethodEntryRequest(event.referenceType()); + Optional createdRequest = AsyncJdwpUtils.await( + createMethodEntryRequest(event.referenceType()) + ); if (createdRequest.isPresent()) { MethodEntryRequest methodEntryRequest = createdRequest.get(); requests.add(methodEntryRequest); @@ -206,22 +220,44 @@ public CompletableFuture install() { }); subscriptions.add(subscription); - List types = vm.classesByName(className); - for (ReferenceType type : types) { - Optional createdRequest = createMethodEntryRequest(type); - if (createdRequest.isPresent()) { - MethodEntryRequest methodEntryRequest = createdRequest.get(); - requests.add(methodEntryRequest); - if (!future.isDone()) { - this.putProperty("verified", true); - future.complete(this); - } + Runnable createRequestsFromLoadedClasses = () -> { + List types = vm.classesByName(className); + for (ReferenceType type : types) { + createMethodEntryRequest(type).whenComplete((createdRequest, ex) -> { + if (ex != null) { + return; + } + + if (createdRequest.isPresent()) { + MethodEntryRequest methodEntryRequest = createdRequest.get(); + requests.add(methodEntryRequest); + if (!future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + } + }); } + }; + + if (async()) { + AsyncJdwpUtils.runAsync(createRequestsFromLoadedClasses); + } else { + createRequestsFromLoadedClasses.run(); } + return future; } - private Optional createMethodEntryRequest(ReferenceType type) { + private CompletableFuture> createMethodEntryRequest(ReferenceType type) { + if (async()) { + return CompletableFuture.supplyAsync(() -> createMethodEntryRequest0(type)); + } else { + return CompletableFuture.completedFuture(createMethodEntryRequest0(type)); + } + } + + private Optional createMethodEntryRequest0(ReferenceType type) { return type.methodsByName(functionName).stream().findFirst().map(method -> { MethodEntryRequest request = vm.eventRequestManager().createMethodEntryRequest(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java index e292026c8..911dca529 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java @@ -190,6 +190,12 @@ public static void recordEvent(Event event) { } } + public static void recordInfo(String key, Object value) { + Map map = new HashMap<>(); + map.put(key, value); + usageDataLogger.log(Level.INFO, "session info", map); + } + /** * Record counts for each user errors encountered. */ diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 30fb12200..4185d7597 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -21,6 +21,7 @@ import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.DebugSettings.AsyncMode; import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; import com.microsoft.java.debug.core.adapter.variables.VariableFormatterFactory; import com.microsoft.java.debug.core.protocol.IProtocolServer; @@ -57,6 +58,8 @@ public class DebugAdapterContext implements IDebugAdapterContext { private long shellProcessId = -1; private long processId = -1; + private boolean localDebugging = true; + private IdCollection sourceReferences = new IdCollection<>(); private RecyclableObjectPool recyclableIdPool = new RecyclableObjectPool<>(); private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter(); @@ -65,6 +68,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private IExceptionManager exceptionManager = new ExceptionManager(); private IBreakpointManager breakpointManager = new BreakpointManager(); private IStepResultManager stepResultManager = new StepResultManager(); + private ThreadCache threadCache = new ThreadCache(); public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) { this.providerContext = providerContext; @@ -349,4 +353,27 @@ public void setProcessId(long processId) { public void setShellProcessId(long shellProcessId) { this.shellProcessId = shellProcessId; } + + @Override + public void setThreadCache(ThreadCache cache) { + this.threadCache = cache; + } + + @Override + public ThreadCache getThreadCache() { + return this.threadCache; + } + + @Override + public boolean asyncJDWP() { + return DebugSettings.getCurrent().asyncJDWP == AsyncMode.ON; + } + + public boolean isLocalDebugging() { + return localDebugging; + } + + public void setLocalDebugging(boolean local) { + this.localDebugging = local; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index a1b21ecee..bfcad8905 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -136,4 +136,14 @@ public interface IDebugAdapterContext { void setProcessId(long processId); long getProcessId(); + + void setThreadCache(ThreadCache cache); + + ThreadCache getThreadCache(); + + boolean asyncJDWP(); + + boolean isLocalDebugging(); + + void setLocalDebugging(boolean local); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java index de10319f6..f3ae95d6e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java @@ -31,4 +31,14 @@ public interface IStackFrameManager { * @return all the stackframes in the specified thread */ StackFrame[] reloadStackFrames(ThreadReference thread); + + /** + * Refersh the stackframes starting from the specified depth and length. + * + * @param thread the jdi thread + * @param start the index of the first frame to refresh. Index 0 represents the current frame. + * @param length the number of frames to refersh + * @return the refreshed stackframes + */ + StackFrame[] reloadStackFrames(ThreadReference thread, int start, int length); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java index 9e1a86970..d2f934901 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -11,7 +11,6 @@ package com.microsoft.java.debug.core.adapter; -import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -21,10 +20,10 @@ import com.sun.jdi.ThreadReference; public class StackFrameManager implements IStackFrameManager { - private Map threadStackFrameMap = Collections.synchronizedMap(new HashMap<>()); + private Map threadStackFrameMap = new HashMap<>(); @Override - public StackFrame getStackFrame(StackFrameReference ref) { + public synchronized StackFrame getStackFrame(StackFrameReference ref) { ThreadReference thread = ref.getThread(); int depth = ref.getDepth(); StackFrame[] frames = threadStackFrameMap.get(thread.uniqueID()); @@ -32,13 +31,39 @@ public StackFrame getStackFrame(StackFrameReference ref) { } @Override - public StackFrame[] reloadStackFrames(ThreadReference thread) { + public synchronized StackFrame[] reloadStackFrames(ThreadReference thread) { return threadStackFrameMap.compute(thread.uniqueID(), (key, old) -> { try { - return thread.frames().toArray(new StackFrame[0]); + if (old == null || old.length == 0) { + return thread.frames().toArray(new StackFrame[0]); + } else { + return thread.frames(0, old.length).toArray(new StackFrame[0]); + } } catch (IncompatibleThreadStateException e) { return new StackFrame[0]; } }); } + + @Override + public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, int start, int length) { + long threadId = thread.uniqueID(); + StackFrame[] old = threadStackFrameMap.get(threadId); + try { + StackFrame[] newFrames = thread.frames(start, length).toArray(new StackFrame[0]); + if (old == null || (start == 0 && length == old.length)) { + threadStackFrameMap.put(threadId, newFrames); + } else { + int maxLength = Math.max(old.length, start + length); + StackFrame[] totalFrames = new StackFrame[maxLength]; + System.arraycopy(old, 0, totalFrames, 0, old.length); + System.arraycopy(newFrames, 0, totalFrames, start, length); + threadStackFrameMap.put(threadId, totalFrames); + } + + return newFrames; + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException e) { + return new StackFrame[0]; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java new file mode 100644 index 000000000..3232dbf18 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java @@ -0,0 +1,72 @@ +/******************************************************************************* +* Copyright (c) 2022 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.core.adapter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.sun.jdi.ThreadReference; + +public class ThreadCache { + private List allThreads = new ArrayList<>(); + private Map threadNameMap = new ConcurrentHashMap<>(); + private Map deathThreads = Collections.synchronizedMap(new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + return this.size() > 100; + } + }); + + public synchronized void resetThreads(List threads) { + allThreads.clear(); + allThreads.addAll(threads); + } + + public synchronized List getThreads() { + return allThreads; + } + + public synchronized ThreadReference getThread(long threadId) { + for (ThreadReference thread : allThreads) { + if (threadId == thread.uniqueID()) { + return thread; + } + } + + return null; + } + + public void setThreadName(long threadId, String name) { + threadNameMap.put(threadId, name); + } + + public String getThreadName(long threadId) { + return threadNameMap.get(threadId); + } + + public void addDeathThread(long threadId) { + threadNameMap.remove(threadId); + deathThreads.put(threadId, true); + } + + public void removeDeathThread(long threadId) { + deathThreads.remove(threadId); + } + + public boolean isDeathThread(long threadId) { + return deathThreads.containsKey(threadId); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index 7069bc39e..ecadbaaac 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -23,6 +23,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.UsageDataSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.ErrorCode; @@ -39,6 +40,7 @@ import com.microsoft.java.debug.core.protocol.Requests.AttachArguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.sun.jdi.connect.IllegalConnectorArgumentsException; +import com.sun.jdi.request.EventRequest; import org.apache.commons.lang3.StringUtils; @@ -58,6 +60,7 @@ public CompletableFuture handle(Command command, Arguments arguments, context.setSourcePaths(attachArguments.sourcePaths); context.setDebuggeeEncoding(StandardCharsets.UTF_8); // Use UTF-8 as debuggee's default encoding format. context.setStepFilters(attachArguments.stepFilters); + context.setLocalDebugging(isLocalHost(attachArguments.hostName)); IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); @@ -96,6 +99,14 @@ public CompletableFuture handle(Command command, Arguments arguments, logger.warning(warnMessage); context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); } + + EventRequest request = debugSession.getVM().eventRequestManager().createVMDeathRequest(); + request.setSuspendPolicy(EventRequest.SUSPEND_NONE); + long sent = System.currentTimeMillis(); + request.enable(); + long received = System.currentTimeMillis(); + logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); + UsageDataSession.recordInfo("networkLatency", (received - sent)); } IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); @@ -111,4 +122,13 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private boolean isLocalHost(String hostName) { + if (hostName == null || "localhost".equals(hostName) || "127.0.0.1".equals(hostName)) { + return true; + } + + // TODO: Check the host name of current computer as well. + return false; + } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 9cd3aa446..311db11c3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -55,6 +55,7 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); vmHandler.setVmProvider(context.getProvider(IVirtualMachineManagerProvider.class)); + UsageDataSession.recordInfo("asyncJDWP", context.asyncJDWP()); if (debugSession != null) { // This is a global event handler to handle the JDI Event from Virtual Machine. debugSession.getEventHub().events().subscribe(debugEvent -> { @@ -104,6 +105,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, ThreadReference deathThread = ((ThreadDeathEvent) event).thread(); Events.ThreadEvent threadDeathEvent = new Events.ThreadEvent("exited", deathThread.uniqueID()); context.getProtocolServer().sendEvent(threadDeathEvent); + context.getThreadCache().addDeathThread(deathThread.uniqueID()); } else if (event instanceof BreakpointEvent) { // ignore since SetBreakpointsRequestHandler has already handled } else if (event instanceof ExceptionEvent) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 9cf99742d..1a0e41003 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -64,6 +64,11 @@ public CompletableFuture handle(Command command, Arguments arguments, VariableUtils.applyFormatterOptions(options, evalArguments.format != null && evalArguments.format.hex); String expression = evalArguments.expression; + // Async mode is supposed to be performant, then disable the advanced features like hover evaluation. + if (!context.isLocalDebugging() && context.asyncJDWP() && "hover".equals(evalArguments.context)) { + return CompletableFuture.completedFuture(response); + } + if (StringUtils.isBlank(expression)) { throw new CompletionException(AdapterUtils.createUserErrorDebugException( "Failed to evaluate. Reason: Empty expression cannot be evaluated.", diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java index e9697f899..b30959cd5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java @@ -72,7 +72,7 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { InlineValuesArguments inlineValuesArgs = (InlineValuesArguments) arguments; - int variableCount = inlineValuesArgs == null || inlineValuesArgs.variables == null ? 0 : inlineValuesArgs.variables.length; + final int variableCount = inlineValuesArgs == null || inlineValuesArgs.variables == null ? 0 : inlineValuesArgs.variables.length; InlineVariable[] inlineVariables = inlineValuesArgs.variables; StackFrameReference stackFrameReference = (StackFrameReference) context.getRecyclableIdPool().getObjectById(inlineValuesArgs.frameId); if (stackFrameReference == null) { @@ -81,6 +81,12 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + // Async mode is supposed to be performant, then disable the advanced features like inline values. + if (!context.isLocalDebugging() && context.asyncJDWP()) { + response.body = new Responses.InlineValuesResponse(null); + return CompletableFuture.completedFuture(response); + } + IStackFrameManager stackFrameManager = context.getStackFrameManager(); StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); if (frame == null) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java index 3479c9a8a..2023209f4 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java @@ -30,6 +30,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.RestartFrameArguments; +import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.request.StepRequest; @@ -59,7 +60,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (canRestartFrame(context, stackFrameReference)) { try { ThreadReference reference = stackFrameReference.getThread(); - popStackFrames(context, reference, stackFrameReference.getDepth()); + popStackFrames(context, stackFrameReference); stepInto(context, reference); } catch (DebugException de) { context.getProtocolServer().sendEvent(new Events.UserNotificationEvent(NotificationType.ERROR, de.getMessage())); @@ -80,10 +81,20 @@ private boolean canRestartFrame(IDebugAdapterContext context, StackFrameReferenc return false; } ThreadReference reference = frameReference.getThread(); - StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(reference); + int totalFrames; + try { + totalFrames = reference.frameCount(); + } catch (IncompatibleThreadStateException e) { + return false; + } // The frame cannot be the bottom one of the call stack: - if (frames.length <= frameReference.getDepth() + 1) { + if (totalFrames <= frameReference.getDepth() + 1) { + return false; + } + + StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(reference, 0, frameReference.getDepth() + 2); + if (frames.length == 0) { return false; } @@ -96,9 +107,12 @@ private boolean canRestartFrame(IDebugAdapterContext context, StackFrameReferenc return true; } - private void popStackFrames(IDebugAdapterContext context, ThreadReference thread, int depth) throws DebugException { - StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread); - StackFrameUtility.pop(frames[depth]); + private void popStackFrames(IDebugAdapterContext context, StackFrameReference stackFrameRef) throws DebugException { + StackFrame frame = context.getStackFrameManager().getStackFrame(stackFrameRef); + if (frame == null) { + return; + } + StackFrameUtility.pop(frame); } private void stepInto(IDebugAdapterContext context, ThreadReference thread) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 6dc74a0bd..085dfe63f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -130,10 +130,11 @@ public CompletableFuture handle(Command command, Arguments arguments, IBreakpoint[] added = context.getBreakpointManager() .setBreakpoints(AdapterUtils.decodeURIComponent(sourcePath), toAdds, bpArguments.sourceModified); for (int i = 0; i < bpArguments.breakpoints.length; i++) { + added[i].setAsync(context.asyncJDWP()); // For newly added breakpoint, should install it to debuggee first. if (toAdds[i] == added[i] && added[i].className() != null) { added[i].install().thenAccept(bp -> { - Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("new", this.convertDebuggerBreakpointToClient(bp, context)); + Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("changed", this.convertDebuggerBreakpointToClient(bp, context)); context.getProtocolServer().sendEvent(bpEvent); }); } else if (added[i].className() != null) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index fa4d23388..3f370b429 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -93,6 +93,7 @@ public CompletableFuture handle(Command command, Arguments arguments, continue; } + currentMethodBreakpoints[i].setAsync(context.asyncJDWP()); // If the requested method breakpoint exists in the manager, it will reuse // the cached breakpoint exists object. // Otherwise add the requested method breakpoint to the cache. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 021bae609..ffa74206e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -16,11 +16,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -36,9 +39,12 @@ import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.LocalVariable; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; @@ -57,26 +63,32 @@ public CompletableFuture handle(Command command, Arguments arguments, response.body = new Responses.StackTraceResponseBody(result, 0); return CompletableFuture.completedFuture(response); } - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId); + ThreadReference thread = context.getThreadCache().getThread(stacktraceArgs.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId); + } int totalFrames = 0; if (thread != null) { try { totalFrames = thread.frameCount(); + int count = stacktraceArgs.levels == 0 ? totalFrames - stacktraceArgs.startFrame + : Math.min(totalFrames - stacktraceArgs.startFrame, stacktraceArgs.levels); if (totalFrames <= stacktraceArgs.startFrame) { response.body = new Responses.StackTraceResponseBody(result, totalFrames); return CompletableFuture.completedFuture(response); } - StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread); - int count = stacktraceArgs.levels == 0 ? totalFrames - stacktraceArgs.startFrame - : Math.min(totalFrames - stacktraceArgs.startFrame, stacktraceArgs.levels); - for (int i = stacktraceArgs.startFrame; i < frames.length && count-- > 0; i++) { + StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count); + List jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP()); + for (int i = stacktraceArgs.startFrame; i < jdiFrames.size() && count-- > 0; i++) { StackFrameReference stackframe = new StackFrameReference(thread, i); - int frameId = context.getRecyclableIdPool().addObject(thread.uniqueID(), stackframe); - result.add(convertDebuggerStackFrameToClient(frames[i], frameId, context)); + int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); + StackFrameInfo jdiFrame = jdiFrames.get(i - stacktraceArgs.startFrame); + result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, context)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException - | AbsentInformationException | ObjectCollectedException e) { + | AbsentInformationException | ObjectCollectedException + | CancellationException | CompletionException e) { // when error happens, the possible reason is: // 1. the vscode has wrong parameter/wrong uri // 2. the thread actually terminates @@ -87,18 +99,79 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } - private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame, int frameId, IDebugAdapterContext context) + private static List resolveStackFrameInfos(StackFrame[] frames, boolean async) + throws AbsentInformationException, IncompatibleThreadStateException { + List jdiFrames = new ArrayList<>(); + List> futures = new ArrayList<>(); + for (StackFrame frame : frames) { + StackFrameInfo jdiFrame = new StackFrameInfo(frame); + jdiFrame.location = jdiFrame.frame.location(); + jdiFrame.method = jdiFrame.location.method(); + jdiFrame.methodName = jdiFrame.method.name(); + jdiFrame.isNative = jdiFrame.method.isNative(); + jdiFrame.declaringType = jdiFrame.location.declaringType(); + if (async) { + // JDWP Command: M_LINE_TABLE + futures.add(AsyncJdwpUtils.runAsync(() -> { + jdiFrame.lineNumber = jdiFrame.location.lineNumber(); + })); + + // JDWP Commands: RT_SOURCE_DEBUG_EXTENSION, RT_SOURCE_FILE + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + // When the .class file doesn't contain source information in meta data, + // invoking Location#sourceName() would throw AbsentInformationException. + jdiFrame.sourceName = jdiFrame.declaringType.sourceName(); + } catch (AbsentInformationException e) { + jdiFrame.sourceName = null; + } + })); + + // JDWP Command: RT_SIGNATURE + futures.add(AsyncJdwpUtils.runAsync(() -> { + jdiFrame.typeSignature = jdiFrame.declaringType.signature(); + })); + } else { + jdiFrame.lineNumber = jdiFrame.location.lineNumber(); + jdiFrame.typeSignature = jdiFrame.declaringType.signature(); + try { + // When the .class file doesn't contain source information in meta data, + // invoking Location#sourceName() would throw AbsentInformationException. + jdiFrame.sourceName = jdiFrame.declaringType.sourceName(); + } catch (AbsentInformationException e) { + jdiFrame.sourceName = null; + } + } + + jdiFrames.add(jdiFrame); + } + + AsyncJdwpUtils.await(futures); + for (StackFrameInfo jdiFrame : jdiFrames) { + jdiFrame.typeName = jdiFrame.declaringType.name(); + jdiFrame.argumentTypeNames = jdiFrame.method.argumentTypeNames(); + if (jdiFrame.sourceName == null) { + String enclosingType = AdapterUtils.parseEnclosingType(jdiFrame.typeName); + jdiFrame.sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java"; + jdiFrame.sourcePath = enclosingType.replace('.', File.separatorChar) + ".java"; + } else { + jdiFrame.sourcePath = jdiFrame.declaringType.sourcePaths(null).get(0); + } + } + + return jdiFrames; + } + + private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFrame, int frameId, IDebugAdapterContext context) throws URISyntaxException, AbsentInformationException { - Location location = stackFrame.location(); - Method method = location.method(); - Types.Source clientSource = this.convertDebuggerSourceToClient(location, context); - String methodName = formatMethodName(method, true, true); - int lineNumber = AdapterUtils.convertLineNumber(location.lineNumber(), context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + Types.Source clientSource = convertDebuggerSourceToClient(jdiFrame.typeName, jdiFrame.sourceName, jdiFrame.sourcePath, context); + String methodName = formatMethodName(jdiFrame.methodName, jdiFrame.argumentTypeNames, jdiFrame.typeName, true, true); + int clientLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); // Line number returns -1 if the information is not available; specifically, always returns -1 for native methods. String presentationHint = null; - if (lineNumber < 0) { + if (clientLineNumber < 0) { presentationHint = "subtle"; - if (method.isNative()) { + if (jdiFrame.isNative) { // For native method, display a tip text "native method" in the Call Stack View. methodName += "[native method]"; } else { @@ -107,25 +180,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame clientSource = null; } } - return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); - } - - private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdapterContext context) throws URISyntaxException { - final String fullyQualifiedName = location.declaringType().name(); - String sourceName = ""; - String relativeSourcePath = ""; - try { - // When the .class file doesn't contain source information in meta data, - // invoking Location#sourceName() would throw AbsentInformationException. - sourceName = location.sourceName(); - relativeSourcePath = location.sourcePath(); - } catch (AbsentInformationException e) { - String enclosingType = AdapterUtils.parseEnclosingType(fullyQualifiedName); - sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java"; - relativeSourcePath = enclosingType.replace('.', File.separatorChar) + ".java"; - } - - return convertDebuggerSourceToClient(fullyQualifiedName, sourceName, relativeSourcePath, context); + return new Types.StackFrame(frameId, methodName, clientSource, clientLineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); } /** @@ -164,20 +219,42 @@ public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedNa } } - private String formatMethodName(Method method, boolean showContextClass, boolean showParameter) { + private String formatMethodName(String methodName, List argumentTypeNames, String fqn, boolean showContextClass, boolean showParameter) { StringBuilder formattedName = new StringBuilder(); if (showContextClass) { - String fullyQualifiedClassName = method.declaringType().name(); - formattedName.append(SimpleTypeFormatter.trimTypeName(fullyQualifiedClassName)); + formattedName.append(SimpleTypeFormatter.trimTypeName(fqn)); formattedName.append("."); } - formattedName.append(method.name()); + formattedName.append(methodName); if (showParameter) { - List argumentTypeNames = method.argumentTypeNames().stream().map(SimpleTypeFormatter::trimTypeName).collect(Collectors.toList()); + argumentTypeNames = argumentTypeNames.stream().map(SimpleTypeFormatter::trimTypeName).collect(Collectors.toList()); formattedName.append("("); formattedName.append(String.join(",", argumentTypeNames)); formattedName.append(")"); } return formattedName.toString(); } + + static class StackFrameInfo { + public StackFrame frame; + public Location location; + public Method method; + public String methodName; + public List argumentTypeNames = new ArrayList<>(); + public boolean isNative = false; + public int lineNumber; + public ReferenceType declaringType = null; + public String typeName; + public String typeSignature; + public String sourceName = ""; + public String sourcePath = ""; + + // variables + public List visibleVariables = null; + public ObjectReference thisObject; + + public StackFrameInfo(StackFrame frame) { + this.frame = frame; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 72d14eb5d..4f58c113f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -11,12 +11,15 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import org.apache.commons.lang3.ArrayUtils; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugEvent; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; @@ -68,16 +71,18 @@ public CompletableFuture handle(Command command, Arguments arguments, } long threadId = ((StepArguments) arguments).threadId; - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), threadId); + ThreadReference thread = context.getThreadCache().getThread(threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), threadId); + } if (thread != null) { JdiExceptionReference exception = context.getExceptionManager().removeException(threadId); context.getStepResultManager().removeMethodResult(threadId); try { + final ThreadReference targetThread = thread; ThreadState threadState = new ThreadState(); threadState.threadId = threadId; threadState.pendingStepType = command; - threadState.stackDepth = thread.frameCount(); - threadState.stepLocation = getTopFrame(thread).location(); threadState.eventSubscription = context.getDebugSession().getEventHub().events() .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) @@ -98,27 +103,82 @@ public CompletableFuture handle(Command command, Arguments arguments, } else { threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingStepRequest.enable(); - MethodExitRequest methodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); - methodExitRequest.addThreadFilter(thread); - methodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); - if (thread.virtualMachine().canUseInstanceFilters()) { + threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); + threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + + if (context.asyncJDWP()) { + List> futures = new ArrayList<>(); + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + // JDWP Command: TR_FRAMES + threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + // JDWP Command: SF_THIS_OBJECT + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore + } + } + } catch (IncompatibleThreadStateException e) { + throw new CompletionException(e); + } + })); + futures.add(AsyncJdwpUtils.runAsync( + // JDWP Command: OR_IS_COLLECTED + () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread) + )); + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + // JDWP Command: TR_FRAME_COUNT + threadState.stackDepth = targetThread.frameCount(); + } catch (IncompatibleThreadStateException e) { + throw new CompletionException(e); + } + })); + futures.add( + // JDWP Command: ER_SET + AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable()) + ); + try { - ObjectReference thisObject = getTopFrame(thread).thisObject(); - if (thisObject != null) { - methodExitRequest.addInstanceFilter(thisObject); + AsyncJdwpUtils.await(futures); + } catch (CompletionException ex) { + if (ex.getCause() instanceof IncompatibleThreadStateException) { + throw (IncompatibleThreadStateException) ex.getCause(); } - } catch (Exception e) { - // ignore + throw ex; } + + // JDWP Command: ER_SET + threadState.pendingMethodExitRequest.enable(); + } else { + threadState.stackDepth = targetThread.frameCount(); + threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addThreadFilter(thread); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore + } + } + threadState.pendingStepRequest.enable(); + threadState.pendingMethodExitRequest.enable(); } - methodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - threadState.pendingMethodExitRequest = methodExitRequest; - methodExitRequest.enable(); DebugUtility.resumeThread(thread); - ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context); } catch (IncompatibleThreadStateException ex) { // Roll back the Exception info if stepping fails. @@ -136,6 +196,14 @@ public CompletableFuture handle(Command command, Arguments arguments, failureMessage, ErrorCode.STEP_FAILURE, ex); + } catch (Exception ex) { + // Roll back the Exception info if stepping fails. + context.getExceptionManager().setException(threadId, exception); + final String failureMessage = String.format("Failed to step because of the error '%s'", ex.getMessage()); + throw AdapterUtils.createCompletionException( + failureMessage, + ErrorCode.STEP_FAILURE, + ex.getCause() != null ? ex.getCause() : ex); } } @@ -280,6 +348,7 @@ class ThreadState { StepRequest pendingStepRequest = null; MethodExitRequest pendingMethodExitRequest = null; int stackDepth = -1; + StackFrame topFrame = null; Location stepLocation = null; Disposable eventSubscription = null; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index 3e4b481d5..cedbf2ae1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -14,9 +14,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; +import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -77,14 +81,14 @@ public CompletableFuture handle(Command command, Arguments arguments, private CompletableFuture threads(Requests.ThreadsArguments arguments, Response response, IDebugAdapterContext context) { ArrayList threads = new ArrayList<>(); try { - for (ThreadReference thread : context.getDebugSession().getAllThreads()) { - if (thread.isCollected()) { - continue; - } - Types.Thread clientThread = new Types.Thread(thread.uniqueID(), "Thread [" + thread.name() + "]"); - threads.add(clientThread); + List allThreads = context.getDebugSession().getAllThreads(); + context.getThreadCache().resetThreads(allThreads); + allThreads = allThreads.stream().filter((thread) -> !context.getThreadCache().isDeathThread(thread.uniqueID())).toList(); + List jdiThreads = resolveThreadInfos(allThreads, context); + for (ThreadInfo jdiThread : jdiThreads) { + threads.add(new Types.Thread(jdiThread.thread.uniqueID(), "Thread [" + jdiThread.name + "]")); } - } catch (ObjectCollectedException ex) { + } catch (ObjectCollectedException | CancellationException | CompletionException ex) { // allThreads may throw VMDisconnectedException when VM terminates and thread.name() may throw ObjectCollectedException // when the thread is exiting. } @@ -92,6 +96,33 @@ private CompletableFuture threads(Requests.ThreadsArguments arguments, return CompletableFuture.completedFuture(response); } + private static List resolveThreadInfos(List allThreads, IDebugAdapterContext context) { + List threadInfos = new ArrayList<>(allThreads.size()); + List> futures = new ArrayList<>(); + for (ThreadReference thread : allThreads) { + ThreadInfo threadInfo = new ThreadInfo(thread); + long threadId = thread.uniqueID(); + if (context.getThreadCache().getThreadName(threadId) != null) { + threadInfo.name = context.getThreadCache().getThreadName(threadId); + } else { + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> { + threadInfo.name = threadInfo.thread.name(); + context.getThreadCache().setThreadName(threadId, threadInfo.name); + })); + } else { + threadInfo.name = threadInfo.thread.name(); + context.getThreadCache().setThreadName(threadId, threadInfo.name); + } + } + + threadInfos.add(threadInfo); + } + + AsyncJdwpUtils.await(futures); + return threadInfos; + } + private CompletableFuture pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) { ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); if (thread != null) { @@ -108,7 +139,10 @@ private CompletableFuture pause(Requests.PauseArguments arguments, Res private CompletableFuture resume(Requests.ContinueArguments arguments, Response response, IDebugAdapterContext context) { boolean allThreadsContinued = true; - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + ThreadReference thread = context.getThreadCache().getThread(arguments.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + } /** * See the jdi doc https://docs.oracle.com/javase/7/docs/jdk/api/jpda/jdi/com/sun/jdi/ThreadReference.html#resume(), * suspends of both the virtual machine and individual threads are counted. Before a thread will run again, it must @@ -123,7 +157,11 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); - context.getDebugSession().resume(); + if (context.asyncJDWP()) { + resumeVMAsync(context.getDebugSession()); + } else { + context.getDebugSession().resume(); + } context.getRecyclableIdPool().removeAllObjects(); } response.body = new Responses.ContinueResponseBody(allThreadsContinued); @@ -132,7 +170,11 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { context.getExceptionManager().removeAllExceptions(); - context.getDebugSession().resume(); + if (context.asyncJDWP()) { + resumeVMAsync(context.getDebugSession()); + } else { + context.getDebugSession().resume(); + } context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); context.getRecyclableIdPool().removeAllObjects(); return CompletableFuture.completedFuture(response); @@ -140,16 +182,19 @@ private CompletableFuture resumeAll(Requests.ThreadOperationArguments private CompletableFuture resumeOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { - long threadId = thread.uniqueID(); - if (threadId != arguments.threadId && thread.isSuspended()) { - context.getExceptionManager().removeException(threadId); - DebugUtility.resumeThread(thread); - context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); - checkThreadRunningAndRecycleIds(thread, context); + if (thread.uniqueID() == arguments.threadId) { + continue; } - } + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> resumeThread(thread, context))); + } else { + resumeThread(thread, context); + } + } + AsyncJdwpUtils.await(futures); return CompletableFuture.completedFuture(response); } @@ -161,14 +206,19 @@ private CompletableFuture pauseAll(Requests.ThreadOperationArguments a private CompletableFuture pauseOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { - long threadId = thread.uniqueID(); - if (threadId != arguments.threadId && !thread.isCollected() && !thread.isSuspended()) { - thread.suspend(); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + if (thread.uniqueID() == arguments.threadId) { + continue; } - } + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> pauseThread(thread, context))); + } else { + pauseThread(thread, context); + } + } + AsyncJdwpUtils.await(futures); return CompletableFuture.completedFuture(response); } @@ -179,13 +229,7 @@ public static void checkThreadRunningAndRecycleIds(ThreadReference thread, IDebu try { IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); engine.clearState(thread); - boolean allThreadsRunning = !DebugUtility.getAllThreadsSafely(context.getDebugSession()).stream() - .anyMatch(ThreadReference::isSuspended); - if (allThreadsRunning) { - context.getRecyclableIdPool().removeAllObjects(); - } else { - context.getRecyclableIdPool().removeObjectsByOwner(thread.uniqueID()); - } + context.getRecyclableIdPool().removeObjectsByOwner(thread.uniqueID()); } catch (VMDisconnectedException ex) { // isSuspended may throw VMDisconnectedException when the VM terminates context.getRecyclableIdPool().removeAllObjects(); @@ -194,4 +238,58 @@ public static void checkThreadRunningAndRecycleIds(ThreadReference thread, IDebu context.getRecyclableIdPool().removeObjectsByOwner(thread.uniqueID()); } } + + private void resumeVMAsync(IDebugSession debugSession) { + List> futures = new ArrayList<>(); + for (ThreadReference tr : DebugUtility.getAllThreadsSafely(debugSession)) { + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + while (tr.suspendCount() > 1) { + tr.resume(); + } + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + })); + } + + AsyncJdwpUtils.await(futures); + debugSession.getVM().resume(); + } + + private void resumeThread(ThreadReference thread, IDebugAdapterContext context) { + try { + int suspends = thread.suspendCount(); + if (suspends > 0) { + long threadId = thread.uniqueID(); + context.getExceptionManager().removeException(threadId); + DebugUtility.resumeThread(thread, suspends); + context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); + checkThreadRunningAndRecycleIds(thread, context); + } + } catch (ObjectCollectedException ex) { + // ignore it. + } + } + + private void pauseThread(ThreadReference thread, IDebugAdapterContext context) { + try { + if (!thread.isSuspended()) { + long threadId = thread.uniqueID(); + thread.suspend(); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + } + } catch (ObjectCollectedException ex) { + // ignore it if the thread is garbage collected. + } + } + + static class ThreadInfo { + public ThreadReference thread; + public String name; + + public ThreadInfo(ThreadReference thread) { + this.thread = thread; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 8f37ef574..76bb5a4db 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2021 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -19,11 +19,14 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.JdiMethodResult; @@ -39,6 +42,7 @@ import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable; import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.adapter.variables.StringReferenceProxy; import com.microsoft.java.debug.core.adapter.variables.Variable; import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils; import com.microsoft.java.debug.core.adapter.variables.VariableProxy; @@ -57,6 +61,7 @@ import com.sun.jdi.InvalidStackFrameException; import com.sun.jdi.ObjectReference; import com.sun.jdi.StackFrame; +import com.sun.jdi.StringReference; import com.sun.jdi.Type; import com.sun.jdi.Value; @@ -96,7 +101,7 @@ public CompletableFuture handle(Command command, Arguments arguments, VariableProxy containerNode = (VariableProxy) container; - if (containerNode.isLazyVariable() && DebugSettings.getCurrent().showToString) { + if (supportsToStringView(context) && containerNode.isLazyVariable()) { Types.Variable typedVariable = this.resolveLazyVariable(context, containerNode, variableFormatter, options, evaluationEngine); if (typedVariable != null) { list.add(typedVariable); @@ -123,24 +128,29 @@ public CompletableFuture handle(Command command, Arguments arguments, String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->"; childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null)); } - childrenList.addAll(VariableUtils.listLocalVariables(frame)); - Variable thisVariable = VariableUtils.getThisVariable(frame); - if (thisVariable != null) { - childrenList.add(thisVariable); - } - if (showStaticVariables && frame.location().method().isStatic()) { - childrenList.addAll(VariableUtils.listStaticVariables(frame)); + + if (context.asyncJDWP()) { + childrenList.addAll(getVariablesOfFrameAsync(frame, showStaticVariables)); + } else { + childrenList.addAll(VariableUtils.listLocalVariables(frame)); + Variable thisVariable = VariableUtils.getThisVariable(frame); + if (thisVariable != null) { + childrenList.add(thisVariable); + } + if (showStaticVariables && frame.location().method().isStatic()) { + childrenList.addAll(VariableUtils.listStaticVariables(frame)); + } } - } catch (AbsentInformationException | InternalException | InvalidStackFrameException e) { + } catch (CompletionException | InternalException | InvalidStackFrameException | CancellationException | AbsentInformationException e) { throw AdapterUtils.createCompletionException( String.format("Failed to get variables. Reason: %s", e.toString()), ErrorCode.GET_VARIABLE_FAILURE, - e); + e.getCause() != null ? e.getCause() : e); } } else { try { ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable(); - if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { + if (supportsLogicStructureView(context) && evaluationEngine != null) { JavaLogicalStructure logicalStructure = null; try { logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); @@ -232,6 +242,17 @@ public CompletableFuture handle(Command command, Arguments arguments, } }); } + + // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. + if (context.asyncJDWP()) { + try { + AsyncJdwpUtils.await(warmUpJDICache(childrenList)); + } catch (CompletionException | CancellationException e) { + response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); + } + } + for (Variable javaVariable : childrenList) { Value value = javaVariable.value; String name = javaVariable.name; @@ -242,7 +263,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Value sizeValue = null; if (value instanceof ArrayReference) { indexedVariables = ((ArrayReference) value).length(); - } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { + } else if (supportsLogicStructureView(context) && value instanceof ObjectReference && evaluationEngine != null) { try { JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); if (structure != null && structure.getSizeExpression() != null) { @@ -310,7 +331,7 @@ public CompletableFuture handle(Command command, Arguments arguments, // If failed to resolve the variable value, skip the details info as well. } else if (sizeValue != null) { detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options); - } else if (DebugSettings.getCurrent().showToString) { + } else if (supportsToStringView(context)) { if (VariableDetailUtils.isLazyLoadingSupported(value) && varProxy != null) { varProxy.setLazyVariable(true); } else { @@ -352,6 +373,14 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private boolean supportsLogicStructureView(IDebugAdapterContext context) { + return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showLogicalStructure; + } + + private boolean supportsToStringView(IDebugAdapterContext context) { + return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showToString; + } + private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter, Map options, IEvaluationProvider evaluationEngine) { VariableProxy valueReferenceProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), @@ -384,4 +413,57 @@ private Set getDuplicateNames(Collection list) { } return result; } + + private List getVariablesOfFrameAsync(StackFrame frame, boolean showStaticVariables) { + CompletableFuture> localVariables = VariableUtils.listLocalVariablesAsync(frame); + CompletableFuture thisVariable = VariableUtils.getThisVariableAsync(frame); + CompletableFuture>[] staticVariables = new CompletableFuture[1]; + if (showStaticVariables && frame.location().method().isStatic()) { + staticVariables[0] = VariableUtils.listStaticVariablesAsync(frame); + } + + CompletableFuture futures = staticVariables[0] == null ? CompletableFuture.allOf(localVariables, thisVariable) + : CompletableFuture.allOf(localVariables, thisVariable, staticVariables[0]); + + AsyncJdwpUtils.await(futures); + + List result = new ArrayList<>(); + result.addAll(localVariables.join()); + Variable thisVar = thisVariable.join(); + if (thisVar != null) { + result.add(thisVar); + } + + if (staticVariables[0] != null) { + result.addAll(staticVariables[0].join()); + } + + return result; + } + + private CompletableFuture warmUpJDICache(List variables) { + List> fetchVariableInfoFutures = new ArrayList<>(); + for (Variable javaVariable : variables) { + Value value = javaVariable.value; + if (value instanceof ArrayReference) { + // JDWP Command: AR_LENGTH + fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> ((ArrayReference) value).length())); + } else if (value instanceof StringReference) { + // JDWP Command: SR_VALUE + fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + String strValue = ((StringReference) value).value(); + javaVariable.value = new StringReferenceProxy((StringReference) value, strValue); + })); + } + + if (value instanceof ObjectReference) { + // JDWP Command: OR_REFERENCE_TYPE, RT_SIGNATURE + fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + value.type().signature(); + })); + } + } + + return CompletableFuture.allOf(fetchVariableInfoFutures.toArray(new CompletableFuture[0])); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java new file mode 100644 index 000000000..82c4da56e --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2022 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.core.adapter.variables; + +import java.util.List; +import java.util.Map; + +import com.sun.jdi.ClassNotLoadedException; +import com.sun.jdi.Field; +import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.InvalidTypeException; +import com.sun.jdi.InvocationException; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.StringReference; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.Type; +import com.sun.jdi.Value; +import com.sun.jdi.VirtualMachine; + +public class StringReferenceProxy implements StringReference { + private StringReference delegateStringRef; + private String value = null; + + public StringReferenceProxy(StringReference sr, String value) { + this.delegateStringRef = sr; + this.value = value; + } + + public String value() { + if (value != null) { + return value; + } + + return delegateStringRef.value(); + } + + public ReferenceType referenceType() { + return delegateStringRef.referenceType(); + } + + public VirtualMachine virtualMachine() { + return delegateStringRef.virtualMachine(); + } + + public String toString() { + return delegateStringRef.toString(); + } + + public Value getValue(Field sig) { + return delegateStringRef.getValue(sig); + } + + public Map getValues(List fields) { + return delegateStringRef.getValues(fields); + } + + public void setValue(Field field, Value value) throws InvalidTypeException, ClassNotLoadedException { + delegateStringRef.setValue(field, value); + } + + public Value invokeMethod(ThreadReference thread, Method method, List arguments, int options) + throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, + InvocationException { + return delegateStringRef.invokeMethod(thread, method, arguments, options); + } + + public Type type() { + return delegateStringRef.type(); + } + + public void disableCollection() { + delegateStringRef.disableCollection(); + } + + public void enableCollection() { + delegateStringRef.enableCollection(); + } + + public boolean isCollected() { + return delegateStringRef.isCollected(); + } + + public long uniqueID() { + return delegateStringRef.uniqueID(); + } + + public List waitingThreads() throws IncompatibleThreadStateException { + return delegateStringRef.waitingThreads(); + } + + public ThreadReference owningThread() throws IncompatibleThreadStateException { + return delegateStringRef.owningThread(); + } + + public int entryCount() throws IncompatibleThreadStateException { + return delegateStringRef.entryCount(); + } + + public List referringObjects(long maxReferrers) { + return delegateStringRef.referringObjects(maxReferrers); + } + + public boolean equals(Object obj) { + return delegateStringRef.equals(obj); + } + + public int hashCode() { + return delegateStringRef.hashCode(); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index d53e2705c..bd8c8036d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -15,11 +15,15 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.function.Consumer; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.adapter.formatter.NumericFormatEnum; @@ -213,6 +217,91 @@ public static List listLocalVariables(StackFrame stackFrame) throws Ab return res; } + public static CompletableFuture> listLocalVariablesAsync(StackFrame stackFrame) { + CompletableFuture> future = new CompletableFuture<>(); + if (stackFrame.location().method().isNative()) { + return CompletableFuture.completedFuture(new ArrayList<>()); + } + + AsyncJdwpUtils.supplyAsync(() -> { + try { + return stackFrame.visibleVariables(); + } catch (AbsentInformationException ex) { + throw new CompletionException(ex); + } + }).thenCompose((visibleVariables) -> { + // When using the API StackFrame.getValues() to batch fetch the variable values, the JDI + // probably throws timeout exception if the variables to be passed at one time are large. + // So use paging to fetch the values in chunks. + return bulkFetchValuesAsync(visibleVariables, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage) -> { + Map values = stackFrame.getValues(currentPage); + List result = new ArrayList<>(); + for (LocalVariable localVariable : currentPage) { + Variable var = new Variable(localVariable.name(), values.get(localVariable)); + var.local = localVariable; + result.add(var); + } + + return result; + }); + }).whenComplete((res, ex) -> { + if (ex instanceof CompletionException && ex.getCause() != null) { + ex = ex.getCause(); + } + + if (ex instanceof AbsentInformationException) { + // avoid listing variable on native methods + try { + if (stackFrame.location().method().argumentTypes().size() == 0) { + future.complete(new ArrayList<>()); + return; + } + } catch (ClassNotLoadedException ex2) { + // ignore since the method is hit. + } + // 1. in oracle implementations, when there is no debug information, the AbsentInformationException will be + // thrown, then we need to retrieve arguments from stackFrame#getArgumentValues. + // 2. in eclipse jdt implementations, when there is no debug information, stackFrame#visibleVariables will + // return some generated variables like arg0, arg1, and the stackFrame#getArgumentValues will return null + + // for both scenarios, we need to handle the possible null returned by stackFrame#getArgumentValues and + // we need to call stackFrame.getArgumentValues get the arguments if AbsentInformationException is thrown + int argId = 0; + try { + List arguments = stackFrame.getArgumentValues(); + if (arguments == null) { + future.complete(new ArrayList<>()); + return; + } + + List variables = new ArrayList<>(); + for (Value argValue : arguments) { + Variable var = new Variable("arg" + argId, argValue); + var.argumentIndex = argId++; + variables.add(var); + } + future.complete(variables); + } catch (InternalException ex2) { + // From Oracle's forums: + // This could be a JPDA bug. Unexpected JDWP Error: 32 means that an 'opaque' frame was + // detected at the lower JPDA levels, + // typically a native frame. + if (ex2.errorCode() != 32) { + throw ex2; + } + } + } else if (ex != null) { + future.complete(new ArrayList<>()); + } else { + future.complete(res.stream() + .flatMap(List::stream) + .collect(Collectors.toList())); + } + }); + + return future; + } + /** * Get the this variable of an stack frame. * @@ -228,6 +317,16 @@ public static Variable getThisVariable(StackFrame stackFrame) { return new Variable("this", thisObject); } + public static CompletableFuture getThisVariableAsync(StackFrame stackFrame) { + return AsyncJdwpUtils.supplyAsync(() -> { + ObjectReference thisObject = stackFrame.thisObject(); + if (thisObject == null) { + return null; + } + return new Variable("this", thisObject); + }); + } + /** * Get the static variable of an stack frame. * @@ -251,6 +350,40 @@ public static List listStaticVariables(StackFrame stackFrame) { return res; } + public static CompletableFuture> listStaticVariablesAsync(StackFrame stackFrame) { + CompletableFuture> future = new CompletableFuture<>(); + ReferenceType type = stackFrame.location().declaringType(); + AsyncJdwpUtils.supplyAsync(() -> { + return type.allFields().stream().filter(TypeComponent::isStatic).collect(Collectors.toList()); + }).thenCompose((fields) -> { + return bulkFetchValuesAsync(fields, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage) -> { + List variables = new ArrayList<>(); + Map fieldValues = type.getValues(currentPage); + for (Field currentField : currentPage) { + Variable var = new Variable(currentField.name(), fieldValues.get(currentField)); + var.field = currentField; + variables.add(var); + } + + return variables; + }); + }).whenComplete((res, ex) -> { + if (ex instanceof CompletionException && ex.getCause() != null) { + ex = ex.getCause(); + } + + if (ex != null) { + future.complete(new ArrayList<>()); + } else { + future.complete(res.stream() + .flatMap(List::stream) + .collect(Collectors.toList())); + } + }); + + return future; + } + /** * Apply the display options for variable formatter, it is used in variable and evaluate requests, controls the display content in * variable view/debug console. @@ -316,6 +449,23 @@ private static void bulkFetchValues(List elements, int numberPerPage, Con } } + private static CompletableFuture> bulkFetchValuesAsync(List elements, int numberPerPage, Function, R> function) { + int size = elements.size(); + numberPerPage = numberPerPage < 1 ? 1 : numberPerPage; + int page = size / numberPerPage + Math.min(size % numberPerPage, 1); + List> futures = new ArrayList<>(); + for (int i = 0; i < page; i++) { + int pageStart = i * numberPerPage; + int pageEnd = Math.min(pageStart + numberPerPage, size); + final List currentPage = elements.subList(pageStart, pageEnd); + futures.add(AsyncJdwpUtils.supplyAsync(() -> { + return function.apply(currentPage); + })); + } + + return AsyncJdwpUtils.all(futures); + } + private VariableUtils() { } From 216a7c650a8786b1272cb203d3edbc89dccae675 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 09:18:09 +0800 Subject: [PATCH 109/206] Support listing field variables async (#437) --- .../java/debug/core/UsageDataSession.java | 4 + .../adapter/handler/AttachRequestHandler.java | 97 ++++++++++--------- .../handler/VariablesRequestHandler.java | 64 +++++++----- .../core/adapter/variables/VariableUtils.java | 72 +++++++++++++- 4 files changed, 169 insertions(+), 68 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java index 911dca529..b645e2843 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java @@ -196,6 +196,10 @@ public static void recordInfo(String key, Object value) { usageDataLogger.log(Level.INFO, "session info", map); } + public static void recordInfo(String description, Map data) { + usageDataLogger.log(Level.INFO, description, data); + } + /** * Record counts for each user errors encountered. */ diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index ecadbaaac..4d11ef6fb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -62,59 +62,66 @@ public CompletableFuture handle(Command command, Arguments arguments, context.setStepFilters(attachArguments.stepFilters); context.setLocalDebugging(isLocalHost(attachArguments.hostName)); + Map traceInfo = new HashMap<>(); + traceInfo.put("localAttach", context.isLocalDebugging()); + IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); IDebugSession debugSession = null; try { - logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port)); - debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, - attachArguments.timeout); - context.setDebugSession(debugSession); - vmHandler.connectVirtualMachine(debugSession.getVM()); - logger.info("Attaching to debuggee VM succeeded."); - } catch (IOException | IllegalConnectorArgumentsException e) { - throw AdapterUtils.createCompletionException( - String.format("Failed to attach to remote debuggee VM. Reason: %s", e.toString()), - ErrorCode.ATTACH_FAILURE, - e); - } + try { + logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port)); + debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, + attachArguments.timeout); + context.setDebugSession(debugSession); + vmHandler.connectVirtualMachine(debugSession.getVM()); + logger.info("Attaching to debuggee VM succeeded."); + } catch (IOException | IllegalConnectorArgumentsException e) { + throw AdapterUtils.createCompletionException( + String.format("Failed to attach to remote debuggee VM. Reason: %s", e.toString()), + ErrorCode.ATTACH_FAILURE, + e); + } - Map options = new HashMap<>(); - options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding()); - if (attachArguments.projectName != null) { - options.put(Constants.PROJECT_NAME, attachArguments.projectName); - } - // TODO: Clean up the initialize mechanism - ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); - sourceProvider.initialize(context, options); - // If the debugger and debuggee run at the different JVM platforms, show a warning message. - if (debugSession != null) { - String debuggeeVersion = debugSession.getVM().version(); - String debuggerVersion = sourceProvider.getJavaRuntimeVersion(attachArguments.projectName); - if (StringUtils.isNotBlank(debuggerVersion) && !debuggerVersion.equals(debuggeeVersion)) { - String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. " - + "You could see wrong source mapping results.\n" - + "Debugger JVM version: %s\n" - + "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion); - logger.warning(warnMessage); - context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); + Map options = new HashMap<>(); + options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding()); + if (attachArguments.projectName != null) { + options.put(Constants.PROJECT_NAME, attachArguments.projectName); } + // TODO: Clean up the initialize mechanism + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); + sourceProvider.initialize(context, options); + // If the debugger and debuggee run at the different JVM platforms, show a warning message. + if (debugSession != null) { + String debuggeeVersion = debugSession.getVM().version(); + String debuggerVersion = sourceProvider.getJavaRuntimeVersion(attachArguments.projectName); + if (StringUtils.isNotBlank(debuggerVersion) && !debuggerVersion.equals(debuggeeVersion)) { + String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. " + + "You could see wrong source mapping results.\n" + + "Debugger JVM version: %s\n" + + "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion); + logger.warning(warnMessage); + context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); + } - EventRequest request = debugSession.getVM().eventRequestManager().createVMDeathRequest(); - request.setSuspendPolicy(EventRequest.SUSPEND_NONE); - long sent = System.currentTimeMillis(); - request.enable(); - long received = System.currentTimeMillis(); - logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); - UsageDataSession.recordInfo("networkLatency", (received - sent)); - } + EventRequest request = debugSession.getVM().eventRequestManager().createVMDeathRequest(); + request.setSuspendPolicy(EventRequest.SUSPEND_NONE); + long sent = System.currentTimeMillis(); + request.enable(); + long received = System.currentTimeMillis(); + logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); + traceInfo.put("networkLatency", (received - sent)); + } - IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); - evaluationProvider.initialize(context, options); - IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class); - hcrProvider.initialize(context, options); - ICompletionsProvider completionsProvider = context.getProvider(ICompletionsProvider.class); - completionsProvider.initialize(context, options); + IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); + evaluationProvider.initialize(context, options); + IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class); + hcrProvider.initialize(context, options); + ICompletionsProvider completionsProvider = context.getProvider(ICompletionsProvider.class); + completionsProvider.initialize(context, options); + } finally { + UsageDataSession.recordInfo("attach debug info", traceInfo); + } // Send an InitializedEvent to indicate that the debugger is ready to accept configuration requests // (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest). diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 76bb5a4db..c9d7cb666 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -60,6 +60,7 @@ import com.sun.jdi.InternalException; import com.sun.jdi.InvalidStackFrameException; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.StringReference; import com.sun.jdi.Type; @@ -197,7 +198,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (varArgs.count > 0) { childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count); } else { - childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables); + childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables, context.asyncJDWP()); } } } catch (AbsentInformationException e) { @@ -210,12 +211,24 @@ public CompletableFuture handle(Command command, Arguments arguments, // Find variable name duplicates Set duplicateNames = getDuplicateNames(childrenList.stream().map(var -> var.name).collect(Collectors.toList())); - Map variableNameMap = new HashMap<>(); - if (!duplicateNames.isEmpty()) { - Map> duplicateVars = childrenList.stream() - .filter(var -> duplicateNames.contains(var.name)).collect(Collectors.groupingBy(var -> var.name, Collectors.toList())); + List duplicateVars = childrenList.stream() + .filter(var -> duplicateNames.contains(var.name)) + .collect(Collectors.toList()); + // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. + if (context.asyncJDWP()) { + try { + AsyncJdwpUtils.await(warmUpJDICache(childrenList, duplicateVars)); + } catch (CompletionException | CancellationException e) { + response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); + } + } - duplicateVars.forEach((k, duplicateVariables) -> { + Map variableNameMap = new HashMap<>(); + if (!duplicateVars.isEmpty()) { + Map> duplicateVarGroups = duplicateVars.stream() + .collect(Collectors.groupingBy(var -> var.name, Collectors.toList())); + duplicateVarGroups.forEach((k, duplicateVariables) -> { Set declarationTypeNames = new HashSet<>(); boolean declarationTypeNameConflict = false; // try use type formatter to resolve name conflict @@ -243,16 +256,6 @@ public CompletableFuture handle(Command command, Arguments arguments, }); } - // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. - if (context.asyncJDWP()) { - try { - AsyncJdwpUtils.await(warmUpJDICache(childrenList)); - } catch (CompletionException | CancellationException e) { - response.body = new Responses.VariablesResponseBody(list); - return CompletableFuture.completedFuture(response); - } - } - for (Variable javaVariable : childrenList) { Value value = javaVariable.value; String name = javaVariable.name; @@ -441,16 +444,33 @@ private List getVariablesOfFrameAsync(StackFrame frame, boolean showSt return result; } - private CompletableFuture warmUpJDICache(List variables) { - List> fetchVariableInfoFutures = new ArrayList<>(); + private CompletableFuture warmUpJDICache(List variables, List duplicatedVars) { + List> futures = new ArrayList<>(); + if (duplicatedVars != null && !duplicatedVars.isEmpty()) { + Set declaringTypes = new HashSet<>(); + duplicatedVars.forEach((var) -> { + Type declarationType = var.getDeclaringType(); + if (declarationType != null) { + declaringTypes.add(declarationType); + } + }); + + for (Type type : declaringTypes) { + if (type instanceof ReferenceType) { + // JDWP Command: RT_SIGNATURE + futures.add(AsyncJdwpUtils.runAsync(() -> type.signature())); + } + } + } + for (Variable javaVariable : variables) { Value value = javaVariable.value; if (value instanceof ArrayReference) { // JDWP Command: AR_LENGTH - fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> ((ArrayReference) value).length())); + futures.add(AsyncJdwpUtils.runAsync(() -> ((ArrayReference) value).length())); } else if (value instanceof StringReference) { // JDWP Command: SR_VALUE - fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + futures.add(AsyncJdwpUtils.runAsync(() -> { String strValue = ((StringReference) value).value(); javaVariable.value = new StringReferenceProxy((StringReference) value, strValue); })); @@ -458,12 +478,12 @@ private CompletableFuture warmUpJDICache(List variables) { if (value instanceof ObjectReference) { // JDWP Command: OR_REFERENCE_TYPE, RT_SIGNATURE - fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + futures.add(AsyncJdwpUtils.runAsync(() -> { value.type().signature(); })); } } - return CompletableFuture.allOf(fetchVariableInfoFutures.toArray(new CompletableFuture[0])); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index bd8c8036d..1a8139fa9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -12,9 +12,12 @@ package com.microsoft.java.debug.core.adapter.variables; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.function.Consumer; @@ -33,6 +36,8 @@ import com.sun.jdi.AbsentInformationException; import com.sun.jdi.ArrayReference; import com.sun.jdi.ArrayType; +import com.sun.jdi.ClassType; +import com.sun.jdi.InterfaceType; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.Field; import com.sun.jdi.InternalException; @@ -77,6 +82,10 @@ public static boolean hasChildren(Value value, boolean includeStatic) { * when there is any error in retrieving information */ public static List listFieldVariables(ObjectReference obj, boolean includeStatic) throws AbsentInformationException { + return listFieldVariables(obj, includeStatic, false); + } + + public static List listFieldVariables(ObjectReference obj, boolean includeStatic, boolean async) throws AbsentInformationException { List res = new ArrayList<>(); ReferenceType type = obj.referenceType(); if (type instanceof ArrayType) { @@ -89,7 +98,7 @@ public static List listFieldVariables(ObjectReference obj, boolean inc } return res; } - List fields = type.allFields().stream().filter(t -> includeStatic || !t.isStatic()) + List fields = resolveAllFields(type, async).stream().filter(t -> includeStatic || !t.isStatic()) .sorted((a, b) -> { try { boolean v1isStatic = a.isStatic(); @@ -466,6 +475,67 @@ private static CompletableFuture> bulkFetchValuesAsync(List el return AsyncJdwpUtils.all(futures); } + private static List resolveAllFields(ReferenceType type, boolean async) { + if (async) { + return resolveAllFieldsAsync(type); + } + + return type.allFields(); + } + + private static List resolveAllFieldsAsync(ReferenceType type) { + Set result = Collections.synchronizedSet(new HashSet<>()); + AsyncJdwpUtils.await(resolveAllFieldsAsync(type, result)); + List fields = new ArrayList<>(); + fields.addAll(result); + return fields; + } + + private static CompletableFuture resolveAllFieldsAsync(ReferenceType type, Set result) { + List> futures = new ArrayList<>(); + // JDWP Command: RT_FIELDS_WITH_GENERIC + futures.add( + AsyncJdwpUtils.runAsync(() -> result.addAll(type.fields())) + ); + + if (type instanceof ClassType) { + ClassType classType = (ClassType) type; + // JDWP Command: RT_INTERFACES + futures.add(AsyncJdwpUtils.supplyAsync(() -> classType.interfaces()) + .thenCompose((its) -> { + List> itFutures = new ArrayList<>(); + for (InterfaceType it : its) { + itFutures.add(resolveAllFieldsAsync(it, result)); + } + + return CompletableFuture.allOf(itFutures.toArray(new CompletableFuture[0])); + })); + + // JDWP Command: CT_SUPERCLASS + AsyncJdwpUtils.supplyAsync(() -> classType.superclass()) + .thenCompose((superclass) -> { + if (superclass != null) { + return resolveAllFieldsAsync(superclass, result); + } + return CompletableFuture.completedFuture(null); + }); + } else if (type instanceof InterfaceType) { + InterfaceType interfaceType = (InterfaceType) type; + // JDWP Command: RT_INTERFACES + futures.add(AsyncJdwpUtils.supplyAsync(() -> interfaceType.superinterfaces()) + .thenCompose((its) -> { + List> itFutures = new ArrayList<>(); + for (InterfaceType it : its) { + itFutures.add(resolveAllFieldsAsync(it, result)); + } + + return CompletableFuture.allOf(itFutures.toArray(new CompletableFuture[0])); + })); + } + + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + } + private VariableUtils() { } From fa392425f656b1ed3bab0102764d4ca7dfdcc5f4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 09:22:06 +0800 Subject: [PATCH 110/206] Bump version to 0.40.0 (#438) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 287c599fe..83fed3cc9 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.39.0 + 0.40.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c3d2cffbb..b7e330e32 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index c39f5a9ac..9667f9e51 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.39.0 +Bundle-Version: 0.40.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.39.0.jar + lib/com.microsoft.java.debug.core-0.40.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 7e86528b5..56bb813e6 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.39.0 + 0.40.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.39.0 + 0.40.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index d4f44d697..7d8519073 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 4b76e45fb..43cd80e43 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.39.0 + 0.40.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 30c6ed253..2b0497f26 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.39.0 + 0.40.0 pom Java Debug Server for Visual Studio Code From a5bbebc661414528fe0a6b8bbf5111aeb342ce63 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 14:02:44 +0800 Subject: [PATCH 111/206] Clear stackframe cache if the thread state has changed (#439) --- .../java/debug/core/adapter/IStackFrameManager.java | 7 +++++++ .../java/debug/core/adapter/StackFrameManager.java | 5 +++++ .../adapter/handler/StackTraceRequestHandler.java | 11 ++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java index f3ae95d6e..abce2ff3e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java @@ -41,4 +41,11 @@ public interface IStackFrameManager { * @return the refreshed stackframes */ StackFrame[] reloadStackFrames(ThreadReference thread, int start, int length); + + /** + * Clear the stackframes cache from the specified thread. + * + * @param thread the jdi thread + */ + void clearStackFrames(ThreadReference thread); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java index d2f934901..2a9d1e47e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java @@ -66,4 +66,9 @@ public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, int s return new StackFrame[0]; } } + + @Override + public synchronized void clearStackFrames(ThreadReference thread) { + threadStackFrameMap.remove(thread.uniqueID()); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index ffa74206e..2120cafab 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -70,6 +70,11 @@ public CompletableFuture handle(Command command, Arguments arguments, int totalFrames = 0; if (thread != null) { try { + // Thread state has changed and then invalidate the stack frame cache. + if (stacktraceArgs.startFrame == 0) { + context.getStackFrameManager().clearStackFrames(thread); + } + totalFrames = thread.frameCount(); int count = stacktraceArgs.levels == 0 ? totalFrames - stacktraceArgs.startFrame : Math.min(totalFrames - stacktraceArgs.startFrame, stacktraceArgs.levels); @@ -80,10 +85,10 @@ public CompletableFuture handle(Command command, Arguments arguments, StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count); List jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP()); - for (int i = stacktraceArgs.startFrame; i < jdiFrames.size() && count-- > 0; i++) { - StackFrameReference stackframe = new StackFrameReference(thread, i); + for (int i = 0; i < count; i++) { + StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i); int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); - StackFrameInfo jdiFrame = jdiFrames.get(i - stacktraceArgs.startFrame); + StackFrameInfo jdiFrame = jdiFrames.get(i); result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, context)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException From 5bac075002154f2c8441a39026db53bdc67aef56 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 15:28:26 +0800 Subject: [PATCH 112/206] refine the debug trace (#440) --- .../debug/core/adapter/handler/AttachRequestHandler.java | 1 + .../adapter/handler/ConfigurationDoneRequestHandler.java | 1 - .../debug/core/adapter/handler/LaunchRequestHandler.java | 7 +++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index 4d11ef6fb..cee6e64a3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -64,6 +64,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Map traceInfo = new HashMap<>(); traceInfo.put("localAttach", context.isLocalDebugging()); + traceInfo.put("asyncJDWP", context.asyncJDWP()); IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 311db11c3..737d5d858 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -55,7 +55,6 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); vmHandler.setVmProvider(context.getProvider(IVirtualMachineManagerProvider.class)); - UsageDataSession.recordInfo("asyncJDWP", context.asyncJDWP()); if (debugSession != null) { // This is a global event handler to handle the JDI Event from Virtual Machine. debugSession.getEventHub().events().subscribe(debugEvent -> { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index fec967b84..e5b580f50 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -42,6 +42,7 @@ import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.LaunchException; +import com.microsoft.java.debug.core.UsageDataSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -76,6 +77,12 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { LaunchArguments launchArguments = (LaunchArguments) arguments; + Map traceInfo = new HashMap<>(); + traceInfo.put("asyncJDWP", context.asyncJDWP()); + traceInfo.put("noDebug", launchArguments.noDebug); + traceInfo.put("console", launchArguments.console); + UsageDataSession.recordInfo("launch debug info", traceInfo); + activeLaunchHandler = launchArguments.noDebug ? new LaunchWithoutDebuggingDelegate((daContext) -> handleTerminatedEvent(daContext)) : new LaunchWithDebuggingDelegate(); return handleLaunchCommand(arguments, response, context); From f9592bffae91ee713896af84ab2fc46404e3c67b Mon Sep 17 00:00:00 2001 From: crissNb <83137305+crissNb472@users.noreply.github.com> Date: Tue, 13 Sep 2022 09:49:14 +0200 Subject: [PATCH 113/206] Fixes NumericFormatter failure on systems with other decimal separator (#443) --- .../debug/core/adapter/formatter/NumericFormatterTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java index 001b928e9..372eb8163 100644 --- a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java +++ b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java @@ -29,6 +29,8 @@ import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; +import java.util.Locale; + import static com.microsoft.java.debug.core.adapter.formatter.NumericFormatter.NUMERIC_FORMAT_OPTION; import static com.microsoft.java.debug.core.adapter.formatter.NumericFormatter.NUMERIC_PRECISION_OPTION; import static org.junit.Assert.*; @@ -38,6 +40,7 @@ public class NumericFormatterTest extends BaseJdiTestCase { @Before public void setup() throws Exception { super.setup(); + Locale.setDefault(Locale.US); formatter = new NumericFormatter(); } From 050e59125dc8ee3726f9fb880345cb1fe0b1b659 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Sep 2022 16:28:32 +0800 Subject: [PATCH 114/206] Support for debugging virtual threads (#441) * Support for debugging virtual threads --- .../java/debug/core/DebugSession.java | 35 +++++++- .../java/debug/core/adapter/ThreadCache.java | 52 ++++++++++- .../ConfigurationDoneRequestHandler.java | 6 +- .../handler/ExceptionInfoRequestHandler.java | 8 +- .../handler/SetBreakpointsRequestHandler.java | 4 +- .../SetDataBreakpointsRequestHandler.java | 4 +- .../SetFunctionBreakpointsRequestHandler.java | 2 + .../adapter/handler/StepRequestHandler.java | 2 + .../handler/ThreadsRequestHandler.java | 87 +++++++++++-------- 9 files changed, 154 insertions(+), 46 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 220688693..ed711bf82 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -11,6 +11,8 @@ package com.microsoft.java.debug.core; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -31,18 +33,49 @@ public DebugSession(VirtualMachine virtualMachine) { @Override public void start() { + boolean supportsVirtualThreads = mayCreateVirtualThreads(); + // request thread events by default EventRequest threadStartRequest = vm.eventRequestManager().createThreadStartRequest(); threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + if (supportsVirtualThreads) { + addPlatformThreadsOnlyFilter(threadStartRequest); + } threadStartRequest.enable(); EventRequest threadDeathRequest = vm.eventRequestManager().createThreadDeathRequest(); threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + if (supportsVirtualThreads) { + addPlatformThreadsOnlyFilter(threadDeathRequest); + } threadDeathRequest.enable(); eventHub.start(vm); } + private boolean mayCreateVirtualThreads() { + try { + Method method = vm.getClass().getMethod("mayCreateVirtualThreads"); + return (boolean) method.invoke(vm); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // ignore + } + + return false; + } + + /** + * For thread start and thread death events, restrict the events so they are only sent for platform threads. + */ + private void addPlatformThreadsOnlyFilter(EventRequest threadLifecycleRequest) { + try { + Method method = threadLifecycleRequest.getClass().getMethod("addPlatformThreadsOnlyFilter"); + method.invoke(threadLifecycleRequest); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // ignore + } + } + @Override public void suspend() { vm.suspend(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java index 3232dbf18..ce1c17282 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java @@ -13,9 +13,11 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.sun.jdi.ThreadReference; @@ -29,6 +31,7 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) { return this.size() > 100; } }); + private Map eventThreads = new ConcurrentHashMap<>(); public synchronized void resetThreads(List threads) { allThreads.clear(); @@ -46,6 +49,12 @@ public synchronized ThreadReference getThread(long threadId) { } } + for (ThreadReference thread : eventThreads.values()) { + if (threadId == thread.uniqueID()) { + return thread; + } + } + return null; } @@ -59,14 +68,49 @@ public String getThreadName(long threadId) { public void addDeathThread(long threadId) { threadNameMap.remove(threadId); + eventThreads.remove(threadId); deathThreads.put(threadId, true); } - public void removeDeathThread(long threadId) { - deathThreads.remove(threadId); - } - public boolean isDeathThread(long threadId) { return deathThreads.containsKey(threadId); } + + public void addEventThread(ThreadReference thread) { + eventThreads.put(thread.uniqueID(), thread); + } + + public void removeEventThread(long threadId) { + eventThreads.remove(threadId); + } + + public void clearEventThread() { + eventThreads.clear(); + } + + /** + * The visible threads includes: + * 1. The currently running threads returned by the JDI API + * VirtualMachine.allThreads(). + * 2. The threads suspended by events such as Breakpoint, Step, Exception etc. + * + * The part 2 is mainly for virtual threads, since VirtualMachine.allThreads() + * does not include virtual threads by default. For those virtual threads + * that are suspended, we need to show their call stacks in CALL STACK view. + */ + public List visibleThreads(IDebugAdapterContext context) { + List visibleThreads = new ArrayList<>(context.getDebugSession().getAllThreads()); + Set idSet = new HashSet<>(); + visibleThreads.forEach(thread -> idSet.add(thread.uniqueID())); + for (ThreadReference thread : eventThreads.values()) { + if (idSet.contains(thread.uniqueID())) { + continue; + } + + idSet.add(thread.uniqueID()); + visibleThreads.add(thread); + } + + return visibleThreads; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 737d5d858..6805073dc 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -109,15 +109,15 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // ignore since SetBreakpointsRequestHandler has already handled } else if (event instanceof ExceptionEvent) { ThreadReference thread = ((ExceptionEvent) event).thread(); - ThreadReference bpThread = ((ExceptionEvent) event).thread(); IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); - if (engine.isInEvaluation(bpThread)) { + if (engine.isInEvaluation(thread)) { return; } JdiExceptionReference jdiException = new JdiExceptionReference(((ExceptionEvent) event).exception(), ((ExceptionEvent) event).catchLocation() == null); context.getExceptionManager().setException(thread.uniqueID(), jdiException); + context.getThreadCache().addEventThread(thread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID())); debugEvent.shouldResume = false; } else { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java index 13456029d..5e065edd0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2019 Microsoft Corporation and others. +* Copyright (c) 2019-2022 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 @@ -53,7 +53,11 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { ExceptionInfoArguments exceptionInfoArgs = (ExceptionInfoArguments) arguments; - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), exceptionInfoArgs.threadId); + ThreadReference thread = context.getThreadCache().getThread(exceptionInfoArgs.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), exceptionInfoArgs.threadId); + } + if (thread == null) { throw AdapterUtils.createCompletionException("Thread " + exceptionInfoArgs.threadId + " doesn't exist.", ErrorCode.EXCEPTION_INFO_FAILURE); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 085dfe63f..d02f73bfd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -207,12 +207,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } }); }); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java index be15852e4..6d3e8c0b1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2019 Microsoft Corporation and others. +* Copyright (c) 2019-2022 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 @@ -151,11 +151,13 @@ private void registerWatchpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } }); }); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } debugEvent.shouldResume = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index 3f370b429..59a948d37 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -165,6 +165,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent( "function breakpoint", bpThread.uniqueID())); } @@ -172,6 +173,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { }); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer() .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 4f58c113f..51e41bafc 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -178,6 +178,7 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingMethodExitRequest.enable(); } + context.getThreadCache().removeEventThread(thread.uniqueID()); DebugUtility.resumeThread(thread); ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context); } catch (IncompatibleThreadStateException ex) { @@ -255,6 +256,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } + context.getThreadCache().addEventThread(thread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID())); debugEvent.shouldResume = false; } else if (event instanceof MethodExitEvent) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index cedbf2ae1..7d404691c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -17,10 +17,13 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; -import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -81,12 +84,13 @@ public CompletableFuture handle(Command command, Arguments arguments, private CompletableFuture threads(Requests.ThreadsArguments arguments, Response response, IDebugAdapterContext context) { ArrayList threads = new ArrayList<>(); try { - List allThreads = context.getDebugSession().getAllThreads(); + List allThreads = context.getThreadCache().visibleThreads(context); context.getThreadCache().resetThreads(allThreads); - allThreads = allThreads.stream().filter((thread) -> !context.getThreadCache().isDeathThread(thread.uniqueID())).toList(); + allThreads = allThreads.stream().filter((thread) -> !context.getThreadCache().isDeathThread(thread.uniqueID())).collect(Collectors.toList()); List jdiThreads = resolveThreadInfos(allThreads, context); for (ThreadInfo jdiThread : jdiThreads) { - threads.add(new Types.Thread(jdiThread.thread.uniqueID(), "Thread [" + jdiThread.name + "]")); + String name = StringUtils.isBlank(jdiThread.name) ? String.valueOf(jdiThread.thread.uniqueID()) : jdiThread.name; + threads.add(new Types.Thread(jdiThread.thread.uniqueID(), "Thread [" + name + "]")); } } catch (ObjectCollectedException | CancellationException | CompletionException ex) { // allThreads may throw VMDisconnectedException when VM terminates and thread.name() may throw ObjectCollectedException @@ -124,11 +128,12 @@ private static List resolveThreadInfos(List allThre } private CompletableFuture pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) { - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + ThreadReference thread = context.getThreadCache().getThread(arguments.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + } if (thread != null) { - context.getStepResultManager().removeMethodResult(arguments.threadId); - thread.suspend(); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId)); + pauseThread(thread, context); } else { context.getStepResultManager().removeAllMethodResults(); context.getDebugSession().suspend(); @@ -149,6 +154,7 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, * be resumed (through ThreadReference#resume() or VirtualMachine#resume()) the same number of times it has been suspended. */ if (thread != null) { + context.getThreadCache().removeEventThread(arguments.threadId); context.getStepResultManager().removeMethodResult(arguments.threadId); context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; @@ -157,11 +163,7 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); - if (context.asyncJDWP()) { - resumeVMAsync(context.getDebugSession()); - } else { - context.getDebugSession().resume(); - } + resumeVM(context); context.getRecyclableIdPool().removeAllObjects(); } response.body = new Responses.ContinueResponseBody(allThreadsContinued); @@ -169,19 +171,16 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, } private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { + context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); - if (context.asyncJDWP()) { - resumeVMAsync(context.getDebugSession()); - } else { - context.getDebugSession().resume(); - } + resumeVM(context); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); context.getRecyclableIdPool().removeAllObjects(); return CompletableFuture.completedFuture(response); } private CompletableFuture resumeOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { - List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List threads = context.getThreadCache().visibleThreads(context); List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { if (thread.uniqueID() == arguments.threadId) { @@ -205,7 +204,7 @@ private CompletableFuture pauseAll(Requests.ThreadOperationArguments a } private CompletableFuture pauseOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { - List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List threads = context.getThreadCache().visibleThreads(context); List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { if (thread.uniqueID() == arguments.threadId) { @@ -239,26 +238,42 @@ public static void checkThreadRunningAndRecycleIds(ThreadReference thread, IDebu } } - private void resumeVMAsync(IDebugSession debugSession) { + private void resumeVM(IDebugAdapterContext context) { + List visibleThreads = context.getThreadCache().visibleThreads(context); + context.getThreadCache().clearEventThread(); + List> futures = new ArrayList<>(); - for (ThreadReference tr : DebugUtility.getAllThreadsSafely(debugSession)) { - futures.add(AsyncJdwpUtils.runAsync(() -> { - try { - while (tr.suspendCount() > 1) { - tr.resume(); - } - } catch (ObjectCollectedException ex) { - // Ignore it if the thread is garbage collected. + /** + * To ensure that all threads are fully resumed when the VM is resumed, make sure the suspend count + * of each thread is no larger than 1. + * Notes: Decrementing the thread' suspend count to 1 is on purpose, because it doesn't break the + * the thread's suspend state, and also make sure the next instruction vm.resume() is able to resume + * all threads fully. + */ + Consumer resumeThread = (ThreadReference tr) -> { + try { + while (tr.suspendCount() > 1) { + tr.resume(); } - })); + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + }; + for (ThreadReference tr : visibleThreads) { + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> resumeThread.accept(tr))); + } else { + resumeThread.accept(tr); + } } AsyncJdwpUtils.await(futures); - debugSession.getVM().resume(); + context.getDebugSession().getVM().resume(); } private void resumeThread(ThreadReference thread, IDebugAdapterContext context) { try { + context.getThreadCache().removeEventThread(thread.uniqueID()); int suspends = thread.suspendCount(); if (suspends > 0) { long threadId = thread.uniqueID(); @@ -268,19 +283,23 @@ private void resumeThread(ThreadReference thread, IDebugAdapterContext context) checkThreadRunningAndRecycleIds(thread, context); } } catch (ObjectCollectedException ex) { - // ignore it. + // the thread is garbage collected. + context.getThreadCache().addDeathThread(thread.uniqueID()); } } private void pauseThread(ThreadReference thread, IDebugAdapterContext context) { try { - if (!thread.isSuspended()) { + // Ignore it if the thread status is unknown or zombie + if (!thread.isSuspended() && thread.status() > 0) { long threadId = thread.uniqueID(); + context.getStepResultManager().removeMethodResult(threadId); thread.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); } } catch (ObjectCollectedException ex) { - // ignore it if the thread is garbage collected. + // the thread is garbage collected. + context.getThreadCache().addDeathThread(thread.uniqueID()); } } From 195266efa94800c51b14110a72688ab085ed28ae Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Sep 2022 17:23:01 +0800 Subject: [PATCH 115/206] Visualize the inline breakpoint locations (#445) --- .../microsoft/java/debug/core/Breakpoint.java | 110 ++++++++++------ .../java/debug/core/DebugSession.java | 5 + .../debug/core/EvaluatableBreakpoint.java | 8 +- .../java/debug/core/IBreakpoint.java | 6 +- .../java/debug/core/IDebugSession.java | 4 +- .../debug/core/JavaBreakpointLocation.java | 113 +++++++++++++++++ .../java/debug/core/adapter/AdapterUtils.java | 8 +- .../java/debug/core/adapter/DebugAdapter.java | 4 +- .../core/adapter/DebugAdapterContext.java | 6 + .../core/adapter/IDebugAdapterContext.java | 4 +- .../core/adapter/ISourceLookUpProvider.java | 22 +++- .../BreakpointLocationsRequestHander.java | 83 ++++++++++++ .../handler/InitializeRequestHandler.java | 3 +- .../handler/SetBreakpointsRequestHandler.java | 78 ++++++------ .../handler/StackTraceRequestHandler.java | 29 ++++- .../java/debug/core/protocol/Requests.java | 40 +++++- .../java/debug/core/protocol/Responses.java | 18 ++- .../java/debug/core/protocol/Types.java | 53 +++++++- .../java/debug/LambdaExpressionLocator.java | 11 +- .../internal/JdtSourceLookUpProvider.java | 120 +++++++++++++++--- 20 files changed, 605 insertions(+), 120 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 8b865d44f..dfdbad4bb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -37,13 +37,11 @@ public class Breakpoint implements IBreakpoint { private VirtualMachine vm = null; private IEventHub eventHub = null; - private String className = null; - private int lineNumber = 0; + private JavaBreakpointLocation sourceLocation = null; private int hitCount = 0; private String condition = null; private String logMessage = null; private HashMap propertyMap = new HashMap<>(); - private String methodSignature = null; private boolean async = false; @@ -56,21 +54,37 @@ public class Breakpoint implements IBreakpoint { } Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) { + this(vm, eventHub, className, lineNumber, hitCount, condition, null); + } + + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) { this.vm = vm; this.eventHub = eventHub; + String contextClass = className; + String methodName = null; + String methodSignature = null; if (className != null && className.contains("#")) { - this.className = className.substring(0, className.indexOf("#")); - this.methodSignature = className.substring(className.indexOf("#") + 1); - } else { - this.className = className; + contextClass = className.substring(0, className.indexOf("#")); + String[] methodInfo = className.substring(className.indexOf("#") + 1).split("#"); + methodName = methodInfo[0]; + methodSignature = methodInfo[1]; } - this.lineNumber = lineNumber; + + this.sourceLocation = new JavaBreakpointLocation(lineNumber, -1); + this.sourceLocation.setClassName(contextClass); + this.sourceLocation.setMethodName(methodName); + this.sourceLocation.setMethodSignature(methodSignature); this.hitCount = hitCount; this.condition = condition; + this.logMessage = logMessage; } - Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) { - this(vm, eventHub, className, lineNumber, hitCount, condition); + Breakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) { + this.vm = vm; + this.eventHub = eventHub; + this.sourceLocation = sourceLocation; + this.hitCount = hitCount; + this.condition = condition; this.logMessage = logMessage; } @@ -104,14 +118,24 @@ public void close() throws Exception { } // IBreakpoint + @Override + public JavaBreakpointLocation sourceLocation() { + return this.sourceLocation; + } + @Override public String className() { - return className; + return this.sourceLocation.className(); } @Override public int getLineNumber() { - return lineNumber; + return this.sourceLocation.lineNumber(); + } + + @Override + public int getColumnNumber() { + return this.sourceLocation.columnNumber(); } @Override @@ -120,20 +144,20 @@ public String getCondition() { } @Override - public boolean equals(Object obj) { - if (!(obj instanceof Breakpoint)) { - return super.equals(obj); - } - - Breakpoint breakpoint = (Breakpoint) obj; - return Objects.equals(this.className(), breakpoint.className()) - && this.getLineNumber() == breakpoint.getLineNumber() - && Objects.equals(this.methodSignature, breakpoint.methodSignature); + public int hashCode() { + return Objects.hash(sourceLocation); } @Override - public int hashCode() { - return Objects.hash(this.className, this.lineNumber, this.methodSignature); + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Breakpoint)) { + return false; + } + Breakpoint other = (Breakpoint) obj; + return Objects.equals(sourceLocation, other.sourceLocation); } @Override @@ -149,6 +173,7 @@ public void setHitCount(int hitCount) { .filter(request -> request instanceof BreakpointRequest) .subscribe(request -> { request.addCountFilter(hitCount); + request.disable(); request.enable(); }); } @@ -183,13 +208,13 @@ public CompletableFuture install() { // It's possible that different class loaders create new class with the same name. // Here to listen to future class prepare events to handle such case. ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); - classPrepareRequest.addClassFilter(className); + classPrepareRequest.addClassFilter(className()); classPrepareRequest.enable(); requests.add(classPrepareRequest); // Local types also needs to be handled ClassPrepareRequest localClassPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); - localClassPrepareRequest.addClassFilter(className + "$*"); + localClassPrepareRequest.addClassFilter(className() + "$*"); localClassPrepareRequest.enable(); requests.add(localClassPrepareRequest); @@ -202,7 +227,7 @@ public CompletableFuture install() { .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; List newRequests = AsyncJdwpUtils.await( - createBreakpointRequests(event.referenceType(), lineNumber, hitCount, false) + createBreakpointRequests(event.referenceType(), getLineNumber(), hitCount, false) ); requests.addAll(newRequests); if (!newRequests.isEmpty() && !future.isDone()) { @@ -213,8 +238,8 @@ public CompletableFuture install() { subscriptions.add(subscription); Runnable resolveRequestsFromExistingClasses = () -> { - List refTypes = vm.classesByName(className); - createBreakpointRequests(refTypes, lineNumber, hitCount, true) + List refTypes = vm.classesByName(className()); + createBreakpointRequests(refTypes, getLineNumber(), hitCount, true) .whenComplete((newRequests, ex) -> { if (ex != null) { return; @@ -281,14 +306,13 @@ private CompletableFuture> collectLocations(ReferenceType refType }); } - private CompletableFuture> collectLocations(List refTypes, String nameAndSignature) { - String[] segments = nameAndSignature.split("#"); + private CompletableFuture> collectLocations(List refTypes, String methodName, String methodSiguature) { List> futures = new ArrayList<>(); for (ReferenceType refType : refTypes) { if (async()) { - futures.add(AsyncJdwpUtils.supplyAsync(() -> findMethodLocaiton(refType, segments[0], segments[1]))); + futures.add(AsyncJdwpUtils.supplyAsync(() -> findMethodLocaiton(refType, methodName, methodSiguature))); } else { - futures.add(CompletableFuture.completedFuture(findMethodLocaiton(refType, segments[0], segments[1]))); + futures.add(CompletableFuture.completedFuture(findMethodLocaiton(refType, methodName, methodSiguature))); } } @@ -329,10 +353,22 @@ private CompletableFuture> createBreakpointRequests(Refe private CompletableFuture> createBreakpointRequests(List refTypes, int lineNumber, int hitCount, boolean includeNestedTypes) { CompletableFuture> locationsFuture; - if (this.methodSignature != null) { - locationsFuture = collectLocations(refTypes, this.methodSignature); + if (this.sourceLocation.methodName() != null) { + locationsFuture = collectLocations(refTypes, this.sourceLocation.methodName(), this.sourceLocation.methodSignature()); } else { - locationsFuture = collectLocations(refTypes, lineNumber, includeNestedTypes); + locationsFuture = collectLocations(refTypes, lineNumber, includeNestedTypes).thenApply((locations) -> { + if (locations.isEmpty()) { + return locations; + } + + /** + * For a line breakpoint, we default to breaking at the first location + * of the line. If you want to break at other locations on the same line, + * you can add an inline breakpoint based on the locations returned by + * the BreakpointLocation request. + */ + return Arrays.asList(locations.get(0)); + }); } return locationsFuture.thenCompose((locations) -> { @@ -389,11 +425,11 @@ private CompletableFuture> createBreakpointRequests(List } private Object computeRequestType() { - if (this.methodSignature == null) { + if (this.sourceLocation.methodName() == null) { return IBreakpoint.REQUEST_TYPE_LINE; } - if (this.methodSignature.startsWith("lambda$")) { + if (this.sourceLocation.methodName().startsWith("lambda$")) { return IBreakpoint.REQUEST_TYPE_LAMBDA; } else { return IBreakpoint.REQUEST_TYPE_METHOD; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index ed711bf82..1c7990f16 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -114,6 +114,11 @@ public void terminate() { } } + @Override + public IBreakpoint createBreakpoint(JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) { + return new EvaluatableBreakpoint(vm, this.getEventHub(), sourceLocation, hitCount, condition, logMessage); + } + @Override public IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage) { return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java index 9b3fdb2dd..723e2cadf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2022 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 @@ -48,6 +48,12 @@ public class EvaluatableBreakpoint extends Breakpoint implements IEvaluatableBre this.eventHub = eventHub; } + EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, + String condition, String logMessage) { + super(vm, eventHub, sourceLocation, hitCount, condition, logMessage); + this.eventHub = eventHub; + } + @Override public boolean containsEvaluatableExpression() { return containsConditionalExpression() || containsLogpointExpression(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 04fbf5005..40995e9dd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -23,10 +23,14 @@ public interface IBreakpoint extends IDebugResource { int REQUEST_TYPE_LAMBDA = 2; + JavaBreakpointLocation sourceLocation(); + String className(); int getLineNumber(); + int getColumnNumber(); + int getHitCount(); void setHitCount(int hitCount); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 03780c2d9..4e4078c87 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -30,6 +30,8 @@ public interface IDebugSession { // breakpoints IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage); + IBreakpoint createBreakpoint(JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage); + IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount); void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java new file mode 100644 index 000000000..820e80c9b --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java @@ -0,0 +1,113 @@ +/******************************************************************************* +* Copyright (c) 2022 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.core; + +import java.util.Objects; + +import com.microsoft.java.debug.core.protocol.Types; + +public class JavaBreakpointLocation { + /** + * The source line of the breakpoint or logpoint. + */ + private int lineNumber; + /** + * The source column of the breakpoint. + */ + private int columnNumber = -1; + /** + * The declaring class name that encloses the target position. + */ + private String className; + /** + * The method name and signature when the target position + * points to a method declaration. + */ + private String methodName; + private String methodSignature; + /** + * All possible locations for source breakpoints in a given range. + */ + private Types.BreakpointLocation[] availableBreakpointLocations = new Types.BreakpointLocation[0]; + + public JavaBreakpointLocation(int lineNumber, int columnNumber) { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + @Override + public int hashCode() { + return Objects.hash(lineNumber, columnNumber, className, methodName, methodSignature); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof JavaBreakpointLocation)) { + return false; + } + JavaBreakpointLocation other = (JavaBreakpointLocation) obj; + return lineNumber == other.lineNumber && columnNumber == other.columnNumber + && Objects.equals(className, other.className) && Objects.equals(methodName, other.methodName) + && Objects.equals(methodSignature, other.methodSignature); + } + + public int lineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public int columnNumber() { + return columnNumber; + } + + public void setColumnNumber(int columnNumber) { + this.columnNumber = columnNumber; + } + + public String className() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String methodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String methodSignature() { + return methodSignature; + } + + public void setMethodSignature(String methodSignature) { + this.methodSignature = methodSignature; + } + + public Types.BreakpointLocation[] availableBreakpointLocations() { + return availableBreakpointLocations; + } + + public void setAvailableBreakpointLocations(Types.BreakpointLocation[] availableBreakpointLocations) { + this.availableBreakpointLocations = availableBreakpointLocations; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 5c1272102..9b972334d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -119,13 +119,15 @@ public static int convertLineNumber(int line, boolean sourceLinesStartAt1, boole * the column number from the source platform * @param sourceColumnsStartAt1 * the source platform's column starts at 1 or not + * @param targetColumnStartAt1 + * the target platform's column starts at 1 or not * @return the new column number */ - public static int convertColumnNumber(int column, boolean sourceColumnsStartAt1) { + public static int convertColumnNumber(int column, boolean sourceColumnsStartAt1, boolean targetColumnStartAt1) { if (sourceColumnsStartAt1) { - return column - 1; + return targetColumnStartAt1 ? column : column - 1; } else { - return column; + return targetColumnStartAt1 ? column + 1 : column; } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 0bf5d2683..edc771163 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -21,6 +21,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.adapter.handler.AttachRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.BreakpointLocationsRequestHander; import com.microsoft.java.debug.core.adapter.handler.CompletionsHandler; import com.microsoft.java.debug.core.adapter.handler.ConfigurationDoneRequestHandler; import com.microsoft.java.debug.core.adapter.handler.DataBreakpointInfoRequestHandler; @@ -105,7 +106,7 @@ private void initialize() { registerHandler(new InitializeRequestHandler()); registerHandler(new LaunchRequestHandler()); - // DEBUG node only + // DEBUG mode only registerHandlerForDebug(new AttachRequestHandler()); registerHandlerForDebug(new ConfigurationDoneRequestHandler()); registerHandlerForDebug(new DisconnectRequestHandler()); @@ -129,6 +130,7 @@ private void initialize() { registerHandlerForDebug(new RefreshVariablesHandler()); registerHandlerForDebug(new ProcessIdHandler()); registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); + registerHandlerForDebug(new BreakpointLocationsRequestHander()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); registerHandlerForNoDebug(new ProcessIdHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 4185d7597..e317ddf1c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -38,6 +38,8 @@ public class DebugAdapterContext implements IDebugAdapterContext { private IDebugSession debugSession; private boolean debuggerLinesStartAt1 = true; + // The Java model on debugger uses 0-based column number. + private boolean debuggerColumnStartAt1 = false; private boolean debuggerPathsAreUri = true; private boolean clientLinesStartAt1 = true; private boolean clientColumnsStartAt1 = true; @@ -105,6 +107,10 @@ public void setDebuggerLinesStartAt1(boolean debuggerLinesStartAt1) { this.debuggerLinesStartAt1 = debuggerLinesStartAt1; } + public boolean isDebuggerColumnsStartAt1() { + return debuggerColumnStartAt1; + } + @Override public boolean isDebuggerPathsAreUri() { return debuggerPathsAreUri; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index bfcad8905..5d3b75639 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -55,6 +55,8 @@ public interface IDebugAdapterContext { void setClientColumnsStartAt1(boolean clientColumnsStartAt1); + boolean isDebuggerColumnsStartAt1(); + boolean isClientPathsAreUri(); void setClientPathsAreUri(boolean clientPathsAreUri); 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 dd3e4dbde..9976fc16f 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -12,12 +12,32 @@ package com.microsoft.java.debug.core.adapter; import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.JavaBreakpointLocation; +import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; public interface ISourceLookUpProvider extends IProvider { boolean supportsRealtimeBreakpointVerification(); + /** + * Deprecated, please use {@link #getBreakpointLocations(String, SourceBreakpoint[])} instead. + */ + @Deprecated String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) throws DebugException; + /** + * Given a set of source breakpoint locations with line and column numbers, + * verify if they are valid breakpoint locations. If it's a valid location, + * resolve its enclosing class name, method name and signature (for method + * breakpoint) and all possible inline breakpoint locations in that line. + * + * @param sourceUri + * the source file uri + * @param sourceBreakpoints + * the source breakpoints with line and column numbers + * @return Locations of Breakpoints containing context class and method information. + */ + JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException; + /** * Given a fully qualified class name and source file path, search the associated disk source file. * diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java new file mode 100644 index 000000000..57c7ea15e --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2022 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.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Requests; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.BreakpointLocationsArguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; + +/** + * The breakpointLocations request returns all possible locations for source breakpoints in a given range. + * Clients should only call this request if the corresponding capability supportsBreakpointLocationsRequest is true. + */ +public class BreakpointLocationsRequestHander implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Requests.Command.BREAKPOINTLOCATIONS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + BreakpointLocationsArguments bpArgs = (BreakpointLocationsArguments) arguments; + String sourceUri = SetBreakpointsRequestHandler.normalizeSourcePath(bpArgs.source, context); + // When breakpoint source path is null or an invalid file path, send an ErrorResponse back. + if (StringUtils.isBlank(sourceUri)) { + throw AdapterUtils.createCompletionException( + String.format("Failed to get BreakpointLocations. Reason: '%s' is an invalid path.", bpArgs.source.path), + ErrorCode.SET_BREAKPOINT_FAILURE); + } + + int debuggerLine = AdapterUtils.convertLineNumber(bpArgs.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); + IBreakpoint[] breakpoints = context.getBreakpointManager().getBreakpoints(sourceUri); + BreakpointLocation[] locations = new BreakpointLocation[0]; + for (int i = 0; i < breakpoints.length; i++) { + if (breakpoints[i].getLineNumber() == debuggerLine && ArrayUtils.isNotEmpty( + breakpoints[i].sourceLocation().availableBreakpointLocations())) { + locations = Stream.of(breakpoints[i].sourceLocation().availableBreakpointLocations()).map(location -> { + BreakpointLocation newLocaiton = new BreakpointLocation(); + newLocaiton.line = AdapterUtils.convertLineNumber(location.line, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + newLocaiton.column = AdapterUtils.convertColumnNumber(location.column, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + newLocaiton.endLine = AdapterUtils.convertLineNumber(location.endLine, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + newLocaiton.endColumn = AdapterUtils.convertColumnNumber(location.endColumn, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + return newLocaiton; + }).toArray(BreakpointLocation[]::new); + break; + } + } + + response.body = new Responses.BreakpointLocationsResponseBody(locations); + return CompletableFuture.completedFuture(response); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index ae92f356f..19bbd7d62 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -64,6 +64,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsDataBreakpoints = true; caps.supportsFunctionBreakpoints = true; caps.supportsClipboardContext = true; + caps.supportsBreakpointLocationsRequest = true; response.body = caps; return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index d02f73bfd..7fe6c3fdd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -17,6 +17,7 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Stream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; @@ -26,6 +27,7 @@ import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.IEvaluatableBreakpoint; +import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent.EventType; @@ -93,28 +95,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } SetBreakpointArguments bpArguments = (SetBreakpointArguments) arguments; - String clientPath = bpArguments.source.path; - if (AdapterUtils.isWindows()) { - // VSCode may send drive letters with inconsistent casing which will mess up the key - // in the BreakpointManager. See https://github.com/Microsoft/vscode/issues/6268 - // Normalize the drive letter casing. Note that drive letters - // are not localized so invariant is safe here. - String drivePrefix = FilenameUtils.getPrefix(clientPath); - if (drivePrefix != null && drivePrefix.length() >= 2 - && Character.isLowerCase(drivePrefix.charAt(0)) && drivePrefix.charAt(1) == ':') { - drivePrefix = drivePrefix.substring(0, 2); // d:\ is an illegal regex string, convert it to d: - clientPath = clientPath.replaceFirst(drivePrefix, drivePrefix.toUpperCase()); - } - } - String sourcePath = clientPath; - if (bpArguments.source.sourceReference != 0 && context.getSourceUri(bpArguments.source.sourceReference) != null) { - sourcePath = context.getSourceUri(bpArguments.source.sourceReference); - } else if (StringUtils.isNotBlank(clientPath)) { - // See the bug https://github.com/Microsoft/vscode/issues/30996 - // Source.path in the SetBreakpointArguments could be a file system path or uri. - sourcePath = AdapterUtils.convertPath(clientPath, AdapterUtils.isUri(clientPath), context.isDebuggerPathsAreUri()); - } - + String sourcePath = normalizeSourcePath(bpArguments.source, context); // When breakpoint source path is null or an invalid file path, send an ErrorResponse back. if (StringUtils.isBlank(sourcePath)) { throw AdapterUtils.createCompletionException( @@ -163,6 +144,32 @@ public CompletableFuture handle(Command command, Arguments arguments, } } + public static String normalizeSourcePath(Types.Source source, IDebugAdapterContext context) { + String clientPath = source.path; + if (AdapterUtils.isWindows()) { + // VSCode may send drive letters with inconsistent casing which will mess up the key + // in the BreakpointManager. See https://github.com/Microsoft/vscode/issues/6268 + // Normalize the drive letter casing. Note that drive letters + // are not localized so invariant is safe here. + String drivePrefix = FilenameUtils.getPrefix(clientPath); + if (drivePrefix != null && drivePrefix.length() >= 2 + && Character.isLowerCase(drivePrefix.charAt(0)) && drivePrefix.charAt(1) == ':') { + drivePrefix = drivePrefix.substring(0, 2); // d:\ is an illegal regex string, convert it to d: + clientPath = clientPath.replaceFirst(drivePrefix, drivePrefix.toUpperCase()); + } + } + String sourcePath = clientPath; + if (source.sourceReference != 0 && context.getSourceUri(source.sourceReference) != null) { + sourcePath = context.getSourceUri(source.sourceReference); + } else if (StringUtils.isNotBlank(clientPath)) { + // See the bug https://github.com/Microsoft/vscode/issues/30996 + // Source.path in the SetBreakpointArguments could be a file system path or uri. + sourcePath = AdapterUtils.convertPath(clientPath, AdapterUtils.isUri(clientPath), context.isDebuggerPathsAreUri()); + } + + return sourcePath; + } + private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream().filter( bp -> { @@ -173,11 +180,6 @@ private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext cont ).findFirst().orElse(null); } - private IBreakpoint getAssociatedBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { - return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream() - .filter(bp -> bp.requests().contains(event.request())).findFirst().orElse(null); - } - private void registerBreakpointHandler(IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); if (debugSession != null) { @@ -300,27 +302,25 @@ private Types.Breakpoint convertDebuggerBreakpointToClient(IBreakpoint breakpoin private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Types.SourceBreakpoint[] sourceBreakpoints, IDebugAdapterContext context) throws DebugException { - int[] lines = Arrays.asList(sourceBreakpoints).stream().map(sourceBreakpoint -> { - return AdapterUtils.convertLineNumber(sourceBreakpoint.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); - }).mapToInt(line -> line).toArray(); - - int[] columns = Arrays.asList(sourceBreakpoints).stream().map(b -> { - return AdapterUtils.convertColumnNumber(b.column, context.isClientColumnsStartAt1()); - }).mapToInt(b -> b).toArray(); + Types.SourceBreakpoint[] debugSourceBreakpoints = Stream.of(sourceBreakpoints).map(sourceBreakpoint -> { + int line = AdapterUtils.convertLineNumber(sourceBreakpoint.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); + int column = AdapterUtils.convertColumnNumber(sourceBreakpoint.column, context.isClientColumnsStartAt1(), context.isDebuggerColumnsStartAt1()); + return new Types.SourceBreakpoint(line, column); + }).toArray(Types.SourceBreakpoint[]::new); ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); - String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, columns); - IBreakpoint[] breakpoints = new IBreakpoint[lines.length]; - for (int i = 0; i < lines.length; i++) { + JavaBreakpointLocation[] locations = sourceProvider.getBreakpointLocations(sourceFile, debugSourceBreakpoints); + IBreakpoint[] breakpoints = new IBreakpoint[locations.length]; + for (int i = 0; i < locations.length; i++) { int hitCount = 0; try { hitCount = Integer.parseInt(sourceBreakpoints[i].hitCondition); } catch (NumberFormatException e) { hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. } - breakpoints[i] = context.getDebugSession().createBreakpoint(fqns[i], lines[i], hitCount, sourceBreakpoints[i].condition, + breakpoints[i] = context.getDebugSession().createBreakpoint(locations[i], hitCount, sourceBreakpoints[i].condition, sourceBreakpoints[i].logMessage); - if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(fqns[i])) { + if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(locations[i].className())) { breakpoints[i].putProperty("verified", true); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 2120cafab..f22966b03 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -25,6 +26,7 @@ import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; +import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; @@ -47,6 +49,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; +import com.sun.jdi.request.BreakpointRequest; public class StackTraceRequestHandler implements IDebugRequestHandler { @@ -89,7 +92,7 @@ public CompletableFuture handle(Command command, Arguments arguments, StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i); int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); StackFrameInfo jdiFrame = jdiFrames.get(i); - result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, context)); + result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException @@ -167,7 +170,7 @@ private static List resolveStackFrameInfos(StackFrame[] frames, return jdiFrames; } - private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFrame, int frameId, IDebugAdapterContext context) + private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFrame, int frameId, boolean isTopFrame, IDebugAdapterContext context) throws URISyntaxException, AbsentInformationException { Types.Source clientSource = convertDebuggerSourceToClient(jdiFrame.typeName, jdiFrame.sourceName, jdiFrame.sourcePath, context); String methodName = formatMethodName(jdiFrame.methodName, jdiFrame.argumentTypeNames, jdiFrame.typeName, true, true); @@ -185,7 +188,27 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra clientSource = null; } } - return new Types.StackFrame(frameId, methodName, clientSource, clientLineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); + + int clientColumnNumber = context.isClientColumnsStartAt1() ? 1 : 0; + // If the top-level frame is a lambda method, it might be paused on a lambda breakpoint. + // We can associate its column number with the target lambda breakpoint. + if (isTopFrame && jdiFrame.methodName.startsWith("lambda$")) { + for (IBreakpoint breakpoint : context.getBreakpointManager().getBreakpoints()) { + if (breakpoint.getColumnNumber() > 0 && breakpoint.getLineNumber() == jdiFrame.lineNumber + && Objects.equals(jdiFrame.typeName, breakpoint.className())) { + boolean match = breakpoint.requests().stream().anyMatch(request -> { + return request instanceof BreakpointRequest + && Objects.equals(((BreakpointRequest) request).location(), jdiFrame.location); + }); + if (match) { + clientColumnNumber = AdapterUtils.convertColumnNumber(breakpoint.getColumnNumber(), + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + } + } + } + } + + return new Types.StackFrame(frameId, methodName, clientSource, clientLineNumber, clientColumnNumber, presentationHint); } /** diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 9b444155c..0301bdb38 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -17,6 +17,7 @@ import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.Types.DataBreakpoint; +import com.microsoft.java.debug.core.protocol.Types.Source; /** * The request arguments types defined by VSCode Debug Protocol. @@ -376,6 +377,42 @@ public boolean equals(Object obj) { } } + /** + * Arguments for breakpointLocations request. + */ + public static class BreakpointLocationsArguments extends Arguments { + /** + * The source location of the breakpoints; either `source.path` or + * `source.reference` must be specified. + */ + public Source source; + + /** + * Start line of range to search possible breakpoint locations in. If only the + * line is specified, the request returns all possible locations in that line. + */ + public int line; + + /** + * Start column of range to search possible breakpoint locations in. If no + * start column is given, the first column in the start line is assumed. + */ + public int column; + + /** + * End line of range to search possible breakpoint locations in. If no end + * line is given, then the end line is assumed to be the start line. + */ + public int endLine; + + /** + * End column of range to search possible breakpoint locations in. If no end + * column is given, then it is assumed to be in the last column of the end + * line. + */ + public int endColumn; + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -411,6 +448,7 @@ public static enum Command { INLINEVALUES("inlineValues", InlineValuesArguments.class), REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), PROCESSID("processId", Arguments.class), + BREAKPOINTLOCATIONS("breakpointLocations", BreakpointLocationsArguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index 2210c8e93..ea660f812 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -13,6 +13,7 @@ import java.util.List; +import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; @@ -282,6 +283,21 @@ public DataBreakpointInfoResponseBody(String dataId, String description, DataBre } } + /** + * Response to breakpointLocations request. + * Contains possible locations for source breakpoints. + */ + public static class BreakpointLocationsResponseBody extends ResponseBody { + /** + * Sorted set of possible breakpoint locations. + */ + public BreakpointLocation[] breakpoints; + + public BreakpointLocationsResponseBody(BreakpointLocation[] breakpoints) { + this.breakpoints = breakpoints; + } + } + public static class ContinueResponseBody extends ResponseBody { public boolean allThreadsContinued; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 979c7d632..4781c7a57 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -16,7 +16,7 @@ import com.google.gson.annotations.SerializedName; /** - * The data types defined by VSCode Debug Protocol. + * The data types defined by Debug Adapter Protocol. */ public class Types { public static class Message { @@ -200,6 +200,9 @@ public Breakpoint(int id, boolean verified, int line, String message) { } } + /** + * Properties of a breakpoint or logpoint passed to the setBreakpoints request. + */ public static class SourceBreakpoint { public int line; public int column; @@ -207,7 +210,9 @@ public static class SourceBreakpoint { public String condition; public String logMessage; - public SourceBreakpoint() { + public SourceBreakpoint(int line, int column) { + this.line = line; + this.column = column; } /** @@ -300,6 +305,46 @@ public DataBreakpoint(String dataId, DataBreakpointAccessType accessType, String } } + /** + * Properties of a breakpoint location returned from the breakpointLocations request. + */ + public static class BreakpointLocation { + /** + * Start line of breakpoint location. + */ + public int line; + + /** + * The start column of breakpoint location. + */ + public int column; + + /** + * The end line of breakpoint location if the location covers a range. + */ + public int endLine; + + /** + * The end column of breakpoint location if the location covers a range. + */ + public int endColumn; + + public BreakpointLocation() { + } + + public BreakpointLocation(int line, int column) { + this.line = line; + this.column = column; + } + + public BreakpointLocation(int line, int column, int endLine, int endColumn) { + this.line = line; + this.column = column; + this.endLine = endLine; + this.endColumn = endColumn; + } + } + public static class CompletionItem { public String label; public String text; @@ -386,5 +431,7 @@ public static class Capabilities { public boolean supportsDataBreakpoints; public boolean supportsClipboardContext; public boolean supportsFunctionBreakpoints; + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_BreakpointLocations + public boolean supportsBreakpointLocationsRequest; } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java index 351be8cb3..b37ff7b1f 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -34,13 +34,9 @@ public LambdaExpressionLocator(CompilationUnit compilationUnit, int line, int co @Override public boolean visit(LambdaExpression node) { - // we only support inline breakpoints which are added before the expression part of the - // lambda. And we don't support lambda blocks since they can be debugged using line - // breakpoints. if (column > -1) { int startPosition = node.getStartPosition(); - int endPosition = node.getBody().getStartPosition(); - int offset = this.compilationUnit.getPosition(line, column); + int breakOffset = this.compilationUnit.getPosition(line, column); // lambda on same line: // list.stream().map(i -> i + 1); // @@ -48,7 +44,10 @@ public boolean visit(LambdaExpression node) { // list.stream().map(user // -> user.isSystem() ? new SystemUser(user) : new EndUser(user)); - if (offset >= startPosition && offset <= endPosition) { + // Since the debugger supports BreakpointLocations Request to hint user + // about possible inline breakpoint locations, we will only support + // inline breakpoints added where a lambda expression begins. + if (breakOffset == startPosition) { this.lambdaMethodBinding = node.resolveMethodBinding(); this.found = true; this.lambdaExpression = node; 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 1dc1c0e64..92ce9477a 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2021 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -19,12 +19,15 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Stream; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -39,7 +42,9 @@ 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.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; @@ -49,10 +54,13 @@ import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; +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; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; +import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); @@ -112,12 +120,38 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return new String[0]; } + SourceBreakpoint[] sourceBreakpoints = new SourceBreakpoint[lines.length]; + for (int i = 0; i < lines.length; i++) { + sourceBreakpoints[i] = new SourceBreakpoint(lines[i], columns[i]); + } + + JavaBreakpointLocation[] locations = getBreakpointLocations(uri, sourceBreakpoints); + return Stream.of(locations).map(location -> { + if (location.className() != null && location.methodName() != null) { + return location.className() + .concat("#").concat(location.methodName()) + .concat("#").concat(location.methodSignature()); + } + return location.className(); + }).toArray(String[]::new); + } + + @Override + public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException { + if (sourceUri == null) { + throw new IllegalArgumentException("sourceUri is null"); + } + + if (sourceBreakpoints == null || sourceBreakpoints.length == 0) { + return new JavaBreakpointLocation[0]; + } + final ASTParser parser = ASTParser.newParser(this.latestASTLevel); parser.setResolveBindings(true); parser.setBindingsRecovery(true); parser.setStatementsRecovery(true); CompilationUnit astUnit = null; - String filePath = AdapterUtils.toPath(uri); + String filePath = AdapterUtils.toPath(sourceUri); // For file uri, read the file contents directly and pass them to the ast parser. if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { String source = readFile(filePath); @@ -146,27 +180,41 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th } else { // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), // leverage jdt to load the source contents. - ITypeRoot typeRoot = resolveClassFile(uri); + ITypeRoot typeRoot = resolveClassFile(sourceUri); if (typeRoot != null) { parser.setSource(typeRoot); astUnit = (CompilationUnit) parser.createAST(null); } } - String[] fqns = new String[lines.length]; + JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints) + .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) + .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { - for (int i = 0; i < lines.length; i++) { - if (columns[i] > -1) { + Map resolvedLocations = new HashMap<>(); + for (JavaBreakpointLocation sourceLocation : sourceLocations) { + int sourceLine = sourceLocation.lineNumber(); + int sourceColumn = sourceLocation.columnNumber(); + if (sourceColumn > -1) { // if we have a column, try to find the lambda expression at that column - LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, lines[i], - columns[i]); + LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, + sourceLine, sourceColumn); astUnit.accept(lambdaExpressionLocator); if (lambdaExpressionLocator.isFound()) { - fqns[i] = lambdaExpressionLocator.getFullyQualifiedTypeName().concat("#") - .concat(lambdaExpressionLocator.getMethodName()) - .concat("#").concat(lambdaExpressionLocator.getMethodSignature()); - continue; + sourceLocation.setClassName(lambdaExpressionLocator.getFullyQualifiedTypeName()); + sourceLocation.setMethodName(lambdaExpressionLocator.getMethodName()); + sourceLocation.setMethodSignature(lambdaExpressionLocator.getMethodSignature()); } + + if (resolvedLocations.containsKey(sourceLine)) { + sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); + } else { + BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine); + sourceLocation.setAvailableBreakpointLocations(inlineLocations); + resolvedLocations.put(sourceLine, inlineLocations); + } + + continue; } // TODO @@ -181,24 +229,56 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th // mark it as "unverified". // In future, we could consider supporting to update the breakpoint to a valid // location. - BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, - true); + BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, + sourceLine, true, true); astUnit.accept(locator); // When the final valid line location is same as the original line, that // represents it's a valid breakpoint. // Add location type check to avoid breakpoint on method/field which will never // be hit in current implementation. - if (lines[i] == locator.getLineLocation() + if (sourceLine == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { - fqns[i] = locator.getFullyQualifiedTypeName(); + sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + if (resolvedLocations.containsKey(sourceLine)) { + sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); + } else { + BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine); + sourceLocation.setAvailableBreakpointLocations(inlineLocations); + resolvedLocations.put(sourceLine, inlineLocations); + } } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - fqns[i] = locator.getFullyQualifiedTypeName().concat("#") - .concat(locator.getMethodName()) - .concat("#").concat(locator.getMethodSignature()); + sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setMethodName(locator.getMethodName()); + sourceLocation.setMethodSignature(locator.getMethodSignature()); } } } - return fqns; + + return sourceLocations; + } + + private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { + List locations = new ArrayList<>(); + // The starting position of each line is the default breakpoint location for that line. + locations.add(new BreakpointLocation(sourceLine, 0)); + astUnit.accept(new ASTVisitor() { + @Override + public boolean visit(LambdaExpression node) { + int lambdaStart = node.getStartPosition(); + int startLine = astUnit.getLineNumber(lambdaStart); + if (startLine == sourceLine) { + int startColumn = astUnit.getColumnNumber(lambdaStart); + int lambdaEnd = lambdaStart + node.getLength(); + 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); + } + }); + + return locations.toArray(BreakpointLocation[]::new); } @Override From dea29f55459ac693cb9a29717c62d120b82cb8ad Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 28 Sep 2022 17:36:31 +0800 Subject: [PATCH 116/206] Enable async jdwp based on network latency for auto mode (#447) --- .../core/adapter/DebugAdapterContext.java | 27 +++++++++++++++- .../core/adapter/IDebugAdapterContext.java | 6 ++++ .../adapter/handler/AttachRequestHandler.java | 6 ++-- .../handler/EvaluateRequestHandler.java | 30 +++++++++++++++--- .../handler/InlineValuesRequestHandler.java | 3 +- .../handler/VariablesRequestHandler.java | 31 ++++++++++++++++--- 6 files changed, 90 insertions(+), 13 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index e317ddf1c..345b2fcdf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -61,6 +61,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private long processId = -1; private boolean localDebugging = true; + private long jdwpLatency = 0; private IdCollection sourceReferences = new IdCollection<>(); private RecyclableObjectPool recyclableIdPool = new RecyclableObjectPool<>(); @@ -372,7 +373,21 @@ public ThreadCache getThreadCache() { @Override public boolean asyncJDWP() { - return DebugSettings.getCurrent().asyncJDWP == AsyncMode.ON; + /** + * If we take 1 second as the acceptable latency for DAP requests, + * With a single-threaded strategy for handling JDWP requests, + * a latency of about 15ms per JDWP request can ensure the responsiveness + * for most DAPs. It allows sending 66 JDWP requests within 1 seconds, + * which can cover most DAP operations such as breakpoint, threads, + * call stack, step and continue. + */ + return asyncJDWP(15); + } + + @Override + public boolean asyncJDWP(long usableLatency) { + return DebugSettings.getCurrent().asyncJDWP == AsyncMode.ON + || (DebugSettings.getCurrent().asyncJDWP == AsyncMode.AUTO && this.jdwpLatency > usableLatency); } public boolean isLocalDebugging() { @@ -382,4 +397,14 @@ public boolean isLocalDebugging() { public void setLocalDebugging(boolean local) { this.localDebugging = local; } + + @Override + public long getJDWPLatency() { + return this.jdwpLatency; + } + + @Override + public void setJDWPLatency(long baseLatency) { + this.jdwpLatency = baseLatency; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 5d3b75639..9df539e1d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -145,7 +145,13 @@ public interface IDebugAdapterContext { boolean asyncJDWP(); + boolean asyncJDWP(long usableLatency/**ms*/); + boolean isLocalDebugging(); void setLocalDebugging(boolean local); + + long getJDWPLatency(); + + void setJDWPLatency(long baseLatency); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index cee6e64a3..5ab848c8a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -110,8 +110,10 @@ public CompletableFuture handle(Command command, Arguments arguments, long sent = System.currentTimeMillis(); request.enable(); long received = System.currentTimeMillis(); - logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); - traceInfo.put("networkLatency", (received - sent)); + long latency = received - sent; + context.setJDWPLatency(latency); + logger.info("Network latency for JDWP command: " + latency + "ms"); + traceInfo.put("networkLatency", latency); } IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 1a0e41003..d135ee5b2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2021 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -65,7 +65,9 @@ public CompletableFuture handle(Command command, Arguments arguments, String expression = evalArguments.expression; // Async mode is supposed to be performant, then disable the advanced features like hover evaluation. - if (!context.isLocalDebugging() && context.asyncJDWP() && "hover".equals(evalArguments.context)) { + if (context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY) + && context.getJDWPLatency() > VariablesRequestHandler.USABLE_JDWP_LATENCY + && "hover".equals(evalArguments.context)) { return CompletableFuture.completedFuture(response); } @@ -98,7 +100,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Value sizeValue = null; if (value instanceof ArrayReference) { indexedVariables = ((ArrayReference) value).length(); - } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && engine != null) { + } else if (value instanceof ObjectReference && supportsLogicStructureView(context, evalArguments.context) && engine != null) { try { JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); if (structure != null && structure.getSizeExpression() != null) { @@ -135,7 +137,7 @@ public CompletableFuture handle(Command command, Arguments arguments, // If failed to resolve the variable value, skip the details info as well. } else if (sizeValue != null) { detailsString = "size=" + variableFormatter.valueToString(sizeValue, options); - } else if (DebugSettings.getCurrent().showToString) { + } else if (supportsToStringView(context, evalArguments.context)) { try { detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); } catch (OutOfMemoryError e) { @@ -182,4 +184,24 @@ public CompletableFuture handle(Command command, Arguments arguments, } }); } + + private boolean supportsLogicStructureView(IDebugAdapterContext context, String evalContext) { + if (!"watch".equals(evalContext)) { + return true; + } + + return (!context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY) + || context.getJDWPLatency() <= VariablesRequestHandler.USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showLogicalStructure; + } + + private boolean supportsToStringView(IDebugAdapterContext context, String evalContext) { + if (!"watch".equals(evalContext)) { + return true; + } + + return (!context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY) + || context.getJDWPLatency() <= VariablesRequestHandler.USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showToString; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java index b30959cd5..21f77ee5a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java @@ -82,7 +82,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } // Async mode is supposed to be performant, then disable the advanced features like inline values. - if (!context.isLocalDebugging() && context.asyncJDWP()) { + if (context.getJDWPLatency() > VariablesRequestHandler.USABLE_JDWP_LATENCY + && context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY)) { response.body = new Responses.InlineValuesResponse(null); return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index c9d7cb666..11a6391de 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -68,6 +68,21 @@ public class VariablesRequestHandler implements IDebugRequestHandler { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + /** + * When the debugger enables logical structures and + * toString settings, for each Object variable in the + * variable list, the debugger needs to check its + * superclass and interface to find out if it inherits + * from Collection or overrides the toString method. + * This will cause the debugger to send a lot of JDWP + * requests for them. For a test case with 4 object + * variables, the debug adapter may need to send more + * than 100 JDWP requests to handle these variable + * requests. To achieve a DAP latency of 1s with a + * single-threaded JDWP request processing strategy, + * a single JDWP latency is about 10ms. + */ + static final long USABLE_JDWP_LATENCY = 10/**ms*/; @Override public List getTargetCommands() { @@ -130,7 +145,7 @@ public CompletableFuture handle(Command command, Arguments arguments, childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null)); } - if (context.asyncJDWP()) { + if (useAsyncJDWP(context)) { childrenList.addAll(getVariablesOfFrameAsync(frame, showStaticVariables)); } else { childrenList.addAll(VariableUtils.listLocalVariables(frame)); @@ -198,7 +213,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (varArgs.count > 0) { childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count); } else { - childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables, context.asyncJDWP()); + childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables, useAsyncJDWP(context)); } } } catch (AbsentInformationException e) { @@ -215,7 +230,7 @@ public CompletableFuture handle(Command command, Arguments arguments, .filter(var -> duplicateNames.contains(var.name)) .collect(Collectors.toList()); // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. - if (context.asyncJDWP()) { + if (useAsyncJDWP(context)) { try { AsyncJdwpUtils.await(warmUpJDICache(childrenList, duplicateVars)); } catch (CompletionException | CancellationException e) { @@ -377,11 +392,17 @@ public CompletableFuture handle(Command command, Arguments arguments, } private boolean supportsLogicStructureView(IDebugAdapterContext context) { - return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showLogicalStructure; + return (!useAsyncJDWP(context) || context.getJDWPLatency() <= USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showLogicalStructure; } private boolean supportsToStringView(IDebugAdapterContext context) { - return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showToString; + return (!useAsyncJDWP(context) || context.getJDWPLatency() <= USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showToString; + } + + private boolean useAsyncJDWP(IDebugAdapterContext context) { + return context.asyncJDWP(USABLE_JDWP_LATENCY); } private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter, From 2a48adff464e53b9fa92f4737b52f2dba9eba3fd Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Sat, 8 Oct 2022 15:29:55 +0800 Subject: [PATCH 117/206] Fix NPE from logHandler when stopping debug plugin (#449) --- .../java/debug/plugin/internal/JavaDebuggerServerPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java index 975338371..d3307a5b2 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java @@ -34,7 +34,6 @@ public void start(BundleContext context) throws Exception { @Override public void stop(BundleContext context) throws Exception { - logger.info("Stopping " + PLUGIN_ID); LogUtils.cleanupHandlers(); } From f5ab6904f55853e4baf5c459eb38125b42338da7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 12 Oct 2022 12:42:12 +0800 Subject: [PATCH 118/206] Bump version to 0.41.0 (#450) --- .project | 4 ++-- com.microsoft.java.debug.core/.project | 4 ++-- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/.project | 4 ++-- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/.project | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.project b/.project index d34865de3..f00ccfc41 100644 --- a/.project +++ b/.project @@ -16,12 +16,12 @@ - 1600224298170 + 1665543654766 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.core/.project b/com.microsoft.java.debug.core/.project index a7480133e..353c44be5 100644 --- a/com.microsoft.java.debug.core/.project +++ b/com.microsoft.java.debug.core/.project @@ -28,12 +28,12 @@ - 1599036548523 + 1665543654702 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 83fed3cc9..8bb9b9f47 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.40.0 + 0.41.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index b7e330e32..349cb7844 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/.project b/com.microsoft.java.debug.plugin/.project index 72ba17ff7..529a656dd 100644 --- a/com.microsoft.java.debug.plugin/.project +++ b/com.microsoft.java.debug.plugin/.project @@ -39,12 +39,12 @@ - 1599036548577 + 1665543654719 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 9667f9e51..21e32bbde 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.40.0 +Bundle-Version: 0.41.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.40.0.jar + lib/com.microsoft.java.debug.core-0.41.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 56bb813e6..949512717 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.40.0 + 0.41.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.40.0 + 0.41.0 diff --git a/com.microsoft.java.debug.repository/.project b/com.microsoft.java.debug.repository/.project index 887e4a7a8..255908647 100644 --- a/com.microsoft.java.debug.repository/.project +++ b/com.microsoft.java.debug.repository/.project @@ -16,12 +16,12 @@ - 1600224298119 + 1665543654735 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 7d8519073..27c76516b 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 43cd80e43..b8524fa90 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.40.0 + 0.41.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 2b0497f26..4d31432d9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.40.0 + 0.41.0 pom Java Debug Server for Visual Studio Code From c471efc6671adfb12538a94b13f659d590c06b4d Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 9 Aug 2022 20:50:57 +0200 Subject: [PATCH 119/206] Add support for StepInTarget request The change implement the StepInTarget request and also update the StepRequestHandler to support StepInTarget as well. --- .../java/debug/core/adapter/DebugAdapter.java | 3 + .../core/adapter/ISourceLookUpProvider.java | 52 +++++ .../handler/InitializeRequestHandler.java | 1 + .../handler/StepInTargetsRequestHandler.java | 81 ++++++++ .../adapter/handler/StepRequestHandler.java | 195 ++++++++++++------ .../java/debug/core/protocol/Requests.java | 6 + .../java/debug/core/protocol/Responses.java | 9 + .../java/debug/core/protocol/Types.java | 16 ++ .../microsoft/java/debug/BindingUtils.java | 74 +++++++ .../java/debug/BreakpointLocationLocator.java | 20 +- .../java/debug/LambdaExpressionLocator.java | 5 +- .../internal/JdtSourceLookUpProvider.java | 147 +++++++++---- .../internal/MethodInvocationLocator.java | 66 ++++++ 13 files changed, 551 insertions(+), 124 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index edc771163..b853e0469 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -44,6 +44,7 @@ import com.microsoft.java.debug.core.adapter.handler.SetVariableRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SourceRequestHandler; import com.microsoft.java.debug.core.adapter.handler.StackTraceRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.StepInTargetsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.StepRequestHandler; import com.microsoft.java.debug.core.adapter.handler.ThreadsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.VariablesRequestHandler; @@ -131,6 +132,8 @@ private void initialize() { registerHandlerForDebug(new ProcessIdHandler()); registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); registerHandlerForDebug(new BreakpointLocationsRequestHander()); + registerHandlerForDebug(new StepInTargetsRequestHandler()); + // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); registerHandlerForNoDebug(new ProcessIdHandler()); 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 9976fc16f..5e8d94451 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 @@ -11,10 +11,15 @@ package com.microsoft.java.debug.core.adapter; +import java.util.List; +import java.util.Objects; + import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; +import com.sun.jdi.StackFrame; + public interface ISourceLookUpProvider extends IProvider { boolean supportsRealtimeBreakpointVerification(); @@ -60,4 +65,51 @@ public interface ISourceLookUpProvider extends IProvider { default String getJavaRuntimeVersion(String projectName) { return null; } + + /** + * Return method invocation found in the statement as the given line number of + * the source file. + * + * @param stackframe The stack frame where the invocation must be searched. + * + * @return List of found method invocation or empty if not method invocations + * can be found. + */ + List findMethodInvocations(StackFrame stackframe); + + public static class MethodInvocation { + public String expression; + public String methodName; + public String methodSignature; + public String declaringTypeName; + public int lineStart; + public int lineEnd; + public int columnStart; + public int columnEnd; + + @Override + public int hashCode() { + return Objects.hash(columnEnd, columnStart, declaringTypeName, expression, lineEnd, lineStart, methodName, + methodSignature); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + MethodInvocation other = (MethodInvocation) obj; + return columnEnd == other.columnEnd && columnStart == other.columnStart + && Objects.equals(declaringTypeName, other.declaringTypeName) + && Objects.equals(expression, other.expression) && lineEnd == other.lineEnd + && lineStart == other.lineStart && Objects.equals(methodName, other.methodName) + && Objects.equals(methodSignature, other.methodSignature); + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index 19bbd7d62..bf60b6456 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -65,6 +65,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsFunctionBreakpoints = true; caps.supportsClipboardContext = true; caps.supportsBreakpointLocationsRequest = true; + caps.supportsStepInTargetsRequest = true; response.body = caps; return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java new file mode 100644 index 000000000..f61d2a8b9 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java @@ -0,0 +1,81 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider.MethodInvocation; +import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.StepInTargetsArguments; +import com.microsoft.java.debug.core.protocol.Responses.StepInTargetsResponse; +import com.microsoft.java.debug.core.protocol.Types.StepInTarget; +import com.sun.jdi.StackFrame; + +public class StepInTargetsRequestHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.STEPIN_TARGETS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + final StepInTargetsArguments stepInTargetsArguments = (StepInTargetsArguments) arguments; + + final int frameId = stepInTargetsArguments.frameId; + return CompletableFuture.supplyAsync(() -> { + response.body = new StepInTargetsResponse( + findFrame(frameId, context).map(f -> findTargets(f.thread().uniqueID(), f, context)) + .orElse(Collections.emptyList()).toArray(StepInTarget[]::new)); + return response; + }); + } + + private Optional findFrame(int frameId, IDebugAdapterContext context) { + Object object = context.getRecyclableIdPool().getObjectById(frameId); + if (object instanceof StackFrameReference) { + return Optional.of(context.getStackFrameManager().getStackFrame((StackFrameReference) object)); + } + return Optional.empty(); + } + + private List findTargets(long threadId, StackFrame stackframe, IDebugAdapterContext context) { + ISourceLookUpProvider sourceLookUpProvider = context.getProvider(ISourceLookUpProvider.class); + List invocations = sourceLookUpProvider.findMethodInvocations(stackframe); + if (invocations.isEmpty()) { + return Collections.emptyList(); + } + + List targets = new ArrayList<>(invocations.size()); + for (MethodInvocation methodInvocation : invocations) { + int id = context.getRecyclableIdPool().addObject(threadId, methodInvocation); + StepInTarget target = new StepInTarget(id, methodInvocation.expression); + target.column = methodInvocation.columnStart; + target.endColumn = methodInvocation.columnEnd; + target.line = methodInvocation.lineStart; + target.endLine = methodInvocation.lineEnd; + targets.add(target); + } + return targets; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 51e41bafc..f9baa0e5d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -29,19 +31,24 @@ import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider.MethodInvocation; import com.microsoft.java.debug.core.protocol.Events; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.StepArguments; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; +import com.microsoft.java.debug.core.protocol.Requests.StepInArguments; +import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; +import com.sun.jdi.VirtualMachine; import com.sun.jdi.VoidValue; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; @@ -67,10 +74,13 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { if (context.getDebugSession() == null) { - return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Debug Session doesn't exist."); + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, + "Debug Session doesn't exist."); } - long threadId = ((StepArguments) arguments).threadId; + StepArguments stepArguments = (StepArguments) arguments; + long threadId = stepArguments.threadId; + int targetId = (stepArguments instanceof StepInArguments) ? ((StepInArguments) stepArguments).targetId : 0; ThreadReference thread = context.getThreadCache().getThread(threadId); if (thread == null) { thread = DebugUtility.getThread(context.getDebugSession(), threadId); @@ -83,28 +93,34 @@ public CompletableFuture handle(Command command, Arguments arguments, ThreadState threadState = new ThreadState(); threadState.threadId = threadId; threadState.pendingStepType = command; + threadState.stackDepth = thread.frameCount(); + threadState.stepLocation = resolveLocation(thread, targetId, context); + threadState.targetStepIn = targetId > 0; threadState.eventSubscription = context.getDebugSession().getEventHub().events() - .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) - || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) - || debugEvent.event instanceof BreakpointEvent - || debugEvent.event instanceof ExceptionEvent) - .subscribe(debugEvent -> { - handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); - }); + .filter(debugEvent -> (debugEvent.event instanceof StepEvent + && debugEvent.event.request().equals(threadState.pendingStepRequest)) + || (debugEvent.event instanceof MethodExitEvent + && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) + || debugEvent.event instanceof BreakpointEvent + || debugEvent.event instanceof ExceptionEvent) + .subscribe(debugEvent -> { + handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); + }); if (command == Command.STEPIN) { threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else { threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); + threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager() + .createMethodExitRequest(); threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); if (context.asyncJDWP()) { @@ -114,7 +130,8 @@ public CompletableFuture handle(Command command, Arguments arguments, // JDWP Command: TR_FRAMES threadState.topFrame = getTopFrame(targetThread); threadState.stepLocation = threadState.topFrame.location(); - threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + threadState.pendingMethodExitRequest + .addClassFilter(threadState.stepLocation.declaringType()); if (targetThread.virtualMachine().canUseInstanceFilters()) { try { // JDWP Command: SF_THIS_OBJECT @@ -131,9 +148,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } })); futures.add(AsyncJdwpUtils.runAsync( - // JDWP Command: OR_IS_COLLECTED - () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread) - )); + // JDWP Command: OR_IS_COLLECTED + () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread))); futures.add(AsyncJdwpUtils.runAsync(() -> { try { // JDWP Command: TR_FRAME_COUNT @@ -143,9 +159,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } })); futures.add( - // JDWP Command: ER_SET - AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable()) - ); + // JDWP Command: ER_SET + AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable())); try { AsyncJdwpUtils.await(futures); @@ -184,39 +199,80 @@ public CompletableFuture handle(Command command, Arguments arguments, } catch (IncompatibleThreadStateException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); + final String failureMessage = String.format( + "Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (IndexOutOfBoundsException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); + final String failureMessage = String.format( + "Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); + } catch (AbsentInformationException ex) { + // Roll back the Exception info if stepping fails. + context.getExceptionManager().setException(threadId, exception); + final String failureMessage = String.format( + "Failed to step because the thread '%s' doesn't contain required line information in stack frame", + thread.name()); + throw AdapterUtils.createCompletionException( + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (Exception ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because of the error '%s'", ex.getMessage()); + final String failureMessage = String.format("Failed to step because of the error '%s'", + ex.getMessage()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex.getCause() != null ? ex.getCause() : ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex.getCause() != null ? ex.getCause() : ex); } } return CompletableFuture.completedFuture(response); } + private Location resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) + throws IncompatibleThreadStateException, AbsentInformationException { + if (targetId > 0) { + Object value = context.getRecyclableIdPool().getObjectById(targetId); + if (value instanceof MethodInvocation) { + MethodInvocation invocation = (MethodInvocation) value; + VirtualMachine vm = thread.virtualMachine(); + List refTypes = vm.classesByName(invocation.declaringTypeName); + for (ReferenceType referenceType : refTypes) { + Optional location = referenceType.allLineLocations().stream() + .filter(l -> matchesLocation(l, invocation)) + .findFirst(); + if (location.isPresent()) { + return location.get(); + } + } + } + } + return getTopFrame(thread).location(); + } + + private boolean matchesLocation(Location l, MethodInvocation invocation) { + Method method = l.method(); + return method != null && Objects.equals(method.name(), invocation.methodName) + && Objects.equals(method.signature(), invocation.methodSignature); + } + private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); - // When a breakpoint occurs, abort any pending step requests from the same thread. + // When a breakpoint occurs, abort any pending step requests from the same + // thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { @@ -230,19 +286,20 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); threadState.deleteStepRequest(eventRequestManager); - if (isStepFiltersConfigured(context.getStepFilters())) { + if (isStepFiltersConfigured(context.getStepFilters()) || threadState.targetStepIn) { try { if (threadState.pendingStepType == Command.STEPIN) { int currentStackDepth = thread.frameCount(); Location currentStepLocation = getTopFrame(thread).location(); - - // If the ending step location is filtered, or same as the original location where the step into operation is originated, + // If the ending step location is filtered, or same as the original location + // where the step into operation is originated, // do another step of the same kind. if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) - || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, currentStackDepth, currentStepLocation)) { + || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, + currentStackDepth, currentStepLocation, threadState.targetStepIn)) { threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; @@ -262,7 +319,8 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (event instanceof MethodExitEvent) { MethodExitEvent methodExitEvent = (MethodExitEvent) event; long threadId = methodExitEvent.thread().uniqueID(); - if (threadId == threadState.threadId && methodExitEvent.method().equals(threadState.stepLocation.method())) { + if (threadId == threadState.threadId + && methodExitEvent.method().equals(threadState.stepLocation.method())) { Value returnValue = methodExitEvent.returnValue(); if (returnValue instanceof VoidValue) { context.getStepResultManager().removeMethodResult(threadId); @@ -280,22 +338,26 @@ private boolean isStepFiltersConfigured(StepFilters filters) { return false; } return ArrayUtils.isNotEmpty(filters.allowClasses) || ArrayUtils.isNotEmpty(filters.skipClasses) - || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors - || filters.skipStaticInitializers || filters.skipSynthetics; + || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors + || filters.skipStaticInitializers || filters.skipSynthetics; } /** - * Return true if the StepEvent's location is a Method that the user has indicated to filter. + * Return true if the StepEvent's location is a Method that the user has + * indicated to filter. * * @throws IncompatibleThreadStateException - * if the thread is not suspended in the target VM. + * if the thread is not suspended in + * the target VM. */ - private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, IDebugAdapterContext context) + private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, + IDebugAdapterContext context) throws IncompatibleThreadStateException { if (originalLocation == null || currentLocation == null) { return false; } - return !shouldFilterMethod(originalLocation.method(), context) && shouldFilterMethod(currentLocation.method(), context); + return !shouldFilterMethod(originalLocation.method(), context) + && shouldFilterMethod(currentLocation.method(), context); } private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) { @@ -305,40 +367,50 @@ private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) } /** - * Check if the current top stack is same as the original top stack. + * Check if the current top stack is same as the original top stack and if we + * are not in target step in we should not request an extra step in. But if we + * are processing a target step in, we only check if the original and current + * location are same. If they are not same we request a extra step in. * * @throws IncompatibleThreadStateException - * if the thread is not suspended in the target VM. + * if the thread is not suspended in + * the target VM. */ - private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, Location currentLocation) + private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, + Location currentLocation, boolean targetStepIn) throws IncompatibleThreadStateException { - if (originalStackDepth != currentStackDepth) { - return false; - } - if (originalLocation == null) { - return false; + if (!targetStepIn) { + if (originalStackDepth != currentStackDepth) { + return false; + } + if (originalLocation == null) { + return false; + } } + Method originalMethod = originalLocation.method(); Method currentMethod = currentLocation.method(); if (!originalMethod.equals(currentMethod)) { - return false; + return targetStepIn; } if (originalLocation.lineNumber() != currentLocation.lineNumber()) { - return false; + return targetStepIn; } - return true; + return !targetStepIn; } /** * Return the top stack frame of the target thread. * * @param thread - * the target thread. + * the target thread. * @return the top frame. * @throws IncompatibleThreadStateException - * if the thread is not suspended in the target VM. + * if the thread is not suspended in + * the target VM. * @throws IndexOutOfBoundsException - * if the thread doesn't contain any stack frame. + * if the thread doesn't contain any + * stack frame. */ private StackFrame getTopFrame(ThreadReference thread) throws IncompatibleThreadStateException { return thread.frame(0); @@ -353,6 +425,7 @@ class ThreadState { StackFrame topFrame = null; Location stepLocation = null; Disposable eventSubscription = null; + boolean targetStepIn = false; public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 0301bdb38..697eef93d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -271,6 +271,10 @@ public static class StepOutArguments extends StepArguments { } + public static class StepInTargetsArguments extends Arguments { + public int frameId; + } + public static class PauseArguments extends Arguments { public long threadId; } @@ -423,6 +427,8 @@ public static enum Command { CONTINUE("continue", ContinueArguments.class), STEPIN("stepIn", StepInArguments.class), STEPOUT("stepOut", StepOutArguments.class), + STEPIN_TARGETS("stepInTargets", + StepInTargetsArguments.class), PAUSE("pause", PauseArguments.class), STACKTRACE("stackTrace", StackTraceArguments.class), RESTARTFRAME("restartFrame", RestartFrameArguments.class), diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index ea660f812..af701552e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -17,6 +17,7 @@ import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; +import com.microsoft.java.debug.core.protocol.Types.StepInTarget; import com.microsoft.java.debug.core.protocol.Types.Variable; /** @@ -364,4 +365,12 @@ public InlineValuesResponse(Variable[] variables) { this.variables = variables; } } + + public static class StepInTargetsResponse extends ResponseBody { + public StepInTarget[] targets; + + public StepInTargetsResponse(StepInTarget[] targets) { + this.targets = targets; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 4781c7a57..33308af6d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -433,5 +433,21 @@ public static class Capabilities { public boolean supportsFunctionBreakpoints; // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_BreakpointLocations public boolean supportsBreakpointLocationsRequest; + public boolean supportsStepInTargetsRequest; } + + public static class StepInTarget { + public int id; + public String label; + public int line; + public int column; + public int endLine; + public int endColumn; + + public StepInTarget(int id, String label) { + this.id = id; + this.label = label; + } + } + } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java new file mode 100644 index 000000000..e30b0dd30 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2017-2020 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; + +import org.eclipse.jdt.core.dom.IMethodBinding; + +/** + * Utility methods around working with JDT Bindings. + */ +public final class BindingUtils { + private BindingUtils() { + + } + + /** + * Return the method name from the binding using either the + * {@link IMethodBinding#getKey()} or {@link IMethodBinding#getName()}. The key + * can be used to find the name of a generated lambda method if the minding + * represents a lambda method. + * + * @param binding the binding to extract the name from. + * @param fromKey use binging key to resolve the method name. + * @return the name of the method. + */ + public static String getMethodName(IMethodBinding binding, boolean fromKey) { + if (fromKey) { + String key = binding.getKey(); + return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + } else { + return binding.getName(); + } + } + + /** + * Returns the method signature of the method represented by the binding. Since + * this implementation use the {@link IMethodBinding#getKey()} to extract the + * signature from, the method name must be passed in. + * + * @param binding the binding which the signature must be resolved for. + * @param name the name of the method. + * @return the signature or null if the signature could not be resolved from the + * key. + */ + public static String toSignature(IMethodBinding binding, String name) { + // use key for now until JDT core provides a public API for this. + // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" + // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" + if (!binding.getName().equals(name)) { + throw new IllegalArgumentException("The method name and binding method name doesn't match."); + } + + String signatureString = binding.getKey(); + if (signatureString != null) { + int index = signatureString.indexOf(name); + if (index > -1) { + int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); + if (exceptionIndex > -1) { + return signatureString.substring(index + name.length(), exceptionIndex); + } + return signatureString.substring(index + name.length()); + } + } + return null; + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index 00cffb766..68f593aff 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -46,7 +46,7 @@ public String getMethodSignature() { if (this.methodBinding == null) { return null; } - return toSignature(this.methodBinding, getMethodName()); + return BindingUtils.toSignature(this.methodBinding, getMethodName()); } /** @@ -70,22 +70,4 @@ public String getFullyQualifiedTypeName() { } return super.getFullyQualifiedTypeName(); } - - static String toSignature(IMethodBinding binding, String name) { - // use key for now until JDT core provides a public API for this. - // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" - // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" - String signatureString = binding.getKey(); - if (signatureString != null) { - int index = signatureString.indexOf(name); - if (index > -1) { - int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); - if (exceptionIndex > -1) { - return signatureString.substring(index + name.length(), exceptionIndex); - } - return signatureString.substring(index + name.length()); - } - } - return null; - } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java index b37ff7b1f..afffd9741 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -71,7 +71,7 @@ public String getMethodSignature() { if (!this.found) { return null; } - return BreakpointLocationLocator.toSignature(this.lambdaMethodBinding, getMethodName()); + return BindingUtils.toSignature(this.lambdaMethodBinding, getMethodName()); } /** @@ -81,8 +81,7 @@ public String getMethodName() { if (!this.found) { return null; } - String key = this.lambdaMethodBinding.getKey(); - return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + return BindingUtils.getMethodName(lambdaMethodBinding, true); } /** 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 92ce9477a..eba9a42e6 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 @@ -15,11 +15,13 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +30,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; +import java.util.stream.Collectors; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -37,6 +40,7 @@ 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; import org.eclipse.jdt.core.JavaModelException; @@ -45,11 +49,14 @@ import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import com.microsoft.java.debug.BindingUtils; import com.microsoft.java.debug.BreakpointLocationLocator; import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; @@ -62,6 +69,8 @@ import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; +import com.sun.jdi.StackFrame; + public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; @@ -146,47 +155,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB return new JavaBreakpointLocation[0]; } - final ASTParser parser = ASTParser.newParser(this.latestASTLevel); - parser.setResolveBindings(true); - parser.setBindingsRecovery(true); - parser.setStatementsRecovery(true); - CompilationUnit astUnit = null; - String filePath = AdapterUtils.toPath(sourceUri); - // For file uri, read the file contents directly and pass them to the ast parser. - if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { - String source = readFile(filePath); - parser.setSource(source.toCharArray()); - /** - * See the java doc for { @link ASTParser#setResolveBindings(boolean) }. - * Binding information is obtained from the Java model. This means that the compilation unit must be located relative to the Java model. - * This happens automatically when the source code comes from either setSource(ICompilationUnit) or setSource(IClassFile). - * When source is supplied by setSource(char[]), the location must be established explicitly - * by setting an environment using setProject(IJavaProject) or setEnvironment(String [], String [], String [], boolean) - * and a unit name setUnitName(String). - */ - parser.setEnvironment(new String[0], new String[0], null, true); - parser.setUnitName(Paths.get(filePath).getFileName().toString()); - /** - * See the java doc for { @link ASTParser#setSource(char[]) }, - * the user need specify the compiler options explicitly. - */ - Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); - parser.setCompilerOptions(javaOptions); - astUnit = (CompilationUnit) parser.createAST(null); - } else { - // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), - // leverage jdt to load the source contents. - ITypeRoot typeRoot = resolveClassFile(sourceUri); - if (typeRoot != null) { - parser.setSource(typeRoot); - astUnit = (CompilationUnit) parser.createAST(null); - } - } - + CompilationUnit astUnit = asCompilationUnit(sourceUri); JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints) .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) .toArray(JavaBreakpointLocation[]::new); @@ -281,6 +250,55 @@ public boolean visit(LambdaExpression node) { return locations.toArray(BreakpointLocation[]::new); } + private CompilationUnit asCompilationUnit(String uri) { + final ASTParser parser = ASTParser.newParser(this.latestASTLevel); + parser.setResolveBindings(true); + parser.setBindingsRecovery(true); + parser.setStatementsRecovery(true); + CompilationUnit astUnit = null; + String filePath = AdapterUtils.toPath(uri); + // For file uri, read the file contents directly and pass them to the ast + // parser. + if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { + String source = readFile(filePath); + parser.setSource(source.toCharArray()); + /** + * See the java doc for { @link ASTParser#setResolveBindings(boolean) }. + * Binding information is obtained from the Java model. This means that the + * compilation unit must be located relative to the Java model. + * This happens automatically when the source code comes from either + * setSource(ICompilationUnit) or setSource(IClassFile). + * When source is supplied by setSource(char[]), the location must be + * established explicitly + * by setting an environment using setProject(IJavaProject) or + * setEnvironment(String [], String [], String [], boolean) + * and a unit name setUnitName(String). + */ + parser.setEnvironment(new String[0], new String[0], null, true); + parser.setUnitName(Paths.get(filePath).getFileName().toString()); + /** + * See the java doc for { @link ASTParser#setSource(char[]) }, + * the user need specify the compiler options explicitly. + */ + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + parser.setCompilerOptions(javaOptions); + astUnit = (CompilationUnit) parser.createAST(null); + } else { + // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), + // leverage jdt to load the source contents. + ITypeRoot typeRoot = resolveClassFile(uri); + if (typeRoot != null) { + parser.setSource(typeRoot); + astUnit = (CompilationUnit) parser.createAST(null); + } + } + return astUnit; + } + @Override public String getSourceFileURI(String fullyQualifiedName, String sourcePath) { if (sourcePath == null) { @@ -431,4 +449,51 @@ private static String resolveSystemLibraryVersion(IJavaProject project, IVMInsta return null; } + + @Override + public List findMethodInvocations(StackFrame frame) { + if (frame == null) { + throw new IllegalArgumentException("frame is null"); + } + + IJavaProject project = JdtUtils.findProject(frame, getSourceContainers()); + if (project == null) { + logger.log(Level.WARNING, + String.format("Failed to resolve project for the frame: %s", frame)); + return Collections.emptyList(); + } + + String uri; + try { + IType type = project.findType(JdtUtils.getDeclaringTypeName(frame)); + uri = type.getResource().getLocationURI().toURL().toString(); + } catch (JavaModelException | DebugException | MalformedURLException e) { + logger.log(Level.SEVERE, + String.format("Failed to resolve type for the frame: %s", frame)); + return Collections.emptyList(); + } + + CompilationUnit astUnit = asCompilationUnit(uri); + if (astUnit == null) { + return Collections.emptyList(); + } + + MethodInvocationLocator locator = new MethodInvocationLocator(frame.location().lineNumber(), astUnit); + astUnit.accept(locator); + + return locator.getTargets().entrySet().stream().map(entry -> { + MethodInvocation invocation = new MethodInvocation(); + Expression expression = entry.getKey(); + invocation.expression = expression.toString(); + IMethodBinding binding = entry.getValue(); + invocation.methodName = binding.getName(); + invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + invocation.methodSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); + invocation.lineStart = astUnit.getLineNumber(expression.getStartPosition()); + invocation.lineEnd = astUnit.getLineNumber(expression.getStartPosition() + expression.getLength()); + invocation.columnStart = astUnit.getColumnNumber(expression.getStartPosition()); + invocation.columnEnd = astUnit.getColumnNumber(expression.getStartPosition() + expression.getLength()); + return invocation; + }).collect(Collectors.toList()); + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java new file mode 100644 index 000000000..ab6efdaa2 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -0,0 +1,66 @@ +/******************************************************************************* +* Copyright (c) 2022 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: +* Gayan Perera (gayanper@gmail.com) - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.plugin.internal; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; + +public class MethodInvocationLocator extends ASTVisitor { + private int line; + private CompilationUnit unit; + private Map targets; + + private boolean collectMethodInvocations = false; + + public MethodInvocationLocator(int line, CompilationUnit unit) { + super(false); + this.line = line; + this.unit = unit; + this.targets = new HashMap<>(); + } + + @Override + public boolean visit(ExpressionStatement node) { + int start = unit.getLineNumber(node.getStartPosition()); + int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + + if (line >= start && line <= end) { + collectMethodInvocations = true; + } + return collectMethodInvocations; + } + + @Override + public boolean visit(MethodInvocation node) { + int lineNumber = unit.getLineNumber(node.getStartPosition()); + if (lineNumber == this.line) { + targets.put(node, node.resolveMethodBinding()); + return true; + } + return false; + } + + @Override + public void endVisit(ExpressionStatement node) { + collectMethodInvocations = false; + } + + public Map getTargets() { + return targets; + } +} From 69230b339d68dfa2b3a1c251c29f3bd577fd0cf8 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Fri, 19 Aug 2022 19:12:01 +0200 Subject: [PATCH 120/206] Fix and improve targets and stepin Improve to only show valid targets within an expression, also added support for stepin into unloaded classes by delaying the stepin. --- .../adapter/handler/StepRequestHandler.java | 256 ++++++++++-------- .../microsoft/java/debug/BindingUtils.java | 9 +- .../internal/JdtSourceLookUpProvider.java | 16 +- .../internal/MethodInvocationLocator.java | 29 +- 4 files changed, 193 insertions(+), 117 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index f9baa0e5d..660d4bbdd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -74,8 +74,7 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { if (context.getDebugSession() == null) { - return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, - "Debug Session doesn't exist."); + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Debug Session doesn't exist."); } StepArguments stepArguments = (StepArguments) arguments; @@ -93,63 +92,59 @@ public CompletableFuture handle(Command command, Arguments arguments, ThreadState threadState = new ThreadState(); threadState.threadId = threadId; threadState.pendingStepType = command; - threadState.stackDepth = thread.frameCount(); - threadState.stepLocation = resolveLocation(thread, targetId, context); - threadState.targetStepIn = targetId > 0; threadState.eventSubscription = context.getDebugSession().getEventHub().events() - .filter(debugEvent -> (debugEvent.event instanceof StepEvent - && debugEvent.event.request().equals(threadState.pendingStepRequest)) - || (debugEvent.event instanceof MethodExitEvent - && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) - || debugEvent.event instanceof BreakpointEvent - || debugEvent.event instanceof ExceptionEvent) - .subscribe(debugEvent -> { - handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); - }); + .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) + || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) + || debugEvent.event instanceof BreakpointEvent + || debugEvent.event instanceof ExceptionEvent) + .subscribe(debugEvent -> { + handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); + }); if (command == Command.STEPIN) { + String[] allowedClasses = threadState.pendingTargetStepIn != null + ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} + : context.getStepFilters().allowClasses; threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + allowedClasses, + context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else { threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager() - .createMethodExitRequest(); + threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + LocationResponse locationResponse = resolveLocation(thread, targetId, context); if (context.asyncJDWP()) { List> futures = new ArrayList<>(); futures.add(AsyncJdwpUtils.runAsync(() -> { - try { - // JDWP Command: TR_FRAMES - threadState.topFrame = getTopFrame(targetThread); - threadState.stepLocation = threadState.topFrame.location(); - threadState.pendingMethodExitRequest - .addClassFilter(threadState.stepLocation.declaringType()); - if (targetThread.virtualMachine().canUseInstanceFilters()) { - try { - // JDWP Command: SF_THIS_OBJECT - ObjectReference thisObject = threadState.topFrame.thisObject(); - if (thisObject != null) { - threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); - } - } catch (Exception e) { - // ignore + // JDWP Command: TR_FRAMES + threadState.stepLocation = locationResponse.location; + threadState.pendingTargetStepIn = locationResponse.methodInvocation; + threadState.targetStepIn = targetId > 0; + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + // JDWP Command: SF_THIS_OBJECT + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); } + } catch (Exception e) { + // ignore } - } catch (IncompatibleThreadStateException e) { - throw new CompletionException(e); } })); futures.add(AsyncJdwpUtils.runAsync( - // JDWP Command: OR_IS_COLLECTED - () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread))); + // JDWP Command: OR_IS_COLLECTED + () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread) + )); futures.add(AsyncJdwpUtils.runAsync(() -> { try { // JDWP Command: TR_FRAME_COUNT @@ -159,8 +154,9 @@ public CompletableFuture handle(Command command, Arguments arguments, } })); futures.add( - // JDWP Command: ER_SET - AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable())); + // JDWP Command: ER_SET + AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable()) + ); try { AsyncJdwpUtils.await(futures); @@ -175,7 +171,9 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingMethodExitRequest.enable(); } else { threadState.stackDepth = targetThread.frameCount(); - threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = locationResponse.location; + threadState.pendingTargetStepIn = locationResponse.methodInvocation; + threadState.targetStepIn = targetId > 0; threadState.stepLocation = threadState.topFrame.location(); threadState.pendingMethodExitRequest.addThreadFilter(thread); threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); @@ -199,81 +197,97 @@ public CompletableFuture handle(Command command, Arguments arguments, } catch (IncompatibleThreadStateException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format( - "Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); + final String failureMessage = String.format("Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (IndexOutOfBoundsException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format( - "Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); - throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); - } catch (AbsentInformationException ex) { - // Roll back the Exception info if stepping fails. - context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format( - "Failed to step because the thread '%s' doesn't contain required line information in stack frame", - thread.name()); + final String failureMessage = String.format("Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (Exception ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because of the error '%s'", - ex.getMessage()); + final String failureMessage = String.format("Failed to step because of the error '%s'", ex.getMessage()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex.getCause() != null ? ex.getCause() : ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex.getCause() != null ? ex.getCause() : ex); } } return CompletableFuture.completedFuture(response); } - private Location resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) + private LocationResponse resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) throws IncompatibleThreadStateException, AbsentInformationException { if (targetId > 0) { Object value = context.getRecyclableIdPool().getObjectById(targetId); if (value instanceof MethodInvocation) { - MethodInvocation invocation = (MethodInvocation) value; - VirtualMachine vm = thread.virtualMachine(); - List refTypes = vm.classesByName(invocation.declaringTypeName); - for (ReferenceType referenceType : refTypes) { - Optional location = referenceType.allLineLocations().stream() - .filter(l -> matchesLocation(l, invocation)) - .findFirst(); - if (location.isPresent()) { - return location.get(); - } + return resolveLocation((MethodInvocation) value, thread); + } + } + return LocationResponse.absolute(getTopFrame(thread).location()); + } + + private LocationResponse resolveLocation(MethodInvocation invocation, ThreadReference thread) + throws AbsentInformationException, IncompatibleThreadStateException { + VirtualMachine vm = thread.virtualMachine(); + List refTypes = vm.classesByName(invocation.declaringTypeName); + + // if class is not yet loaded try to make the debugger step in until class is + // loaded. + if (refTypes.isEmpty()) { + return LocationResponse.waitFor(invocation, getTopFrame(thread).location()); + } else { + for (ReferenceType referenceType : refTypes) { + Optional location = referenceType.allLineLocations().stream() + .filter(l -> matchesLocation(l, invocation)) + .findFirst(); + if (location.isPresent()) { + return LocationResponse.absolute(location.get()); } } + return LocationResponse.absolute(getTopFrame(thread).location()); } - return getTopFrame(thread).location(); } private boolean matchesLocation(Location l, MethodInvocation invocation) { Method method = l.method(); - return method != null && Objects.equals(method.name(), invocation.methodName) + return method != null && Objects.equals(fixedName(method), invocation.methodName) && Objects.equals(method.signature(), invocation.methodSignature); } + /* + * Fix the name so for constructors we return empty to match the captured AST + * invocations. + */ + private String fixedName(Method method) { + String name = method.name(); + if ("".equals(name)) { + return ""; + } + return name; + } + private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); - // When a breakpoint occurs, abort any pending step requests from the same - // thread. + // When a breakpoint occurs, abort any pending step requests from the same thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { + // if we have a pending target step in then ignore and continue. + if (threadState.pendingTargetStepIn != null) { + debugEvent.shouldResume = true; + return; + } + long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { threadState.deleteStepRequest(eventRequestManager); @@ -291,21 +305,35 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.pendingStepType == Command.STEPIN) { int currentStackDepth = thread.frameCount(); Location currentStepLocation = getTopFrame(thread).location(); - // If the ending step location is filtered, or same as the original location - // where the step into operation is originated, + + // if we are in targetStepIn where the location was pending, then try to resolve + // the location and use it. + if (threadState.pendingTargetStepIn != null) { + LocationResponse newLocation = resolveLocation(threadState.pendingTargetStepIn, thread); + if (!newLocation.isLocationPending()) { + threadState.stepLocation = newLocation.location; + threadState.pendingTargetStepIn = null; + } + } + + // If the ending step location is filtered, or same as the original location where the step into operation is originated, // do another step of the same kind. if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, - currentStackDepth, currentStepLocation, threadState.targetStepIn)) { + currentStackDepth, currentStepLocation, threadState.targetStepIn) + || threadState.pendingTargetStepIn != null) { + String[] allowedClasses = threadState.pendingTargetStepIn != null + ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} + : context.getStepFilters().allowClasses; threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + allowedClasses, + context.getStepFilters().skipClasses); threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; } } - } catch (IncompatibleThreadStateException | IndexOutOfBoundsException ex) { + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | AbsentInformationException ex) { // ignore. } } @@ -319,8 +347,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (event instanceof MethodExitEvent) { MethodExitEvent methodExitEvent = (MethodExitEvent) event; long threadId = methodExitEvent.thread().uniqueID(); - if (threadId == threadState.threadId - && methodExitEvent.method().equals(threadState.stepLocation.method())) { + if (threadId == threadState.threadId && methodExitEvent.method().equals(threadState.stepLocation.method())) { Value returnValue = methodExitEvent.returnValue(); if (returnValue instanceof VoidValue) { context.getStepResultManager().removeMethodResult(threadId); @@ -338,26 +365,22 @@ private boolean isStepFiltersConfigured(StepFilters filters) { return false; } return ArrayUtils.isNotEmpty(filters.allowClasses) || ArrayUtils.isNotEmpty(filters.skipClasses) - || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors - || filters.skipStaticInitializers || filters.skipSynthetics; + || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors + || filters.skipStaticInitializers || filters.skipSynthetics; } /** - * Return true if the StepEvent's location is a Method that the user has - * indicated to filter. + * Return true if the StepEvent's location is a Method that the user has indicated to filter. * * @throws IncompatibleThreadStateException - * if the thread is not suspended in - * the target VM. + * if the thread is not suspended in the target VM. */ - private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, - IDebugAdapterContext context) + private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, IDebugAdapterContext context) throws IncompatibleThreadStateException { if (originalLocation == null || currentLocation == null) { return false; } - return !shouldFilterMethod(originalLocation.method(), context) - && shouldFilterMethod(currentLocation.method(), context); + return !shouldFilterMethod(originalLocation.method(), context) && shouldFilterMethod(currentLocation.method(), context); } private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) { @@ -387,7 +410,6 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL return false; } } - Method originalMethod = originalLocation.method(); Method currentMethod = currentLocation.method(); if (!originalMethod.equals(currentMethod)) { @@ -403,14 +425,12 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL * Return the top stack frame of the target thread. * * @param thread - * the target thread. + * the target thread. * @return the top frame. * @throws IncompatibleThreadStateException - * if the thread is not suspended in - * the target VM. + * if the thread is not suspended in the target VM. * @throws IndexOutOfBoundsException - * if the thread doesn't contain any - * stack frame. + * if the thread doesn't contain any stack frame. */ private StackFrame getTopFrame(ThreadReference thread) throws IncompatibleThreadStateException { return thread.frame(0); @@ -426,6 +446,7 @@ class ThreadState { Location stepLocation = null; Disposable eventSubscription = null; boolean targetStepIn = false; + MethodInvocation pendingTargetStepIn; public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); @@ -437,4 +458,27 @@ public void deleteStepRequest(EventRequestManager manager) { this.pendingStepRequest = null; } } + + static class LocationResponse { + Location location; + MethodInvocation methodInvocation; + + private LocationResponse(Location location, MethodInvocation methodInvocation) { + this.location = location; + this.methodInvocation = methodInvocation; + } + + public static LocationResponse waitFor(MethodInvocation invocation, Location location) { + return new LocationResponse(location, invocation); + } + + public static LocationResponse absolute(Location location) { + return new LocationResponse(location, null); + } + + boolean isLocationPending() { + return methodInvocation != null; + } + + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java index e30b0dd30..a6535715a 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -34,7 +34,14 @@ private BindingUtils() { public static String getMethodName(IMethodBinding binding, boolean fromKey) { if (fromKey) { String key = binding.getKey(); - return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + int dotAt = key.indexOf('.'); + int end = key.indexOf('<', dotAt); + if (end == -1) { + end = key.indexOf('('); + } else { + end = Math.min(end, key.indexOf('(')); + } + return key.substring(dotAt + 1, end); } else { return binding.getName(); } 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 eba9a42e6..eb3eb46f2 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 @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; @@ -51,6 +52,7 @@ import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; @@ -487,13 +489,23 @@ public List findMethodInvocations(StackFrame frame) { invocation.expression = expression.toString(); IMethodBinding binding = entry.getValue(); invocation.methodName = binding.getName(); - invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + if (binding.getDeclaringClass().isAnonymous()) { + ITypeBinding superclass = binding.getDeclaringClass().getSuperclass(); + if (superclass != null + && !superclass.isEqualTo(astUnit.getAST().resolveWellKnownType("java.lang.Object"))) { + invocation.declaringTypeName = superclass.getQualifiedName(); + } else { + return null; + } + } else { + invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + } invocation.methodSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); invocation.lineStart = astUnit.getLineNumber(expression.getStartPosition()); invocation.lineEnd = astUnit.getLineNumber(expression.getStartPosition() + expression.getLength()); invocation.columnStart = astUnit.getColumnNumber(expression.getStartPosition()); invocation.columnEnd = astUnit.getColumnNumber(expression.getStartPosition() + expression.getLength()); return invocation; - }).collect(Collectors.toList()); + }).filter(Objects::nonNull).collect(Collectors.toList()); } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java index ab6efdaa2..f0364d98b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -14,6 +14,7 @@ import java.util.Map; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; @@ -25,7 +26,7 @@ public class MethodInvocationLocator extends ASTVisitor { private CompilationUnit unit; private Map targets; - private boolean collectMethodInvocations = false; + private int expressionCount = 0; public MethodInvocationLocator(int line, CompilationUnit unit) { super(false); @@ -40,24 +41,36 @@ public boolean visit(ExpressionStatement node) { int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); if (line >= start && line <= end) { - collectMethodInvocations = true; + expressionCount++; } - return collectMethodInvocations; + return expressionCount > 0; } @Override public boolean visit(MethodInvocation node) { - int lineNumber = unit.getLineNumber(node.getStartPosition()); - if (lineNumber == this.line) { + if (expressionCount > 0) { targets.put(node, node.resolveMethodBinding()); - return true; } - return false; + return expressionCount > 0; + } + + @Override + public boolean visit(ClassInstanceCreation node) { + if (expressionCount > 0) { + targets.put(node, node.resolveConstructorBinding()); + expressionCount--; + } + return expressionCount > 0; } @Override public void endVisit(ExpressionStatement node) { - collectMethodInvocations = false; + expressionCount--; + } + + @Override + public void endVisit(ClassInstanceCreation node) { + expressionCount++; } public Map getTargets() { From a6ca87a0572d55ac9c289ba5a5a0f16318883a44 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 25 Oct 2022 17:17:32 +0800 Subject: [PATCH 121/206] Enable stepInTarget feature on more use cases --- .../core/adapter/ISourceLookUpProvider.java | 27 +- .../handler/StackTraceRequestHandler.java | 8 +- .../handler/StepInTargetsRequestHandler.java | 75 ++++- .../adapter/handler/StepRequestHandler.java | 276 +++++++++--------- .../variables/StackFrameReference.java | 12 +- .../microsoft/java/debug/BindingUtils.java | 7 +- .../internal/JdtSourceLookUpProvider.java | 83 +++--- .../internal/MethodInvocationLocator.java | 199 +++++++++++-- 8 files changed, 459 insertions(+), 228 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 5e8d94451..ead10e33c 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 @@ -18,8 +18,6 @@ import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; -import com.sun.jdi.StackFrame; - public interface ISourceLookUpProvider extends IProvider { boolean supportsRealtimeBreakpointVerification(); @@ -70,17 +68,19 @@ default String getJavaRuntimeVersion(String projectName) { * Return method invocation found in the statement as the given line number of * the source file. * - * @param stackframe The stack frame where the invocation must be searched. + * @param uri The source file where the invocation must be searched. + * @param line The line number where the invocation must be searched. * * @return List of found method invocation or empty if not method invocations * can be found. */ - List findMethodInvocations(StackFrame stackframe); + List findMethodInvocations(String uri, int line); public static class MethodInvocation { public String expression; public String methodName; public String methodSignature; + public String methodGenericSignature; public String declaringTypeName; public int lineStart; public int lineEnd; @@ -89,8 +89,8 @@ public static class MethodInvocation { @Override public int hashCode() { - return Objects.hash(columnEnd, columnStart, declaringTypeName, expression, lineEnd, lineStart, methodName, - methodSignature); + return Objects.hash(expression, methodName, methodSignature, methodGenericSignature, declaringTypeName, + lineStart, lineEnd, columnStart, columnEnd); } @Override @@ -98,18 +98,15 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (!(obj instanceof MethodInvocation)) { return false; } MethodInvocation other = (MethodInvocation) obj; - return columnEnd == other.columnEnd && columnStart == other.columnStart - && Objects.equals(declaringTypeName, other.declaringTypeName) - && Objects.equals(expression, other.expression) && lineEnd == other.lineEnd - && lineStart == other.lineStart && Objects.equals(methodName, other.methodName) - && Objects.equals(methodSignature, other.methodSignature); + return Objects.equals(expression, other.expression) && Objects.equals(methodName, other.methodName) + && Objects.equals(methodSignature, other.methodSignature) + && Objects.equals(methodGenericSignature, other.methodGenericSignature) + && Objects.equals(declaringTypeName, other.declaringTypeName) && lineStart == other.lineStart + && lineEnd == other.lineEnd && columnStart == other.columnStart && columnEnd == other.columnEnd; } } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index f22966b03..fbfce49ef 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -89,10 +89,12 @@ public CompletableFuture handle(Command command, Arguments arguments, StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count); List jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP()); for (int i = 0; i < count; i++) { - StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i); - int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); + StackFrameReference frameReference = new StackFrameReference(thread, stacktraceArgs.startFrame + i); + int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, frameReference); StackFrameInfo jdiFrame = jdiFrames.get(i); - result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context)); + Types.StackFrame lspFrame = convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context); + result.add(lspFrame); + frameReference.setSource(lspFrame.source); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java index f61d2a8b9..5b3b735fe 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java @@ -10,13 +10,19 @@ *******************************************************************************/ package com.microsoft.java.debug.core.adapter.handler; +import java.io.File; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; @@ -27,10 +33,14 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.StepInTargetsArguments; import com.microsoft.java.debug.core.protocol.Responses.StepInTargetsResponse; +import com.microsoft.java.debug.core.protocol.Types.Source; import com.microsoft.java.debug.core.protocol.Types.StepInTarget; +import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; public class StepInTargetsRequestHandler implements IDebugRequestHandler { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); @Override public List getTargetCommands() { @@ -45,37 +55,84 @@ public CompletableFuture handle(Command command, Arguments arguments, final int frameId = stepInTargetsArguments.frameId; return CompletableFuture.supplyAsync(() -> { response.body = new StepInTargetsResponse( - findFrame(frameId, context).map(f -> findTargets(f.thread().uniqueID(), f, context)) + findFrame(frameId, context).map(f -> findTargets(f, context)) .orElse(Collections.emptyList()).toArray(StepInTarget[]::new)); return response; }); } - private Optional findFrame(int frameId, IDebugAdapterContext context) { + private Optional findFrame(int frameId, IDebugAdapterContext context) { Object object = context.getRecyclableIdPool().getObjectById(frameId); if (object instanceof StackFrameReference) { - return Optional.of(context.getStackFrameManager().getStackFrame((StackFrameReference) object)); + return Optional.of((StackFrameReference) object); } return Optional.empty(); } - private List findTargets(long threadId, StackFrame stackframe, IDebugAdapterContext context) { + private List findTargets(StackFrameReference frameReference, IDebugAdapterContext context) { + StackFrame stackframe = context.getStackFrameManager().getStackFrame(frameReference); + if (stackframe == null) { + return Collections.emptyList(); + } + + Source source = frameReference.getSource() == null ? findSource(stackframe, context) : frameReference.getSource(); + if (source == null) { + return Collections.emptyList(); + } + + String sourceUri = AdapterUtils.convertPath(source.path, AdapterUtils.isUri(source.path), true); + if (sourceUri == null) { + return Collections.emptyList(); + } + ISourceLookUpProvider sourceLookUpProvider = context.getProvider(ISourceLookUpProvider.class); - List invocations = sourceLookUpProvider.findMethodInvocations(stackframe); + List invocations = sourceLookUpProvider.findMethodInvocations(sourceUri, stackframe.location().lineNumber()); if (invocations.isEmpty()) { return Collections.emptyList(); } + long threadId = stackframe.thread().uniqueID(); List targets = new ArrayList<>(invocations.size()); for (MethodInvocation methodInvocation : invocations) { int id = context.getRecyclableIdPool().addObject(threadId, methodInvocation); StepInTarget target = new StepInTarget(id, methodInvocation.expression); - target.column = methodInvocation.columnStart; - target.endColumn = methodInvocation.columnEnd; - target.line = methodInvocation.lineStart; - target.endLine = methodInvocation.lineEnd; + target.column = AdapterUtils.convertColumnNumber(methodInvocation.columnStart, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + target.endColumn = AdapterUtils.convertColumnNumber(methodInvocation.columnEnd, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + target.line = AdapterUtils.convertLineNumber(methodInvocation.lineStart, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + target.endLine = AdapterUtils.convertLineNumber(methodInvocation.lineEnd, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); targets.add(target); } + + // TODO remove the executed method calls. return targets; } + + private Source findSource(StackFrame frame, IDebugAdapterContext context) { + ReferenceType declaringType = frame.location().declaringType(); + String typeName = declaringType.name(); + String sourceName = null; + String sourcePath = null; + try { + // When the .class file doesn't contain source information in meta data, + // invoking ReferenceType#sourceName() would throw AbsentInformationException. + sourceName = declaringType.sourceName(); + sourcePath = declaringType.sourcePaths(null).get(0); + } catch (AbsentInformationException e) { + String enclosingType = AdapterUtils.parseEnclosingType(typeName); + sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java"; + sourcePath = enclosingType.replace('.', File.separatorChar) + ".java"; + } + + try { + return StackTraceRequestHandler.convertDebuggerSourceToClient(typeName, sourceName, sourcePath, context); + } catch (URISyntaxException e) { + logger.log(Level.SEVERE, "Failed to resolve the source info of the stack frame.", e); + } + + return null; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 660d4bbdd..bd324a7fa 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -15,7 +15,6 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -39,8 +38,9 @@ import com.microsoft.java.debug.core.protocol.Requests.StepArguments; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; import com.microsoft.java.debug.core.protocol.Requests.StepInArguments; -import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.ClassType; import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.InterfaceType; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; @@ -48,7 +48,6 @@ import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; -import com.sun.jdi.VirtualMachine; import com.sun.jdi.VoidValue; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; @@ -102,11 +101,8 @@ public CompletableFuture handle(Command command, Arguments arguments, }); if (command == Command.STEPIN) { - String[] allowedClasses = threadState.pendingTargetStepIn != null - ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} - : context.getStepFilters().allowClasses; threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - allowedClasses, + context.getStepFilters().allowClasses, context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, @@ -119,26 +115,29 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - LocationResponse locationResponse = resolveLocation(thread, targetId, context); + threadState.targetStepIn = targetId > 0 + ? (MethodInvocation) context.getRecyclableIdPool().getObjectById(targetId) : null; if (context.asyncJDWP()) { List> futures = new ArrayList<>(); futures.add(AsyncJdwpUtils.runAsync(() -> { // JDWP Command: TR_FRAMES - threadState.stepLocation = locationResponse.location; - threadState.pendingTargetStepIn = locationResponse.methodInvocation; - threadState.targetStepIn = targetId > 0; - threadState.stepLocation = threadState.topFrame.location(); - threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); - if (targetThread.virtualMachine().canUseInstanceFilters()) { - try { - // JDWP Command: SF_THIS_OBJECT - ObjectReference thisObject = threadState.topFrame.thisObject(); - if (thisObject != null) { - threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + try { + threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + // JDWP Command: SF_THIS_OBJECT + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore } - } catch (Exception e) { - // ignore } + } catch (IncompatibleThreadStateException e1) { + throw new CompletionException(e1); } })); futures.add(AsyncJdwpUtils.runAsync( @@ -170,10 +169,8 @@ public CompletableFuture handle(Command command, Arguments arguments, // JDWP Command: ER_SET threadState.pendingMethodExitRequest.enable(); } else { + threadState.topFrame = getTopFrame(targetThread); threadState.stackDepth = targetThread.frameCount(); - threadState.stepLocation = locationResponse.location; - threadState.pendingTargetStepIn = locationResponse.methodInvocation; - threadState.targetStepIn = targetId > 0; threadState.stepLocation = threadState.topFrame.location(); threadState.pendingMethodExitRequest.addThreadFilter(thread); threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); @@ -224,57 +221,6 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } - private LocationResponse resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) - throws IncompatibleThreadStateException, AbsentInformationException { - if (targetId > 0) { - Object value = context.getRecyclableIdPool().getObjectById(targetId); - if (value instanceof MethodInvocation) { - return resolveLocation((MethodInvocation) value, thread); - } - } - return LocationResponse.absolute(getTopFrame(thread).location()); - } - - private LocationResponse resolveLocation(MethodInvocation invocation, ThreadReference thread) - throws AbsentInformationException, IncompatibleThreadStateException { - VirtualMachine vm = thread.virtualMachine(); - List refTypes = vm.classesByName(invocation.declaringTypeName); - - // if class is not yet loaded try to make the debugger step in until class is - // loaded. - if (refTypes.isEmpty()) { - return LocationResponse.waitFor(invocation, getTopFrame(thread).location()); - } else { - for (ReferenceType referenceType : refTypes) { - Optional location = referenceType.allLineLocations().stream() - .filter(l -> matchesLocation(l, invocation)) - .findFirst(); - if (location.isPresent()) { - return LocationResponse.absolute(location.get()); - } - } - return LocationResponse.absolute(getTopFrame(thread).location()); - } - } - - private boolean matchesLocation(Location l, MethodInvocation invocation) { - Method method = l.method(); - return method != null && Objects.equals(fixedName(method), invocation.methodName) - && Objects.equals(method.signature(), invocation.methodSignature); - } - - /* - * Fix the name so for constructors we return empty to match the captured AST - * invocations. - */ - private String fixedName(Method method) { - String name = method.name(); - if ("".equals(name)) { - return ""; - } - return name; - } - private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; @@ -283,7 +229,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // When a breakpoint occurs, abort any pending step requests from the same thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { // if we have a pending target step in then ignore and continue. - if (threadState.pendingTargetStepIn != null) { + if (threadState.targetStepIn != null) { debugEvent.shouldResume = true; return; } @@ -299,41 +245,62 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); + long threadId = thread.uniqueID(); threadState.deleteStepRequest(eventRequestManager); - if (isStepFiltersConfigured(context.getStepFilters()) || threadState.targetStepIn) { + if (isStepFiltersConfigured(context.getStepFilters()) || threadState.targetStepIn != null) { try { - if (threadState.pendingStepType == Command.STEPIN) { + if (threadState.pendingStepType == Command.STEPIN || threadState.targetStepIn != null) { int currentStackDepth = thread.frameCount(); - Location currentStepLocation = getTopFrame(thread).location(); - - // if we are in targetStepIn where the location was pending, then try to resolve - // the location and use it. - if (threadState.pendingTargetStepIn != null) { - LocationResponse newLocation = resolveLocation(threadState.pendingTargetStepIn, thread); - if (!newLocation.isLocationPending()) { - threadState.stepLocation = newLocation.location; - threadState.pendingTargetStepIn = null; + StackFrame topFrame = getTopFrame(thread); + Location currentStepLocation = topFrame.location(); + if (threadState.targetStepIn != null) { + if (isStoppedAtSelectedMethod(topFrame, threadState.targetStepIn)) { + // hit: send StoppedEvent + } else { + if (currentStackDepth > threadState.stackDepth) { + context.getStepResultManager().removeMethodResult(threadId); + threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); + threadState.pendingStepRequest.enable(); + debugEvent.shouldResume = true; + return; + } else if (currentStackDepth == threadState.stackDepth) { + // If the ending step location is same as the original location where the step into operation is originated, + // do another step of the same kind. + if (isSameLocation(currentStepLocation, threadState.stepLocation)) { + context.getStepResultManager().removeMethodResult(threadId); + threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); + threadState.pendingStepRequest.enable(); + debugEvent.shouldResume = true; + return; + } + } } - } - - // If the ending step location is filtered, or same as the original location where the step into operation is originated, - // do another step of the same kind. - if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) + } else if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, - currentStackDepth, currentStepLocation, threadState.targetStepIn) - || threadState.pendingTargetStepIn != null) { - String[] allowedClasses = threadState.pendingTargetStepIn != null - ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} - : context.getStepFilters().allowClasses; - threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, + currentStackDepth, currentStepLocation)) { + // If the ending step location is filtered, or same as the original location where the step into operation is originated, + // do another step of the same kind. + context.getStepResultManager().removeMethodResult(threadId); + String[] allowedClasses = context.getStepFilters().allowClasses; + if (currentStackDepth > threadState.stackDepth) { + threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, allowedClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().skipClasses); + } else { + threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, + allowedClasses, + context.getStepFilters().skipClasses); + } threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; } } - } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | AbsentInformationException ex) { + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException ex) { // ignore. } } @@ -360,6 +327,56 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } } + private boolean isStoppedAtSelectedMethod(StackFrame frame, MethodInvocation selectedMethod) { + Method method = frame.location().method(); + if (method != null + && Objects.equals(method.name(), selectedMethod.methodName) + && (Objects.equals(method.signature(), selectedMethod.methodSignature) + || Objects.equals(method.genericSignature(), selectedMethod.methodGenericSignature))) { + ObjectReference thisObject = frame.thisObject(); + ReferenceType currentType = (thisObject == null) ? method.declaringType() : thisObject.referenceType(); + if ("java.lang.Object".equals(selectedMethod.declaringTypeName)) { + return true; + } + + return isSubType(currentType, selectedMethod.declaringTypeName); + } + + return false; + } + + private boolean isSubType(ReferenceType currentType, String baseType) { + if (baseType.equals(currentType.name())) { + return true; + } + + if (currentType instanceof ClassType) { + ClassType classType = (ClassType) currentType; + ClassType superClassType = classType.superclass(); + if (superClassType != null && isSubType(superClassType, baseType)) { + return true; + } + + List interfaces = classType.allInterfaces(); + for (InterfaceType iface : interfaces) { + if (isSubType(iface, baseType)) { + return true; + } + } + } + + if (currentType instanceof InterfaceType) { + List superInterfaces = ((InterfaceType) currentType).superinterfaces(); + for (InterfaceType superInterface : superInterfaces) { + if (isSubType(superInterface, baseType)) { + return true; + } + } + } + + return false; + } + private boolean isStepFiltersConfigured(StepFilters filters) { if (filters == null) { return false; @@ -400,25 +417,36 @@ private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) * the target VM. */ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, - Location currentLocation, boolean targetStepIn) + Location currentLocation) throws IncompatibleThreadStateException { - if (!targetStepIn) { - if (originalStackDepth != currentStackDepth) { - return false; - } - if (originalLocation == null) { - return false; - } + if (originalStackDepth != currentStackDepth) { + return false; + } + if (originalLocation == null) { + return false; } + Method originalMethod = originalLocation.method(); Method currentMethod = currentLocation.method(); if (!originalMethod.equals(currentMethod)) { - return targetStepIn; + return false; } if (originalLocation.lineNumber() != currentLocation.lineNumber()) { - return targetStepIn; + return false; } - return !targetStepIn; + + return true; + } + + private boolean isSameLocation(Location original, Location current) { + if (original == null || current == null) { + return false; + } + + Method originalMethod = original.method(); + Method currentMethod = current.method(); + return originalMethod.equals(currentMethod) + && original.lineNumber() == current.lineNumber(); } /** @@ -445,8 +473,7 @@ class ThreadState { StackFrame topFrame = null; Location stepLocation = null; Disposable eventSubscription = null; - boolean targetStepIn = false; - MethodInvocation pendingTargetStepIn; + MethodInvocation targetStepIn = null; public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); @@ -458,27 +485,4 @@ public void deleteStepRequest(EventRequestManager manager) { this.pendingStepRequest = null; } } - - static class LocationResponse { - Location location; - MethodInvocation methodInvocation; - - private LocationResponse(Location location, MethodInvocation methodInvocation) { - this.location = location; - this.methodInvocation = methodInvocation; - } - - public static LocationResponse waitFor(MethodInvocation invocation, Location location) { - return new LocationResponse(location, invocation); - } - - public static LocationResponse absolute(Location location) { - return new LocationResponse(location, null); - } - - boolean isLocationPending() { - return methodInvocation != null; - } - - } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java index 4dc895007..46726cde8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -11,12 +11,14 @@ package com.microsoft.java.debug.core.adapter.variables; +import com.microsoft.java.debug.core.protocol.Types.Source; import com.sun.jdi.ThreadReference; public class StackFrameReference { private final int depth; private final int hash; private final ThreadReference thread; + private Source source; /** * Create a wrapper of JDI stackframe to keep the immutable properties of a stackframe, IStackFrameManager will use @@ -48,6 +50,14 @@ public ThreadReference getThread() { return thread; } + public Source getSource() { + return source; + } + + public void setSource(Source source) { + this.source = source; + } + @Override public int hashCode() { return hash; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java index a6535715a..b696be049 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -61,12 +61,9 @@ public static String toSignature(IMethodBinding binding, String name) { // use key for now until JDT core provides a public API for this. // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" - if (!binding.getName().equals(name)) { - throw new IllegalArgumentException("The method name and binding method name doesn't match."); - } - String signatureString = binding.getKey(); if (signatureString != null) { + name = "." + name; int index = signatureString.indexOf(name); if (index > -1) { int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); 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 eb3eb46f2..c4d4fe318 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 @@ -15,7 +15,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; @@ -35,28 +34,31 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.URIUtil; 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; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.LambdaExpression; -import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import org.eclipse.jdt.ls.core.internal.JDTUtils; import com.microsoft.java.debug.BindingUtils; import com.microsoft.java.debug.BreakpointLocationLocator; @@ -71,8 +73,6 @@ import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; -import com.sun.jdi.StackFrame; - public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; @@ -452,60 +452,71 @@ private static String resolveSystemLibraryVersion(IJavaProject project, IVMInsta return null; } - @Override - public List findMethodInvocations(StackFrame frame) { - if (frame == null) { - throw new IllegalArgumentException("frame is null"); - } - - IJavaProject project = JdtUtils.findProject(frame, getSourceContainers()); - if (project == null) { - logger.log(Level.WARNING, - String.format("Failed to resolve project for the frame: %s", frame)); + public List findMethodInvocations(String uri, int line) { + if (uri == null) { return Collections.emptyList(); } - String uri; - try { - IType type = project.findType(JdtUtils.getDeclaringTypeName(frame)); - uri = type.getResource().getLocationURI().toURL().toString(); - } catch (JavaModelException | DebugException | MalformedURLException e) { - logger.log(Level.SEVERE, - String.format("Failed to resolve type for the frame: %s", frame)); - return Collections.emptyList(); + boolean useCache = false; + CompilationUnit cachedUnit = CoreASTProvider.getInstance().getCachedAST(); + if (cachedUnit != null) { + ITypeRoot cachedElement = cachedUnit.getTypeRoot(); + if (cachedElement != null && isSameURI(JDTUtils.toUri(cachedElement), uri)) { + useCache = true; + } } - CompilationUnit astUnit = asCompilationUnit(uri); + final CompilationUnit astUnit = useCache ? cachedUnit : asCompilationUnit(uri); if (astUnit == null) { return Collections.emptyList(); } - MethodInvocationLocator locator = new MethodInvocationLocator(frame.location().lineNumber(), astUnit); + MethodInvocationLocator locator = new MethodInvocationLocator(line, astUnit); astUnit.accept(locator); return locator.getTargets().entrySet().stream().map(entry -> { MethodInvocation invocation = new MethodInvocation(); - Expression expression = entry.getKey(); - invocation.expression = expression.toString(); - IMethodBinding binding = entry.getValue(); - invocation.methodName = binding.getName(); + ASTNode astNode = entry.getKey(); + invocation.expression = astNode.toString(); + IMethodBinding binding = entry.getValue().getMethodDeclaration(); + invocation.methodName = binding.isConstructor() ? "" : binding.getName(); if (binding.getDeclaringClass().isAnonymous()) { ITypeBinding superclass = binding.getDeclaringClass().getSuperclass(); if (superclass != null && !superclass.isEqualTo(astUnit.getAST().resolveWellKnownType("java.lang.Object"))) { - invocation.declaringTypeName = superclass.getQualifiedName(); + invocation.declaringTypeName = superclass.getBinaryName(); } else { return null; } } else { - invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + // Keep consistent with JDI since JDI uses binary class name + invocation.declaringTypeName = binding.getDeclaringClass().getBinaryName(); } - invocation.methodSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); - invocation.lineStart = astUnit.getLineNumber(expression.getStartPosition()); - invocation.lineEnd = astUnit.getLineNumber(expression.getStartPosition() + expression.getLength()); - invocation.columnStart = astUnit.getColumnNumber(expression.getStartPosition()); - invocation.columnEnd = astUnit.getColumnNumber(expression.getStartPosition() + expression.getLength()); + invocation.methodGenericSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); + invocation.methodSignature = Signature.getTypeErasure(invocation.methodGenericSignature); + int startOffset = astNode.getStartPosition(); + if (astNode instanceof org.eclipse.jdt.core.dom.MethodInvocation) { + // The range covered by the stepIn target should start with the method name. + startOffset = ((org.eclipse.jdt.core.dom.MethodInvocation) astNode).getName().getStartPosition(); + } + invocation.lineStart = astUnit.getLineNumber(startOffset); + invocation.columnStart = astUnit.getColumnNumber(startOffset); + int endOffset = astNode.getStartPosition() + astNode.getLength(); + invocation.lineEnd = astUnit.getLineNumber(endOffset); + invocation.columnEnd = astUnit.getColumnNumber(endOffset); return invocation; }).filter(Objects::nonNull).collect(Collectors.toList()); } + + private boolean isSameURI(String uri1, String uri2) { + if (Objects.equals(uri1, uri2)) { + return true; + } + + try { + return URIUtil.sameURI(new URI(uri1), new URI(uri2)); + } catch (URISyntaxException e) { + return false; + } + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java index f0364d98b..a25fbc3f6 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -13,20 +13,44 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; +import org.eclipse.jdt.core.dom.AssertStatement; +import org.eclipse.jdt.core.dom.BreakStatement; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ConstructorInvocation; +import org.eclipse.jdt.core.dom.ContinueStatement; +import org.eclipse.jdt.core.dom.DoStatement; +import org.eclipse.jdt.core.dom.EmptyStatement; +import org.eclipse.jdt.core.dom.EnhancedForStatement; +import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.IfStatement; +import org.eclipse.jdt.core.dom.LabeledStatement; +import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.ReturnStatement; +import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.SwitchStatement; +import org.eclipse.jdt.core.dom.SynchronizedStatement; +import org.eclipse.jdt.core.dom.ThrowStatement; +import org.eclipse.jdt.core.dom.TryStatement; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.TypeDeclarationStatement; +import org.eclipse.jdt.core.dom.VariableDeclarationStatement; +import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.dom.YieldStatement; public class MethodInvocationLocator extends ASTVisitor { private int line; private CompilationUnit unit; - private Map targets; - - private int expressionCount = 0; + private Map targets; public MethodInvocationLocator(int line, CompilationUnit unit) { super(false); @@ -35,45 +59,174 @@ public MethodInvocationLocator(int line, CompilationUnit unit) { this.targets = new HashMap<>(); } + @Override + public boolean visit(FieldDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(MethodDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(TypeDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(EnumDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(VariableDeclarationStatement node) { + return shouldVisitNode(node); + } + @Override public boolean visit(ExpressionStatement node) { - int start = unit.getLineNumber(node.getStartPosition()); - int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + return shouldVisitNode(node); + } + + @Override + public boolean visit(AssertStatement node) { + return shouldVisitNode(node); - if (line >= start && line <= end) { - expressionCount++; - } - return expressionCount > 0; } @Override - public boolean visit(MethodInvocation node) { - if (expressionCount > 0) { - targets.put(node, node.resolveMethodBinding()); + public boolean visit(BreakStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ContinueStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(DoStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(EmptyStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(EnhancedForStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ForStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(IfStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(LabeledStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ReturnStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(SwitchStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(SynchronizedStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ThrowStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(TryStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(TypeDeclarationStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(WhileStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(YieldStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ConstructorInvocation node) { + if (shouldVisitNode(node)) { + targets.put(node, node.resolveConstructorBinding()); + return true; } - return expressionCount > 0; + return false; } @Override - public boolean visit(ClassInstanceCreation node) { - if (expressionCount > 0) { + public boolean visit(SuperConstructorInvocation node) { + if (shouldVisitNode(node)) { targets.put(node, node.resolveConstructorBinding()); - expressionCount--; + return true; } - return expressionCount > 0; + return false; } @Override - public void endVisit(ExpressionStatement node) { - expressionCount--; + public boolean visit(MethodInvocation node) { + targets.put(node, node.resolveMethodBinding()); + return true; } @Override - public void endVisit(ClassInstanceCreation node) { - expressionCount++; + public boolean visit(ClassInstanceCreation node) { + targets.put(node, node.resolveConstructorBinding()); + return true; + } + + private boolean shouldVisitNode(ASTNode node) { + int start = unit.getLineNumber(node.getStartPosition()); + int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + + if (line >= start && line <= end) { + return true; + } + + return false; } - public Map getTargets() { + public Map getTargets() { return targets; } } From ec39583b4b5ae79c0c220e4300ede4be5b87c928 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 31 Oct 2022 14:56:22 +0800 Subject: [PATCH 122/206] Conditional Breakpoint got error code in reply:504 (#453) --- .../debug/core/adapter/IStackFrameManager.java | 16 +++++++++++++++- .../debug/core/adapter/StackFrameManager.java | 16 +++++++++++++++- .../adapter/handler/ThreadsRequestHandler.java | 4 ++++ .../internal/eval/JdtEvaluationProvider.java | 2 +- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java index abce2ff3e..8d6c74486 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 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 @@ -32,6 +32,15 @@ public interface IStackFrameManager { */ StackFrame[] reloadStackFrames(ThreadReference thread); + /** + * Refresh all stackframes from jdi thread. + * + * @param thread the jdi thread + * @param force Whether to load the whole frames if the thread's stackframes haven't been cached. + * @return all the stackframes in the specified thread + */ + StackFrame[] reloadStackFrames(ThreadReference thread, boolean force); + /** * Refersh the stackframes starting from the specified depth and length. * @@ -48,4 +57,9 @@ public interface IStackFrameManager { * @param thread the jdi thread */ void clearStackFrames(ThreadReference thread); + + /** + * Clear the whole stackframes cache. + */ + void clearStackFrames(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java index 2a9d1e47e..518cc7761 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java @@ -32,10 +32,19 @@ public synchronized StackFrame getStackFrame(StackFrameReference ref) { @Override public synchronized StackFrame[] reloadStackFrames(ThreadReference thread) { + return reloadStackFrames(thread, true); + } + + @Override + public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, boolean force) { return threadStackFrameMap.compute(thread.uniqueID(), (key, old) -> { try { if (old == null || old.length == 0) { - return thread.frames().toArray(new StackFrame[0]); + if (force) { + return thread.frames().toArray(new StackFrame[0]); + } else { + return new StackFrame[0]; + } } else { return thread.frames(0, old.length).toArray(new StackFrame[0]); } @@ -71,4 +80,9 @@ public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, int s public synchronized void clearStackFrames(ThreadReference thread) { threadStackFrameMap.remove(thread.uniqueID()); } + + @Override + public synchronized void clearStackFrames() { + threadStackFrameMap.clear(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index 7d404691c..fceac73a5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -159,11 +159,13 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; DebugUtility.resumeThread(thread); + context.getStackFrameManager().clearStackFrames(thread); checkThreadRunningAndRecycleIds(thread, context); } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); resumeVM(context); + context.getStackFrameManager().clearStackFrames(); context.getRecyclableIdPool().removeAllObjects(); } response.body = new Responses.ContinueResponseBody(allThreadsContinued); @@ -175,6 +177,7 @@ private CompletableFuture resumeAll(Requests.ThreadOperationArguments context.getExceptionManager().removeAllExceptions(); resumeVM(context); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); + context.getStackFrameManager().clearStackFrames(); context.getRecyclableIdPool().removeAllObjects(); return CompletableFuture.completedFuture(response); } @@ -280,6 +283,7 @@ private void resumeThread(ThreadReference thread, IDebugAdapterContext context) context.getExceptionManager().removeException(threadId); DebugUtility.resumeThread(thread, suspends); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); + context.getStackFrameManager().clearStackFrames(thread); checkThreadRunningAndRecycleIds(thread, context); } } catch (ObjectCollectedException ex) { diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index d4fac9054..a48cf7ead 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -317,7 +317,7 @@ private JDIThread getMockJDIThread(ThreadReference thread) { @Override protected synchronized void invokeComplete(int restoreTimeout) { super.invokeComplete(restoreTimeout); - context.getStackFrameManager().reloadStackFrames(thread); + context.getStackFrameManager().reloadStackFrames(thread, false); } }); } From fbf15977219a45c2e0f3c3317a5dd422f627a253 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 31 Oct 2022 17:26:56 +0800 Subject: [PATCH 123/206] Bump version to 0.42.0 (#454) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 8bb9b9f47..d9fb24c2e 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.41.0 + 0.42.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 21e32bbde..0858d90eb 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.41.0 +Bundle-Version: 0.42.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.41.0.jar + lib/com.microsoft.java.debug.core-0.42.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 949512717..c1b4144fa 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.41.0 + 0.42.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.41.0 + 0.42.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 27c76516b..ff39c1d7f 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index b8524fa90..c5a1f46a0 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.41.0 + 0.42.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4d31432d9..2adb8badf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.41.0 + 0.42.0 pom Java Debug Server for Visual Studio Code From 4b773bd9b1891dbadd93a0e995656b575c80213a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 8 Nov 2022 18:29:58 +0800 Subject: [PATCH 124/206] Fix version info in .classpath (#456) --- com.microsoft.java.debug.plugin/.classpath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 349cb7844..43f07f603 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + From de77ff37cb5e3a9301e4dc83ae47af211bd59be5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 15 Nov 2022 10:29:11 +0800 Subject: [PATCH 125/206] "shortenCommandLine": "argfile|auto" should include "vmArgs" (#457) --- .../debug/core/adapter/handler/LaunchRequestHandler.java | 4 ++-- .../java/debug/core/adapter/handler/LaunchUtils.java | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index e5b580f50..e99706bda 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -134,8 +134,8 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { - Path tempfile = LaunchUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); - launchArguments.vmArgs += " \"@" + tempfile.toAbsolutePath().toString() + "\""; + Path tempfile = LaunchUtils.generateArgfile(launchArguments.vmArgs, launchArguments.classPaths, launchArguments.modulePaths); + launchArguments.vmArgs = " \"@" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; launchArguments.modulePaths = new String[0]; context.setArgsfile(tempfile); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index ca7d69a99..3be6fcbac 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -40,6 +40,7 @@ import com.microsoft.java.debug.core.adapter.AdapterUtils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; public class LaunchUtils { @@ -81,10 +82,14 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws * @return the file path of the generated argfile * @throws IOException Some errors occur during generating the argfile */ - public static synchronized Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { + public static synchronized Path generateArgfile(String vmArgs, String[] classPaths, String[] modulePaths) throws IOException { String argfile = ""; + if (StringUtils.isNotBlank(vmArgs)) { + argfile += vmArgs; + } + if (ArrayUtils.isNotEmpty(classPaths)) { - argfile = "-cp \"" + String.join(File.pathSeparator, classPaths) + "\""; + argfile += " -cp \"" + String.join(File.pathSeparator, classPaths) + "\""; } if (ArrayUtils.isNotEmpty(modulePaths)) { From 791cd97260f204e2cb851e79287881f6d634c1b4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 16 Nov 2022 17:03:13 +0800 Subject: [PATCH 126/206] Use the system encoding to generate the *.argfile (#458) --- .../adapter/handler/LaunchRequestHandler.java | 52 ++++++++++++++++--- .../core/adapter/handler/LaunchUtils.java | 40 ++++++++++++-- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index e99706bda..b1daf1b01 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018-2021 Microsoft Corporation and others. +* Copyright (c) 2018-2022 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 @@ -16,6 +16,7 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -134,11 +135,50 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { - Path tempfile = LaunchUtils.generateArgfile(launchArguments.vmArgs, launchArguments.classPaths, launchArguments.modulePaths); - launchArguments.vmArgs = " \"@" + tempfile.toAbsolutePath().toString() + "\""; - launchArguments.classPaths = new String[0]; - launchArguments.modulePaths = new String[0]; - context.setArgsfile(tempfile); + /** + * See the JDK spec https://docs.oracle.com/en/java/javase/18/docs/specs/man/java.html#java-command-line-argument-files. + * The argument file must contain only ASCII characters or characters in system default encoding that's ASCII friendly. + */ + Charset systemCharset = LaunchUtils.getSystemCharset(); + CharsetEncoder encoder = systemCharset.newEncoder(); + String vmArgsForShorten = null; + String[] classPathsForShorten = null; + String[] modulePathsForShorten = null; + if (StringUtils.isNotBlank(launchArguments.vmArgs)) { + if (!encoder.canEncode(launchArguments.vmArgs)) { + logger.warning(String.format("Cannot generate the 'vmArgs' argument into the argfile because it contains characters " + + "that cannot be encoded in the system charset '%s'.", systemCharset.displayName())); + } else { + vmArgsForShorten = launchArguments.vmArgs; + } + } + + if (ArrayUtils.isNotEmpty(launchArguments.classPaths)) { + if (!encoder.canEncode(String.join(File.pathSeparator, launchArguments.classPaths))) { + logger.warning(String.format("Cannot generate the '-cp' argument into the argfile because it contains characters " + + "that cannot be encoded in the system charset '%s'.", systemCharset.displayName())); + } else { + classPathsForShorten = launchArguments.classPaths; + } + } + + if (ArrayUtils.isNotEmpty(launchArguments.modulePaths)) { + if (!encoder.canEncode(String.join(File.pathSeparator, launchArguments.modulePaths))) { + logger.warning(String.format("Cannot generate the '--module-path' argument into the argfile because it contains characters " + + "that cannot be encoded in the system charset '%s'.", systemCharset.displayName())); + } else { + modulePathsForShorten = launchArguments.modulePaths; + } + } + + if (vmArgsForShorten != null || classPathsForShorten != null || modulePathsForShorten != null) { + Path tempfile = LaunchUtils.generateArgfile(vmArgsForShorten, classPathsForShorten, modulePathsForShorten, systemCharset); + launchArguments.vmArgs = (vmArgsForShorten == null ? launchArguments.vmArgs : "") + + " \"@" + tempfile.toAbsolutePath().toString() + "\""; + launchArguments.classPaths = (classPathsForShorten == null ? launchArguments.classPaths : new String[0]); + launchArguments.modulePaths = (modulePathsForShorten == null ? launchArguments.modulePaths : new String[0]); + context.setArgsfile(tempfile); + } } catch (IOException e) { logger.log(Level.SEVERE, String.format("Failed to create a temp argfile: %s", e.toString()), e); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 3be6fcbac..27fdb1813 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.math.BigInteger; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -36,16 +37,45 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -import com.microsoft.java.debug.core.Configuration; -import com.microsoft.java.debug.core.adapter.AdapterUtils; - import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.adapter.AdapterUtils; + public class LaunchUtils { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static Set tempFilesInUse = new HashSet<>(); + private static final Charset SYSTEM_CHARSET; + + static { + Charset result = null; + try { + // JEP 400: Java 17+ populates this system property. + String encoding = System.getProperty("native.encoding"); //$NON-NLS-1$ + if (encoding != null && !encoding.isBlank()) { + result = Charset.forName(encoding); + } else { + // JVM internal property, works on older JVM's too + encoding = System.getProperty("sun.jnu.encoding"); //$NON-NLS-1$ + if (encoding != null && !encoding.isBlank()) { + result = Charset.forName(encoding); + } + } + } catch (Exception e) { + logger.log(Level.SEVERE, "Error occurs during resolving system encoding", e); + } + if (result == null) { + // This is always UTF-8 on Java >= 18. + result = Charset.defaultCharset(); + } + SYSTEM_CHARSET = result; + } + + public static Charset getSystemCharset() { + return SYSTEM_CHARSET; + } /** * Generate the classpath parameters to a temporary classpath.jar. @@ -82,7 +112,7 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws * @return the file path of the generated argfile * @throws IOException Some errors occur during generating the argfile */ - public static synchronized Path generateArgfile(String vmArgs, String[] classPaths, String[] modulePaths) throws IOException { + public static synchronized Path generateArgfile(String vmArgs, String[] classPaths, String[] modulePaths, Charset encoding) throws IOException { String argfile = ""; if (StringUtils.isNotBlank(vmArgs)) { argfile += vmArgs; @@ -100,7 +130,7 @@ public static synchronized Path generateArgfile(String vmArgs, String[] classPat String baseName = "cp_" + getMd5(argfile); cleanupTempFiles(baseName, ".argfile"); Path tempfile = createTempFile(baseName, ".argfile"); - Files.write(tempfile, argfile.getBytes()); + Files.writeString(tempfile, argfile, encoding); lockTempLaunchFile(tempfile); return tempfile; From 4384d854bc60a9fffd95b90131f0ca1e98f79cfe Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 21 Nov 2022 10:08:17 +0800 Subject: [PATCH 127/206] Only pop-up build errors notification when the errors are on the running project's classpath (#459) --- .../java/debug/plugin/internal/Compile.java | 145 ++++++++++++++++++ .../JavaDebugDelegateCommandHandler.java | 10 +- 2 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java new file mode 100644 index 000000000..415456ce5 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2022 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package com.microsoft.java.debug.plugin.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; +import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; + +import com.microsoft.java.debug.core.Configuration; + +public class Compile { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + + public static BuildWorkspaceStatus compile(CompileParams params, IProgressMonitor monitor) { + try { + if (monitor.isCanceled()) { + return BuildWorkspaceStatus.CANCELLED; + } + + long compileAt = System.currentTimeMillis(); + if (params != null && params.isFullBuild()) { + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor); + } else { + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor); + } + logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); + + IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); + IResource currentResource = mainProject; + if (isUnmanagedFolder(mainProject) && StringUtils.isNotBlank(params.getMainClass())) { + IType mainType = ProjectUtils.getJavaProject(mainProject).findType(params.getMainClass()); + if (mainType != null && mainType.getResource() != null) { + currentResource = mainType.getResource(); + } + } + + List problemMarkers = new ArrayList<>(); + if (currentResource != null) { + List markers = ResourceUtils.getErrorMarkers(currentResource); + if (markers != null) { + problemMarkers.addAll(markers); + } + + // Check if the referenced projects contain compilation errors. + if (currentResource instanceof IProject && ProjectUtils.isJavaProject((IProject) currentResource)) { + IJavaProject currentJavaProject = ProjectUtils.getJavaProject((IProject) currentResource); + IJavaProject[] javaProjects = ProjectUtils.getJavaProjects(); + for (IJavaProject otherJavaProject : javaProjects) { + IProject other = otherJavaProject.getProject(); + if (!other.equals(getDefaultProject()) && !other.equals((IProject) currentResource) + && currentJavaProject.isOnClasspath(otherJavaProject)) { + markers = ResourceUtils.getErrorMarkers(other); + if (markers != null) { + problemMarkers.addAll(markers); + } + } + } + } + } else { + IJavaProject[] javaProjects = ProjectUtils.getJavaProjects(); + for (IJavaProject javaProject : javaProjects) { + IProject project = javaProject.getProject(); + if (!project.equals(getDefaultProject())) { + List markers = ResourceUtils.getErrorMarkers(project); + if (markers != null) { + problemMarkers.addAll(markers); + } + } + } + } + + if (problemMarkers.isEmpty()) { + return BuildWorkspaceStatus.SUCCEED; + } + + return BuildWorkspaceStatus.WITH_ERROR; + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Failed to build workspace.", e); + return BuildWorkspaceStatus.FAILED; + } catch (OperationCanceledException e) { + return BuildWorkspaceStatus.CANCELLED; + } + } + + private static boolean isUnmanagedFolder(IProject project) { + return project != null && ProjectUtils.isUnmanagedFolder(project) + && ProjectUtils.isJavaProject(project); + } + + private static IProject getDefaultProject() { + return getWorkspaceRoot().getProject(ProjectsManager.DEFAULT_PROJECT_NAME); + } + + private static IWorkspaceRoot getWorkspaceRoot() { + return ResourcesPlugin.getWorkspace().getRoot(); + } + + class CompileParams { + String projectName; + String mainClass; + boolean isFullBuild = false; + + public String getMainClass() { + return mainClass; + } + + public boolean isFullBuild() { + return isFullBuild; + } + + public String getProjectName() { + return projectName; + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 1875d514e..f4e7b0ba3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -64,8 +64,12 @@ public Object executeCommand(String commandId, List arguments, IProgress ResolveMainClassHandler resolveMainClassHandler = new ResolveMainClassHandler(); return resolveMainClassHandler.resolveMainClass(arguments); case BUILD_WORKSPACE: - // TODO - break; + Compile.CompileParams params = null; + if (arguments != null && !arguments.isEmpty()) { + params = JsonUtils.fromJson((String) arguments.get(0), Compile.CompileParams.class); + } + + return Compile.compile(params, progress); case FETCH_USER_DATA: return UsageDataStore.getInstance().fetchAll(); case UPDATE_DEBUG_SETTINGS: From 42f9ac7d4843b75b1fdde64762d8c5ce4a4c51b2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Nov 2022 16:14:19 +0800 Subject: [PATCH 128/206] Bump version to 0.43.0 (#461) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index d9fb24c2e..6d4f847a0 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.42.0 + 0.43.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 43f07f603..505cc54c2 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 0858d90eb..949581277 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.42.0 +Bundle-Version: 0.43.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.42.0.jar + lib/com.microsoft.java.debug.core-0.43.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index c1b4144fa..98bf36ba0 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.42.0 + 0.43.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.42.0 + 0.43.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index ff39c1d7f..f2141039b 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index c5a1f46a0..3670a20f1 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.42.0 + 0.43.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 2adb8badf..e75506938 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.42.0 + 0.43.0 pom Java Debug Server for Visual Studio Code From e0f2bd29a87eececabb2726f08d4d6d5479eb15b Mon Sep 17 00:00:00 2001 From: mozhuanzuojing <63572041+mozhuanzuojing@users.noreply.github.com> Date: Thu, 12 Jan 2023 22:20:19 +0800 Subject: [PATCH 129/206] prioritize lookup the project source code during debugging (#463) * prioritize lookup the project source code during debugging * fix null check --- .../java/debug/plugin/internal/InlineValueHandler.java | 2 +- .../com/microsoft/java/debug/plugin/internal/JdtUtils.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java index 5c2cf8834..15aeeb2b4 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java @@ -166,7 +166,7 @@ private static IMethod findMethodInLocalTypes(IMethod enclosingMethod, int stopp */ private static Position getPosition(IBuffer buffer, int offset) { int[] result = JsonRpcHelpers.toLine(buffer, offset); - if (result == null && result.length < 1) { + if (result == null || result.length < 1) { return new Position(-1, -1); } 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 9a918112b..a4c06c6d4 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 @@ -213,10 +213,10 @@ public static ISourceContainer[] getSourceContainers(String projectName) { 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. - containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); // Add source containers associated with the project's source folders. containers.add(new JavaProjectSourceContainer(javaProject)); + // Add source containers associated with the project's runtime classpath entries. + containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); }); return containers.toArray(new ISourceContainer[0]); From 6e294e1e7c00014eb1b0ec1e67aa643efd97e19f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Feb 2023 10:33:52 +0800 Subject: [PATCH 130/206] Reject invalid DAP request (#466) --- .../core/adapter/DebugAdapterContext.java | 11 +++++++ .../java/debug/core/adapter/ErrorCode.java | 5 ++-- .../core/adapter/IDebugAdapterContext.java | 4 +++ .../handler/InitializeRequestHandler.java | 1 + .../adapter/handler/LaunchRequestHandler.java | 6 ++++ .../core/protocol/AbstractProtocolServer.java | 30 +++++++++++++++---- 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 345b2fcdf..f8ac01c3e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -56,6 +56,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private StepFilters stepFilters; private Path classpathJar = null; private Path argsfile = null; + private boolean isInitialized = false; private long shellProcessId = -1; private long processId = -1; @@ -407,4 +408,14 @@ public long getJDWPLatency() { public void setJDWPLatency(long baseLatency) { this.jdwpLatency = baseLatency; } + + @Override + public boolean isInitialized() { + return isInitialized; + } + + @Override + public void setInitialized(boolean isInitialized) { + this.isInitialized = isInitialized; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java index 1cdc4f9ce..6cfe523cf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -35,7 +35,8 @@ public enum ErrorCode { EXCEPTION_INFO_FAILURE(1018), EVALUATION_COMPILE_ERROR(2001), EVALUATE_NOT_SUSPENDED_THREAD(2002), - HCR_FAILURE(3001); + HCR_FAILURE(3001), + INVALID_DAP_HEADER(3002); private int id; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 9df539e1d..9a38e8598 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -154,4 +154,8 @@ public interface IDebugAdapterContext { long getJDWPLatency(); void setJDWPLatency(long baseLatency); + + boolean isInitialized(); + + void setInitialized(boolean isInitialized); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index bf60b6456..6b9245166 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -67,6 +67,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsBreakpointLocationsRequest = true; caps.supportsStepInTargetsRequest = true; response.body = caps; + context.setInitialized(true); return CompletableFuture.completedFuture(response); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index b1daf1b01..e5662f936 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -77,6 +77,12 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + if (!context.isInitialized()) { + final String errorMessage = "'launch' request is rejected since the debug session has not been initialized yet."; + logger.log(Level.SEVERE, errorMessage); + return CompletableFuture.completedFuture( + AdapterUtils.setErrorResponse(response, ErrorCode.LAUNCH_FAILURE, errorMessage)); + } LaunchArguments launchArguments = (LaunchArguments) arguments; Map traceInfo = new HashMap<>(); traceInfo.put("asyncJDWP", context.asyncJDWP()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java index 9bec3c228..61d57bd85 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 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 @@ -33,6 +33,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.protocol.Events.DebugEvent; import io.reactivex.disposables.Disposable; @@ -54,6 +56,7 @@ public abstract class AbstractProtocolServer implements IProtocolServer { private ByteBuffer rawData; private int contentLength = -1; private AtomicInteger sequenceNumber = new AtomicInteger(1); + private boolean isValidDAPRequest = true; private PublishSubject responseSubject = PublishSubject.create(); private PublishSubject requestSubject = PublishSubject.create(); @@ -217,7 +220,14 @@ private void processData() { if (message.type.equals("request")) { Messages.Request request = JsonUtils.fromJson(messageData, Messages.Request.class); - requestSubject.onNext(request); + if (this.isValidDAPRequest) { + requestSubject.onNext(request); + } else { + Messages.Response response = new Messages.Response(request.seq, request.command); + sendResponse(AdapterUtils.setErrorResponse(response, + ErrorCode.INVALID_DAP_HEADER, + String.format("'%s' request is rejected due to not being a valid DAP message.", request.command))); + } } else if (message.type.equals("response")) { Messages.Response response = JsonUtils.fromJson(messageData, Messages.Response.class); responseSubject.onNext(response); @@ -235,10 +245,20 @@ private void processData() { if (idx != -1) { Matcher matcher = CONTENT_LENGTH_MATCHER.matcher(rawMessage); if (matcher.find()) { - this.contentLength = Integer.parseInt(matcher.group(1)); - int headerByteLength = rawMessage.substring(0, idx + TWO_CRLF.length()) - .getBytes(PROTOCOL_ENCODING).length; + final String contentLengthText = matcher.group(1); + this.contentLength = Integer.parseInt(contentLengthText); + final String headerMessage = rawMessage.substring(0, idx + TWO_CRLF.length()); + final int headerByteLength = headerMessage.getBytes(PROTOCOL_ENCODING).length; this.rawData.removeFirst(headerByteLength); // Remove the header from the raw message. + + int expectedHeaderLength = 16 /*"Content-Length: ".length()*/ + contentLengthText.length(); + int actualHeaderLength = idx; + if (expectedHeaderLength != actualHeaderLength) { + this.isValidDAPRequest = false; + logger.log(Level.SEVERE, String.format("Illegal DAP request is detected: %s", headerMessage)); + } else { + this.isValidDAPRequest = true; + } continue; } } From f329b41a61518463a40f176d980737abf6fef5d8 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Feb 2023 14:59:55 +0800 Subject: [PATCH 131/206] Bump version to 0.44.0 (#467) --- .github/CODEOWNERS | 1 + com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..3f8625455 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @testforstephen @jdneo @Eskibear @CsCherrYY \ No newline at end of file diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 6d4f847a0..f253b45da 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.43.0 + 0.44.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 505cc54c2..3f86555d4 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 949581277..3bc8a69fc 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.43.0 +Bundle-Version: 0.44.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.43.0.jar + lib/com.microsoft.java.debug.core-0.44.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 98bf36ba0..75466e4df 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.43.0 + 0.44.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.43.0 + 0.44.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index f2141039b..cd199eabf 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 3670a20f1..48d8b7f68 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.43.0 + 0.44.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index e75506938..6af31be29 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.43.0 + 0.44.0 pom Java Debug Server for Visual Studio Code From ed35d9a4584076950584b701344987ecdf51d619 Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Mon, 6 Mar 2023 21:41:33 -0500 Subject: [PATCH 132/206] Add LSP4J 0.20.0 to the target platform. (#473) - JDT-LS now uses LSP4J 0.20.0, which is not available in Orbit or the main release repository Signed-off-by: Roland Grunberg --- pom.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6af31be29..f1c64e509 100644 --- a/pom.xml +++ b/pom.xml @@ -154,9 +154,9 @@ - 202112 + 202212 p2 - https://download.eclipse.org/releases/2021-12/202112081000/ + https://download.eclipse.org/releases/2022-12/202212071000/ oss.sonatype.org @@ -180,5 +180,10 @@ p2 https://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ + + lsp4j + p2 + https://download.eclipse.org/lsp4j/updates/releases/0.20.0/ + From 31cc03389a7890aa8ff5be6c897ca2575b910329 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 7 Mar 2023 16:09:04 +0800 Subject: [PATCH 133/206] Make pom.xml to reuse the tp file (#476) --- .../com.microsoft.java.debug.tp.target | 9 ++-- com.microsoft.java.debug.target/pom.xml | 12 +++++ javaConfig.json | 2 +- pom.xml | 44 ++++++++----------- 4 files changed, 38 insertions(+), 29 deletions(-) rename java.debug.target => com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target (86%) create mode 100644 com.microsoft.java.debug.target/pom.xml diff --git a/java.debug.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target similarity index 86% rename from java.debug.target rename to com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index dcb0b8c5e..187803840 100644 --- a/java.debug.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,12 +16,11 @@ - + - - + @@ -31,5 +30,9 @@ + + + + \ No newline at end of file diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml new file mode 100644 index 000000000..537f0ddcd --- /dev/null +++ b/com.microsoft.java.debug.target/pom.xml @@ -0,0 +1,12 @@ + + 4.0.0 + + com.microsoft.java + java-debug-parent + 0.44.0 + + com.microsoft.java.debug.tp + ${base.name} :: Target Platform + eclipse-target-definition + diff --git a/javaConfig.json b/javaConfig.json index 91ccf4714..8ab611931 100644 --- a/javaConfig.json +++ b/javaConfig.json @@ -3,5 +3,5 @@ "com.microsoft.java.debug.core", "com.microsoft.java.debug.plugin" ], - "targetPlatform": "java.debug.target" + "targetPlatform": "com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target" } diff --git a/pom.xml b/pom.xml index f1c64e509..fcfa85628 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ com.microsoft.java.debug.core com.microsoft.java.debug.plugin com.microsoft.java.debug.repository + com.microsoft.java.debug.target @@ -135,9 +136,27 @@ org.eclipse.tycho target-platform-configuration ${tycho-version} + + p2 + + + com.microsoft.java + com.microsoft.java.debug.tp + ${project.version} + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + @@ -153,11 +172,6 @@ - - 202212 - p2 - https://download.eclipse.org/releases/2022-12/202212071000/ - oss.sonatype.org https://oss.sonatype.org/content/repositories/snapshots/ @@ -165,25 +179,5 @@ true - - JDT.LS - p2 - https://download.eclipse.org/jdtls/snapshots/repository/latest/ - - - JBOLL.TOOLS - p2 - https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.3-2019-11-08_11-04-22-H22/ - - - orbit - p2 - https://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ - - - lsp4j - p2 - https://download.eclipse.org/lsp4j/updates/releases/0.20.0/ - From 9df6f5dbe303e6927d5abe4b559d32541d9880a7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Sun, 23 Apr 2023 17:11:38 +0800 Subject: [PATCH 134/206] Support specifying the exception types you want to break on (#481) * support specifying the exception types you want to break on --- .github/CODEOWNERS | 2 +- .../java/debug/core/DebugSession.java | 98 ++++++++++++++++--- .../java/debug/core/DebugSettings.java | 4 +- .../java/debug/core/IDebugSession.java | 2 + ...SetExceptionBreakpointsRequestHandler.java | 7 +- .../java/debug/core/protocol/Requests.java | 9 ++ 6 files changed, 104 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f8625455..b11684f52 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @testforstephen @jdneo @Eskibear @CsCherrYY \ No newline at end of file +* @testforstephen @jdneo \ No newline at end of file diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 1c7990f16..9dfea1a98 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -16,16 +16,26 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; +import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ClassPrepareEvent; +import com.sun.jdi.request.ClassPrepareRequest; import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.EventRequestManager; import com.sun.jdi.request.ExceptionRequest; +import io.reactivex.disposables.Disposable; + public class DebugSession implements IDebugSession { private VirtualMachine vm; private EventHub eventHub = new EventHub(); + private List eventRequests = new ArrayList<>(); + private List subscriptions = new ArrayList<>(); public DebugSession(VirtualMachine virtualMachine) { vm = virtualMachine; @@ -136,9 +146,27 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught @Override public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters) { + setExceptionBreakpoints(notifyCaught, notifyUncaught, null, classFilters, classExclusionFilters); + } + + @Override + public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, + String[] classFilters, String[] classExclusionFilters) { EventRequestManager manager = vm.eventRequestManager(); - ArrayList legacy = new ArrayList<>(manager.exceptionRequests()); - manager.deleteEventRequests(legacy); + + try { + ArrayList legacy = new ArrayList<>(manager.exceptionRequests()); + manager.deleteEventRequests(legacy); + manager.deleteEventRequests(eventRequests); + } catch (VMDisconnectedException ex) { + // ignore since removing breakpoints is meaningless when JVM is terminated. + } + subscriptions.forEach(subscription -> { + subscription.dispose(); + }); + subscriptions.clear(); + eventRequests.clear(); + // When no exception breakpoints are requested, no need to create an empty exception request. if (notifyCaught || notifyUncaught) { // from: https://www.javatips.net/api/REPLmode-master/src/jm/mode/replmode/REPLRunner.java @@ -153,20 +181,48 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught // a thread to be available, and queries it by calling allThreads(). // See org.eclipse.debug.jdi.tests.AbstractJDITest for the example. - // get only the uncaught exceptions - ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - if (classFilters != null) { - for (String classFilter : classFilters) { - request.addClassFilter(classFilter); + if (exceptionTypes == null || exceptionTypes.length == 0) { + ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (classFilters != null) { + for (String classFilter : classFilters) { + request.addClassFilter(classFilter); + } } + if (classExclusionFilters != null) { + for (String exclusionFilter : classExclusionFilters) { + request.addClassExclusionFilter(exclusionFilter); + } + } + request.enable(); + return; } - if (classExclusionFilters != null) { - for (String exclusionFilter : classExclusionFilters) { - request.addClassExclusionFilter(exclusionFilter); + + for (String exceptionType : exceptionTypes) { + if (StringUtils.isBlank(exceptionType)) { + continue; + } + + // register exception breakpoint in the future loaded classes. + ClassPrepareRequest classPrepareRequest = manager.createClassPrepareRequest(); + classPrepareRequest.addClassFilter(exceptionType); + classPrepareRequest.enable(); + eventRequests.add(classPrepareRequest); + + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent + && eventRequests.contains(debugEvent.event.request())) + .subscribe(debugEvent -> { + ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; + createExceptionBreakpoint(event.referenceType(), notifyCaught, notifyUncaught, classFilters, classExclusionFilters); + }); + subscriptions.add(subscription); + + // register exception breakpoint in the loaded classes. + for (ReferenceType refType : vm.classesByName(exceptionType)) { + createExceptionBreakpoint(refType, notifyCaught, notifyUncaught, classFilters, classExclusionFilters); } } - request.enable(); } } @@ -195,4 +251,22 @@ public IMethodBreakpoint createFunctionBreakpoint(String className, String funct int hitCount) { return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount); } + + private void createExceptionBreakpoint(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught, + String[] classFilters, String[] classExclusionFilters) { + EventRequestManager manager = vm.eventRequestManager(); + ExceptionRequest request = manager.createExceptionRequest(refType, notifyCaught, notifyUncaught); + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (classFilters != null) { + for (String classFilter : classFilters) { + request.addClassFilter(classFilter); + } + } + if (classExclusionFilters != null) { + for (String exclusionFilter : classExclusionFilters) { + request.addClassExclusionFilter(exclusionFilter); + } + } + request.enable(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index f59f10043..eaafdd064 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -19,7 +19,7 @@ import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.JsonUtils; -import com.microsoft.java.debug.core.protocol.Requests.ClassFilters; +import com.microsoft.java.debug.core.protocol.Requests.ExceptionFilters; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; public final class DebugSettings { @@ -39,7 +39,7 @@ public final class DebugSettings { public String javaHome; public HotCodeReplace hotCodeReplace = HotCodeReplace.MANUAL; public StepFilters stepFilters = new StepFilters(); - public ClassFilters exceptionFilters = new ClassFilters(); + public ExceptionFilters exceptionFilters = new ExceptionFilters(); public boolean exceptionFiltersUpdated = false; public int limitOfVariablesPerJdwpRequest = 100; public int jdwpRequestTimeout = 3000; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 4e4078c87..f490fb0c0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -38,6 +38,8 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters); + void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters); + IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount); Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java index 3a4e642c8..62a30595a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java @@ -26,8 +26,8 @@ import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; -import com.microsoft.java.debug.core.protocol.Requests.ClassFilters; import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.ExceptionFilters; import com.microsoft.java.debug.core.protocol.Requests.SetExceptionBreakpointsArguments; import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.event.VMDeathEvent; @@ -77,10 +77,11 @@ public synchronized CompletableFuture handle(Command command, Argument } private void setExceptionBreakpoints(IDebugSession debugSession, boolean notifyCaught, boolean notifyUncaught) { - ClassFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters; + ExceptionFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters; + String[] exceptionTypes = (exceptionFilters == null ? null : exceptionFilters.exceptionTypes); String[] classFilters = (exceptionFilters == null ? null : exceptionFilters.allowClasses); String[] classExclusionFilters = (exceptionFilters == null ? null : exceptionFilters.skipClasses); - debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, classFilters, classExclusionFilters); + debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); } @Override diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 697eef93d..09de7bfd9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -67,6 +67,15 @@ public static class ClassFilters { public String[] skipClasses = new String[0]; } + public static class ExceptionFilters extends ClassFilters { + /** + * Specifies that exceptions which are instances of refType will be reported. + * Note: this will include instances of sub-types. If null, all instances + * will be reported. + */ + public String[] exceptionTypes = new String[0]; + } + public static class StepFilters extends ClassFilters { /** * Deprecated - please use {@link ClassFilters#skipClasses } instead. From 87fae71227a58888d56b54c68fc47eaf24087102 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Apr 2023 13:05:08 +0800 Subject: [PATCH 135/206] Support set exception breakpoint asynchronously & bump veriosn to 0.45.0 (#482) * Support set exception breakpoint asynchronously & bump veriosn to 0.45.0 --- com.microsoft.java.debug.core/pom.xml | 2 +- .../com/microsoft/java/debug/core/DebugSession.java | 12 ++++++++++++ .../com/microsoft/java/debug/core/IDebugSession.java | 3 +++ .../SetExceptionBreakpointsRequestHandler.java | 4 +++- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 28 insertions(+), 11 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index f253b45da..a347b1883 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 9dfea1a98..fbad52fe2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -226,6 +226,18 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught } } + @Override + public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, + String[] classFilters, String[] classExclusionFilters, boolean async) { + if (async) { + AsyncJdwpUtils.runAsync(() -> { + setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); + }); + } else { + setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); + } + } + @Override public Process process() { return vm.process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index f490fb0c0..6cc3f3a46 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -40,6 +40,9 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters); + void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters, + boolean async); + IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount); Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java index 62a30595a..b51c5fe27 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java @@ -38,6 +38,7 @@ public class SetExceptionBreakpointsRequestHandler implements IDebugRequestHandl private boolean isInitialized = false; private boolean notifyCaught = false; private boolean notifyUncaught = false; + private boolean asyncJDWP = false; @Override public List getTargetCommands() { @@ -53,6 +54,7 @@ public synchronized CompletableFuture handle(Command command, Argument if (!isInitialized) { isInitialized = true; debugSession = context.getDebugSession(); + asyncJDWP = context.asyncJDWP(); DebugSettings.addDebugSettingChangeListener(this); debugSession.getEventHub().events().subscribe(debugEvent -> { if (debugEvent.event instanceof VMDeathEvent @@ -81,7 +83,7 @@ private void setExceptionBreakpoints(IDebugSession debugSession, boolean notifyC String[] exceptionTypes = (exceptionFilters == null ? null : exceptionFilters.exceptionTypes); String[] classFilters = (exceptionFilters == null ? null : exceptionFilters.allowClasses); String[] classExclusionFilters = (exceptionFilters == null ? null : exceptionFilters.skipClasses); - debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); + debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters, this.asyncJDWP); } @Override diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3f86555d4..0fc9cfb6c 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 3bc8a69fc..f04641553 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.44.0 +Bundle-Version: 0.45.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.44.0.jar + lib/com.microsoft.java.debug.core-0.45.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 75466e4df..b8d0b7582 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.44.0 + 0.45.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index cd199eabf..38801a366 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 48d8b7f68..096a5ecc9 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 537f0ddcd..c333a4d02 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index fcfa85628..4eda39462 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.44.0 + 0.45.0 pom Java Debug Server for Visual Studio Code From 31f79a5f3ab7efb1d66904fcdfdceb547aae4b83 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 12 May 2023 11:28:27 +0800 Subject: [PATCH 136/206] Add a task to sign maven artifacts with gpg (#485) * Add a task to sign maven artifacts with gpg --- scripts/publishMaven.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/scripts/publishMaven.js b/scripts/publishMaven.js index 1f960766c..deb75fb8b 100644 --- a/scripts/publishMaven.js +++ b/scripts/publishMaven.js @@ -1,7 +1,8 @@ /** * Usage: - * node publishMaven.js -task [upload|promote] + * node publishMaven.js -task [gpg][upload|promote] * + * gpg: Sign artifacts with GPG. * upload: Upload artifacts to a nexus staging repo. * promote: Promote a repo to get it picked up by Maven Central. */ @@ -33,7 +34,9 @@ main(configs, artifactFolder); function main() { const argv = process.argv; const task = argv[argv.indexOf("-task") + 1]; - if (task === "upload") { + if (task === "gpg") { + gpgSign(configs, artifactFolder); + } else if (task === "upload") { uploadToStaging(configs, artifactFolder); } else if (task === "promote") { promoteToCentral(configs); @@ -43,6 +46,27 @@ function main() { } } +/** + * Task gpg: Sign artifacts with GPG. + * + * Required binaries: + * - gpg + * + * Required Environment Variables: + * - artifactFolder: folder containing *.jar/*.pom files. + * - GPGPASS: passphrase of GPG key. + */ +function gpgSign(configs, artifactFolder) { + const props = ["artifactFolder", "gpgpass" ]; + for (const prop of props) { + if (!configs[prop]) { + console.error(`${prop} is not set.`); + process.exit(1); + } + } + addChecksumsAndGpgSignature(configs, artifactFolder); +} + /** * Task upload: Upload artifacts to a nexus staging repo. * @@ -141,7 +165,7 @@ function addChecksumsAndGpgSignature(configs, artifactFolder) { fs.readdirSync(modulePath) .filter(name => name.endsWith(".md5") || name.endsWith(".sha1") || name.endsWith(".asc")) .forEach(name => fs.unlinkSync(path.join(modulePath, name))); - + const files = fs.readdirSync(modulePath); for (let file of files) { // calc md5. @@ -153,7 +177,7 @@ function addChecksumsAndGpgSignature(configs, artifactFolder) { const sha1 = childProcess.execSync(`sha1sum "${path.join(modulePath, file)}"`); const sha1Match = /([a-z0-9]{40})/.exec(sha1.toString()); fs.writeFileSync(path.join(modulePath, file + ".sha1"), sha1Match[0]); - + // gpg sign. childProcess.execSync(`gpg --batch --pinentry-mode loopback --passphrase "${configs.gpgpass}" -ab "${path.join(modulePath, file)}"`) } From a01104960e77323db7a80f1cc7b39f8787855c2a Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Wed, 24 May 2023 07:30:28 +0200 Subject: [PATCH 137/206] Improve lambda method discovery Fixes: #477 (#478) * Improve lambda method discovery Fixes: #477 --- .../microsoft/java/debug/BindingUtils.java | 31 +++++-------------- .../java/debug/BreakpointLocationLocator.java | 2 +- .../java/debug/LambdaExpressionLocator.java | 2 +- .../internal/JdtSourceLookUpProvider.java | 18 ++++++++--- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java index b696be049..085bf0d4c 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -12,10 +12,12 @@ package com.microsoft.java.debug; import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.internal.debug.core.breakpoints.LambdaLocationLocatorHelper; /** * Utility methods around working with JDT Bindings. */ +@SuppressWarnings("restriction") public final class BindingUtils { private BindingUtils() { @@ -48,31 +50,14 @@ public static String getMethodName(IMethodBinding binding, boolean fromKey) { } /** - * Returns the method signature of the method represented by the binding. Since - * this implementation use the {@link IMethodBinding#getKey()} to extract the - * signature from, the method name must be passed in. + * Returns the method signature of the method represented by the binding + * including the synthetic outer locals. * * @param binding the binding which the signature must be resolved for. - * @param name the name of the method. - * @return the signature or null if the signature could not be resolved from the - * key. + * @return the signature or null if the signature could not be resolved. */ - public static String toSignature(IMethodBinding binding, String name) { - // use key for now until JDT core provides a public API for this. - // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" - // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" - String signatureString = binding.getKey(); - if (signatureString != null) { - name = "." + name; - int index = signatureString.indexOf(name); - if (index > -1) { - int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); - if (exceptionIndex > -1) { - return signatureString.substring(index + name.length(), exceptionIndex); - } - return signatureString.substring(index + name.length()); - } - } - return null; + public static String toSignature(IMethodBinding binding) { + return LambdaLocationLocatorHelper.toMethodSignature(binding); } + } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index 68f593aff..b14afde83 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -46,7 +46,7 @@ public String getMethodSignature() { if (this.methodBinding == null) { return null; } - return BindingUtils.toSignature(this.methodBinding, getMethodName()); + return BindingUtils.toSignature(this.methodBinding); } /** diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java index afffd9741..a5456809a 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -71,7 +71,7 @@ public String getMethodSignature() { if (!this.found) { return null; } - return BindingUtils.toSignature(this.lambdaMethodBinding, getMethodName()); + return BindingUtils.toSignature(this.lambdaMethodBinding); } /** 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 c4d4fe318..6d42572e4 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 @@ -29,10 +29,12 @@ import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Stream; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.URIUtil; import org.eclipse.debug.core.sourcelookup.ISourceContainer; @@ -50,10 +52,10 @@ import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.LambdaExpression; -import org.eclipse.jdt.core.manipulation.CoreASTProvider; 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.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; @@ -276,7 +278,13 @@ private CompilationUnit asCompilationUnit(String uri) { * setEnvironment(String [], String [], String [], boolean) * and a unit name setUnitName(String). */ - parser.setEnvironment(new String[0], new String[0], null, true); + IFile resource = (IFile) JDTUtils.findResource(JDTUtils.toURI(uri), + ResourcesPlugin.getWorkspace().getRoot()::findFilesForLocationURI); + if (resource != null && JdtUtils.isJavaProject(resource.getProject())) { + parser.setProject(JavaCore.create(resource.getProject())); + } else { + parser.setEnvironment(new String[0], new String[0], null, true); + } parser.setUnitName(Paths.get(filePath).getFileName().toString()); /** * See the java doc for { @link ASTParser#setSource(char[]) }, @@ -492,7 +500,7 @@ public List findMethodInvocations(String uri, int line) { // Keep consistent with JDI since JDI uses binary class name invocation.declaringTypeName = binding.getDeclaringClass().getBinaryName(); } - invocation.methodGenericSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); + invocation.methodGenericSignature = BindingUtils.toSignature(binding); invocation.methodSignature = Signature.getTypeErasure(invocation.methodGenericSignature); int startOffset = astNode.getStartPosition(); if (astNode instanceof org.eclipse.jdt.core.dom.MethodInvocation) { From d37cb100fb3b47c70479306c4d855060eafaabbd Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Fri, 26 May 2023 09:45:14 +0200 Subject: [PATCH 138/206] Improve ASTParser options when project is resolved. Fix #488 (#490) * Improve ASTParser options when project is resolved. Fix #488 Co-authored-by: Jinbo Wang --- .../internal/JdtSourceLookUpProvider.java | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 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 6d42572e4..bd41f99ca 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 @@ -100,7 +100,8 @@ public void initialize(IDebugAdapterContext context, Map props) throw new IllegalArgumentException("argument is null"); } options.putAll(props); - // During initialization, trigger a background job to load the source containers to improve the perf. + // During initialization, trigger a background job to load the source containers + // to improve the perf. new Thread(() -> { getSourceContainers(); }).start(); @@ -142,15 +143,16 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return Stream.of(locations).map(location -> { if (location.className() != null && location.methodName() != null) { return location.className() - .concat("#").concat(location.methodName()) - .concat("#").concat(location.methodSignature()); + .concat("#").concat(location.methodName()) + .concat("#").concat(location.methodSignature()); } return location.className(); }).toArray(String[]::new); } @Override - public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException { + public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) + throws DebugException { if (sourceUri == null) { throw new IllegalArgumentException("sourceUri is null"); } @@ -161,8 +163,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB CompilationUnit astUnit = asCompilationUnit(sourceUri); JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints) - .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) - .toArray(JavaBreakpointLocation[]::new); + .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) + .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { Map resolvedLocations = new HashMap<>(); for (JavaBreakpointLocation sourceLocation : sourceLocations) { @@ -171,7 +173,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB if (sourceColumn > -1) { // if we have a column, try to find the lambda expression at that column LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, - sourceLine, sourceColumn); + sourceLine, sourceColumn); astUnit.accept(lambdaExpressionLocator); if (lambdaExpressionLocator.isFound()) { sourceLocation.setClassName(lambdaExpressionLocator.getFullyQualifiedTypeName()); @@ -232,7 +234,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { List locations = new ArrayList<>(); - // The starting position of each line is the default breakpoint location for that line. + // The starting position of each line is the default breakpoint location for + // that line. locations.add(new BreakpointLocation(sourceLine, 0)); astUnit.accept(new ASTVisitor() { @Override @@ -284,18 +287,18 @@ private CompilationUnit asCompilationUnit(String uri) { parser.setProject(JavaCore.create(resource.getProject())); } else { parser.setEnvironment(new String[0], new String[0], null, true); + /** + * See the java doc for { @link ASTParser#setSource(char[]) }, + * the user need specify the compiler options explicitly. + */ + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + parser.setCompilerOptions(javaOptions); } parser.setUnitName(Paths.get(filePath).getFileName().toString()); - /** - * See the java doc for { @link ASTParser#setSource(char[]) }, - * the user need specify the compiler options explicitly. - */ - Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); - parser.setCompilerOptions(javaOptions); astUnit = (CompilationUnit) parser.createAST(null); } else { // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), @@ -336,7 +339,8 @@ public String getJavaRuntimeVersion(String projectName) { return resolveSystemLibraryVersion(project, vmInstall); } catch (CoreException e) { - logger.log(Level.SEVERE, "Failed to get Java runtime version for project '" + projectName + "': " + e.getMessage(), e); + logger.log(Level.SEVERE, + "Failed to get Java runtime version for project '" + projectName + "': " + e.getMessage(), e); } } @@ -345,6 +349,7 @@ public String getJavaRuntimeVersion(String projectName) { /** * Get the project associated source containers. + * * @return the initialized source container list */ public synchronized ISourceContainer[] getSourceContainers() { @@ -373,7 +378,8 @@ private String getContents(IClassFile cf) { source = buffer.getContents(); } } catch (JavaModelException e) { - logger.log(Level.SEVERE, String.format("Failed to parse the source contents of the class file: %s", e.toString()), e); + logger.log(Level.SEVERE, + String.format("Failed to parse the source contents of the class file: %s", e.toString()), e); } if (source == null) { source = ""; @@ -388,7 +394,7 @@ private static String getFileURI(IClassFile classFile) { try { return new URI(JDT_SCHEME, "contents", PATH_SEPARATOR + jarName + PATH_SEPARATOR + packageName + PATH_SEPARATOR + classFile.getElementName(), classFile.getHandleIdentifier(), null) - .toASCIIString(); + .toASCIIString(); } catch (URISyntaxException e) { return null; } @@ -425,8 +431,7 @@ private static IClassFile resolveClassFile(String uriString) { private static String readFile(String filePath) { StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferReader = - new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) { + try (BufferedReader bufferReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) { final int BUFFER_SIZE = 4096; char[] buffer = new char[BUFFER_SIZE]; while (true) { @@ -442,7 +447,8 @@ private static String readFile(String filePath) { return builder.toString(); } - private static String resolveSystemLibraryVersion(IJavaProject project, IVMInstall vmInstall) throws JavaModelException { + private static String resolveSystemLibraryVersion(IJavaProject project, IVMInstall vmInstall) + throws JavaModelException { LibraryLocation[] libraries = JavaRuntime.getLibraryLocations(vmInstall); if (libraries != null && libraries.length > 0) { IPackageFragmentRoot root = project.findPackageFragmentRoot(libraries[0].getSystemLibraryPath()); From 3e9adddd8c21a13b66114fb2b8f3bc711aa11ec0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 30 May 2023 13:29:16 +0800 Subject: [PATCH 139/206] Bump version to 0.46.0 (#492) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index a347b1883..9faa391fb 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 0fc9cfb6c..d3415ec62 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index f04641553..e754ee286 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.45.0 +Bundle-Version: 0.46.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.45.0.jar + lib/com.microsoft.java.debug.core-0.46.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index b8d0b7582..c69609528 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.45.0 + 0.46.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 38801a366..4efab9fba 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 096a5ecc9..9ff9e2352 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index c333a4d02..61bb2fca2 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 4eda39462..269077f6e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.45.0 + 0.46.0 pom Java Debug Server for Visual Studio Code From 25dd38f7202f0841ec8dd0686112e8809ee79c19 Mon Sep 17 00:00:00 2001 From: "microsoft-github-policy-service[bot]" <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 13:06:22 +0800 Subject: [PATCH 140/206] Microsoft mandatory file (#493) Co-authored-by: microsoft-github-policy-service[bot] <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> --- SECURITY.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e138ec5d6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + From 6f5a9b386035e0663c343abc898b2c9e9c31b69d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 26 Jun 2023 15:27:49 +0800 Subject: [PATCH 141/206] Debug support on the decompiled source code (#495) * Debug support on the decompiled source code --- .../java/debug/core/DebugSettings.java | 8 ++ .../debug/core/JavaBreakpointLocation.java | 14 ++- .../java/debug/core/adapter/AdapterUtils.java | 54 ++++++++++ .../core/adapter/ISourceLookUpProvider.java | 20 ++++ .../handler/SetBreakpointsRequestHandler.java | 18 +++- .../handler/StackTraceRequestHandler.java | 10 ++ .../internal/JdtSourceLookUpProvider.java | 100 +++++++++++++++++- .../com.microsoft.java.debug.tp.target | 2 +- 8 files changed, 219 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index eaafdd064..0a3e05ec8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -44,6 +44,7 @@ public final class DebugSettings { public int limitOfVariablesPerJdwpRequest = 100; public int jdwpRequestTimeout = 3000; public AsyncMode asyncJDWP = AsyncMode.OFF; + public Switch debugSupportOnDecompiledSource = Switch.OFF; public static DebugSettings getCurrent() { return current; @@ -97,6 +98,13 @@ public static enum AsyncMode { OFF } + public static enum Switch { + @SerializedName("on") + ON, + @SerializedName("off") + OFF + } + public static interface IDebugSettingChangeListener { public void update(DebugSettings oldSettings, DebugSettings newSettings); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java index 820e80c9b..3dbc1a24d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java @@ -17,7 +17,11 @@ public class JavaBreakpointLocation { /** - * The source line of the breakpoint or logpoint. + * The line number in the source file. + */ + private int lineNumberInSourceFile = Integer.MIN_VALUE; + /** + * The line number in the class file. */ private int lineNumber; /** @@ -110,4 +114,12 @@ public Types.BreakpointLocation[] availableBreakpointLocations() { public void setAvailableBreakpointLocations(Types.BreakpointLocation[] availableBreakpointLocations) { this.availableBreakpointLocations = availableBreakpointLocations; } + + public int lineNumberInSourceFile() { + return lineNumberInSourceFile == Integer.MIN_VALUE ? lineNumber : lineNumberInSourceFile; + } + + public void setLineNumberInSourceFile(int lineNumberInSourceFile) { + this.lineNumberInSourceFile = lineNumberInSourceFile; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 9b972334d..c30aa8742 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -310,4 +310,58 @@ public static String decodeURIComponent(String uri) { return uri; } } + + /** + * Find the mapped lines based on the given line number. + * + * The line mappings format is as follows: + * - [i]: key + * - [i+1]: value + */ + public static int[] binarySearchMappedLines(int[] lineMappings, int targetLine) { + if (lineMappings == null || lineMappings.length == 0 || lineMappings.length % 2 != 0) { + return null; + } + + final int MAX = lineMappings.length / 2 - 1; + int low = 0; + int high = MAX; + int found = -1; + while (low <= high) { + int mid = low + (high - low) / 2; + int actualMid = mid * 2; + if (lineMappings[actualMid] == targetLine) { + found = mid; + break; + } + + if (lineMappings[actualMid] < targetLine) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + if (found == -1) { + return null; + } + + // Find the duplicates in the sorted array + int left = found; + while ((left - 1) >= 0 && lineMappings[(left - 1) * 2] == targetLine) { + left--; + } + + int right = found; + while ((right + 1) <= MAX && lineMappings[(right + 1) * 2] == targetLine) { + right++; + } + + int[] values = new int[right - left + 1]; + for (int i = 0; i < values.length; i++) { + values[i] = lineMappings[(left + i) * 2 + 1]; + } + + return values; + } } 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 ead10e33c..5ffed6e0f 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 @@ -76,6 +76,26 @@ default String getJavaRuntimeVersion(String projectName) { */ List findMethodInvocations(String uri, int line); + /** + * Return the line mappings from the original line to the decompiled line. + * + * @param uri The uri + * @return the line mappings from the original line to the decompiled line. + */ + default int[] getOriginalLineMappings(String uri) { + return null; + } + + /** + * Return the line mappings from the decompiled line to the original line. + * + * @param uri The uri + * @return the line mappings from the decompiled line to the original line. + */ + default int[] getDecompiledLineMappings(String uri) { + return null; + } + public static class MethodInvocation { public String expression; public String methodName; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 7fe6c3fdd..a8774ad37 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -24,6 +24,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.DebugSettings.Switch; import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.IEvaluatableBreakpoint; @@ -296,7 +298,8 @@ public static boolean handleEvaluationResult(IDebugAdapterContext context, Threa private Types.Breakpoint convertDebuggerBreakpointToClient(IBreakpoint breakpoint, IDebugAdapterContext context) { int id = (int) breakpoint.getProperty("id"); boolean verified = breakpoint.getProperty("verified") != null && (boolean) breakpoint.getProperty("verified"); - int lineNumber = AdapterUtils.convertLineNumber(breakpoint.getLineNumber(), context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + int lineNumber = AdapterUtils.convertLineNumber(breakpoint.sourceLocation().lineNumberInSourceFile(), + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); return new Types.Breakpoint(id, verified, lineNumber, ""); } @@ -318,6 +321,19 @@ private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Type } catch (NumberFormatException e) { hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. } + + if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON) { + // Align the decompiled line with the original line. + int[] lineMappings = sourceProvider.getDecompiledLineMappings(sourceFile); + if (locations[i] != null && lineMappings != null) { + int lineNumberInSourceFile = locations[i].lineNumber(); + int[] originalLines = AdapterUtils.binarySearchMappedLines(lineMappings, lineNumberInSourceFile); + if (originalLines != null && originalLines.length > 0) { + locations[i].setLineNumberInSourceFile(lineNumberInSourceFile); + locations[i].setLineNumber(originalLines[0]); + } + } + } breakpoints[i] = context.getDebugSession().createBreakpoint(locations[i], hitCount, sourceBreakpoints[i].condition, sourceBreakpoints[i].logMessage); if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(locations[i].className())) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index fbfce49ef..601a29f56 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -25,8 +25,10 @@ import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.AsyncJdwpUtils; +import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; @@ -189,6 +191,14 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra // display "Unknown Source" in the Call Stack View. clientSource = null; } + } else if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON + && clientSource != null && clientSource.path != null) { + // Align the original line with the decompiled line. + int[] lineMappings = context.getProvider(ISourceLookUpProvider.class).getOriginalLineMappings(clientSource.path); + int[] renderLines = AdapterUtils.binarySearchMappedLines(lineMappings, clientLineNumber); + if (renderLines != null && renderLines.length > 0) { + clientLineNumber = renderLines[0]; + } } int clientColumnNumber = context.isClientColumnsStartAt1() ? 1 : 0; 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 bd41f99ca..f0c8795cc 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 @@ -36,6 +36,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.URIUtil; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.jdt.core.IBuffer; @@ -60,14 +61,19 @@ import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import org.eclipse.jdt.ls.core.internal.DecompilerResult; import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.managers.ContentProviderManager; import com.microsoft.java.debug.BindingUtils; import com.microsoft.java.debug.BreakpointLocationLocator; import com.microsoft.java.debug.LambdaExpressionLocator; 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.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -303,10 +309,42 @@ private CompilationUnit asCompilationUnit(String uri) { } else { // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), // leverage jdt to load the source contents. - ITypeRoot typeRoot = resolveClassFile(uri); - if (typeRoot != null) { - parser.setSource(typeRoot); - astUnit = (CompilationUnit) parser.createAST(null); + IClassFile typeRoot = resolveClassFile(uri); + try { + if (typeRoot != null && typeRoot.getSourceRange() != null) { + parser.setSource(typeRoot); + astUnit = (CompilationUnit) parser.createAST(null); + } else if (typeRoot != null && DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON) { + ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager(); + try { + String contents = contentProvider.getSource(typeRoot, new NullProgressMonitor()); + if (contents != null && !contents.isBlank()) { + IJavaProject javaProject = typeRoot.getJavaProject(); + if (javaProject != null) { + parser.setProject(javaProject); + } else { + parser.setEnvironment(new String[0], new String[0], null, true); + /** + * See the java doc for { @link ASTParser#setSource(char[]) }, + * the user need specify the compiler options explicitly. + */ + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + parser.setCompilerOptions(javaOptions); + } + parser.setUnitName(typeRoot.getElementName()); + parser.setSource(contents.toCharArray()); + astUnit = (CompilationUnit) parser.createAST(null); + } + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + } catch (JavaModelException e) { + // ignore } } return astUnit; @@ -533,4 +571,58 @@ private boolean isSameURI(String uri1, String uri2) { return false; } } + + public int[] getOriginalLineMappings(String uri) { + IClassFile classFile = resolveClassFile(uri); + try { + if (classFile == null) { + return null; + } + + IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) classFile.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); + if (packageRoot != null && packageRoot.getSourceAttachmentPath() != null) { + return null; + } + + if (classFile.getSourceRange() == null) { + ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager(); + try { + DecompilerResult result = contentProvider.getSourceResult(classFile, new NullProgressMonitor()); + if (result != null) { + return result.getOriginalLineMappings(); + } + } catch (NoSuchMethodError e) { + // ignore it if old language server version is installed. + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + } catch (JavaModelException e) { + // ignore + } + return null; + } + + public int[] getDecompiledLineMappings(String uri) { + IClassFile classFile = resolveClassFile(uri); + try { + if (classFile != null && classFile.getSourceRange() == null) { + ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager(); + try { + DecompilerResult result = contentProvider.getSourceResult(classFile, new NullProgressMonitor()); + if (result != null) { + return result.getDecompiledLineMappings(); + } + } catch (NoSuchMethodError e) { + // ignore it if old Java language server version is installed. + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + } catch (JavaModelException e) { + // ignore + } + + return null; + } } diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 187803840..fbc981533 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -31,7 +31,7 @@ - + From b6564781f1b212036d5fceed4e9b757d40cc0ce4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 30 Jun 2023 09:57:33 +0800 Subject: [PATCH 142/206] Track the perf of stackTrace dap request (#496) --- .../handler/StackTraceRequestHandler.java | 15 ++++++++++++ .../java/debug/core/protocol/Events.java | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 601a29f56..6b44d52f5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils; +import com.google.gson.JsonObject; import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.DebugUtility; @@ -41,6 +42,7 @@ import com.microsoft.java.debug.core.protocol.Requests.StackTraceArguments; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; +import com.microsoft.java.debug.core.protocol.Events.TelemetryEvent; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.LocalVariable; @@ -54,6 +56,7 @@ import com.sun.jdi.request.BreakpointRequest; public class StackTraceRequestHandler implements IDebugRequestHandler { + private ThreadLocal isDecompilerInvoked = new ThreadLocal<>(); @Override public List getTargetCommands() { @@ -62,6 +65,8 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + final long startAt = System.currentTimeMillis(); + isDecompilerInvoked.set(false); StackTraceArguments stacktraceArgs = (StackTraceArguments) arguments; List result = new ArrayList<>(); if (stacktraceArgs.startFrame < 0 || stacktraceArgs.levels < 0) { @@ -108,6 +113,15 @@ public CompletableFuture handle(Command command, Arguments arguments, } } response.body = new Responses.StackTraceResponseBody(result, totalFrames); + long duration = System.currentTimeMillis() - startAt; + JsonObject properties = new JsonObject(); + properties.addProperty("command", "stackTrace"); + properties.addProperty("duration", duration); + properties.addProperty("decompileSupport", DebugSettings.getCurrent().debugSupportOnDecompiledSource.toString()); + if (isDecompilerInvoked.get() != null) { + properties.addProperty("isDecompilerInvoked", Boolean.toString(isDecompilerInvoked.get())); + } + context.getProtocolServer().sendEvent(new TelemetryEvent("dap", properties)); return CompletableFuture.completedFuture(response); } @@ -198,6 +212,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra int[] renderLines = AdapterUtils.binarySearchMappedLines(lineMappings, clientLineNumber); if (renderLines != null && renderLines.length > 0) { clientLineNumber = renderLines[0]; + isDecompilerInvoked.set(true); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index 681ec543d..1973444b6 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -246,6 +246,30 @@ public UserNotificationEvent(NotificationType notifyType, String message) { } } + public static class TelemetryEvent extends DebugEvent { + /** + * The telemetry event name. + */ + public String name; + + /** + * The properties is an object as below. + * { + * [key: string]: string | number; + * } + */ + public Object properties; + + /** + * Constructor. + */ + public TelemetryEvent(String name, Object data) { + super("telemetry"); + this.name = name; + this.properties = data; + } + } + public static enum InvalidatedAreas { @SerializedName("all") ALL, From 7bdf2effd7be8f54745e04ea8334e337ed86b75b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 30 Jun 2023 10:44:35 +0800 Subject: [PATCH 143/206] Bump version to 0.47.0 (#497) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 9faa391fb..3b1d57547 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index d3415ec62..5c303928d 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index e754ee286..0965d8ed5 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.46.0 +Bundle-Version: 0.47.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.46.0.jar + lib/com.microsoft.java.debug.core-0.47.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index c69609528..c41c9de3b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.46.0 + 0.47.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 4efab9fba..20eac94c8 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9ff9e2352..665b94a6a 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 61bb2fca2..6e22d46ab 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 269077f6e..c296ce2c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.46.0 + 0.47.0 pom Java Debug Server for Visual Studio Code From f9c02cffebf3f7b62ca5f980fa842033f3d53049 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Jul 2023 13:57:20 +0800 Subject: [PATCH 144/206] Custom request 'refreshFrames' to refresh the thread's frames (#501) * Custom request 'refreshFrames' to refresh the thread's frames --- .../java/debug/core/adapter/DebugAdapter.java | 2 + .../java/debug/core/adapter/ThreadCache.java | 46 ++++++ .../ConfigurationDoneRequestHandler.java | 3 +- .../adapter/handler/RefreshFramesHandler.java | 136 ++++++++++++++++++ .../adapter/handler/RestartFrameHandler.java | 1 + .../handler/SetBreakpointsRequestHandler.java | 4 +- .../SetDataBreakpointsRequestHandler.java | 4 +- .../SetFunctionBreakpointsRequestHandler.java | 4 +- .../handler/StackTraceRequestHandler.java | 19 ++- .../adapter/handler/StepRequestHandler.java | 2 +- .../handler/ThreadsRequestHandler.java | 7 + .../java/debug/core/protocol/Events.java | 5 + .../java/debug/core/protocol/Requests.java | 10 ++ 13 files changed, 233 insertions(+), 10 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index b853e0469..10c0df902 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -34,6 +34,7 @@ import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; import com.microsoft.java.debug.core.adapter.handler.ProcessIdHandler; +import com.microsoft.java.debug.core.adapter.handler.RefreshFramesHandler; import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; @@ -133,6 +134,7 @@ private void initialize() { registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); registerHandlerForDebug(new BreakpointLocationsRequestHander()); registerHandlerForDebug(new StepInTargetsRequestHandler()); + registerHandlerForDebug(new RefreshFramesHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java index ce1c17282..57d39154e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -32,6 +33,8 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) { } }); private Map eventThreads = new ConcurrentHashMap<>(); + private Map> decompiledClassesByThread = new HashMap<>(); + private Map threadStoppedReasons = new HashMap<>(); public synchronized void resetThreads(List threads) { allThreads.clear(); @@ -80,6 +83,13 @@ public void addEventThread(ThreadReference thread) { eventThreads.put(thread.uniqueID(), thread); } + public void addEventThread(ThreadReference thread, String reason) { + eventThreads.put(thread.uniqueID(), thread); + if (reason != null) { + threadStoppedReasons.put(thread.uniqueID(), reason); + } + } + public void removeEventThread(long threadId) { eventThreads.remove(threadId); } @@ -113,4 +123,40 @@ public List visibleThreads(IDebugAdapterContext context) { return visibleThreads; } + + public Set getDecompiledClassesByThread(long threadId) { + return decompiledClassesByThread.get(threadId); + } + + public void setDecompiledClassesByThread(long threadId, Set decompiledClasses) { + if (decompiledClasses == null || decompiledClasses.isEmpty()) { + decompiledClassesByThread.remove(threadId); + return; + } + + decompiledClassesByThread.put(threadId, decompiledClasses); + } + + public String getThreadStoppedReason(long threadId) { + return threadStoppedReasons.get(threadId); + } + + public void setThreadStoppedReason(long threadId, String reason) { + if (reason == null) { + threadStoppedReasons.remove(threadId); + return; + } + + threadStoppedReasons.put(threadId, reason); + } + + public void clearThreadStoppedState(long threadId) { + threadStoppedReasons.remove(threadId); + decompiledClassesByThread.remove(threadId); + } + + public void clearAllThreadStoppedState() { + threadStoppedReasons.clear(); + decompiledClassesByThread.clear(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 6805073dc..1c543bce4 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -76,6 +76,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (context.isVmStopOnEntry()) { DebugUtility.stopOnEntry(debugSession, context.getMainClass()).thenAccept(threadId -> { context.getProtocolServer().sendEvent(new Events.StoppedEvent("entry", threadId)); + context.getThreadCache().setThreadStoppedReason(threadId, "entry"); }); } } else if (event instanceof VMDeathEvent) { @@ -117,7 +118,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, JdiExceptionReference jdiException = new JdiExceptionReference(((ExceptionEvent) event).exception(), ((ExceptionEvent) event).catchLocation() == null); context.getExceptionManager().setException(thread.uniqueID(), jdiException); - context.getThreadCache().addEventThread(thread); + context.getThreadCache().addEventThread(thread, "exception"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID())); debugEvent.shouldResume = false; } else { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java new file mode 100644 index 000000000..b91daaf80 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java @@ -0,0 +1,136 @@ +/******************************************************************************* +* Copyright (c) 2023 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.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.AsyncJdwpUtils; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Events.StoppedEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.RefreshFramesArguments; +import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ThreadReference; + +public class RefreshFramesHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.REFRESHFRAMES); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + RefreshFramesArguments refreshArgs = (RefreshFramesArguments) arguments; + String[] affectedRootPaths = refreshArgs == null ? null : refreshArgs.affectedRootPaths; + List pausedThreads = getPausedThreads(context); + for (long threadId : pausedThreads) { + if (affectedRootPaths == null || affectedRootPaths.length == 0) { + refreshFrames(threadId, context); + continue; + } + + Set decompiledClasses = context.getThreadCache().getDecompiledClassesByThread(threadId); + if (decompiledClasses == null || decompiledClasses.isEmpty()) { + continue; + } + + if (anyInAffectedRootPaths(decompiledClasses, affectedRootPaths)) { + refreshFrames(threadId, context); + } + } + + return CompletableFuture.completedFuture(response); + } + + List getPausedThreads(IDebugAdapterContext context) { + List results = new ArrayList<>(); + List> futures = new ArrayList<>(); + List threads = context.getThreadCache().visibleThreads(context); + for (ThreadReference thread : threads) { + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.supplyAsync(() -> { + try { + if (thread.isSuspended()) { + return thread.uniqueID(); + } + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + + return -1L; + })); + } else { + try { + if (thread.isSuspended()) { + results.add(thread.uniqueID()); + } + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + } + } + + List awaitedResutls = AsyncJdwpUtils.await(futures); + for (Long threadId : awaitedResutls) { + if (threadId > 0) { + results.add(threadId); + } + } + + return results; + } + + /** + * See https://github.com/microsoft/vscode/issues/188606, + * VS Code doesn't provide a simple way to refetch the stack frames. + * We're going to resend a thread stopped event to trick the client + * into refetching the thread stack frames. + */ + void refreshFrames(long threadId, IDebugAdapterContext context) { + StoppedEvent stoppedEvent = new StoppedEvent(context.getThreadCache().getThreadStoppedReason(threadId), threadId); + stoppedEvent.preserveFocusHint = true; + context.getProtocolServer().sendEvent(stoppedEvent); + } + + boolean anyInAffectedRootPaths(Collection classes, String[] affectedRootPaths) { + if (affectedRootPaths == null || affectedRootPaths.length == 0) { + return true; + } + + for (String classUri : classes) { + // decompiled class uri is like 'jdt://contents/rt.jar/java.io/PrintStream.class?=1.helloworld/%5C/usr%5C/lib%5C/jvm%5C/ + // java-8-oracle%5C/jre%5C/lib%5C/rt.jar%3Cjava.io(PrintStream.class'. + if (classUri.startsWith("jdt://contents/")) { + String jarName = classUri.substring("jdt://contents/".length()); + int sep = jarName.indexOf("/"); + jarName = sep >= 0 ? jarName.substring(0, sep) : jarName; + for (String affectedRootPath : affectedRootPaths) { + if (affectedRootPath.endsWith("/" + jarName) || affectedRootPath.endsWith("\\" + jarName)) { + return true; + } + } + } + } + + return false; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java index 2023209f4..164909656 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java @@ -122,6 +122,7 @@ private void stepInto(IDebugAdapterContext context, ThreadReference thread) { // Have to send two events to keep the UI sync with the step in operations: context.getProtocolServer().sendEvent(new Events.ContinuedEvent(thread.uniqueID())); context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID())); + context.getThreadCache().setThreadStoppedReason(thread.uniqueID(), "restartframe"); }); request.enable(); thread.resume(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index a8774ad37..09dafd1b0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -211,14 +211,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, breakpointName); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } }); }); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, breakpointName); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java index 6d3e8c0b1..373b1c31b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java @@ -151,13 +151,13 @@ private void registerWatchpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "data breakpoint"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } }); }); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "data breakpoint"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } debugEvent.shouldResume = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index 59a948d37..96a0e395b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -165,7 +165,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "function breakpoint"); context.getProtocolServer().sendEvent(new Events.StoppedEvent( "function breakpoint", bpThread.uniqueID())); } @@ -173,7 +173,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { }); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "function breakpoint"); context.getProtocolServer() .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 6b44d52f5..0ba3b2004 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -15,8 +15,10 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -73,16 +75,23 @@ public CompletableFuture handle(Command command, Arguments arguments, response.body = new Responses.StackTraceResponseBody(result, 0); return CompletableFuture.completedFuture(response); } - ThreadReference thread = context.getThreadCache().getThread(stacktraceArgs.threadId); + long threadId = stacktraceArgs.threadId; + ThreadReference thread = context.getThreadCache().getThread(threadId); if (thread == null) { - thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId); + thread = DebugUtility.getThread(context.getDebugSession(), threadId); } int totalFrames = 0; if (thread != null) { + Set decompiledClasses = new LinkedHashSet<>(); try { // Thread state has changed and then invalidate the stack frame cache. if (stacktraceArgs.startFrame == 0) { context.getStackFrameManager().clearStackFrames(thread); + } else { + Set existing = context.getThreadCache().getDecompiledClassesByThread(threadId); + if (existing != null) { + decompiledClasses.addAll(existing); + } } totalFrames = thread.frameCount(); @@ -102,6 +111,10 @@ public CompletableFuture handle(Command command, Arguments arguments, Types.StackFrame lspFrame = convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context); result.add(lspFrame); frameReference.setSource(lspFrame.source); + int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + if (jdiLineNumber != lspFrame.line) { + decompiledClasses.add(lspFrame.source.path); + } } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException @@ -110,6 +123,8 @@ public CompletableFuture handle(Command command, Arguments arguments, // 1. the vscode has wrong parameter/wrong uri // 2. the thread actually terminates // TODO: should record a error log here. + } finally { + context.getThreadCache().setDecompiledClassesByThread(threadId, decompiledClasses); } } response.body = new Responses.StackTraceResponseBody(result, totalFrames); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index bd324a7fa..71f28355a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -308,7 +308,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } - context.getThreadCache().addEventThread(thread); + context.getThreadCache().addEventThread(thread, "step"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID())); debugEvent.shouldResume = false; } else if (event instanceof MethodExitEvent) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index fceac73a5..6573f11da 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -138,6 +138,7 @@ private CompletableFuture pause(Requests.PauseArguments arguments, Res context.getStepResultManager().removeAllMethodResults(); context.getDebugSession().suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); + context.getThreadCache().setThreadStoppedReason(arguments.threadId, "pause"); } return CompletableFuture.completedFuture(response); } @@ -158,12 +159,14 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, context.getStepResultManager().removeMethodResult(arguments.threadId); context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; + context.getThreadCache().clearThreadStoppedState(arguments.threadId); DebugUtility.resumeThread(thread); context.getStackFrameManager().clearStackFrames(thread); checkThreadRunningAndRecycleIds(thread, context); } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); + context.getThreadCache().clearAllThreadStoppedState(); resumeVM(context); context.getStackFrameManager().clearStackFrames(); context.getRecyclableIdPool().removeAllObjects(); @@ -175,6 +178,7 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); + context.getThreadCache().clearAllThreadStoppedState(); resumeVM(context); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); context.getStackFrameManager().clearStackFrames(); @@ -190,6 +194,7 @@ private CompletableFuture resumeOthers(Requests.ThreadOperationArgumen continue; } + context.getThreadCache().clearThreadStoppedState(thread.uniqueID()); if (context.asyncJDWP()) { futures.add(AsyncJdwpUtils.runAsync(() -> resumeThread(thread, context))); } else { @@ -203,6 +208,7 @@ private CompletableFuture resumeOthers(Requests.ThreadOperationArgumen private CompletableFuture pauseAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { context.getDebugSession().suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); + context.getThreadCache().setThreadStoppedReason(arguments.threadId, "pause"); return CompletableFuture.completedFuture(response); } @@ -300,6 +306,7 @@ private void pauseThread(ThreadReference thread, IDebugAdapterContext context) { context.getStepResultManager().removeMethodResult(threadId); thread.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + context.getThreadCache().setThreadStoppedReason(threadId, "pause"); } } catch (ObjectCollectedException ex) { // the thread is garbage collected. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index 1973444b6..e692e9b8d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -38,6 +38,11 @@ public static class StoppedEvent extends DebugEvent { public String description; public String text; public boolean allThreadsStopped; + /** + * A value of true hints to the client that this event should not change the + * focus. + */ + public Boolean preserveFocusHint; /** * Constructor. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 09de7bfd9..1129d230e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -426,6 +426,15 @@ public static class BreakpointLocationsArguments extends Arguments { public int endColumn; } + public static class RefreshFramesArguments extends Arguments { + /** + * If provided, refresh the stack frames of the paused threads that previously + * requested decompiled sources for classes in the affected root paths. + * Otherwise, refresh all paused threads. + */ + public String[] affectedRootPaths; + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -464,6 +473,7 @@ public static enum Command { REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), PROCESSID("processId", Arguments.class), BREAKPOINTLOCATIONS("breakpointLocations", BreakpointLocationsArguments.class), + REFRESHFRAMES("refreshFrames", RefreshFramesArguments.class), UNSUPPORTED("", Arguments.class); private String command; From 1d76af515d6926db737f89438c056c2d0d2150df Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 31 Jul 2023 12:13:50 +0800 Subject: [PATCH 145/206] Bump version to 0.48.0 (#502) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 3b1d57547..8ebf3fb5f 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 5c303928d..2f7e62dc3 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 0965d8ed5..520d7d0da 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.47.0 +Bundle-Version: 0.48.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.47.0.jar + lib/com.microsoft.java.debug.core-0.48.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index c41c9de3b..e50a7f992 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.47.0 + 0.48.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 20eac94c8..8cf6b4f60 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 665b94a6a..27132434c 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 6e22d46ab..c7fcc1716 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index c296ce2c1..55e421c19 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.47.0 + 0.48.0 pom Java Debug Server for Visual Studio Code From 5174aa3744281ad05531c1c1984631e2ff1c68de Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 4 Aug 2023 16:12:49 +0800 Subject: [PATCH 146/206] Only build main project for build server project (#503) * Only build main project for build server project * Add a dedicated error code for build server --------- Signed-off-by: Sheng Chen --- .../java/debug/plugin/internal/Compile.java | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java index 415456ce5..d38227785 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -18,9 +18,11 @@ import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; @@ -40,7 +42,42 @@ public class Compile { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - public static BuildWorkspaceStatus compile(CompileParams params, IProgressMonitor monitor) { + private static final int GRADLE_BS_COMPILATION_ERROR = 100; + + public static Object compile(CompileParams params, IProgressMonitor monitor) { + IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); + if (mainProject == null) { + try { + // Q: is infer project by main class name necessary? perf impact? + List javaProjects = ResolveClasspathsHandler.getJavaProjectFromType(params.getMainClass()); + if (javaProjects.size() == 1) { + mainProject = javaProjects.get(0).getProject(); + } + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Failed to resolve project from main class name.", e); + } + } + + if (isBspProject(mainProject)) { + // Just need to trigger a build for the target project, the Gradle build server will + // handle the build dependencies for us. + try { + ResourcesPlugin.getWorkspace().build( + new IBuildConfiguration[]{mainProject.getActiveBuildConfig()}, + IncrementalProjectBuilder.INCREMENTAL_BUILD, + false /*buildReference*/, + monitor + ); + } catch (CoreException e) { + if (e.getStatus().getCode() == IResourceStatus.BUILD_FAILED) { + return GRADLE_BS_COMPILATION_ERROR; + } else { + return BuildWorkspaceStatus.FAILED; + } + } + return BuildWorkspaceStatus.SUCCEED; + } + try { if (monitor.isCanceled()) { return BuildWorkspaceStatus.CANCELLED; @@ -55,7 +92,6 @@ public static BuildWorkspaceStatus compile(CompileParams params, IProgressMonito } logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); - IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); IResource currentResource = mainProject; if (isUnmanagedFolder(mainProject) && StringUtils.isNotBlank(params.getMainClass())) { IType mainType = ProjectUtils.getJavaProject(mainProject).findType(params.getMainClass()); @@ -117,6 +153,11 @@ private static boolean isUnmanagedFolder(IProject project) { && ProjectUtils.isJavaProject(project); } + private static boolean isBspProject(IProject project) { + return project != null && ProjectUtils.isJavaProject(project) + && ProjectUtils.hasNature(project, "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature"); + } + private static IProject getDefaultProject() { return getWorkspaceRoot().getProject(ProjectsManager.DEFAULT_PROJECT_NAME); } From 0e03327569c18a83d0c56f63bbb80ba404bb20b3 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 15 Aug 2023 12:46:31 +0800 Subject: [PATCH 147/206] Code completion: Use fully qualified name as needed in DEBUG CONSOLE (#504) --- .../internal/CompletionProposalRequestor.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index be7932992..41b4da929 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -45,6 +45,7 @@ import org.eclipse.jdt.ls.core.internal.handlers.CompletionResponses; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.CompletionItemLabelDetails; import com.google.common.collect.ImmutableSet; import com.microsoft.java.debug.core.Configuration; @@ -159,6 +160,8 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, String.valueOf(index)); $.setData(data); this.descriptionProvider.updateDescription(proposal, $); + // Use fully qualified name as needed. + $.setInsertText(String.valueOf(proposal.getCompletion())); adjustCompleteItem($); $.setSortText(SortTextHelper.computeSortText(proposal)); return $; @@ -166,6 +169,17 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { private void adjustCompleteItem(CompletionItem item) { if (item.getKind() == CompletionItemKind.Function) { + // Merge the label details into the label property + // because the completion provider in DEBUG CONSOLE + // doesn't support the label details. + CompletionItemLabelDetails labelDetails = item.getLabelDetails(); + if (labelDetails != null && StringUtils.isNotBlank(labelDetails.getDetail())) { + item.setLabel(item.getLabel() + labelDetails.getDetail()); + } + if (labelDetails != null && StringUtils.isNotBlank(labelDetails.getDescription())) { + item.setLabel(item.getLabel() + " : " + labelDetails.getDescription()); + } + String text = item.getInsertText(); if (StringUtils.isNotBlank(text) && !text.endsWith(")")) { item.setInsertText(text + "()"); From f8b6db6eea40a6faab6be9c8ec8a8844584533ac Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 17 Aug 2023 14:35:42 +0800 Subject: [PATCH 148/206] Display the package name in the label property when suggesting types in DEBUG CONSOLE (#505) --- .../internal/CompletionProposalRequestor.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index 41b4da929..1df752f24 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -28,6 +28,7 @@ import org.eclipse.jdt.core.CompletionContext; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.core.CompletionRequestor; +import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; @@ -154,7 +155,7 @@ public List getCompletionItems() { */ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { final CompletionItem $ = new CompletionItem(); - $.setKind(mapKind(proposal.getKind())); + $.setKind(mapKind(proposal.getKind(), proposal.getFlags())); Map data = new HashMap<>(); data.put(CompletionResolveHandler.DATA_FIELD_REQUEST_ID, String.valueOf(response.getId())); data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, String.valueOf(index)); @@ -168,7 +169,15 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { } private void adjustCompleteItem(CompletionItem item) { - if (item.getKind() == CompletionItemKind.Function) { + CompletionItemKind itemKind = item.getKind(); + if (itemKind == CompletionItemKind.Class || itemKind == CompletionItemKind.Interface + || itemKind == CompletionItemKind.Enum) { + // Display the package name in the label property. + CompletionItemLabelDetails labelDetails = item.getLabelDetails(); + if (labelDetails != null && StringUtils.isNotBlank(labelDetails.getDescription())) { + item.setLabel(item.getLabel() + " - " + labelDetails.getDescription()); + } + } else if (itemKind == CompletionItemKind.Function) { // Merge the label details into the label property // because the completion provider in DEBUG CONSOLE // doesn't support the label details. @@ -195,7 +204,7 @@ public void acceptContext(CompletionContext context) { this.descriptionProvider = new CompletionProposalDescriptionProvider(context); } - private CompletionItemKind mapKind(final int kind) { + private CompletionItemKind mapKind(final int kind, final int flags) { // When a new CompletionItemKind is added, don't forget to update // SUPPORTED_KINDS switch (kind) { @@ -204,6 +213,11 @@ private CompletionItemKind mapKind(final int kind) { return CompletionItemKind.Constructor; case CompletionProposal.ANONYMOUS_CLASS_DECLARATION: case CompletionProposal.TYPE_REF: + if (Flags.isInterface(flags)) { + return CompletionItemKind.Interface; + } else if (Flags.isEnum(flags)) { + return CompletionItemKind.Enum; + } return CompletionItemKind.Class; case CompletionProposal.FIELD_IMPORT: case CompletionProposal.METHOD_IMPORT: @@ -213,6 +227,9 @@ private CompletionItemKind mapKind(final int kind) { return CompletionItemKind.Module; case CompletionProposal.FIELD_REF: case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER: + if (Flags.isStatic(flags) && Flags.isFinal(flags)) { + return CompletionItemKind.Constant; + } return CompletionItemKind.Field; case CompletionProposal.KEYWORD: return CompletionItemKind.Keyword; From c0e8f92f54defe9af77c6d091bb96a57ff114ac1 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Aug 2023 11:33:14 +0800 Subject: [PATCH 149/206] bump version to 0.49.0 (#506) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 8ebf3fb5f..5412d44a4 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 2f7e62dc3..c2b013548 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 520d7d0da..cdc8e2f81 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.48.0 +Bundle-Version: 0.49.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.48.0.jar + lib/com.microsoft.java.debug.core-0.49.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index e50a7f992..7fd392bbe 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.48.0 + 0.49.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 8cf6b4f60..dc3efd066 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 27132434c..0adda64ab 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index c7fcc1716..cfae4adb8 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 55e421c19..c71560b64 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.48.0 + 0.49.0 pom Java Debug Server for Visual Studio Code From 8905c4a7367991a492ba280d8ba616f73fdea557 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Tue, 19 Sep 2023 00:51:18 -0700 Subject: [PATCH 150/206] Special handling build only when it's build server project and not buildship project (#511) Signed-off-by: Sheng Chen --- .../java/com/microsoft/java/debug/plugin/internal/Compile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java index d38227785..9cb6fbbfa 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -58,7 +58,7 @@ public static Object compile(CompileParams params, IProgressMonitor monitor) { } } - if (isBspProject(mainProject)) { + if (isBspProject(mainProject) && !ProjectUtils.isGradleProject(mainProject)) { // Just need to trigger a build for the target project, the Gradle build server will // handle the build dependencies for us. try { From 4f42baa62997c29507f930e009f28d4453f59596 Mon Sep 17 00:00:00 2001 From: Vladimir Makaev Date: Tue, 26 Sep 2023 09:50:31 +0100 Subject: [PATCH 151/206] Add extensibility points to DebugAdapter and ProtocolServer (#509) Co-authored-by: Vladimir Makayev Co-authored-by: Jinbo Wang --- .../java/debug/core/adapter/DebugAdapter.java | 9 +++++---- .../core/adapter/IDebugAdapterFactory.java | 19 +++++++++++++++++++ .../debug/core/adapter/ProtocolServer.java | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 10c0df902..8f595151f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -102,7 +102,7 @@ public CompletableFuture dispatchRequest(Messages.Request req } } - private void initialize() { + protected void initialize() { // Register request handlers. // When there are multiple handlers registered for the same request, follow the rule "first register, first execute". registerHandler(new InitializeRequestHandler()); @@ -141,15 +141,15 @@ private void initialize() { registerHandlerForNoDebug(new ProcessIdHandler()); } - private void registerHandlerForDebug(IDebugRequestHandler handler) { + protected void registerHandlerForDebug(IDebugRequestHandler handler) { registerHandler(requestHandlersForDebug, handler); } - private void registerHandlerForNoDebug(IDebugRequestHandler handler) { + protected void registerHandlerForNoDebug(IDebugRequestHandler handler) { registerHandler(requestHandlersForNoDebug, handler); } - private void registerHandler(IDebugRequestHandler handler) { + protected void registerHandler(IDebugRequestHandler handler) { registerHandler(requestHandlersForDebug, handler); registerHandler(requestHandlersForNoDebug, handler); } @@ -165,4 +165,5 @@ private void registerHandler(Map> requestHan handlerList.add(handler); } } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java new file mode 100644 index 000000000..d392d3b48 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2023 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.core.adapter; + +import com.microsoft.java.debug.core.protocol.IProtocolServer; + +@FunctionalInterface +public interface IDebugAdapterFactory { + public IDebugAdapter create(IProtocolServer server); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java index 0526293b9..9e503e0af 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java @@ -52,6 +52,20 @@ public ProtocolServer(InputStream input, OutputStream output, IProviderContext c debugAdapter = new DebugAdapter(this, context); } + /** + * Constructs a protocol server instance based on the given input stream and output stream. + * @param input + * the input stream + * @param output + * the output stream + * @param debugAdapterFactory + * factory to create debug adapter that implements DAP communication + */ + public ProtocolServer(InputStream input, OutputStream output, IDebugAdapterFactory debugAdapterFactory) { + super(input, output); + debugAdapter = debugAdapterFactory.create(this); + } + /** * A while-loop to parse input data and send output data constantly. */ From 488a56ef9fe4c8766b47af6443986936283e1b7f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Nov 2023 14:02:42 +0800 Subject: [PATCH 152/206] Bump version to 0.50.0 (#516) --- .mvn/wrapper/maven-wrapper.properties | 2 +- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- .../com.microsoft.java.debug.tp.target | 4 ++-- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 56bb0164e..9e0264d04 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip \ No newline at end of file +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip \ No newline at end of file diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 5412d44a4..635993d56 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c2b013548..ca5f41e6f 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index cdc8e2f81..84a0ab36f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.49.0 +Bundle-Version: 0.50.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.49.0.jar + lib/com.microsoft.java.debug.core-0.50.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 7fd392bbe..b895ffd28 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.49.0 + 0.50.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index dc3efd066..639d50082 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 0adda64ab..4f05eb331 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index fbc981533..1c10398c4 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,11 +16,11 @@ - + - + diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index cfae4adb8..80395f6dd 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index c71560b64..d562f82b5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,12 +6,12 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.49.0 + 0.50.0 pom Java Debug Server for Visual Studio Code UTF-8 - 2.7.3 + 4.0.3 ${basedir} From b0c70e3ddbf8cca9dc697f02f84914d7792b5982 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 21 Nov 2023 18:01:28 +0800 Subject: [PATCH 153/206] Update target platform dependencies to fix build errors (#518) --- .../com.microsoft.java.debug.tp.target | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 1c10398c4..ef0a5284e 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,11 +16,8 @@ - - - - + From 39046b7ab8b1acc47894745f517fcd5fe010cf64 Mon Sep 17 00:00:00 2001 From: John Olson <69521422+olsonjohn@users.noreply.github.com> Date: Sun, 10 Dec 2023 20:21:59 -0600 Subject: [PATCH 154/206] updated dependency location (#528) --- .../com.microsoft.java.debug.tp.target | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index ef0a5284e..2e7920ed7 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -17,7 +17,7 @@ - + @@ -32,4 +32,4 @@ - \ No newline at end of file + From 27f72fbdf79097794d8a546a1206e620ecdde863 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 12 Dec 2023 03:24:24 +0100 Subject: [PATCH 155/206] Fix generic return type lambda breakpoint issue. Fix #1359 (#499) * Fix generic return type lambda breakpoint issue. Fix #1359 The fix tries to compare the runtime lambda method signature with none generic version of the method signature found from AST traversal as a additional comparison to what is there. --- .../microsoft/java/debug/core/Breakpoint.java | 25 ++++++++++++++++++- .../java/debug/core/BreakpointTest.java | 17 +++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index dfdbad4bb..82859b268 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -325,7 +325,8 @@ private Location findMethodLocaiton(ReferenceType refType, String methodName, St for (Method method : methods) { if (!method.isAbstract() && !method.isNative() && methodName.equals(method.name()) - && (methodSiguature.equals(method.genericSignature()) || methodSiguature.equals(method.signature()))) { + && (methodSiguature.equals(method.genericSignature()) || methodSiguature.equals(method.signature()) + || toNoneGeneric(methodSiguature).equals(method.signature()))) { location = method.location(); break; } @@ -334,6 +335,28 @@ private Location findMethodLocaiton(ReferenceType refType, String methodName, St return location; } + static String toNoneGeneric(String genericSig) { + StringBuilder builder = new StringBuilder(); + boolean append = true; + int depth = 0; + char[] chars = genericSig.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (c == '<') { + depth++; + append = (depth == 0); + } + if (append) { + builder.append(c); + } + if (c == '>') { + depth--; + append = (depth == 0); + } + } + return builder.toString(); + } + private List findLocaitonsOfLine(Method method, int lineNumber) { try { return method.locationsOfLine(lineNumber); diff --git a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java new file mode 100644 index 000000000..5cc6fa3d7 --- /dev/null +++ b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java @@ -0,0 +1,17 @@ +package com.microsoft.java.debug.core; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class BreakpointTest { + @Test + public void testToNoneGeneric() { + assertEquals("Ljava.util.List;", Breakpoint.toNoneGeneric("Ljava.util.List;")); + assertEquals("(Ljava/util/Map;)Ljava/util/Map;", Breakpoint.toNoneGeneric( + "(Ljava/util/Map;>;)Ljava/util/Map;>;")); + assertEquals("(Ljava/util/Map;)Ljava/util/Map;", + Breakpoint.toNoneGeneric( + "(Ljava/util/Map;Ljava/util/List;>;)Ljava/util/Map;Ljava/util/List;>;")); + } +} From 9bdd9977860e5912a39b8efbaf62b9ee5577ec98 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Wed, 13 Dec 2023 02:54:30 +0100 Subject: [PATCH 156/206] Improve inline breakpoint discovery when expression is multiline. Fix #521 (#522) * Improve inline breakpoint discovery when expression is multiline. Fix #521 --- .../microsoft/java/debug/BreakpointLocationLocator.java | 4 ++-- .../debug/plugin/internal/JdtSourceLookUpProvider.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index b14afde83..97581242e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -22,8 +22,8 @@ public class BreakpointLocationLocator public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, - boolean bestMatch) { - super(compilationUnit, lineNumber, bindingsResolved, bestMatch); + boolean bestMatch, int offset, int end) { + super(compilationUnit, lineNumber, bindingsResolved, bestMatch, offset, end); } @Override 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 f0c8795cc..1ebf4c7c3 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 @@ -72,8 +72,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; @@ -210,8 +210,11 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB // mark it as "unverified". // In future, we could consider supporting to update the breakpoint to a valid // location. + + // passing the offset to the constructor, it can recognize the multiline lambda + // expression well BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, - sourceLine, true, true); + sourceLine, true, true, astUnit.getPosition(sourceLine, 0), 0); astUnit.accept(locator); // When the final valid line location is same as the original line, that // represents it's a valid breakpoint. From f8da9e2e2f55a2cefaf658ae24154c784eaa113e Mon Sep 17 00:00:00 2001 From: Vladimir Makaev Date: Mon, 8 Jan 2024 03:22:46 +0000 Subject: [PATCH 157/206] Allow ISourceLookUpProvider specify whether file is Local or Remote (#515) --- .../core/adapter/DebugAdapterContext.java | 4 +-- .../core/adapter/IDebugAdapterContext.java | 2 +- .../core/adapter/ISourceLookUpProvider.java | 28 ++++++++++----- .../java/debug/core/adapter/Source.java | 30 ++++++++++++++++ .../java/debug/core/adapter/SourceType.java | 16 +++++++++ .../handler/StackTraceRequestHandler.java | 35 +++++++++++++------ 6 files changed, 93 insertions(+), 22 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index f8ac01c3e..bbf215c67 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -32,7 +32,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private static final int MAX_CACHE_ITEMS = 10000; private final StepFilters defaultFilters = new StepFilters(); - private Map sourceMappingCache = Collections.synchronizedMap(new LRUCache<>(MAX_CACHE_ITEMS)); + private Map sourceMappingCache = Collections.synchronizedMap(new LRUCache<>(MAX_CACHE_ITEMS)); private IProviderContext providerContext; private IProtocolServer server; @@ -212,7 +212,7 @@ public void setVariableFormatter(IVariableFormatter variableFormatter) { } @Override - public Map getSourceLookupCache() { + public Map getSourceLookupCache() { return sourceMappingCache; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 9a38e8598..e65270eef 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -85,7 +85,7 @@ public interface IDebugAdapterContext { void setVariableFormatter(IVariableFormatter variableFormatter); - Map getSourceLookupCache(); + Map getSourceLookupCache(); void setDebuggeeEncoding(Charset encoding); 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 5ffed6e0f..f33742852 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 @@ -8,7 +8,6 @@ * Contributors: * Microsoft Corporation - initial API and implementation *******************************************************************************/ - package com.microsoft.java.debug.core.adapter; import java.util.List; @@ -42,18 +41,31 @@ public interface ISourceLookUpProvider extends IProvider { JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException; /** - * 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 path (e.g. com\microsoft\java\debug\core\adapter\ISourceLookupProvider.java). - * @return the associated source file uri. + * Deprecated, please use {@link #getSource(String, String)} instead. */ + @Deprecated String getSourceFileURI(String fullyQualifiedName, String sourcePath); String getSourceContents(String uri); + /** + * Retrieves a {@link Source} object representing the source code associated with the given fully qualified class name and source file path. + * The implementation of this interface can determine a source is "local" or "remote". + * In case of "remote" a follow up "source" request will be issued by the client + * + * @param fullyQualifiedName + * the fully qualified class name, + * e.g., "com.microsoft.java.debug.core.adapter.ISourceLookUpProvider". + * @param sourcePath + * the qualified source file path, + * e.g., "com/microsoft/java/debug/core/adapter/ISourceLookupProvider.java". + * @return A {@link Source} object encapsulating the source file URI obtained from + * {@link #getSourceFileURI(String, String)} and the source type as {@link SourceType#LOCAL}. + */ + default Source getSource(String fullyQualifiedName, String sourcePath) { + return new Source(getSourceFileURI(fullyQualifiedName, sourcePath), SourceType.LOCAL); + } + /** * Returns the Java runtime that the specified project's build path used. * @param projectName diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java new file mode 100644 index 000000000..d00b4cb4c --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.core.adapter; + +public class Source { + public final String uri; + public final SourceType type; + + public Source(String uri, SourceType type) { + this.uri = uri; + this.type = type; + } + + public String getUri() { + return this.uri; + } + + public SourceType getType() { + return this.type; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java new file mode 100644 index 000000000..724bf3bda --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * 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.core.adapter; + +public enum SourceType { + REMOTE, + LOCAL +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 0ba3b2004..49f304771 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -36,6 +36,8 @@ import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.adapter.Source; +import com.microsoft.java.debug.core.adapter.SourceType; import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; import com.microsoft.java.debug.core.protocol.Messages.Response; @@ -141,7 +143,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } private static List resolveStackFrameInfos(StackFrame[] frames, boolean async) - throws AbsentInformationException, IncompatibleThreadStateException { + throws AbsentInformationException, IncompatibleThreadStateException { List jdiFrames = new ArrayList<>(); List> futures = new ArrayList<>(); for (StackFrame frame : frames) { @@ -221,7 +223,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra clientSource = null; } } else if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON - && clientSource != null && clientSource.path != null) { + && clientSource != null && clientSource.path != null) { // Align the original line with the decompiled line. int[] lineMappings = context.getProvider(ISourceLookUpProvider.class).getOriginalLineMappings(clientSource.path); int[] renderLines = AdapterUtils.binarySearchMappedLines(lineMappings, clientLineNumber); @@ -244,7 +246,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra }); if (match) { clientColumnNumber = AdapterUtils.convertColumnNumber(breakpoint.getColumnNumber(), - context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); } } } @@ -256,33 +258,44 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra /** * Find the source mapping for the specified source file name. */ - public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedName, String sourceName, String relativeSourcePath, + public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedName, String sourceName, + String relativeSourcePath, IDebugAdapterContext context) throws URISyntaxException { + // use a lru cache for better performance - String uri = context.getSourceLookupCache().computeIfAbsent(fullyQualifiedName, key -> { - String fromProvider = context.getProvider(ISourceLookUpProvider.class).getSourceFileURI(key, relativeSourcePath); - // avoid return null which will cause the compute function executed again - return StringUtils.isBlank(fromProvider) ? "" : fromProvider; + Source source = context.getSourceLookupCache().computeIfAbsent(fullyQualifiedName, key -> { + Source result = context.getProvider(ISourceLookUpProvider.class).getSource(key, relativeSourcePath); + if (result == null) { + return new Source("", SourceType.LOCAL); + } + return result; }); + Integer sourceReference = 0; + String uri = source.getUri(); + + if (source.getType().equals(SourceType.REMOTE)) { + sourceReference = context.createSourceReference(source.getUri()); + } + if (!StringUtils.isBlank(uri)) { // The Source.path could be a file system path or uri string. if (uri.startsWith("file:")) { String clientPath = AdapterUtils.convertPath(uri, context.isDebuggerPathsAreUri(), context.isClientPathsAreUri()); - return new Types.Source(sourceName, clientPath, 0); + return new Types.Source(sourceName, clientPath, sourceReference); } else { // If the debugger returns uri in the Source.path for the StackTrace response, VSCode client will try to find a TextDocumentContentProvider // to render the contents. // Language Support for Java by Red Hat extension has already registered a jdt TextDocumentContentProvider to parse the jdt-based uri. // The jdt uri looks like 'jdt://contents/rt.jar/java.io/PrintStream.class?=1.helloworld/%5C/usr%5C/lib%5C/jvm%5C/java-8-oracle%5C/jre%5C/ // lib%5C/rt.jar%3Cjava.io(PrintStream.class'. - return new Types.Source(sourceName, uri, 0); + return new Types.Source(sourceName, uri, sourceReference); } } else { // If the source lookup engine cannot find the source file, then lookup it in the source directories specified by user. String absoluteSourcepath = AdapterUtils.sourceLookup(context.getSourcePaths(), relativeSourcePath); if (absoluteSourcepath != null) { - return new Types.Source(sourceName, absoluteSourcepath, 0); + return new Types.Source(sourceName, absoluteSourcepath, sourceReference); } else { return null; } From b578b50078cef9ce7b37710966bd2c2496c1c00d Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Thu, 18 Jan 2024 15:39:43 +0800 Subject: [PATCH 158/206] Migrate to 1es pipelines (#532) --- .azure-pipelines/signjars-nightly.yml | 146 ++++++++++++++++ .azure-pipelines/signjars-rc.yml | 236 ++++++++++++++++++++++++++ 2 files changed, 382 insertions(+) create mode 100644 .azure-pipelines/signjars-nightly.yml create mode 100644 .azure-pipelines/signjars-rc.yml diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml new file mode 100644 index 000000000..bdaeea871 --- /dev/null +++ b/.azure-pipelines/signjars-nightly.yml @@ -0,0 +1,146 @@ +name: $(Date:yyyyMMdd).$(Rev:r) +variables: + - name: Codeql.Enabled + value: true +schedules: + - cron: 0 5 * * 1,2,3,4,5 + branches: + include: + - refs/heads/main +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release +trigger: none +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + os: linux + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Ubuntu-2004 + sdl: + sourceAnalysisPool: + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Windows_2022 + os: windows + customBuildTags: + - MigrationTooling-mseng-VSJava-13474-Tool + stages: + - stage: Build + jobs: + - job: Job_1 + displayName: Sign-Jars-Nightly + templateContext: + outputs: + - output: pipelineArtifact + artifactName: plugin + targetPath: $(Build.ArtifactStagingDirectory) + displayName: "Publish Artifact: plugin" + steps: + - checkout: self + fetchTags: true + - task: JavaToolInstaller@0 + displayName: Use Java 17 + inputs: + versionSpec: "17" + jdkArchitectureOption: x64 + jdkSourceOption: PreInstalled + - task: CmdLine@2 + displayName: Parse the release version from pom.xml + inputs: + script: |- + #!/bin/bash + + sudo apt-get install xmlstarlet + xmlstarlet --version + RELEASE_VERSION=$(xmlstarlet sel -t -v "/_:project/_:version" pom.xml) + echo $RELEASE_VERSION + echo "##vso[task.setvariable variable=RELEASE_VERSION]$RELEASE_VERSION" + - task: CmdLine@2 + displayName: Build core.jar + inputs: + script: | + ./mvnw clean install -f com.microsoft.java.debug.core/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign core.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.core*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: install signed core.jar + inputs: + script: cp jars/com.microsoft.java.debug.core*.jar .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/ + - task: CmdLine@2 + displayName: Build plugin.jar + inputs: + script: |- + ./mvnw clean install -f com.microsoft.java.debug.target/pom.xml -Dmaven.repo.local=./.repository + ./mvnw clean install -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign plugin.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.plugin*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CopyFiles@2 + displayName: "Copy plugin.jar to: $(Build.ArtifactStagingDirectory)" + inputs: + Contents: |+ + jars/com.microsoft.java.debug.plugin*.jar + + TargetFolder: $(Build.ArtifactStagingDirectory) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml new file mode 100644 index 000000000..4e51eff7f --- /dev/null +++ b/.azure-pipelines/signjars-rc.yml @@ -0,0 +1,236 @@ +name: $(Date:yyyyMMdd).$(Rev:r) +variables: + - name: Codeql.Enabled + value: true +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release +trigger: none +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + os: linux + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Ubuntu-2004 + sdl: + sourceAnalysisPool: + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Windows_2022 + os: windows + customBuildTags: + - MigrationTooling-mseng-VSJava-9151-Tool + stages: + - stage: Build + jobs: + - job: Job_1 + displayName: Sign-Jars-RC + templateContext: + outputs: + - output: pipelineArtifact + artifactName: m2 + targetPath: $(Build.ArtifactStagingDirectory)/m2 + displayName: "Publish Artifact: m2" + - output: pipelineArtifact + artifactName: p2 + targetPath: $(Build.ArtifactStagingDirectory)/p2 + displayName: "Publish Artifact: p2" + steps: + - checkout: self + fetchTags: true + - task: JavaToolInstaller@0 + displayName: Use Java 17 + inputs: + versionSpec: "17" + jdkArchitectureOption: x64 + jdkSourceOption: PreInstalled + - task: CmdLine@2 + displayName: Parse the release version from pom.xml + inputs: + script: |- + #!/bin/bash + + sudo apt-get install xmlstarlet + xmlstarlet --version + RELEASE_VERSION=$(xmlstarlet sel -t -v "/_:project/_:version" pom.xml) + echo $RELEASE_VERSION + echo "##vso[task.setvariable variable=RELEASE_VERSION]$RELEASE_VERSION" + - task: CmdLine@2 + displayName: Build core.jar + inputs: + script: | + ./mvnw -N clean install -Dmaven.repo.local=./.repository + + ./mvnw clean install -f com.microsoft.java.debug.core/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign core.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.core*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: install signed core.jar + inputs: + script: cp jars/com.microsoft.java.debug.core*.jar .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/ + - task: CmdLine@2 + displayName: Build plugin.jar + inputs: + script: |- + ./mvnw clean install -f com.microsoft.java.debug.target/pom.xml -Dmaven.repo.local=./.repository + ./mvnw clean install -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign plugin.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.plugin*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: install signed plugin.jar + inputs: + script: cp jars/com.microsoft.java.debug.plugin*.jar .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/ + - task: CmdLine@2 + displayName: Build p2 artifacts + inputs: + script: |- + # 3. Build the p2 artifacts. + ./mvnw clean package -f com.microsoft.java.debug.repository/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p p2/target + cp -r com.microsoft.java.debug.repository/target/repository p2/target/ + cp com.microsoft.java.debug.repository/pushToBintray.sh p2/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign p2 + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: p2 + Pattern: "*.jar" + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: build m2 artifacts + inputs: + script: | + ./mvnw source:jar -f com.microsoft.java.debug.core/pom.xml -Dmaven.repo.local=./.repository + ./mvnw javadoc:jar -f com.microsoft.java.debug.core/pom.xml -Ddoclint=none -Dmaven.repo.local=./.repository + + ./mvnw source:jar -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository + ./mvnw javadoc:jar -f com.microsoft.java.debug.plugin/pom.xml -Ddoclint=none -Dmaven.repo.local=./.repository + + mkdir -p m2/java-debug-parent + cp pom.xml m2/java-debug-parent/java-debug-parent-$RELEASE_VERSION.pom + + mkdir -p m2/com.microsoft.java.debug.core + cp com.microsoft.java.debug.core/target/com.microsoft.java.debug.core*.jar m2/com.microsoft.java.debug.core + cp com.microsoft.java.debug.core/pom.xml m2/com.microsoft.java.debug.core/com.microsoft.java.debug.core-$RELEASE_VERSION.pom + + mkdir -p m2/com.microsoft.java.debug.plugin + cp com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin*.jar m2/com.microsoft.java.debug.plugin + cp com.microsoft.java.debug.plugin/pom.xml m2/com.microsoft.java.debug.plugin/com.microsoft.java.debug.plugin-$RELEASE_VERSION.pom + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign m2 + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: m2 + Pattern: "*.jar" + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CopyFiles@2 + displayName: "Copy p2/m2 to: $(Build.ArtifactStagingDirectory)" + inputs: + Contents: |+ + p2/** + m2/** + + TargetFolder: $(Build.ArtifactStagingDirectory) From 431de232d233783f2711a880d0c498a27baccb13 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 30 Jan 2024 06:13:54 +0100 Subject: [PATCH 159/206] Fix the step-into target on multi line expression. Fix #519 (#520) * Fix the step-into target on multi line expression. Fix #519 --------- Co-authored-by: Jinbo Wang --- .../core/adapter/handler/StepRequestHandler.java | 11 ++++++++--- .../plugin/internal/MethodInvocationLocator.java | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 71f28355a..e8f782668 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -268,7 +268,8 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (currentStackDepth == threadState.stackDepth) { // If the ending step location is same as the original location where the step into operation is originated, // do another step of the same kind. - if (isSameLocation(currentStepLocation, threadState.stepLocation)) { + if (isSameLocation(currentStepLocation, threadState.stepLocation, + threadState.targetStepIn)) { context.getStepResultManager().removeMethodResult(threadId); threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, context.getStepFilters().allowClasses, @@ -438,15 +439,19 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL return true; } - private boolean isSameLocation(Location original, Location current) { + private boolean isSameLocation(Location current, Location original, MethodInvocation targetStepIn) { if (original == null || current == null) { return false; } Method originalMethod = original.method(); Method currentMethod = current.method(); + // if the lines doesn't match, check if the current line is still behind the + // target if a target exist. This handles where the target is part of a + // expression which is wrapped. return originalMethod.equals(currentMethod) - && original.lineNumber() == current.lineNumber(); + && (original.lineNumber() == current.lineNumber() + || (targetStepIn != null && targetStepIn.lineEnd >= current.lineNumber())); } /** diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java index a25fbc3f6..654cbb00b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -217,7 +217,7 @@ public boolean visit(ClassInstanceCreation node) { private boolean shouldVisitNode(ASTNode node) { int start = unit.getLineNumber(node.getStartPosition()); - int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + int end = unit.getLineNumber(node.getStartPosition() + node.getLength() - 1); if (line >= start && line <= end) { return true; From fcfa80d2ba6b6e60da34d9451c4fc297fc821f0d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 30 Jan 2024 13:59:01 +0800 Subject: [PATCH 160/206] Bump version to 0.51.0 (#533) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 635993d56..cd41ea9bc 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index ca5f41e6f..4d55efda0 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 84a0ab36f..077d85c95 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.50.0 +Bundle-Version: 0.51.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.50.0.jar + lib/com.microsoft.java.debug.core-0.51.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index b895ffd28..68e29fa33 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.50.0 + 0.51.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 639d50082..332d14f9e 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 4f05eb331..314b894cb 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 80395f6dd..42dbf2724 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index d562f82b5..46aece86b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.50.0 + 0.51.0 pom Java Debug Server for Visual Studio Code From b5bc37d18e885c686866b40fbf7e8f9ed82a7d47 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 6 Feb 2024 09:20:06 +0800 Subject: [PATCH 161/206] Fix build errors (#535) * Fix build errors --- .../internal/CompletionProposalRequestor.java | 20 ++++++++++++++++++- .../com.microsoft.java.debug.tp.target | 5 ++++- pom.xml | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index 1df752f24..431c283f5 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -72,6 +72,8 @@ public final class CompletionProposalRequestor extends CompletionRequestor { CompletionItemKind.Text); // @formatter:on + private static boolean isFilterFailed = false; + /** * Constructor. * @param typeRoot ITypeRoot @@ -321,7 +323,7 @@ private boolean isFiltered(CompletionProposal proposal) { case CompletionProposal.JAVADOC_TYPE_REF: case CompletionProposal.TYPE_REF: { char[] declaringType = getDeclaringType(proposal); - return declaringType != null && org.eclipse.jdt.ls.core.internal.contentassist.TypeFilter.isFiltered(declaringType); + return declaringType != null && isFiltered(declaringType); } default: // do nothing } @@ -332,6 +334,22 @@ private boolean isFiltered(CompletionProposal proposal) { return false; } + // Temp workaround for the completion error https://github.com/microsoft/java-debug/issues/534 + private static boolean isFiltered(char[] fullTypeName) { + if (isFilterFailed) { + return false; + } + + try { + return JavaLanguageServerPlugin.getInstance().getTypeFilter().filter(new String(fullTypeName)); + } catch (NoSuchMethodError ex) { + isFilterFailed = true; + JavaLanguageServerPlugin.logException("isFiltered for the completion failed.", ex); + } + + return false; + } + /** * copied from * org.eclipse.jdt.ui.text.java.CompletionProposalCollector.getDeclaringType(CompletionProposal) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 2e7920ed7..828da98a2 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -15,7 +15,10 @@ - + + + + diff --git a/pom.xml b/pom.xml index 46aece86b..8911c9d58 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.3 + 4.0.5 ${basedir} From f212c3a2f7aa2490c2df5dfa8dfda9fd0dd70d09 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 19 Feb 2024 11:27:03 +0800 Subject: [PATCH 162/206] Bump version to 0.51.1 (#536) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index cd41ea9bc..a7c2ca8ce 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 4d55efda0..b4fb64d7d 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 077d85c95..73af47a6d 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.51.0 +Bundle-Version: 0.51.1 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.51.0.jar + lib/com.microsoft.java.debug.core-0.51.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 68e29fa33..4657ab08b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.51.0 + 0.51.1 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 332d14f9e..53e8bf963 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 314b894cb..68ff9fc42 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 42dbf2724..6977f1831 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 8911c9d58..b039fe1c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.51.0 + 0.51.1 pom Java Debug Server for Visual Studio Code From 6e064b0ca2e8d894ab66b6b341dae00f4060317a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 19 Feb 2024 13:39:33 +0800 Subject: [PATCH 163/206] Update signjars-rc.yml for Azure Pipelines --- .azure-pipelines/signjars-rc.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 4e51eff7f..a8f1d3268 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -74,7 +74,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -114,7 +114,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams @@ -155,7 +155,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign p2 inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: p2 Pattern: "*.jar" signConfigType: inlineSignParams @@ -202,7 +202,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign m2 inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: m2 Pattern: "*.jar" signConfigType: inlineSignParams From 5f0d1bb9109f2ebfcc1f8faf12eac77f59b5a380 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 19 Feb 2024 14:59:26 +0800 Subject: [PATCH 164/206] Update signjars-rc.yml for Azure Pipelines --- .azure-pipelines/signjars-rc.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index a8f1d3268..ebb333f70 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -74,7 +74,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -114,7 +114,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams @@ -155,7 +155,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign p2 inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: p2 Pattern: "*.jar" signConfigType: inlineSignParams @@ -202,7 +202,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign m2 inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: m2 Pattern: "*.jar" signConfigType: inlineSignParams From 6c2377eae78c308bf87fbc2a6438a93734b06f5f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 23 Feb 2024 14:18:08 +0800 Subject: [PATCH 165/206] Update signjars-nightly.yml for Azure Pipelines --- .azure-pipelines/signjars-nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index bdaeea871..cae5ac6d5 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -73,7 +73,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -113,7 +113,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams From 98db4b16262488c5351a922f5781f7b95e1290d4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 14 Mar 2024 13:58:54 +0800 Subject: [PATCH 166/206] Update com.microsoft.java.debug.tp.target to 4.31 release (#538) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 828da98a2..55695f6a9 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,7 +16,7 @@ - + From 736dcacf41e4da79b7419e7b7067f9c886252530 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 18 Mar 2024 18:29:13 +0800 Subject: [PATCH 167/206] Update lsp4j to 0.22.0 (#541) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 55695f6a9..200e532e3 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -31,7 +31,7 @@ - + From 56c014531273afd1c3a3b30712c2fb4d257e61f0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 21 Mar 2024 20:34:44 +0800 Subject: [PATCH 168/206] Support debugging Java 21 instance main method and unnamed class (#542) * Support debugging Java 21 instance main method and unnamed class --- .github/workflows/build.yml | 4 +- .../internal/JdtSourceLookUpProvider.java | 32 ++++- .../internal/ResolveMainClassHandler.java | 124 ++++++++++-------- .../internal/ResolveMainMethodHandler.java | 51 ++++++- .../com.microsoft.java.debug.tp.target | 9 +- pom.xml | 2 +- 6 files changed, 158 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41c85cb62..1ace44224 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: ${{ runner.os }}-maven- - name: Verify - run: ./mvnw clean verify + run: ./mvnw clean verify -U - name: Checkstyle run: ./mvnw checkstyle:check @@ -85,7 +85,7 @@ jobs: ${{ runner.os }}-maven- - name: Verify - run: ./mvnw clean verify + run: ./mvnw clean verify -U - name: Checkstyle run: ./mvnw checkstyle:check 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 1ebf4c7c3..c96b98342 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 @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; @@ -56,6 +57,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.UnnamedClass; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; @@ -172,6 +174,11 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { + List types = astUnit.types(); + String unnamedClass = null; + if (types.size() == 1 && types.get(0) instanceof UnnamedClass) { + unnamedClass = inferPrimaryTypeName(sourceUri, astUnit); + } Map resolvedLocations = new HashMap<>(); for (JavaBreakpointLocation sourceLocation : sourceLocations) { int sourceLine = sourceLocation.lineNumber(); @@ -222,7 +229,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB // be hit in current implementation. if (sourceLine == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { - sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setClassName(StringUtils.isBlank(unnamedClass) ? locator.getFullyQualifiedTypeName() : unnamedClass); if (resolvedLocations.containsKey(sourceLine)) { sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); } else { @@ -231,7 +238,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB resolvedLocations.put(sourceLine, inlineLocations); } } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setClassName(StringUtils.isBlank(unnamedClass) ? locator.getFullyQualifiedTypeName() : unnamedClass); sourceLocation.setMethodName(locator.getMethodName()); sourceLocation.setMethodSignature(locator.getMethodSignature()); } @@ -241,6 +248,27 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB return sourceLocations; } + private String inferPrimaryTypeName(String uri, CompilationUnit astUnit) { + String fileName = ""; + String filePath = AdapterUtils.toPath(uri); + if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { + fileName = Paths.get(filePath).getFileName().toString(); + } else if (astUnit.getTypeRoot() != null) { + fileName = astUnit.getTypeRoot().getElementName(); + } + + if (StringUtils.isNotBlank(fileName)) { + String[] extensions = JavaCore.getJavaLikeExtensions(); + for (String extension : extensions) { + if (fileName.endsWith("." + extension)) { + return fileName.substring(0, fileName.length() - 1 - extension.length()); + } + } + } + + return fileName; + } + private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { List locations = new ArrayList<>(); // The starting position of each line is the default breakpoint location for diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index f70d0c044..38ad1532b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -43,6 +43,7 @@ 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.core.SourceMethod; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -101,8 +102,7 @@ private List resolveMainClassUnderPaths(List parentPaths) // Limit to search main method from source code only. IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { @Override @@ -110,40 +110,36 @@ public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IMethod) { IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null) { - String moduleName = JdtUtils.getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (isMainMethod(method)) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; } - String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); - if (parentPaths.isEmpty() - || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) - || isContainedInInvisibleProject(project, parentPaths)) { - String filePath = null; - - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + } + String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); + if (parentPaths.isEmpty() + || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) + || isContainedInInvisibleProject(project, parentPaths)) { + String filePath = null; + + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } - res.add(new ResolutionItem(mainClass, projectName, filePath)); } + res.add(new ResolutionItem(mainClass, projectName, filePath)); } } } - } catch (JavaModelException e) { - // ignore } } } @@ -166,8 +162,7 @@ private List resolveMainClassUnderProject(final String projectNa IJavaProject javaProject = ProjectUtils.getJavaProject(projectName); IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(javaProject == null ? new IJavaProject[0] : new IJavaProject[] {javaProject}, IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { @Override @@ -175,35 +170,31 @@ public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IMethod) { IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null) { - String moduleName = JdtUtils.getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (isMainMethod(method)) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; } + } - String filePath = null; - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } - res.add(new ResolutionItem(mainClass, projectName, filePath)); } + res.add(new ResolutionItem(mainClass, projectName, filePath)); } } - } catch (JavaModelException e) { - // ignore } } } @@ -221,6 +212,29 @@ public void acceptSearchMatch(SearchMatch match) { return resolutions; } + private SearchPattern createMainMethodSearchPattern() { + SearchPattern pattern1 = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern2 = SearchPattern.createPattern("main() void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + return SearchPattern.createOrPattern(pattern1, pattern2); + } + + private boolean isMainMethod(IMethod method) { + try { + if (method instanceof SourceMethod + && ((SourceMethod) method).isMainMethodCandidate()) { + return true; + } + + return method.isMainMethod(); + } catch (JavaModelException e) { + // do nothing + } + + return false; + } + private boolean isContainedInInvisibleProject(IProject project, Collection rootPaths) { if (project == null) { return false; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index a3c61e814..fb8dfc33e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -30,7 +31,11 @@ import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -104,16 +109,58 @@ private static List searchMainMethods(ICompilationUnit compilationUnit) * Returns the main method defined in the type. */ public static IMethod getMainMethod(IType type) throws JavaModelException { + boolean allowInstanceMethod = isInstanceMainMethodSupported(type); + List methods = new ArrayList<>(); for (IMethod method : type.getMethods()) { - // Have at most one main method in the member methods of the type. + if (method instanceof SourceMethod + && ((SourceMethod) method).isMainMethodCandidate()) { + methods.add(method); + } + if (method.isMainMethod()) { - return method; + methods.add(method); + } + + if (!allowInstanceMethod && !methods.isEmpty()) { + return methods.get(0); } } + if (!methods.isEmpty()) { + methods.sort((method1, method2) -> { + return getMainMethodPriority(method1) - getMainMethodPriority(method2); + }); + + return methods.get(0); + } + return null; } + private static boolean isInstanceMainMethodSupported(IType type) { + Map options = type.getJavaProject().getOptions(true); + return CompilerOptions.versionToJdkLevel(options.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)) >= ClassFileConstants.JDK21; + } + + private static int getMainMethodPriority(IMethod method) { + int flags = 0; + try { + flags = method.getFlags(); + } catch (JavaModelException e) { + // do nothing + } + String[] params = method.getParameterTypes(); + if (Flags.isStatic(flags) && params.length == 1) { + return 1; + } else if (Flags.isStatic(flags)) { + return 2; + } else if (params.length == 1) { + return 3; + } + + return 4; + } + private static List getPotentialMainClassTypes(ICompilationUnit compilationUnit) throws JavaModelException { List result = new ArrayList<>(); IType[] topLevelTypes = compilationUnit.getTypes(); diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 200e532e3..a52901d01 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -14,13 +14,18 @@ - + + + + + + - + diff --git a/pom.xml b/pom.xml index b039fe1c1..2917f4092 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.5 + 4.0.6 ${basedir} From 1a06b1f6d27be3e765750433792da5ed02b9b0cd Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 25 Mar 2024 12:50:51 +0800 Subject: [PATCH 169/206] bump version to 0.52.0 (#543) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index a7c2ca8ce..3b42e40cb 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index b4fb64d7d..6de8860a5 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 73af47a6d..b45c99f7a 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.51.1 +Bundle-Version: 0.52.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.51.1.jar + lib/com.microsoft.java.debug.core-0.52.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 4657ab08b..6d58947b0 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.51.1 + 0.52.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 53e8bf963..fc6ff6a62 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 68ff9fc42..3d5537f13 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 6977f1831..e4ee29b92 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 2917f4092..f2e4d0eef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.51.1 + 0.52.0 pom Java Debug Server for Visual Studio Code From ae16f7ce3205047fd687aab9d877eeb78c62ace2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 26 Mar 2024 10:45:23 +0800 Subject: [PATCH 170/206] Check the class name of ASTNode to see if it's unnamed class (#544) --- .../plugin/internal/JdtSourceLookUpProvider.java | 12 ++++++++++-- 1 file changed, 10 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 c96b98342..5652b922e 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,11 +20,14 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; @@ -57,7 +60,6 @@ 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.UnnamedClass; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; @@ -87,6 +89,9 @@ public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; private static final String PATH_SEPARATOR = "/"; + private static final Set IMPLICITLY_DECLARED_CLASSES = new HashSet<>( + Arrays.asList("org.eclipse.jdt.core.dom.UnnamedClass", + "org.eclipse.jdt.core.dom.ImplicitTypeDeclaration")); private ISourceContainer[] sourceContainers = null; private HashMap options = new HashMap(); @@ -176,7 +181,10 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB if (astUnit != null) { List types = astUnit.types(); String unnamedClass = null; - if (types.size() == 1 && types.get(0) instanceof UnnamedClass) { + // See https://github.com/eclipse-jdt/eclipse.jdt.core/pull/2220 + // Given that the JDT plans to rename UnamedClass to ImplicitTypeDeclaration, we will check + // the class name of the ASTNode to prevent the potential breaking in the future. + if (types.size() == 1 && IMPLICITLY_DECLARED_CLASSES.contains(types.get(0).getClass().getName())) { unnamedClass = inferPrimaryTypeName(sourceUri, astUnit); } Map resolvedLocations = new HashMap<>(); From 37d4e66c5717179b1487ff72fcbc8622b31a3648 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 26 Mar 2024 12:47:07 +0800 Subject: [PATCH 171/206] Update signjars-nightly.yml for Azure Pipelines --- .azure-pipelines/signjars-nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index cae5ac6d5..0e53a4219 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -105,6 +105,7 @@ extends: displayName: Build plugin.jar inputs: script: |- + ./mvnw clean install -N -f pom.xml -Dmaven.repo.local=./.repository ./mvnw clean install -f com.microsoft.java.debug.target/pom.xml -Dmaven.repo.local=./.repository ./mvnw clean install -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository From 83403a458e0d01fba5a3871fea81af742460bdf0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 10 Apr 2024 15:59:06 +0800 Subject: [PATCH 172/206] fix Java 22 main method searching order (#548) --- .../internal/ResolveMainMethodHandler.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index fb8dfc33e..be1092bb3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -142,23 +142,15 @@ private static boolean isInstanceMainMethodSupported(IType type) { return CompilerOptions.versionToJdkLevel(options.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)) >= ClassFileConstants.JDK21; } + /** + * See Java 22 JEP 463 https://openjdk.org/jeps/463. + * It searches the main method in the launched class by following a specific order: + * - If the launched class contains a main method with a String[] parameter then choose that method. + * - Otherwise, if the class contains a main method with no parameters then choose that method. + */ private static int getMainMethodPriority(IMethod method) { - int flags = 0; - try { - flags = method.getFlags(); - } catch (JavaModelException e) { - // do nothing - } String[] params = method.getParameterTypes(); - if (Flags.isStatic(flags) && params.length == 1) { - return 1; - } else if (Flags.isStatic(flags)) { - return 2; - } else if (params.length == 1) { - return 3; - } - - return 4; + return params.length == 1 ? 1 : 2; } private static List getPotentialMainClassTypes(ICompilationUnit compilationUnit) throws JavaModelException { From 36572435d0518cb2566ab9705c3d2b8e2a797402 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 14 Jun 2024 16:04:40 +0800 Subject: [PATCH 173/206] Support HCR for gradle build server projects (#555) * Support HCR for gradle build server projects * Fix checkstyle violations * Fix NPE when auto build is disabled Signed-off-by: Sheng Chen * Address comments * Fix checkstyle violations * Use isNotBlank() --------- Signed-off-by: Sheng Chen --- .../java/debug/plugin/internal/Compile.java | 78 ++++++++++--------- .../internal/JavaHotCodeReplaceProvider.java | 45 +++++++++++ .../java/debug/plugin/internal/JdtUtils.java | 34 ++++++++ 3 files changed, 120 insertions(+), 37 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java index 9cb6fbbfa..245281816 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -14,6 +14,7 @@ package com.microsoft.java.debug.plugin.internal; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; @@ -28,14 +29,16 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import org.eclipse.jdt.ls.core.internal.handlers.BuildWorkspaceHandler; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.extended.ProjectBuildParams; import com.microsoft.java.debug.core.Configuration; @@ -45,20 +48,12 @@ public class Compile { private static final int GRADLE_BS_COMPILATION_ERROR = 100; public static Object compile(CompileParams params, IProgressMonitor monitor) { - IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); - if (mainProject == null) { - try { - // Q: is infer project by main class name necessary? perf impact? - List javaProjects = ResolveClasspathsHandler.getJavaProjectFromType(params.getMainClass()); - if (javaProjects.size() == 1) { - mainProject = javaProjects.get(0).getProject(); - } - } catch (CoreException e) { - JavaLanguageServerPlugin.logException("Failed to resolve project from main class name.", e); - } + if (params == null) { + throw new IllegalArgumentException("The compile parameters should not be null."); } - if (isBspProject(mainProject) && !ProjectUtils.isGradleProject(mainProject)) { + IProject mainProject = JdtUtils.getMainProject(params.getProjectName(), params.getMainClass()); + if (JdtUtils.isBspProject(mainProject) && !ProjectUtils.isGradleProject(mainProject)) { // Just need to trigger a build for the target project, the Gradle build server will // handle the build dependencies for us. try { @@ -78,20 +73,37 @@ public static Object compile(CompileParams params, IProgressMonitor monitor) { return BuildWorkspaceStatus.SUCCEED; } - try { - if (monitor.isCanceled()) { - return BuildWorkspaceStatus.CANCELLED; - } + if (monitor.isCanceled()) { + return BuildWorkspaceStatus.CANCELLED; + } - long compileAt = System.currentTimeMillis(); - if (params != null && params.isFullBuild()) { - ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); - ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor); - } else { - ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor); + ProjectBuildParams buildParams = new ProjectBuildParams(); + List identifiers = new LinkedList<>(); + buildParams.setFullBuild(params.isFullBuild); + for (IJavaProject javaProject : ProjectUtils.getJavaProjects()) { + if (ProjectsManager.getDefaultProject().equals(javaProject.getProject())) { + continue; } - logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); + // we only build project which is not a BSP project, in case that the compile request is triggered by + // HCR with auto-build disabled, the build for BSP projects will be triggered by JavaHotCodeReplaceProvider. + if (!JdtUtils.isBspProject(javaProject.getProject())) { + identifiers.add(new TextDocumentIdentifier(javaProject.getProject().getLocationURI().toString())); + } + } + if (identifiers.size() == 0) { + return BuildWorkspaceStatus.SUCCEED; + } + buildParams.setIdentifiers(identifiers); + long compileAt = System.currentTimeMillis(); + BuildWorkspaceHandler buildWorkspaceHandler = new BuildWorkspaceHandler(JavaLanguageServerPlugin.getProjectsManager()); + BuildWorkspaceStatus status = buildWorkspaceHandler.buildProjects(buildParams, monitor); + logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); + if (status == BuildWorkspaceStatus.FAILED || status == BuildWorkspaceStatus.CANCELLED) { + return status; + } + + try { IResource currentResource = mainProject; if (isUnmanagedFolder(mainProject) && StringUtils.isNotBlank(params.getMainClass())) { IType mainType = ProjectUtils.getJavaProject(mainProject).findType(params.getMainClass()); @@ -135,17 +147,14 @@ public static Object compile(CompileParams params, IProgressMonitor monitor) { } } - if (problemMarkers.isEmpty()) { - return BuildWorkspaceStatus.SUCCEED; + if (!problemMarkers.isEmpty()) { + return BuildWorkspaceStatus.WITH_ERROR; } - - return BuildWorkspaceStatus.WITH_ERROR; } catch (CoreException e) { - JavaLanguageServerPlugin.logException("Failed to build workspace.", e); - return BuildWorkspaceStatus.FAILED; - } catch (OperationCanceledException e) { - return BuildWorkspaceStatus.CANCELLED; + JavaLanguageServerPlugin.log(e); } + + return BuildWorkspaceStatus.SUCCEED; } private static boolean isUnmanagedFolder(IProject project) { @@ -153,11 +162,6 @@ private static boolean isUnmanagedFolder(IProject project) { && ProjectUtils.isJavaProject(project); } - private static boolean isBspProject(IProject project) { - return project != null && ProjectUtils.isJavaProject(project) - && ProjectUtils.hasNature(project, "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature"); - } - private static IProject getDefaultProject() { return getWorkspaceRoot().getProject(ProjectsManager.DEFAULT_PROJECT_NAME); } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java index 2bf905c89..3a2b2f3df 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java @@ -34,16 +34,20 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; @@ -55,6 +59,7 @@ import org.eclipse.jdt.core.util.ISourceAttribute; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.jdt.ls.core.internal.JobHelpers; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; @@ -63,6 +68,7 @@ import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.StackFrameUtility; import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -104,6 +110,8 @@ public class JavaHotCodeReplaceProvider implements IHotCodeReplaceProvider, IRes private List deltaClassNames = new ArrayList<>(); + private String mainProjectName = ""; + /** * Visitor for resource deltas. */ @@ -269,6 +277,7 @@ public void initialize(IDebugAdapterContext context, Map options } this.context = context; currentDebugSession = context.getDebugSession(); + this.mainProjectName = ((String) options.get(Constants.PROJECT_NAME)); } @Override @@ -319,6 +328,7 @@ public void onClassRedefined(Consumer> consumer) { @Override public CompletableFuture> redefineClasses() { + triggerBuildForBspProject(); JobHelpers.waitForBuildJobs(10 * 1000); return CompletableFuture.supplyAsync(() -> { List classNames = new ArrayList<>(); @@ -737,4 +747,39 @@ private List getStackFrames(ThreadReference thread, boolean refresh) } }); } + + /** + * Trigger build separately if the main project is a BSP project. + * This is because auto build for BSP project will not update the class files to disk. + */ + private void triggerBuildForBspProject() { + // check if the workspace contains BSP project first. This is for performance consideration. + // Due to that getJavaProjectFromType() is a heavy operation. + if (!containsBspProjects()) { + return; + } + + IProject mainProject = JdtUtils.getMainProject(this.mainProjectName, context.getMainClass()); + if (mainProject != null && JdtUtils.isBspProject(mainProject)) { + try { + ResourcesPlugin.getWorkspace().build( + new IBuildConfiguration[]{mainProject.getActiveBuildConfig()}, + IncrementalProjectBuilder.INCREMENTAL_BUILD, + false /*buildReference*/, + new NullProgressMonitor() + ); + } catch (CoreException e) { + // ignore compilation errors + } + } + } + + private boolean containsBspProjects() { + for (IJavaProject javaProject : ProjectUtils.getJavaProjects()) { + if (JdtUtils.isBspProject(javaProject.getProject())) { + return true; + } + } + return false; + } } 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 a4c06c6d4..66562ae74 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 @@ -40,6 +40,8 @@ import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.StackFrameUtility; @@ -415,4 +417,36 @@ public static boolean isSameFile(IResource resource1, IResource resource2) { return Objects.equals(resource1.getLocation(), resource2.getLocation()); } + + /** + * Check if the project is managed by Gradle Build Server. + */ + public static boolean isBspProject(IProject project) { + return project != null && ProjectUtils.isJavaProject(project) + && ProjectUtils.hasNature(project, "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature"); + } + + /** + * Get main project according to the main project name or main class name, + * or return null if the main project cannot be resolved. + */ + public static IProject getMainProject(String mainProjectName, String mainClassName) { + IProject mainProject = null; + if (StringUtils.isNotBlank(mainProjectName)) { + mainProject = ProjectUtils.getProject(mainProjectName); + } + + if (mainProject == null && StringUtils.isNotBlank(mainClassName)) { + try { + List javaProjects = ResolveClasspathsHandler.getJavaProjectFromType(mainClassName); + if (javaProjects.size() == 1) { + mainProject = javaProjects.get(0).getProject(); + } + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Failed to resolve project from main class name.", e); + } + } + + return mainProject; + } } From 6511f97c76d4dd562f94c3ac503a982407d211a3 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 17 Jun 2024 16:50:27 +0800 Subject: [PATCH 174/206] Update tp to fix the build failure (#556) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index a52901d01..6edb44dec 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -15,7 +15,7 @@ - + From e06a1b686625b28a66e0bd6a22f4668fb4f02b4d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 27 Jun 2024 10:31:27 +0800 Subject: [PATCH 175/206] bump version to 0.53.0 (#557) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 3b42e40cb..bb1449401 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 6de8860a5..dc81da8e6 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index b45c99f7a..192908dcf 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.52.0 +Bundle-Version: 0.53.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.52.0.jar + lib/com.microsoft.java.debug.core-0.53.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 6d58947b0..93323db5c 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.52.0 + 0.53.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index fc6ff6a62..b09605569 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 3d5537f13..edb1c9891 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index e4ee29b92..2dc407f6c 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index f2e4d0eef..c277e8a98 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.52.0 + 0.53.0 pom Java Debug Server for Visual Studio Code From 24c61a7b06b2e3dec661b97688f9b6d033de5fab Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 19 Jul 2024 13:34:22 +0800 Subject: [PATCH 176/206] Remove the deprecated debug parameters from the launching connector (#562) --- .../java/debug/plugin/internal/AdvancedLaunchingConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index 3551ab2e6..087f0fae7 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -195,7 +195,7 @@ private static String[] constructLaunchCommand(Map l StringBuilder execString = new StringBuilder(); execString.append("\"" + javaHome + slash + "bin" + slash + javaExec + "\""); - execString.append(" -Xdebug -Xnoagent -Djava.compiler=NONE"); + execString.append(" -Djava.compiler=NONE"); execString.append(" -Xrunjdwp:transport=dt_socket,address=" + address + ",server=n,suspend=" + (suspend ? "y" : "n")); if (javaOptions != null) { execString.append(" " + javaOptions); From 1b669f8c4c92c179acf89498cd14bcb8397bd3b5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 19 Jul 2024 14:36:08 +0800 Subject: [PATCH 177/206] Adopt the new code sign job in the pipeline (#563) --- .azure-pipelines/signjars-nightly.yml | 18 +++++++++++--- .azure-pipelines/signjars-rc.yml | 36 +++++++++++++++++++++------ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 0e53a4219..2c0373aab 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -70,10 +70,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -111,10 +116,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index ebb333f70..851b73775 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -71,10 +71,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -111,10 +116,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams @@ -152,10 +162,15 @@ extends: mkdir -p p2/target cp -r com.microsoft.java.debug.repository/target/repository p2/target/ cp com.microsoft.java.debug.repository/pushToBintray.sh p2/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign p2 inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: p2 Pattern: "*.jar" signConfigType: inlineSignParams @@ -199,10 +214,15 @@ extends: mkdir -p m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin*.jar m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/pom.xml m2/com.microsoft.java.debug.plugin/com.microsoft.java.debug.plugin-$RELEASE_VERSION.pom - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign m2 inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: m2 Pattern: "*.jar" signConfigType: inlineSignParams From 26daf612fb03f8dd7befaa980feb327644ee43a7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 22 Jul 2024 10:07:30 +0800 Subject: [PATCH 178/206] Remove the obselete parameter '-Djava.compiler=NONE' from launching command (#564) --- .../java/debug/plugin/internal/AdvancedLaunchingConnector.java | 1 - 1 file changed, 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index 087f0fae7..24c65785d 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -195,7 +195,6 @@ private static String[] constructLaunchCommand(Map l StringBuilder execString = new StringBuilder(); execString.append("\"" + javaHome + slash + "bin" + slash + javaExec + "\""); - execString.append(" -Djava.compiler=NONE"); execString.append(" -Xrunjdwp:transport=dt_socket,address=" + address + ",server=n,suspend=" + (suspend ? "y" : "n")); if (javaOptions != null) { execString.append(" " + javaOptions); From 3691f3b5f2cae05b06b43333dc435c172e27f1a8 Mon Sep 17 00:00:00 2001 From: bsolar17 Date: Thu, 8 Aug 2024 03:49:58 +0200 Subject: [PATCH 179/206] Update lsp4j dependency to version 0.23.1 (#565) New version of jdtls core requires lsp4j 0.23 or greater. --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 6edb44dec..41215e0ba 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -36,7 +36,7 @@ - + From e54f5c20c9cac846eab9f4af0f4fef671727b919 Mon Sep 17 00:00:00 2001 From: Vincentius Adrian Date: Wed, 18 Sep 2024 14:36:52 +0700 Subject: [PATCH 180/206] Update eclipse to use v4.33 (#571) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 41215e0ba..5c386e467 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -15,7 +15,7 @@ - + From d4afc7e55107b333c3a1126fbf42c18c0dfd69cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:32:24 +0800 Subject: [PATCH 181/206] Bump commons-io:commons-io in /com.microsoft.java.debug.core (#573) Bumps commons-io:commons-io from 2.11.0 to 2.14.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index bb1449401..d8dae21e7 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -62,7 +62,7 @@ commons-io commons-io - 2.11.0 + 2.14.0 From 5c7562ade22977b15f76f46d13c098e82cc58ced Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 31 Oct 2024 14:38:24 +0800 Subject: [PATCH 182/206] bump version to 0.53.1 (#574) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index d8dae21e7..64827ab13 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index dc81da8e6..dbf84ced8 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 192908dcf..ea6b7127e 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.53.0 +Bundle-Version: 0.53.1 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.53.0.jar + lib/com.microsoft.java.debug.core-0.53.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 93323db5c..dde9c7ec6 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.53.0 + 0.53.1 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index b09605569..5d3e664df 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index edb1c9891..40519fce2 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 2dc407f6c..737decee1 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index c277e8a98..53aec5c9c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.53.0 + 0.53.1 pom Java Debug Server for Visual Studio Code From 4cb23d2493aae8f17618764591fb37d71a178d0f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 27 Feb 2025 18:49:15 -0800 Subject: [PATCH 183/206] Update CI to run against jdk 21 (#581) --- .azure-pipelines/signjars-nightly.yml | 4 ++-- .azure-pipelines/signjars-rc.yml | 4 ++-- .github/workflows/build.yml | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 2c0373aab..8f405a725 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -46,9 +46,9 @@ extends: - checkout: self fetchTags: true - task: JavaToolInstaller@0 - displayName: Use Java 17 + displayName: Use Java 21 inputs: - versionSpec: "17" + versionSpec: "21" jdkArchitectureOption: x64 jdkSourceOption: PreInstalled - task: CmdLine@2 diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 851b73775..c0c11e6cc 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -45,9 +45,9 @@ extends: - checkout: self fetchTags: true - task: JavaToolInstaller@0 - displayName: Use Java 17 + displayName: Use Java 21 inputs: - versionSpec: "17" + versionSpec: "21" jdkArchitectureOption: x64 jdkSourceOption: PreInstalled - task: CmdLine@2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1ace44224..c147c4838 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: '17' + java-version: '21' - name: Cache local Maven repository uses: actions/cache@v2 @@ -45,10 +45,10 @@ jobs: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: '17' + java-version: '21' - name: Cache local Maven repository uses: actions/cache@v2 @@ -71,10 +71,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: '17' + java-version: '21' - name: Cache local Maven repository uses: actions/cache@v2 From dff3538d4f741e7e05a304614e3d5d9e7e0bfb0b Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 28 Feb 2025 07:13:49 +0000 Subject: [PATCH 184/206] Attempt to terminate the process normally (#579) Fixes https://github.com/microsoft/vscode-java-debug/issues/1274 --- .../main/java/com/microsoft/java/debug/core/DebugSession.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index fbad52fe2..38a234fa9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -119,7 +119,9 @@ public void detach() { @Override public void terminate() { - if (vm.process() == null || vm.process().isAlive()) { + if (vm.process() != null && vm.process().isAlive()) { + vm.process().destroy(); + } else if (vm.process() == null || vm.process().isAlive()) { vm.exit(0); } } From 31b22551b27712d265694c7afbe21d0eabccd1d1 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 21 Mar 2025 16:31:31 +0800 Subject: [PATCH 185/206] Onboard pipelines to MicroBuild (#583) * Onboard pipelines to MicroBuild * onboard code sign to microbuild * remove p2 copy logic * remove p2 buildartifact dir * use MicroBuild to publish to maven * fix m2 sign path * upgrade to actions/cache@v4 --- .azure-pipelines/publish-to-maven.yml | 100 ++++++++++++++ .azure-pipelines/signjars-nightly.yml | 97 +++++-------- .azure-pipelines/signjars-rc.yml | 187 +++++++------------------- .github/workflows/build.yml | 6 +- 4 files changed, 183 insertions(+), 207 deletions(-) create mode 100644 .azure-pipelines/publish-to-maven.yml diff --git a/.azure-pipelines/publish-to-maven.yml b/.azure-pipelines/publish-to-maven.yml new file mode 100644 index 000000000..58dc58cf2 --- /dev/null +++ b/.azure-pipelines/publish-to-maven.yml @@ -0,0 +1,100 @@ +name: $(Date:yyyyMMdd).$(Rev:r) +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release +trigger: none +extends: + template: azure-pipelines/1ES.Official.Publish.yml@MicroBuildTemplate + parameters: + pool: + os: linux + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Ubuntu-2004 + sdl: + sourceAnalysisPool: + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Windows_2022 + os: windows + stages: + - stage: PublishToMaven + jobs: + - job: PublishToMaven + steps: + - task: DownloadBuildArtifacts@1 + displayName: 'Download Jar Artifacts' + inputs: + buildType: specific + project: 'a4d27ce2-a42d-4b71-8eef-78cee9a9728e' + pipeline: 16486 + downloadType: specific + extractTars: false + itemPattern: 'm2/**' + - script: | + echo "import public key" + echo $GPG_PUBLIC_B64 | base64 -d | gpg --import + + echo "import secret key" + echo $GPG_SECRET_B64 | base64 -d | gpg --batch --passphrase $GPGPASS --import + displayName: 'import GPG keys' + env: + GPG_PUBLIC_B64: $(GPG_PUBLIC_B64) + GPG_SECRET_B64: $(GPG_SECRET_B64) + GPGPASS: $(GPGPASS) + - task: NodeTool@0 + displayName: 'Use Node 20.x' + inputs: + versionSpec: 20.x + - script: | + cd $(System.ArtifactsDirectory)/m2 + pluginJarFile=$(basename -- java-debug-parent/*.pom) + + # remove .* from end + noExt=${pluginJarFile%.*} + + # remove *- from start + export releaseVersion=${noExt##*-} + echo $releaseVersion + + export artifactFolder=$(pwd .) + wget https://raw.githubusercontent.com/microsoft/java-debug/master/scripts/publishMaven.js + + export GPG_TTY=$(tty) + node publishMaven.js -task gpg + displayName: 'sign artifacts' + env: + GPG_PUBLIC_B64: $(GPG_PUBLIC_B64) + GPG_SECRET_B64: $(GPG_SECRET_B64) + GPGPASS: $(GPGPASS) + NEXUS_OSSRHPASS: $(NEXUS_OSSRHPASS) + NEXUS_OSSRHUSER: $(NEXUS_OSSRHUSER) + NEXUS_STAGINGPROFILEID: $(NEXUS_STAGINGPROFILEID) + - template: MicroBuild.Publish.yml@MicroBuildTemplate + parameters: + intent: 'PackageDistribution' + contentType: 'Maven' + contentSource: 'Folder' + folderLocation: '$(System.ArtifactsDirectory)/m2/java-debug-parent' + waitForReleaseCompletion: true + owners: 'jinbwan@microsoft.com' + approvers: 'roml@microsoft.com' + - template: MicroBuild.Publish.yml@MicroBuildTemplate + parameters: + intent: 'PackageDistribution' + contentType: 'Maven' + contentSource: 'Folder' + folderLocation: '$(System.ArtifactsDirectory)/m2/com.microsoft.java.debug.core' + waitForReleaseCompletion: true + owners: 'jinbwan@microsoft.com' + approvers: 'roml@microsoft.com' + - template: MicroBuild.Publish.yml@MicroBuildTemplate + parameters: + intent: 'PackageDistribution' + contentType: 'Maven' + contentSource: 'Folder' + folderLocation: '$(System.ArtifactsDirectory)/m2/com.microsoft.java.debug.plugin' + waitForReleaseCompletion: true + owners: 'jinbwan@microsoft.com' + approvers: 'roml@microsoft.com' \ No newline at end of file diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 8f405a725..19ab29baf 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -45,6 +45,23 @@ extends: steps: - checkout: self fetchTags: true + - task: UsePythonVersion@0 + displayName: 'Use Python 3.11.x' + inputs: + versionSpec: 3.11.x + - task: UseDotNet@2 + displayName: 'Use .NET Core 3.1.x' + inputs: + packageType: 'sdk' + version: '3.1.x' + - task: MicroBuildSigningPlugin@4 + displayName: 'Install Signing Plugin' + inputs: + signType: real + azureSubscription: 'MicroBuild Signing Task (MSEng)' + feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: @@ -70,38 +87,16 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign core.jar + - task: CmdLine@2 + displayName: Sign core jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.core*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.core*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -116,38 +111,16 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign plugin.jar + - task: CmdLine@2 + displayName: Sign plugin jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.plugin*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.plugin*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CopyFiles@2 displayName: "Copy plugin.jar to: $(Build.ArtifactStagingDirectory)" inputs: diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index c0c11e6cc..201ff20f8 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -37,13 +37,26 @@ extends: artifactName: m2 targetPath: $(Build.ArtifactStagingDirectory)/m2 displayName: "Publish Artifact: m2" - - output: pipelineArtifact - artifactName: p2 - targetPath: $(Build.ArtifactStagingDirectory)/p2 - displayName: "Publish Artifact: p2" steps: - checkout: self fetchTags: true + - task: UsePythonVersion@0 + displayName: 'Use Python 3.11.x' + inputs: + versionSpec: 3.11.x + - task: UseDotNet@2 + displayName: 'Use .NET Core 3.1.x' + inputs: + packageType: 'sdk' + version: '3.1.x' + - task: MicroBuildSigningPlugin@4 + displayName: 'Install Signing Plugin' + inputs: + signType: real + azureSubscription: 'MicroBuild Signing Task (MSEng)' + feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: @@ -71,38 +84,16 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign core.jar + - task: CmdLine@2 + displayName: Sign core jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.core*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.core*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -116,84 +107,20 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign plugin.jar + - task: CmdLine@2 + displayName: Sign plugin jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.plugin*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.plugin*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CmdLine@2 displayName: install signed plugin.jar inputs: script: cp jars/com.microsoft.java.debug.plugin*.jar .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/ - - task: CmdLine@2 - displayName: Build p2 artifacts - inputs: - script: |- - # 3. Build the p2 artifacts. - ./mvnw clean package -f com.microsoft.java.debug.repository/pom.xml -Dmaven.repo.local=./.repository - - mkdir -p p2/target - cp -r com.microsoft.java.debug.repository/target/repository p2/target/ - cp com.microsoft.java.debug.repository/pushToBintray.sh p2/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign p2 - inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: p2 - Pattern: "*.jar" - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] - task: CmdLine@2 displayName: build m2 artifacts inputs: @@ -214,43 +141,19 @@ extends: mkdir -p m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin*.jar m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/pom.xml m2/com.microsoft.java.debug.plugin/com.microsoft.java.debug.plugin-$RELEASE_VERSION.pom - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign m2 + - task: CmdLine@2 + displayName: Sign m2 jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: m2 - Pattern: "*.jar" - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "*.jar") + for file in $files; do + # fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$file" /certs:100010171 + done + workingDirectory: 'm2' - task: CopyFiles@2 - displayName: "Copy p2/m2 to: $(Build.ArtifactStagingDirectory)" + displayName: "Copy m2 to: $(Build.ArtifactStagingDirectory)" inputs: Contents: |+ - p2/** m2/** - TargetFolder: $(Build.ArtifactStagingDirectory) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c147c4838..71d726f63 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: java-version: '21' - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -51,7 +51,7 @@ jobs: java-version: '21' - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: $HOME/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -77,7 +77,7 @@ jobs: java-version: '21' - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From b98d4936c52ea933b00faacfac67fd4fcb86e790 Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Thu, 17 Apr 2025 03:47:29 -0400 Subject: [PATCH 186/206] Update target platform to more closely follow JDT-LS. (#585) - Update Apache Commons IO from 2.11.0 to 2.19.0 Signed-off-by: Roland Grunberg --- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 2 +- com.microsoft.java.debug.plugin/pom.xml | 2 +- .../com.microsoft.java.debug.tp.target | 42 ++++--------------- 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index dbf84ced8..a83e822fe 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -7,7 +7,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index ea6b7127e..073c31c8e 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -21,7 +21,7 @@ Require-Bundle: org.eclipse.core.runtime, org.apache.commons.lang3, org.eclipse.lsp4j, com.google.guava -Bundle-ClassPath: lib/commons-io-2.11.0.jar, +Bundle-ClassPath: lib/commons-io-2.19.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index dde9c7ec6..d2d7b9aa4 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -51,7 +51,7 @@ commons-io commons-io - 2.11.0 + 2.19.0 com.microsoft.java diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 5c386e467..8a85443a4 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -1,43 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - + + + + + + From b77b590ad89345557c87c975a4b41c5f648e65f1 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 23 Apr 2025 13:39:54 +0800 Subject: [PATCH 187/206] Override MicroBuild Output Folder (#586) * Override MicroBuild Output Folder * Use env to override MicroBuildOutputFolder --- .azure-pipelines/signjars-nightly.yml | 1 + .azure-pipelines/signjars-rc.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 19ab29baf..b2ab77ab7 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -62,6 +62,7 @@ extends: feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 201ff20f8..8e639bafb 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -57,6 +57,7 @@ extends: feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: From 54058b9eb466576a7a77c9303963ca88967ec58b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Sun, 27 Apr 2025 10:50:06 +0800 Subject: [PATCH 188/206] Bump version to 0.53.2 (#587) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 64827ab13..d63b93c01 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index a83e822fe..de9b5e366 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 073c31c8e..442e9bb17 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.53.1 +Bundle-Version: 0.53.2 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.19.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.53.1.jar + lib/com.microsoft.java.debug.core-0.53.2.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index d2d7b9aa4..2e2c0e6a8 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.53.1 + 0.53.2 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 5d3e664df..f30a2f4c4 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 40519fce2..573aff2ac 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 737decee1..8e49b638b 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 53aec5c9c..9f59485d9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.53.1 + 0.53.2 pom Java Debug Server for Visual Studio Code From bccb4808571f2fcdce497e5585320ff62491c9f5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 26 Jun 2025 09:54:18 +0800 Subject: [PATCH 189/206] switch sha256 to calculate temp file name (#592) --- .../debug/core/adapter/handler/LaunchUtils.java | 15 +++++++++------ .../com.microsoft.java.debug.tp.target | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 27fdb1813..7370328b2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -95,7 +95,7 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws // In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar String classpathValue = String.join(" ", classpathUrls); attributes.put(Attributes.Name.CLASS_PATH, classpathValue); - String baseName = "cp_" + getMd5(classpathValue); + String baseName = "cp_" + getSha256(classpathValue); cleanupTempFiles(baseName, ".jar"); Path tempfile = createTempFile(baseName, ".jar"); JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest); @@ -127,7 +127,7 @@ public static synchronized Path generateArgfile(String vmArgs, String[] classPat } argfile = argfile.replace("\\", "\\\\"); - String baseName = "cp_" + getMd5(argfile); + String baseName = "cp_" + getSha256(argfile); cleanupTempFiles(baseName, ".argfile"); Path tempfile = createTempFile(baseName, ".argfile"); Files.writeString(tempfile, argfile, encoding); @@ -364,12 +364,15 @@ private static Path createTempFile(String baseName, String suffix) throws IOExce } } - private static String getMd5(String input) { + private static String getSha256(String input) { try { - MessageDigest md = MessageDigest.getInstance("MD5"); + MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] messageDigest = md.digest(input.getBytes()); - BigInteger md5 = new BigInteger(1, messageDigest); - return md5.toString(Character.MAX_RADIX); + // Use only first 16 bytes to keep filename shorter + byte[] truncated = new byte[16]; + System.arraycopy(messageDigest, 0, truncated, 0, 16); + BigInteger hash = new BigInteger(1, truncated); + return hash.toString(Character.MAX_RADIX); } catch (NoSuchAlgorithmException e) { return Integer.toString(input.hashCode(), Character.MAX_RADIX); } diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 8a85443a4..1d322d84c 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -2,7 +2,7 @@ - + From a1f68f4265458e9386bf16d107c2383b68690e43 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 27 Jun 2025 15:01:25 +0800 Subject: [PATCH 190/206] Update codesign task (#593) --- .azure-pipelines/signjars-nightly.yml | 10 ++++++---- .azure-pipelines/signjars-rc.yml | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index b2ab77ab7..2045f54ce 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -45,20 +45,22 @@ extends: steps: - checkout: self fetchTags: true - - task: UsePythonVersion@0 - displayName: 'Use Python 3.11.x' - inputs: - versionSpec: 3.11.x - task: UseDotNet@2 displayName: 'Use .NET Core 3.1.x' inputs: packageType: 'sdk' version: '3.1.x' + - task: UseDotNet@2 + displayName: 'Use .NET Core 8.0.x' + inputs: + packageType: 'sdk' + version: '8.0.x' - task: MicroBuildSigningPlugin@4 displayName: 'Install Signing Plugin' inputs: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' + useEsrpCli: true feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 8e639bafb..cd3377cfd 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -40,20 +40,22 @@ extends: steps: - checkout: self fetchTags: true - - task: UsePythonVersion@0 - displayName: 'Use Python 3.11.x' - inputs: - versionSpec: 3.11.x - task: UseDotNet@2 displayName: 'Use .NET Core 3.1.x' inputs: packageType: 'sdk' version: '3.1.x' + - task: UseDotNet@2 + displayName: 'Use .NET Core 8.0.x' + inputs: + packageType: 'sdk' + version: '8.0.x' - task: MicroBuildSigningPlugin@4 displayName: 'Install Signing Plugin' inputs: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' + useEsrpCli: true feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 36a6b3f3606bd500fd7b257c55ae2572893d319e Mon Sep 17 00:00:00 2001 From: chagong Date: Wed, 13 Aug 2025 14:35:29 +0800 Subject: [PATCH 191/206] Update CODEOWNERS (#599) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b11684f52..9d7579633 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @testforstephen @jdneo \ No newline at end of file +* @testforstephen @jdneo @chagong @wenytang-ms From c7642bc738b2dbfeefa0ea85e338d9eefd0ebc25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:09:45 +0800 Subject: [PATCH 192/206] Bump org.apache.commons:commons-lang3 in /com.microsoft.java.debug.core (#594) Bumps org.apache.commons:commons-lang3 from 3.6 to 3.18.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.18.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: chagong --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index d63b93c01..8bfdb7292 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -42,7 +42,7 @@ org.apache.commons commons-lang3 - 3.6 + 3.18.0 com.google.code.gson From a209e39b9e07e5b2eacb74a2a2250da99c6751e3 Mon Sep 17 00:00:00 2001 From: Snjeza Date: Wed, 13 Aug 2025 09:18:31 +0200 Subject: [PATCH 193/206] Improve ResolveMainClassHandler.resolveMainClassUnderPaths (#596) Co-authored-by: wenyt <75360946+wenytang-ms@users.noreply.github.com> --- .../plugin/internal/ResolveMainClassHandler.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index 38ad1532b..fc8189445 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -35,6 +35,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; @@ -100,8 +101,18 @@ private List resolveMainClassCore(List arguments) { private List resolveMainClassUnderPaths(List parentPaths) { // Limit to search main method from source code only. - IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), - IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); + IJavaProject[] projects; + if (parentPaths == null || parentPaths.isEmpty()) { + projects = ProjectUtils.getJavaProjects(); + } else { + projects = Stream.of(ProjectUtils.getAllProjects()) + .filter(p -> ProjectUtils.isJavaProject(p) && p.getLocation() != null && ResourceUtils.isContainedIn(p.getLocation(), parentPaths)) + .map(p -> JavaCore.create(p)) + .filter(p -> p.exists()) + .toArray(IJavaProject[]::new); + } + IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(projects, + IJavaSearchScope.SOURCES); SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { From 65dcd3d77c548bd842a74e472c8b25cd8489905a Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:24:44 +0800 Subject: [PATCH 194/206] ci: update the pipeline to do publish (#602) --- .azure-pipelines/publish-to-maven.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.azure-pipelines/publish-to-maven.yml b/.azure-pipelines/publish-to-maven.yml index 58dc58cf2..a1ef0a204 100644 --- a/.azure-pipelines/publish-to-maven.yml +++ b/.azure-pipelines/publish-to-maven.yml @@ -22,6 +22,10 @@ extends: - stage: PublishToMaven jobs: - job: PublishToMaven + displayName: Maven Release job + templateContext: + type: releaseJob + isProduction: true steps: - task: DownloadBuildArtifacts@1 displayName: 'Download Jar Artifacts' From 3bec8be6525ff8ced06d57157c847870adc2bc49 Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Mon, 25 Aug 2025 11:27:00 +0800 Subject: [PATCH 195/206] ci: support PME to do code sign (#603) --- .azure-pipelines/signjars-nightly.yml | 1 + .azure-pipelines/signjars-rc.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 2045f54ce..70fe81be2 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -61,6 +61,7 @@ extends: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' useEsrpCli: true + ConnectedPMEServiceName: 0e38ce24-f885-4c86-b997-5887b97a1899 feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index cd3377cfd..819d39de7 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -56,6 +56,7 @@ extends: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' useEsrpCli: true + ConnectedPMEServiceName: 0e38ce24-f885-4c86-b997-5887b97a1899 feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 77d10aa0e8030da7570bdde8400f2d0fb3aa9e7d Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:16:24 +0800 Subject: [PATCH 196/206] ci: sign jar with access token (#604) --- .azure-pipelines/signjars-nightly.yml | 4 ++++ .azure-pipelines/signjars-rc.yml | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 70fe81be2..8b1e8d12a 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -101,6 +101,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -125,6 +127,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CopyFiles@2 displayName: "Copy plugin.jar to: $(Build.ArtifactStagingDirectory)" inputs: diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 819d39de7..e87444603 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -98,6 +98,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -121,6 +123,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CmdLine@2 displayName: install signed plugin.jar inputs: @@ -155,6 +159,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$file" /certs:100010171 done workingDirectory: 'm2' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CopyFiles@2 displayName: "Copy m2 to: $(Build.ArtifactStagingDirectory)" inputs: From 614b10df99acc34ced35fc50b9b70a8191719e2c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:30:26 +0800 Subject: [PATCH 197/206] Add AI triage workflow and LLM documentation for automated issue management (#606) * Initial plan * Add triage agent workflow and LLM documentation from vscode-gradle Co-authored-by: chagong <831821+chagong@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: chagong <831821+chagong@users.noreply.github.com> --- .github/llms.md | 38 +++++++++ .github/workflows/triage-agent.yml | 122 +++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 .github/llms.md create mode 100644 .github/workflows/triage-agent.yml diff --git a/.github/llms.md b/.github/llms.md new file mode 100644 index 000000000..55d69b238 --- /dev/null +++ b/.github/llms.md @@ -0,0 +1,38 @@ +# Extension Pack for Java +Extension Pack for Java is a collection of popular extensions that can help write, test and debug Java applications in Visual Studio Code. By installing Extension Pack for Java, the following extensions are installed: + +- [📦 Language Support for Java™ by Red Hat ](https://marketplace.visualstudio.com/items?itemName=redhat.java) + - Code Navigation + - Auto Completion + - Refactoring + - Code Snippets +- [📦 Debugger for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-debug) + - Debugging +- [📦 Test Runner for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-test) + - Run & Debug JUnit/TestNG Test Cases +- [📦 Maven for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-maven) + - Project Scaffolding + - Custom Goals +- [📦 Gradle for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-gradle) + - View Gradle tasks and project dependencies + - Gradle file authoring + - Import Gradle projects via [Gradle Build Server](https://github.com/microsoft/build-server-for-gradle) +- [📦 Project Manager for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-dependency) + - Manage Java projects, referenced libraries, resource files, packages, classes, and class members +- [📦 Visual Studio IntelliCode](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode) + - AI-assisted development + - Completion list ranked by AI + +## Label +When labeling an issue, follow the rules below per label category: +### General Rules +- Analyze if the issue is related with the scope of using extensions for Java development. If not, STOP labelling IMMEDIATELY. +- Assign label per category. +- If a category is not applicable or you're unsure, you may skip it. +- Do not assign multiple labels within the same category, unless explicitly allowed as an exception. + +### Issue Type Labels +- [bug]: Primary label for real bug issues +- [enhancement]: Primary label for enhancement issues +- [documentation]: Primary label for documentation issues +- [question]: Primary label for question issues \ No newline at end of file diff --git a/.github/workflows/triage-agent.yml b/.github/workflows/triage-agent.yml new file mode 100644 index 000000000..185cf8f32 --- /dev/null +++ b/.github/workflows/triage-agent.yml @@ -0,0 +1,122 @@ +name: AI Triage - Label and Comment on New Issues +on: + issues: + types: [opened] + workflow_dispatch: + inputs: + issue_number: + description: 'Issue number to triage (manual run). e.g. 123' + required: true + +permissions: + issues: write + contents: read + +jobs: + label_and_comment: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Get issue data + id: get_issue + uses: actions/github-script@v6 + with: + script: | + const eventName = context.eventName; + let issue; + if (eventName === 'workflow_dispatch') { + const inputs = context.payload.inputs || {}; + const issueNumber = inputs.issue_number || inputs.issueNumber; + if (!issueNumber) core.setFailed('Input issue_number is required for manual run.'); + const { data } = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(issueNumber, 10), + }); + issue = data; + } else if (context.payload.issue) { + issue = context.payload.issue; + } else { + core.setFailed('No issue information found in the event payload.'); + } + core.setOutput('id', String(issue.number)); + core.setOutput('user', String((issue.user && issue.user.login) || '')); + core.setOutput('title', String(issue.title || '')); + core.setOutput('body', String(issue.body || '')); + const labelNames = (issue.labels || []).map(label => label.name); + core.setOutput('labels', JSON.stringify(labelNames)); + + - name: Call Azure Function + id: call_azure_function + env: + PAYLOAD: >- + { + "authToken": "${{ secrets.GITHUB_TOKEN }}", + "repoId": "microsoft/java-debug", + "issueData": { + "id": ${{ steps.get_issue.outputs.id }}, + "user": ${{ toJson(steps.get_issue.outputs.user) }}, + "title": ${{ toJson(steps.get_issue.outputs.title) }}, + "body": ${{ toJson(steps.get_issue.outputs.body) }}, + "labels": ${{ steps.get_issue.outputs.labels }} + }, + "mode": "DirectUpdate" + } + + run: | + # Make the HTTP request with improved error handling and timeouts + echo "Making request to triage agent..." + + # Add timeout handling and better error detection + set +e # Don't exit on curl failure + response=$(timeout ${{ vars.TRIAGE_AGENT_TIMEOUT }} curl \ + --max-time 0 \ + --connect-timeout 30 \ + --fail-with-body \ + --silent \ + --show-error \ + --write-out "HTTPSTATUS:%{http_code}" \ + --header "Content-Type: application/json" \ + --request POST \ + --data "$PAYLOAD" \ + ${{ secrets.TRIAGE_FUNCTION_LINK }} 2>&1) + + curl_exit_code=$? + set -e # Re-enable exit on error + + echo "Curl exit code: $curl_exit_code" + + # Check if curl command timed out or failed + if [ $curl_exit_code -eq 124 ]; then + echo "❌ Request timed out after 650 seconds" + exit 1 + elif [ $curl_exit_code -ne 0 ]; then + echo "❌ Curl command failed with exit code: $curl_exit_code" + echo "Response: $response" + exit 1 + fi + + # Extract HTTP status code and response body + http_code=$(echo "$response" | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2) + response_body=$(echo "$response" | sed 's/HTTPSTATUS:[0-9]*$//') + + echo "HTTP Status Code: $http_code" + + # Validate HTTP status code + if [ -z "$http_code" ]; then + echo "❌ Failed to extract HTTP status code from response" + echo "Raw response: $response" + exit 1 + fi + + # Check if the request was successful + if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then + echo "✅ Azure Function call succeeded" + else + echo "❌ Azure Function call failed with status code: $http_code" + echo "Response: $response_body" + exit 1 + fi \ No newline at end of file From ab5d534f5af932fc2fcb3f3c54be9777151b5c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Sat, 11 Oct 2025 04:33:46 +0200 Subject: [PATCH 198/206] Update target platform to 4.38 (#607) The 4.37 builds disappeared, breaking the build. --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 1d322d84c..baf2807df 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -2,7 +2,7 @@ - + From 4c80493fa08dd31f4adc22722b8423ed148837ff Mon Sep 17 00:00:00 2001 From: Karl-Erik Enkelmann <110300169+playdohface@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:24:35 +0100 Subject: [PATCH 199/206] Handle unavailable sources in compliance with DAP spec (#609) --- .../debug/core/adapter/handler/StackTraceRequestHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 49f304771..3fa0a9a9b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -220,8 +220,10 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra } else { // For other unavailable method, such as lambda expression's built-in methods run/accept/apply, // display "Unknown Source" in the Call Stack View. - clientSource = null; + clientSource = new Types.Source("Unknown Source", "unknown", 0); } + // DAP specifies lineNumber to be set to 0 when unavailable + clientLineNumber = 0; } else if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON && clientSource != null && clientSource.path != null) { // Align the original line with the decompiled line. From 6ff846580e33dd5f935d86da80674a4de15f1527 Mon Sep 17 00:00:00 2001 From: mozhuanzuojing <63572041+mozhuanzuojing@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:49:23 +0800 Subject: [PATCH 200/206] Update tycho-version 5.0.0 (#598) * Update tycho-version 4.0.13 upgrade tycho * bump JavaSE-11 to JavaSE-21 of eclipse config * Update maven-wrapper.properties bump maven wrapper 3.0.11 * Update pom.xml bump tycho 5.0.0 * mvn wrapper:wrapper -Dmaven=3.9.11 --------- Co-authored-by: Changyong Gong --- .mvn/wrapper/maven-wrapper.jar | Bin 47774 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 3 +- com.microsoft.java.debug.core/.classpath | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../com.microsoft.java.debug.tp.target | 5 +- mvnw | 394 ++++++++++-------- mvnw.cmd | 332 ++++++++------- pom.xml | 2 +- 9 files changed, 430 insertions(+), 312 deletions(-) delete mode 100644 .mvn/wrapper/maven-wrapper.jar diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 41c70a7e0b7da7ecbbcf53b62aacdf8bc81e10b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47774 zcmbTd1CVCTvMxN+wrv~Jwr!hl+qP}nwrxz?wmEIPr*-E$XCM6Mzx#``?;BAOE7rRz ztFkJq^2w?v<)wf@P`*JxKz#f5jqp$TuOH-}M;Q@i0a^)JQF`ES@>1Y`ee(_IA79A- z(~2nny`qeOtc0kDk}{o)XmYFoRR0eIk!Sx+LUeJnX6V!DjtT+@v{s}9N;vDpAJM3` zwpt4LHJh+s@YlES>i}5qB-1>+?g^0!r0H?Grdj_;&Y1kFA1!f_vhcfy*-jArd~*1L z1*#TPYblec;CZ(8Bz(JW5w1fT_5-Zr`wV?LGWr}`pg+_LU!^Xq$Tilr^VAgBGU6I> z{bTe^Jy=W&oJ6~eRIjX=s<+Ax6HM0TJGBSakpI#Y%C^+2?2jJQ-@pCQ{GSaG{D0Tf z8sK7V^Dhk)=KsICxhO!G`flJw*Bv^UcAQuAq zk4{t2NzqV>(Cokeo0Wxs2lnIq(=^AQ^3TT}B~RT2Nc zRsY{6`>+1~qwRke@V}Xr@Bd?6GX39r@*jc(ZEc+#o&Lu=2k|{Jt&zVJ7yFKB3E^*-9eCQMT!%!b8P*r?d8|az5Ah>j~wj#Cx7T_CTF6 zy_tu|40^lja%x*SPu-(r@Wwjn1X4_1Uyw~2lVB@oE9wj86U~`ahwA(k=%t5E{4uD| zO%?#lAgT{tZj$k3q#6i*AoP+72!ioq8iOpiD#dZLr~Ftli!h=7&cj@_6_(>E%1412 z2aNW(6a%hEh(UA*tvy-A9*~-Ogi!$7_GC?GqZmxaW?MR?)G;N4I4qEr#E1b9XO1aF z97humU;81u*A#%Rx#-ep=GdLLFOmgVO|M-?>a{7d)7r1lQbICO7J)hrs~Q+;GluG+ z(S&dwX^kQRNqCSzGGvqspgU6`*Py&LwzzZH0-m5l%`zH87H!B!ka!JQ1a((O}0A}W3~n68&p=__?ouqwUu53z^E1g&i` zs`W{sBKt8`7ns0ctfj1I5WFWH`ty@Zc?P8)<<(49ber*5D2+_Qf@5HDoVZ5Bp^Z|+ zE;7Hi+KH3BU~s2oBTt&8hCr%m!=Xq&CZa{CJk%b%w%iTZc9E35Ph^hBUi*aK&PM4d z6LQ_FM_4UWRg6F%*Bz82L`bVzLE+mLKwv<-;Y#V)s&VGG$lCq_6DezESceULoPJ9u zRhuM{{_0W&OGq5o>9%OR7r2fXlh!65CHm?Q9+j)DBycsKkTr4L=VfyM`>aQ7tumJD z>!-`ABd}o+y2HuL94Q^Df=Yc5c!Ma6n7ArqhMPf6a=CVU!M=65Z}7B}BKJ4i)d?dtvMTOseZ+Tl3=BFf!3sy` zGeQu#j8U;#ONxXhnnbDv%n*3UB6yi`$4Hi&Q1zDRrG_D=8qkA_tPU!Wi06D)59G#W zxs@+KdI>H^w3+)B3$ozdF0<1fpxlEi4f8!~0qI^j$FS~#dzh6L$FHca2$v5HWWQW+ zic@F7U+)!Sw;g{|G0&G7FM@%oB6>(?TSF|conUp=2liNae{be0Cq*=5t6mrK=8Jxk z#Bd365ob5mH$#0R%iYyq{^Bao*s+71S+sL9f&E~Csoif^_`=GG{cXvP{V1kevG;C9 zz{smS@QbC$R;-4vZ|_3>aG1)3TZtJVowSTgL+_Ao?J63qP(Tz+Z<$T}fX$ZAg<~vF zjFLHSHsB=x#=|}=XIuB|nNYNLPr*a<_m2?6Hh<*NLVQw>b7>1}m^R_sWlA?;V_Q5& zI(Kkr3Zzql@gHT!#6^@e`@F@wDAUM_=}1ZBwPZA!D!22hxt@_nEn|Fyy3@WzwsGrC zBwQHvIt@fzSZ*Hc*Vu2Mb7py_Mga~DIPRq(&pv|sCo5vS%L~80UUPeMb^)w+62#vv zU#w8|KI(mJz(-MkNEaoF&+fq~exZnJdoZYdJ9YLjPemzg?!2j%irwQvKYjE4{ueHo zUwt1I1pW3+AL_sRrvJG`EB)OTEo^K8aJF(1wsp0!vIQ6!JCG>3J31L#%m1hQ{PX1I zF5v8BW^ChRZU}I){l^wwoup%nB!CjK1KcFlx~iyL=hOT&p`)zY&DfWQfQ5|Q6s?j_ zqNaoam|_$%2_^cHw6!V>=lh9oob_GVLSjAAroqwVD9e3@$8_<$#O3c-(nxQBO6LX~8hE28jr7?U4T>Q%u3y{FD_?H(g= z#ImDHMu8&EC4yXdawSe&QIK5CQX=%oDO%ds4eVD^OIbI;iU`X*y-%)s3xVTsDG=1h z!o~KgT53$zDpj!eP=z5NY*3e&LncnN1ZO~+pPD{eSK%H_5<2_hb!UNZ9-{zYC6|(j%Uhq*YWmStgr74qTj$vW)vDE zPYYA-Jo@8)KzzjsIxMyG*c>`KEV*-k+CruAb&&TM)rQBIoly`;Q}zn|S$TuaR4JLs z47P}zsBr&QYsn9lk-)h4DzAZ7O{vy)lWq-GHNQ)5koAC) zpLYW?ZR7C zkkHtaC970NDQS@CMM8c7YA16OpfzaO=-CMciIc@jOgah)%Y~rC!6`I^D|k9vWFv#= zxH5*}bTzdWr`7{HK56b9)D6s6Q59V}Oxi^U`bR!;-BTbmgT6kHoqjO0Rlpoyc9 zD7uhwRG+p@cOa0!mU!5B;ZZv`*fjWz0d|C8@(K}k5f_ZSN#oS$+2c#_H7G=3NAg#B z!f9JTJvmEOzxSfv6tSUuh~cR*WP>ghM~IYZ{bP7MJ?X%sbfv)Ys;HcX^mv;3R9?OG zz;Rq6!qY|hrW?V}-w*7k4;0v@OY?TlHq&-d^TJD7RUSrJ`D#W813UtHeHy4rZqjz8 z1o=Geh+e4flVNKkYqge*0>C}X$G1gMj^{%7D|v<~?g`pxu9T6T*|{&ikNIdDORain48RY9 z;nAg7uOKjVOJB5>K*Ks_lEXf2Mt~q`N>fAVAXoxvIgev!SZ3|3J13KVN{qT>KW6$b zo2;HTs8k=m2f5gV=Az?r8mNh^G$6rKzo2d?spIU1Xf)u0_L|qF1ASpA+a+6`CyZy@ z&}Ou3eUB8Y2;hqgc{h{&Eaxrf8Uh9NJt}egDT8bGOb*{B^r`2nU$&36op^io`(U`X zG341k8**w*R)icCZ#4jl>AS**N&V8^z>6CSGrWrmh4dRF+q|u8bFEDE7O+*Tk8{|% zjl5gd>zw*_4W~$9PrQp2rzKdBfI$WzJ3GGNssrqKwC2kjXphbYs`?$CIH)@=-wwyP zw>d7>v!3>*nvl*09cN@{E8zOa@%rqvX_g+Rvm1W@bPnm~v#yw!+>Z0b-paORkH6f_ z+V{B`9;aogMsMbYnIYg8lH#}XiTYUL7veqHJF$KMw6A-zc^mfq0GP|rb%%eaEm2F< zRX|hFHa3R(J^H;ho(b0T{J5zXpQg9YK&nJ^gq+ceiL#PCOuthEc>MK-e(}I%=(=iB6B4d+ zEzwf`RSa(}dW}tyQLKB+8kRMT7J z$z5k|e>O_r$h%fb-`G3S@ld4G%Bl`Z(UZ{Okm&Y4sGtuy~mH2LpdTqLFs zu8$ufr!CpnfqN%# zTHa-WAcmPE0%LMt{E);^l`z04qXAJ#r%ZSvE*d&?Hrkj@glI{i>WKjyGau0hQP8)| z>n(z*UWfiYHlis_4DziIw=Yu%Shi8TvN4h@|M~0Lk*0YL9of!Z_^%U(S(RF^7dh`=o#ud*+GN<2cb1xC-{rDZq9I+#pBbh;f6NO^A;C78*Ie{e8R&!-|u0P|LnV)Yrc3NxA!R3G7S;TL;<^uHJBt?`bjdF-zvQE_q&Y9FD%~y$JZAra4%T^O0Rw+l@p_13DV zFAdeM3T<7*?p%&TPwyDYSE4X#TG6W zY+|JNL7T5_*AF;5DG&R!qhGhFgE8fQS!l+j8un#0q|V*nf@)5-x4*M z?77y*cEbXOY6v;2JG?m|Sf<7sFtalpJJT$em{!ytJ?umC%$%7K9Fy8M2Js$lW-YcL z2FiR|beKx!L!Ey;rnf1{ojYGF7G&txQZW#;<$F||nkq@0UNhG3u=vbEX~n1nblT7I z>sOc$@5ith7$9I>xdgQ9#Q)kBF@e+2^p7W_H)CK*#6sCu;|YB%%Qq+Qs-kox=Z-=I z-jiOILp3HUC}Az&M2$%zf=Y?7 zSb}Qa3`FG@C^FwQo(Vg`b&)c?ERu)^(TLz&+1>NL$k~CbRqx#kDRS;0JyR@_WSryP zBE?Mm?9WmVwd3hJE<}4lV$~Kmw_D6Uf4FF5-ybuUpUEn>$)9=FLm!A6f9@a<$q^!6 zfaB{g{DoA#ntLD3lIT_CAF-iC0({QR^k*SsUssM`8a4Nh8p(!lIZD>zaIqn0Ti9k2 zu7K4bSW0Khd40zQCtiq{5{6Y5%`=CzESmHf?7>VyBM(v*JQlH}*)`WYt5(G+@K)A2 zrKF!L{Pb9Ep>&I15AqFDv`*H$(hx2>v-;*;A{KC3d=Eb~BJ=pt^j1J%uj>&Q-|tk; z!x4p!WM!OXK}wSC*Q8FM4pD71Nx8enA+dIRlSzG30WG}K*>l%MSPGF)uUYYCDT@## zc7f9n9VZOFoSL(I0y=u^&5_uVCY&5!O}Lq*k~@NMq+9ty@qUS6@m^Ciex;ZjuNGz}l6bp<8AU?*e%yw>5JxB=Eb7=CR%4~Dk$6oE=2B24h3wO&kIsg@Ga>uN z^zJGE_w6nTfeXcl#1kU|?-;8dsyhu` z2vFn6sS(g`6VAfG#jXm__lTuuvRcV+_1NxTqaGYFUABM|xsV8ZmhxX&zZa(L_$uDu zrgiW4MB}Hupm#|vTV)#B>>nLlYl3U;?cCsleP*L&)SK<|vCu}(@sF+nl;*xlTsvt) zUz5gce3?_XeGFs^cYc1!S4S1Ml7@s-rJ-(Gn+q`C^mb_&FRv5L33p7Eq^P8RvE3SK z8%*9Ux`SMd++}oif#U6o5jBO}^t85ls3J9wzz-D)i@;>#C`eZAw_+$6ScFFA(5UiK zil(c1Ik~8GQT3}aK-Tw1P8~f5RVl5pmJ8lXr(Zv*C^MF|lAtahCpwh>IIQUMDY8~; ziA!scZ#8?{5P%1TvtKYMu0_#KP257uk5ea-M3`J6Xcv`l+>#9nw5bpOf)VpD4%fF< zTZI~=O`+)3okg)i?h1;26%zVn;M&9g6|4qe6~IC-4yz=+(l4#{u0M@w-5}l^m1ZnY z#+9<9qX@$@TFb&rPT!6)8{607m0ESE)kQ~doxwBn_=x7}dq;D~06v-hAtMW^>XVvo zdR9V{q_bQkqr2x0%hjWq$%=oK!ctO&x?LgpD2Ui-u54FB$b(^)ROJRvH?YLu~$ z0!B5dSz?9c;tk5PwKfCqUg@zAC#sZT#3aJy(#bP!AkzCw_*iL%Xq{S%c}{7!c(WyA zo@S%zH4>z{&_gKwv7@h3rXKg&Np6Rty5u4bzO5v>6u}hD$>1kE+(Pe#)9Ho=WROl* zTr;8^OhqT`DM{-l$7&)=kyMHtXOUFjO;wCb<~*%uTRa=bC1@C{QpduwxWS-g>b2r& zG8gP=W4>shq;0jcKz|KD`(Tgx@Co> zOM-YAur-rIPm)}rg^O>dWvQ##5M3TFPTe?6&DY%}gNrcttIn>g4AjDmi-P5c@ z4ZDuyjUIotw6JD~#Y}|4x-;Wv5W8pMC62g%6LGxym7`+aF`xj50a7)BnmPW)}BczM%eWw zjgX&Y3$z8jDUj~nw%0D%xq3POwXQvR;K;l4TcYsoXh~XMK<2Wtt)k>gg@*gwM)$(z z{-!-+1}sS*wzZ}h;%Dx9NTogOZj5qN8!@W#&)a78{+)C7`9M_zn}UCuCSiPkelkq;x8^ zf0{%_RXNY*fBZY?>7H4@04hYUofR`;y+1`OG=R z1%1^yZ|nl{%e$Hdl@t{HE;MMxG)Pa;;xR@|u5==KY5cK5uDziMu2+yO&@z(+>ufOg zX;J3MqNu1ca*837jt{g)9Vl=c>azhg9zPS_5G!|#Dt9oho4?v(>TF2uKC2gY>j7?z zuOD1Fe<@8LUAbT?^DdmZK&w-s*i)g}e%k_UfnmA)CrWx?#LYnG5X!N7p$J? zZ=X~%j~iL{d#)6-MOBOIncCE|*EC)}1F)aA9k2E=#8XApiEfp?K)t1ip7)Sd2Q$~t zY-qjp^<{R2rZ_9gXlicD&dcPtBjoa$YQEonHT`-H@XBwLQL;N8WOb&uSK8m76e>H@ zsNNKUfy)NA9)9|0a-6Ie56g5^uN|(R6|cnB|BGu92oZsAFc)Uex$vsS=l>ORX%8Jd$kJ zp6Jua=>W>@H>Y&cp~mq;C8pJgQmkTw zZBR}Rr9!)1H)U=h8Y2bUh~t1g2CO^*Rl66T+NfArX#-q=tdjk}_$jqW?0_N#tNhot z<5gKqAy9W|$(&d2x%ck6LpdKk?D64n(dMSE7F{dJujPQ5ywQ7yy@`dq4~Brp125lz zb-vRa6F`L6Z)V3IaxI;x#r%Bz#iZFs(ulrnOC5y09juhJxWQV(_Bzqn0B5OXhQTm@ zY#yi*kzjg!`Xo|k+pLZUovYPUB?HV@(2@%OQu9I6x^FWMR~%J?_v%B>&g`dD&Ag;^ zmisa`SdGl{`dr1)n@30>Hi;YN`^GTu>b(G8(GWknDKFN*Cf1zYR zV&=rjEB2!xEG5_piVQmjyJTYvm6olSmLHlAPn(uM9oGTn9s_!Rn!OTn-ja%*n2YXX zitcEv{LoSq8^bz6sAHyXAwW*aahU}kq_izmGXCAZJ zV?j$?xksrPr_v^s8yC|FuKYoPPh4t1lN>vtq}&Fg>z={;Xjht!hdRncC6|+Y{8G7N zvCQP16Z#JFSBzc2wGRdNhqgk6_jfTi{olpdGJo-0)c&1L@)yhCAJ+!z;BLsrm_A=_ zSu}9iMlh>1!KbtYkTO3A8CoERQy6py<3jN6)^NmK_niz#%tbexiap7#7fNI@uM&!q zbvA>XNUb|uGq1YAaZhD`f4Vl1Of;PH!k=7yzJ1zu?YwzEfB3wYJHi96649yH#q(zy z#|AgGp>b4%8mvof!DKfyyJ@!y0bZScdO}!+l;^4I&oNvUp+#66shjP@8+r^PsxNQX zcRJU3?%K6EmlYo0==+N8?D4$y;{y3#RXOzd4hQf2aJjy+ zvnSNq;7Z5@z#Or<_M~5QIOWP?+a|Me<>a z6oaQ!4@3=$7Ii9HYmLZr9QAW}JzalsO8d@`B>-w!TJ5|?$(~^uih`W<$D8QAvd0=l zRRhwwiq%h1|9RZgvP!6Srb8jxd(;7>{U-aD z>f2KOq|KmSG=V==QAsI`KRaNRt|f+JPM-W+NGhc8G!SVyi`GKCT+lx#`dKj-4LC1w zSdf5Vx9&?A&DC)ZRP@a+^9A`KUbtk%9@-4!qM>R~$|fa^w_og%n{3E+Seq(VN$+*X zyUvd!(#Kp-aaX6b&&U%rnddl6G+VYyNFOwi>}KU~p2|)Jbv@#qd}>XU22dGbqHYa>1!It{c^7Hn1>s`ZBaq}AOuf5q95W0+$e zU@O{xTWQd*BB|`5Dy&WI;fms4F`(teIjsDC$?k~iqyS$6fd6feTcdU}Nl8(9t!%G; zrxZR>Tt<1j_=nOFgiEL;W@9u_nkUXF%ADkVFkDoL0jkkLRkH@URXI{MYO@j1yl$x6 z(4)WY_z=>}j*7)zvx_02HJTSeVKC{+ecB{iSY&R!{ngAAjEjXCBT);=+V z{7e$3%pYZGO6r8Q7_;C(a;n}Ek;pD$Bue|n&x=~5dc-*+EqhaK<2+G7^zFWRwZKot z`m$<2yCGbxu%mic)c?1}uk}X-T1@BlY9*7!`Ayb0D~&AjDJ7pxKP{v19jdx={$`X4 zEzlW&Ue=Ok?GcW(49NpVp!4Y`H)`vIrq;?n}3s6f9!%=e3xfE^xg%) z=-oD5$1KBnWi7R?begIsQYGE)g@8-7w{R`y;T6s*5yZVcLTxvh24hPGg2@WS;NfE0 z2#GmVnh<{TNWH3EL|Nl!!8`xy6Qa};d!LCsZ?>y;*s+46f`O<44XyU>J8M>ktdOGR zXVS0Z&K(RU7IO22i*0u&Kiqr3mh5uB7KoSnZSnH%|)VHV;94$JT+JAHa!3utpY?Fn9&){y6o?pjNM z!uevJ`zFcAv%8+KPnG5I32N|?Wbw7y?RPKvyg}<3Al%@@AEhR0TjDXFXqdl0sPKLU z8tDxND&>9ng77NIZB4pM_JywDmc;i5u1KZxZ)}PlCAuOx+LPeZ`A>b(|AZprI6q3s zgom&1>AL06a`DL;a9>-*LsVybb0u6*{&->ME#F0UGFOegJJ=LjS=Cndf{INLdJmd; zf2gM9E;#h^nb}*Q;yqwB;{Y*hvEfuy?s=_>m>H8Ornwib@evq8|5eeyghwk1(lbf_ z;5;?L?AZ;k_e+@K(YfHRPds8Y?Rz?Rh*2Q^A!Os3O zlnGYUzQRufJD{=7>;KCK^ggS)qgzu81sU!Hy>Tu&tl zGQ(M%6l;FCfeQNKQsxI94B`6y9J7f+DcCpSx5Hwbw012-PMF#u(46nEQ_}*bv^#60 z36cgc4?ajxlExCx$P-#_8Okaelq*aVtMk)^Y%A!1h0CUp-}5H(64}10mF5QzVTE$N zGR-|zft*|$iKUEND3E{0nq1ZeRyLW~b$Mdwu0mJI2vi4Hpa!wD#5|69N+Wk~e298^ zgo*)+68&*AP3+y`Ee%b;Hcg^kx@q1p`7(oYhB!T#%yS%;^x$I{gT)E4zyJ(heCbYJ zwU`s})y5Rg?FylK^uA zo-p!pUOTp`O>o`a1*W(fY7pQomcHG(lS<4w1)RIq1GgXhtl}na{G0y%n zl#9i)4+Jfn*EU^2y*|1E=oWR5sU4Mh8L*hfgrjiZk``kR8ZKHCT^UYn6nMUK1E=0= zhEUgKE7IBs1925$Ar^(sZ)gE?0iz}VW5S-W+IIf$~Khc0_H6Zx8jJzgFv>HTeMh1~t52jTggS0_uk7~F+;MN~y z5*EK1K)n_A)}?+!)s?XVD!$L+Mr&C86Vhqlxv4H_WOwDhCX7?8KG$_4&L<3i)sLNS+sj?(c$}#XzjG0gd+3usA(Pma`Ms;z6Y2Tp_miEFA=LBTS0@}Z z^K$7;56U!e0DSfoNNbX)U*)L@b$C6ridiA?WzD^SobkFl{+1*lYnr~FQF@nnaW~cU z-suxwAyL{EcQ1F4Biwwq@W9Ot2;UHGKJt-6N+W1ON66Ex3%PkUb0C=$9Z{m^<3s|x zgGQzaH0qe)T8|Yk)@Ba}69|ZQ zMPWrkj>q|#k-7or2}~GjM|KaK@|`Al$7w4xSb8vcFU0=+m0N}z^F32ExIQ!*)B#Grly7;IE?=74;RG5sa*yN@3bWqncQCqt5_4ehfRL&%7Tj4Lp% z=A1m`a?#+>-f+~UCgnm~cbe^r8hY+!{7T8a&h|vdZ~HmBoKKLkoh_SCbK(~+H++{` zUExk~5`uo-AN^1Z^;uPLcT)A=N7Ri+>VuQ(fytZ)+jkCapjU2sB9ov8Oc;@E4j-e6 zVJ-_ag0yim22E6>@?tDp5*0Pi{Uu>q+B7x0%l9hxS}drl&KRUXA7Ce0+fG!)M~*^jAth8!#Yc_e6#63zyu&(~1(8AIub-;2*Z* zv>=zaW@w0!h*8luS;Rso7vqfw=@ZBbXN5JP`&z&ol@hq=;QcNU4f<7bSwZ7diu7u- z&Z(HOi{_nd+fA~Vd4I~Zv87f{wT-krBa^|SkH(@IbO%^YmfBVFrZFfFKmx)?twNG~ z6rFIY;N@;3>pU?0P6sG1SA4bSwvzf#se>VGI%*111(S<3z8CI4=dkUz`N79=F}DAe z@JLt2R)eLr$I-G$0H)Vae>Dn?Fdf$QgFE1hP^Oqy8f3*09y{prHHM^0mkSyDLMHsH z3`x)s!8E@f1_H>i;_^nNZAdS7&Xt|Qu}Xvsc{AoypL9K&ASy>r{%yz^-1QQUX~DQ> zkTFI<1pc|L!KT@wEtch{iReUqx&oUf{Eb}&sv}7Z^qg%FAAM?P3wsmvN2LF$V1FU@ zSaap!K3GHG$kQ8$9EU26H|`Ao@n2?wV>=_!`6mOx3Ha~Kgyw&3CjTC)WvjmWB0nQ~ zTS}$SpaDUpt?N@wk0;OulQn?=htt<7PPlmc+haGRpt%cdtGK9vwVG$*kyy{a$x3SyjX0b~cJBq9Uq(j+s1zt-fQ=fL297eQ|ad~vMg#SuQof+2eCd3gW@pGj< zanDPmg4_K8jel8jXmV!b;6Wtf3l&qr9&!NXqRfQ7RD>s1kV)Hlwd8N*6B|L%As$&x zuPOC=mR5}oH#P-%&eL^sL_%AH%_Hn*3yokMHAD$EyPJN=^(_LwqJKyI&;C;h)a#)_ zD7uIRu@N5;i-Pl&d_NkwBU5M6hkEI+G+w_P1CdgOP@>B1 z0b9uvZcZvjY*aY0aM7!34wV(5J5`TQARu9&f5R`rYkkloX78UXilDS7I@Du;>hI;w zQLr~3Fa*%}X@Hh~?UX?8F&=?zgraC~KH-=r33(UTeHyb;sR-PCFP6DGO1Cdo30TwI z0W&&1oz;r>M`#cS&#Rs2S)VJ=gEA55P-~Mkm4mgM7DQM*K|0Wgd3@L{N5DY=&f5UaX)iR5XxXD9U+6e6<9Je%b=5|d zYST!Uqr7fR1kjOR({~K(dXQ!tqvH38Nu)7fG*sSC>_)mwJwnIDmj6Ndeaex+RY#0L ze4HygEXY5}Ilgm`kmwVx*&ZsER2~;8;Cg8enGOP^mZ6dQ@7)BN?uhV3lp6H#yk#Fg z6CPA6_QZd4_tHO(Ad4)ppm9hsc?ekG4x*#t;M7IDS`9XBjsj6eVVLBfh~mr$5e22* z8biLJg4T-~Hhvme^tITGm)5H(zF~<;f9TK>^{H`BxOxzWLvj8&<49oD%WpQ12^1%c zd=te2If31W(<3h;+flmpe)oHvvV?=qTCyEGv|tZWH25M$d2p zy$0l@^Pv{t7Dz>X=ax8%r7Vf*3#IRTKdS4Z?X57BIqy$&WxrvN**U=o10LuWDPUBV z9S&ljiiS84jxvQ^;=GXL0gtrU_3REMqbb5wIAOL&XtsbMDbCL7GtSJxFJ4F7yyiF) zH2{zeG{Ga!oP*=jbVJ|OK@RAGw{F|%!=Dk*27iqLXZXC{+bZm$>iTVH9He(I{d~ta zNPV&yO1wIftjQpV{d7+Sx2@uA=bvJLjRao*f^Amox%NwdOO$?OE&@5Hv?aoMDX|J9 z#gE-DP$l<^)Xv|V3ynU%F^c$9-4&Bu)R5J?~)W2?VRZ5w+*fFwgyG1(!jc($a2&k;`{-S zBn*CHc9Eg(64q@%9VzS9UKnyr#j4z)m75!bI#O;Sl_r{L4~v2pV6*ViC@-g`SQu1#_-V@=MkKFhrRy`fz&AOkt#ZWT}?#HiBkT^q$cs(O&_=Sr%q&Lp+deFaaV7(R= zu6(?pcgT7QwR;1u-4Y^~<9Mgoh?=@@Z(OC^9yj~AZbsDhr@*CN0NJT^sZ$7+PX4-d z3Fk@Ze2QyYk-g}A)lXBh?=XDN2De!{(Z&m!?@n>CdPSWg9JM*uMnA8TuViZc}XroI#$xQ8c4!VL_BR5&dZxNfxGV;J7O-aO^LQzaAJ7E^IosUGZ<1TVJI>i9ug)~cn zoUuMx?!lVmeP1Fjh4j6D`ojIXAt|-X4e%9w#sAAOhk8-x^8I0KZT~G}i}mju^Zz9M zivw(o{)@PzqM?PXhT#(n0@mM_UnU^3SbzYbfY=OTzGP4g8yQj{-wCsq21CYlU_>-K zbyaz(V*AiPamU?V&<#<{E!TY=Yw?yt5(i??+Rkn|-I{&v+57ALbELNSJA3psvlTMC zac6kl9>#4EOlkDnJk(5Q$bmQ;j@G0bBhS@3w_C}iHdEDFk`v1dh|3h_bF|%f9nxNX zItS)6$QhXQ!~+#;F)AmeXQ^tiI`D)1etF5AhbHAsH!XMuE$wxC~ZJ z`LsyEj8q1uKhxaTnVPV^oS;W$JCGP>fxE2?m)DZ?n7HX)+T*0M+n`<93IY>y%AT># zHs8bZJn)?2A;SzywXhMj@s9#P?9Z!&BNpPsq85A^vl~NvII~syywsf{Vn4KgGm}M1 z_=BbpAs|mF6G_0UJpwFN48#RJ8>1D6My#$l@ue7b4An*KT;h~~4AsZ6Q_}$mq1@VV z)ldHf`SkQID{4+BrwW?oVI>z)ixVU>${}xJOLr4ZINHJDgY=eS!lo;AOuE!Y6ARUj zM+j&fdr6SxN*NtFm&`^)oef+M_qkY`ELFrTM6;_^sK&c0Y*XilJsf|fB>+Q= ze7jjb=SpMSQhFGpy}-!*33}P{1yEw1tCdQ=6$0>hek4#I6JqgU4XWDX} zUkWp0;bde8==5~-HvWd0p+9bM4vlm%ZgQY-UOh#>9~xjg2+H8-v{cu+$%5~h=w#LKU1Z{X`_-gabEkHdB`*1Sw3tKGRVZkNX zxf?>3DYs`?*Kx=9UXUJq@O@>$yyTWzf(8mLA!MubJuAvhK}*YaG6!B~p@`%%!F>B7 z3%5A5b%v;cKANT4P|9?M*uT8<{pdr-aBQAdK+hc9J`mS|ok_a4YC)TDOmg$usq>tv z%Q>KG`YZZmil-KNKOL(r(;09Me!|VaeVZ=CzYmfi{1I+aBZj%cFzlHX-=efnBA2Zl z=!MW>7hy^Nd=EhwfG1nOkT#6DN&Lu@HB{>5gtw2=Q#uG=M6V=C=#fc*L`$=ed-eE& zc&(V@94zLBkALWGF%0Y_u18el6BPT(7OnLX#A<2Q3!l5^_)>;?$o?Mi>Jf(K6<(Vb zTN}wiz54Md<1F;m%KPo#*sq>I`m`s1(pXmhB%J?q+UMV9z5e4iO32p6#N5=`0q|b| zaB|X)61ED;NH4k|(tL0bNJUulyv5pJ2(4g~W(2GRm9-F(1n~ID4|Mf`<+{wZt)I$Y zr1(yv!!ht5!0&}PGh18y4V&B#gq}XRZ)={@M>}7i?$G+6Qy9Y-cPU%TZmIa$C+n1a z3o@6RM_V;EmYzK{8b>ptou-Z}b(0uGFy(6RQI@ziEzQ68s6R%H(0?t)(fZ5YMwm%Sl~VQakY6LAz8@d(@4ls+egR(A<5z zZmZOd7`zvZBcteN^1eK(wEm`92dIR!m;+v+`Q9tl#4C@=9v#FzV^DM912=xhOWWu+ zJTVYHwKxG~GfOBF?@MS<$1ugl3cy+O_G!LN?_nse4o#%G~@eT>O`2u%F-J|xBN!UYN21bZ33X?hf6nuJ~7*?1m zheMImM&lb6mG2T*GbG|_76)@Ys8tmH<7f9(RfG;ADJ0y?- z#IaORMtw_%;*VHO@=N-J@aIknP`-J9ZCTq!3T3E0(B2L zW{mT~D3WGLMkFx51_?n|j7$D8nu1b91+D=Zs9bL12_jDRZ7n6BDntNFF)jnghy~^@ z!9tUm-vl_W1EmN7>{Saq0FqwqaEoLj#Cn zLvt(mc$PD>AciK1^&;zplq;=yGHWx!Ebx_&*s~n zjw_Bc-S_XyU%H;aKV9D0!bG49N?6io?=(Fm<)kf5AG8gI=kMINByk*byiFgS=2)u< zfS^>fmZ#0at5PzhVWM-F7g_>Rwm%m~?R=l-;y?5Il*B^8Wl2Lr6TAnA4WWpDRbG>< zG26#@pi-XF+5@V8T`16MEee?_*Kr%7ym)k(VVhi)C9Blc3|qLWRn!`=f+YL9v2-*C z)aXy$ejb)Dj?UWYb-z1+o^cT88lZI$YgzKHu{f*}Bx zO|w1(1T4WpFel59P#=RBXsDd!#TzS6;7ANehjsf|h&DP1TbG#~N4g9p4K_v&jTtxD zQ@Y8mPs(5n(P(CfM^{vmNk;<04W6*jd}SUZiug*Tyqld_b}8a4M%dRu>8kqB6fO z5k{#tK_}uup^a#Y4G@GGe93~odC{(*-=WP$B`gDr%Hq?=#-1;Sv7^8ON}4aqOfU_u z@t|7TZm88K9NJb%El&s?e8q=4>l540O~+;eqF&q(Iwejl5C(>_tfkMrO&2_L7ukcl zq1JUW=cv-=AT^_HHZ)h!rqVW#q`9arT|(;duOavOWqP^PD^3L2fc29WurS-HU6?k> z!OluS!W|NoF*;|d{jQW|FAPPJ3>ek}*{kvnYQ%Ae37gm&V&E%R{h|$=g@^tRYws9b z3A=5Lc1InvW81cE+qP{dE4FRhwr#s(tCMt`+`N0A`+cX*sr{b2Pu*IzeyytY=Xu7Q zW6n9|cq}RrD)5ml=Ll3{O4ULh^0?E9R!Cek%XPR+NhZ6m(Hnn#zp{mgW)xJ%An zq3UWeDMTM+ILeP*xE!3@{op2;MxwwO5LYP5-Ozynr8I1s@1MZWRJipqyI3F7a5t@F z9`f>6iB*aF4w@`C6_qL7z3{^&N8l-q3q(O*V$nu<_egB;b%6rsF+xd7?AAv^cv`mS zji+W0Sb$gReP5*8syVkG#bQ@r>J}ZD5$Ci%P96D}YkrRn09TLgF=@a;NKR{klXZIv zJCT{ZHAkNM0?<24fZS^du`(KGFx~us+Z&jyeyN_1#R~e9$nP<9ZQ?~irF-WW!`=<0 z$`XW^tz5f3k-2C!y*#Z#1cIHddJECFRFqt8O!P$3)2^@8TN3!B6$ZOWNb3pqAHO&IATK9mv^b`&0_q$7)oB$8gnSs$9OTG?H;r<}JrNy1G18Y)jFSGtMF z@71BjFucR7^qo5N`UdQmbS3WyEra&Sx)WN;4R>G6wQ5ImX7|au%A4F!%#Go)c5D=5 zH^l)xp)u`#3IE$5rjRn<5b|x3g_x=I+yy@2ai*%9ylISLQT3ycJ=Cr04q{V(Qe{vb9mW^n%H~#z!bgJh3_%jb3}SF4!#; zXvUS5&QQ&bWwTg%>Z$_;B^!Ll1eYzrgYB&eJgd#4@$U%jq3at@m0G-J$mS;kl@Z)} zkp>cWYL#bjB;Nt?#N}<+~PrvF3e;nSZF~Hk)K@Pl)t`KXc zFGamE(tKrx9;vx;hA6mWqe+P)-lLCsOrqn7fjG7*ax@ujsB~~->DEYgXLJJjGdKIP7veCG-WbaG%pJJA-Cc4gWJD_0L? z?}QYGb6;-VrtIReHE6-vv{MlIm9Y)+Qyz-8&bt1MsCvYH1*B?&?Bt)f zbB>tD-+6>v@k7K{&NS(c#mNtf3x6?FwZ1^8_VAyqP@-~=!yq5#>^sBZ$ci(ngHf0M zGp#Cgr`%$*8{QLg`3v}@g2+|f*^Qe{1DipLwcg* zM_}478k5$8+W{}r>y*~VTq?N%gh=;=XW)I1>_7>&Tbc!P%FjOp2ojx324b_$&eE?; z@BC?F`!PLKe(S9CV~TnHmqPQ9!(Lsuk=3x{y#6M=vVH5eGntBSieqD(`+FcI!h1!M zRs0p!Cf-OdFXkpa9SXd(Qg)y-xz0^tYu=uH;JJKXPtlw22i#vBqo%&n@h~BRhsxfA0$QU$_5x|EE9WH~7mc$k0j9*4p~3`y^#!T^Peld0?ESXlK1KtxKitd zaI+y6FHT6&;!@JyguL6hDE@=p)HX+O7AFGVS{vzM*8Pvt#qm& zHCZK!yVXnCSy(fxB-$pc0xQkKs=}SAtVCDw$)F2F-%`(sZIB;gf(Z55@b4L^TtOdz z4NqoLlTWSaK=#780_&C8;VFR7APE8AgAvktG-*)*S^GipdT28$&^qIe8;N%&a`v!O z7=j$0=k4>*a}%Q_OTH(zws09^hx3>KY=%y7L9XmBJKGQiB2kXGE(>yNg&kjJp(T1s z6>7kVbIGzuk2&hOu|9j%J9OZy$=sgIR>GSBI*Mn}_P&es zDn&6}VZbgw8e=N4NBL)jy3?uZ-q!mCpCsvTL_40y5Vt}2%5k!RKfak4kXHmajq-&> zlcf5Q`{V;1mW9q&f4JF#HiX-w5qQ0wjOejfVZ-BWTW*Tj+$o8C0S7hX6pv& zjKUJID~{|Y@Hz5s3j<<^LMRkG%{#9Qk9y_WR5L>mUDeSJO`@dZkw zhfctn(O@a$x@h7tO6BL7p){dOQz8=qag>;j!DD7>9)+oRUuK2C-JfQq!E~9?Im%9w zzD92iON?<|S;t<75nw_RZVN;El{tOyZ!jVLZa=MYq7O5RF#Jy7CbzAG~x znZM@>`{N;Gsw}z;c?Kq^o4~AD*H}K3YjBdG(QSm_Uw7gw$P4Bq-fSg+J`r|e0?ujx z>A#x~xNGed?LzGLx~JJjnj4{WXx1jW-$%@fmk;7X_1<$9xLBN}lo1P|3w;#U;sMSK zF9uMe8N@ald9IJAgqL5M17xn!`3H#xSyBbA&Y`iOJoo?>8VQW6&#{O z<>WkL-Z_i9(xN;h{^KUpe+woLiXDRTDL_DdfpxKKNILk+pWujup))6Nmz3y_QTalmN`(wzIsZoU%A9A%`JyWh9j*JgY-Be>dwKQJ8XLz$YTPD zKITj)p0+1cwPYzc5^9~p#s;Yl&-P~SCX?k*lIr#FVicF+Zw*^%!=mvvAM6kp8-tytY}RH-bJM)bRoPSi2k}}v%&C9msdt`LA-d% zAf>!Nz=3=NTD@D>U4LgA5x7-YEE1V_uPp&SU7f04_KfeTY9JT8y0nHl@5UIwjuP(O zn<7uf=c*CF7ys-N(B#1ZqvaD!BS0%vD#N!H5CuPbhPqshfSW_T$(8n8tz$ULN6v>5 z!o3C8Ev2;HIs$i4j;w-~H>2~KtY5N3fxfgWHl?~yf1Gl_s!7_37)vG7d;7_vkpNIr z&yZp1W^ORFp#~84o}ZN!zM?hR8)V{HiA&9zg_+_;6F19+BA+Gl?40?wY#y$3eSN7OizE}s2J*% zqNo{Pd{#id`yMy%sA9hl@ctDfEspU-s=qq8)6jnxB>zV`@}EK?zk{i>^_TmVu$!T= z-T&?8X2tT$_S5|w`4nDt%EyBgL5$i2iHr~p#HSZRN*D@+zcOYjFgZ`Q0q#jAMTGPD z#+z7&4HAV2_h+{I@#XsZU3Z69XXl&N-mFmcFN(x$X~Hv^RP0%Dq(4&gWCrst-Zqc@ zc)11_c6~O5DPU4WEi*I`h&Yf)?g1}ISqd8^{SCN^aW{TdR}hhwG;Z_6rP=Jd?LGu; zHyl&={N1S0X+bbn$nt*ta`Ra@^Lm2^e$ieca`pl#FMFFBhM|*u>R<^ zY{g%5o)pf2WsI2}ioh^v)Ac9}Az8UOq(lfv8fn9!%IYOVwRgHCnvQ*s=wV$HTBdI> z@WLyUB?J%R7Ocw4%I-2*A{E>tN0KhKWn1Irq7v#O&EXbM$l&K24?*QxyQBw2^Cw$GmNn$C{SGG|qm4=lj*}o3()tV(y=}Tv1`jzSab(UlO zpR(M4I>7(JBa^;#XkQ1{mDLNe{dGwhsfwWTn8-}3wm z?nV?{l3uKEt}djw%k(kRapUgx`48+jXSU`(0=XDtKK1@L(?Wy^xmnb)co5d;Hk32i0mxtXEjQl6~}~sHEWlwDDtF>OiV;a z31%LnC~7cvWu!kI7sg0M+qcrys6QJxV^Httw$UCdYgJ2e^~BeY&mh6w-!XsWspHK? z&O5(QNNu?hcz5pS<+ITnKU!I+gElT%(sbC;rbvK*SN7dgmM@~_fuO9ODak!JmzHLM zVq&Z@;b~&5vEXrHlwrhb2VL}aZ{wak++ec50R44f+-y0CQeOww@wdV-rhntWWdAeF zlr>e6RDXWJqLKo{dzISMD%6Ao2?je<_$qRf5PMNT`14@L%+aJw=EnvlUnSQ=y47{P z%sL=x&NZ~Xf9F{~nBLnqzE2yCW;I!;E>EU8PH*r0^yL4umcMmkZWV@%#f!6v6u>)_0;v0hgLENU+`;K$br)NPbuj*Uwob~2R2$(rOPr$s@xuk7 z{GPse*DA_NV(4VhU2D;fA5WeacDEx2?bfQ2$<8@C%nKC>m%$WA6dt8Eve=B}*hn#b@ zh4QbG0ws4*Q8fH;eA5G{2$!ZArN+5K-!BWa?*md60M4tzWL0X;JS110_29T2RSt zPjS7jpKlLH_!{(mj|gx+vAJDRxwzbH(P+VT{`<J z)&s<=&$P%drEevFk=P_9d=5(T>{bC^QWW8KMM2c4SkNB@QTR8$=7HE4rrRER#VuRV z!bfD{FQ^Uijv}BD%j826$_+m?q7O?kMmi3%?ff)EapVzU4tw@MOv#9Ug3fuJUNNfz zGOv4LIl|nFSupPpJRwg@lC0M6Qv~!HuqW_((>;T^k_R3VF}6RVcxXj&s%am!exS@f z!Q%MD=yiJP{Wv{zBP#flIM@D&K<~XFx|3hC&2pS}(OX^U5flLWimP|svHHy8@*`XTbgM=& zZi&iY`%ecB$q0XT!m*jH=h{nswKH6~1}aU!jSzH=!dH`1?5JVDxHq&v_ zb}yy0jsu*IHc!n}eLC$Ex_Mo*HJ8OVMu=wRq!^XyV{=9Nnw5w(3yLGt?4^~@e}r5X z)mlk}FXzwfFWL6L&XNCPCHS9mq@2EkZ(oqWPD4 z`=gkeifTf+&8&xzQdV8j3=Nq(v2)|#f-DV<9eZLyxL*y!xwf6+_TIK1P$of2z z3<=@I68i*;E+ngI`|cCwj1wsp>oGB_O1J?iet)a1V1)XN!^KmwT|{OfDOZBrWtDo{ z%{P6$>6{_7b6Cyh!zlPjo1SrBo*dprjeO*Dbpin5bh$c|Kvpn?QU!RvPGA6rKmk`{ zZJav&mZ|~qRIA1jmSABDz;etx4n1Cv-G!&CDiw5C*Ay$8i{9@r75pK3%G|C?zyaKH z%kkVm@x)xeA4UY!3&-cAlROp1Mw93k|;Y5sM^=@#$IaUlI5X@H*0WXz?lh?q+6hV1jynBgBVNBk-53Gn!2YH1^ z;yrxd^uFxUd{I9;{&F!t8vsSoe-ihAj(?#nQ90vqoAEI3^}56ji@hTV+V#;fVQUa|*j&-zdz-8zWUuRU^IiUTX2v!IE^m_q|9KBh&G=!g z?Qj0wVxvcz39J~*l2ww7sGYeNblssR`0dZTGG`h`5f$tfk$`k37OjEI+5<96%7v>G zd_uE@X6yE_#?eEmY-d!;HKT`day|aTkbnRX{d?PE*pg}>S;{I$&;&qU!wP}yAz?@K zz6LrRYBh^by1Zh7m&^@CsGpY%T~D&BVrPm2HR3x6Gveiz{~!Oy?@Twofr;`!%1pr= z7@~xC>uSTta{v>(o;~_ru4y2R2$h|>clZUj1H#1_v-9_Witk<{mqFdQW^2N3?Bns% z;Fr0co~U!i?l!326LUHyr{1sqo*O@^8^r>dqD<2HDW?de?%Z7og5a=Qkwyf$3|J4& z=xcx`aeI-tgli$7y-H6EPz4jxC2$-s;2oQkOeUk;qu*eTKXdc}r|=ngKB`=%GKr^T z3wR4|WPYepBLel>ErwVWB&jk%2LP)PrbXSqXwgy6geCPCB_jS?jPm~*wCF!$^q&;T z@t@c%O0t$k;)fskbfm7*f&xVb!9_qYr^E_<5%40BkfMnHCVdd4zTTAC>^b%D?Omoj zR)~0R5Uw|bVR}2OXcef9DP{9&diu@9#KqG+9H7DF6lNxa^LPkA36hBD>(Y!xFk&bkDU{nYGW#NQBAa+h; zVxvj=v^s2x;)h%FuSB6HGA+#*LH*5*dQ5<*JgUtW#PlGawAXgxMaVg*K>w`LZs00{ zigIaq(^*?{Ih7GBIQ_0lBcpW!fJYRY-^!WK$xq~gySJs7CM*I_6q@S%AkW@W9%W+i zGvyOrBR9M7Q4NfAljIwoSQW(9zBB{?oX5&hC4Ntwmcj#(ldX-E);2}m<5lh`LA zX^RwUd)rGm4$m*OnJ}b?+b~yAkHS)wK-;Gpd_wpo$)~HldG4Q= z&>ydZLw!5?izTVRJGJf2_5Sj9mM{48eLZn`R`8szEcAOa%#SlPBfPilVMO0!J08IH zv5F;n9j$co@^|Y-x=50NUib;7eIOBmVaw*(Xj32kduyIY81MA~N!?H2?mK@&e+#5k z2>*81CEaMdshJ=VUN}P*X68@e6~_=I#TmJ@2$AMi3zjhxzN0cCo1jkd<+*cGhi7l8 z%lG5m^ce`C>E}-_8%NG)wi($0gCnXI*S#0#^kFZp9rJ}&s5{!f5P*rPNt*Cg#r1uO zQ~!0AlKA%#AYg1_>tOsZ($u9Qk}3SBwS%1-JMp|gkuWATBoTlxoEE;w0K_ti53?xh zk8$+AIa|gieM-c=?rF}ftxGYsRhmkV^Dymu496BJJ5j14dn4m}>;?C$OGnR^r%TTJ z-D}P_hbI-`NV}~Eir_YVjr0D}G`Ju`E03e$;2Y=)3rx@!6~?Tl{L4_|g$gL!r}U@k zhrxi$rFR8G=GY23@H+%W%v<&YzhzXE(P;Cc}?VPY{#Z?_XlL{iOTBK zZ0ecsig9CzC68L<8{XY{%{ zg$UunqJVHf+;UvslZ}BX#Bn~Ui|i1{uE^l}rgOLJrgTX`&A|$LSpnR&+2~&7MT{fB zRR0~}Rx@m!`*I3r>5uW6J!(0dZ0dGR4|~mgDa1(5XCI=pDR-+F8~T$hXp3m`csY4X zSu6&-Hg1|6y|h^ODmljQ*8IKSIPy?3*bPsqv_{><7GJXjC-A*T-K{A9Mk`?YF+`R=-uVe@AtY>5m4#sZKcT=7bJHTg*7&`h%zBmzCh|UpA zZRA7TpnUH0U|_gAz;zDS?LFh&l^%Hmjm&h|9BKq*p@+{i5^mUcU<@tWz(@pL=|iA; z2r^bk&qVx*yHBz3KSiFAW3u}IiOZf!2p_nvDLDHH94 zE5yTJH8X-uIK{15mQdE6YQc)}txQF4W&jnsPa0N}$9r6sp2!ZaMeblRrs0AGC6A~t zJ0;*iL@l3AIm3+lI)90Ji%z3UI{M9f`Rx;nxR~)Gz-_;EAQNbzLuy}2H4&{_PebK^ zg>8^_#V+S`B|P&(@Cj`FA48CfpD#OGUupFl%-;nSrGG!D2n0`@UJN+*W=l^lNSNvkZ{69ZTTAYfkikkhxRpjJ70+Dqk(^E-Eq%nxdPhaJN z8H_kLCT-4bZ+Y*BP^G-+cwTclGuO8Ga?uR?2QJzs*>}D^4ZJ?y?_hdxoUl~EfqZb> zJV$|yyHQU{WY+~p`*pM1y(v1JXi=>krxL`lZLmSx7I$(^N@Pl8gzU&&Vv@{-we%}o zkd1-Rg1ecrZO4wx_dHlR5&Q>u6Sat*q;dCM6>h;7-AW7=^s7l0Irs~(GD5H{$y#i! zBehoHxKbh`0=Tz^x^zD~`utwKtBdLsO-VQs@CWoCZJwFXGx@FLqb?;L!_^VjG9Q&V z*%NEe^bXgF;x$n%-A3gk=wkw=%%`vk3=$?+ z&UWN2X4`u2)*gmW4-DZB5mr*n&nV+n#+}bys*l!-`3ky*U8pEa<0pp#%k%sn{?Uv6 zRro4O@s$8;{?-^@^54I!k~Uu!yZ@ws|0IQ9uj@D&|C0<}s-3$2R7UyiDima%0yZR^ z^OG$59zQ*w8)`r&2r_5}u9s>Qz-U&~AqB~)Y_04J)Ed1(MPXx)zc)R4!e*!^6@q5J z6P>;PgZCBpX{d*7V65p zM!s$goLOKyI&OWFlSa#aSR%rX6%4eQN&ObxLst6biOW<}F|*N?SVyq&3t?9E(HW!w zP=*A6f)61!nRWSeJ$Q}OPWm+y@BMNL2+E7ipScHPR5Z6M0#Py06y#=*w4@K7FKBfqn2lqI;VaF3EN1 z$kGXt*DM~5!JHP5SJgGqs-5p{B>_}gl=kQ}sA}^i9V?d8iA?zxF#(hyEN>bRZC#p= zq)s!*bk#pwxzy%T^NGuKtrnLkvz9F8=dqU>wU(A_R+Oidn!e?V_koQck{i0-Uo9XV zjGAZ_=-h0|xl}N{pxD}SR_shXiAf2mM8D7+!G^CR0CNnR z5h`iW#VPhDby{YT%5*&U#bg#@hg||H%b*8Gk)ySBS(a)nIH1W(P6!GDU%L(5ym=$< z+LauP;6x-QfER)(NhN{kj~$T+IijW_xg{BHB<$U9)3BbKMgum@G+odZa4Yh;+RNbm z*!ga*fJdoFJz6Fg_nKb99)4;HL>hv8xg3OLC*l#8QD;fNZb{ zSwn~S<5EB$+@A1HPg~nVsj|0jw^?CgOPkC z_hRN)7VS|Edlcq)e@s5aNs~5@WsCGybyN1certPGTQapAvtU`XUgn7#JRGH4Xd_K# z(i7%~3{#pC9msT96=kLA1TdeGyaj1D?&9Rm)KHP!uYa-)Kn+$eq z6*>Xc!nJgr40;9eE!chVy5*V_9|#c)WjahKoLG=jaNjZp+_Zmn zX6C?%N`r06GckE6d<+3?rv`bhTTSo&p7xdO9HW8gNajWF^S#jpwLPvjXOvEP!sXNy zzmpp`Dv6fBG6f7BEKW<=!lxs=;Tl42Be!~7jid{q*h5lJT+~i#J;$2UqB?FpI97n% zm)~X+3OIdyfTW(Mxus*$?{tG~9}m1>wI4ZYpmXNwn%-=6lz%G>D~Rk|H%V{rVlQHc zRh=u}n^ocn&mI6WMfD7nCC%ZIkS{Q6RSeFe-O)E8+4 zjg5(WB0IB;t?;I(3bq>n!(=dZFZ4=7gKX0g(gwmaY&2+r7?x-Q#C5~;i-YBYhHA(a z)o6m3_HgtfJ*e?kp$I}>W^g1rxsS^TUz|KNX^(g@B1K--ayc#=Ap42&y+(QWw3m9h zbJMc!3cqG(T{YH4I`r6Csq+E5j%qnlZ-{95L1-mJX16^1_i9IL;T=&m&PXxbdBf^g zcSeOx+PcXH$lCaXBp18Doidpbc+Z(^kIeiNOeM_F48b;Ey7N$7?oe_@bJkzxf@BNi zBQygDyF=WQevj$%%WKOuq#P1%TeMi%b53AzgzPbs}fyN4h5FyHXHAKa9?<=3(rJitUP<(-AwOX1`k)*!&OOuqV#zrKIN zoo78F?xl-U6sZZ(_k#Y27^dTaxSjGu%L#}n)AvFNfBQ$bRm78=Mdqu9mHv86^8eR5 zR@&Ur@rz{sGm(Fdng3H3uS)s{(H!|)NKU5Fq=76Y3x`~Vs^4S&E{xEJMj5MTvA5AE zHJ-k;X5!+X`jlVegN#cgFXnXv{FE1I>XM?7ODiqDkj0+h=ySK@X#V;3{(#wwg<}${ z=eAqcK7`rXOih(?4QYN!zHJ6>yo9x(@kc9VqN<*H2u!tGF2UAnr^VR23t=@|OW|S4 z?*~EP&jGKHLNkbc^m!d{BR8EZcqDRje`@&4I_gWkIDO(B?rJ%GF=|iEem+ER5(YZ- zY!_&r=*fV*9U-`RXyA2A>yawRhh$ak@!__5)qR;DO3jU1eTvm)@8BgFS{qGziTT_K zUKLPv3bmL7-gsKyUmcqct(BS?{`s{*{hoo1(dn&z`oTIIybdxB$}0-8!gL2*?vWi^ z!5c@#SC+$zc#$rgU85(=ehZG}@D{mA9?!L^kPY1IEETveoXt$@noZ z5qGwf_gjq#V#;oiOD{O9W4|kNd*u|2BDV<#O`fBCdVm!;uvDp+oMs3my@c}rajPKcF zcF{O+1s8_qzl(G;yRXAZHQCKe%V40|lOM=I4QH-6^`PWXe(+s=QfP$;OgYkbOwoS@k{xSSNxfhmEl9NzC25I>! z1tg90p2Xz^G3ZEO;uo3xTTWf5B-gi0T%cYQ-#}k}1(M{8Ao}|8j=PmmHHlYDz1+^P zaXw}{UT3zszFfYh)O<_6#S4ZX2qvrmv$b=SMRWGSv)Y&YEg~p3P^m7mUAEGuO|I6w zdNhyB@Z7ko9^K_LTGXR>-%4r_N>>S1c-G4ZoyPkPEg7`q&=i{lJB%f`xY4kAprC7| ziVfLevX=Z9+m{Ik39DgNJ_tX?iA)b3+`wUgHVZGJHG+o-9TR{uTSgE^xh#)u3U3@* zQCelvMRmZaQyo`X06KEegqV_C|2`Fp7`iNz|$u33p5Zy#A z+J%NQk$u)xf+__jE!UW2=km`UHW%>$ECu?>$BB96ifASjF$$aXe(Q%9iC;d_m&0}m z__%UK569WD8Ocsv1`QV-%tm^p-9-Z1Z;yQt9yMJQB)5j638^TKnjsO)LYv&9y4 z3~!nD7@1vDo31azBy$NxD-8=L()Mo=vZq_0J`FAbi6pZ7_>_O0>1j999*1oE;w?E7%djRY=bPCjA=C8@9rRO(Vl_v znd00!^T9Rf+wVvRgwVsMP*t=^ghH9e^rwt9@m2Zxr$I5TH_cP1#Jp zPKBCKZu7HiYxyb(P1^F3VlQed(RCzD<&|2gX4N2St%+rLg8Qp*?!1&B z^?BB27vs65SsApOO6C=^^O6t1AL0R~O=AJNxJ@EJ{cB6#Q618~` zf~K+f$&9A3{Pxf0yg%La2UlpY`qZB}Fh<}Sa~t;a27B|TXYLGDFQkt8LiDBjez6wR zECKjhqyWkJD$LgRZXpI=NJsv5bQnSaswz;?uk~Pcjv$gq{Y|+RhlHE6-=lAgrX+3X z!`7Q~bRuYP3HXN$NTSZjecGFb9!k&&wdtcd$BAP(qSarWGF&MmE|O{ZMxEH;SHJ@b z=QyP3k(6k9^e%>HJ$f}7B4jmD-YkO_D$%1|gg!-pjug^fn3thsnu#RC$U$?brx=on z=HZ$6&DUUUM?@paU;+QZ*Aa&K`Gi$-@f$o&InN9DOpRCtm>zf*7aC}V3F@JwAUB+# zbR4-dWL|+@qSWs4Vr$QOyr#)Y;g2HXm+e5@X7@$Fo^ zPr#&#WjwOwxH?3pjid9;HJ`D#-BueBtX7 z{Oooct%R)v-F4Ysb?M=OUhIp1^)I)eOe z+H1`#C6Y27GAkTeZc!`7KJ)~@Lxvzk&Gd?*h?yWPb?)twW!HQZ79t_K<9Pm6HyT*Xvdqj6S9x3}c_ zkQzVoE3epwrsbTQ9-5@pA%$!|pgx+=h4HB&9NN=2K=iC^zfSp9U%h}qT*eI7Vw;qg zw_5Dd>p!~a;#Ka@iOqo5eC*KlpW#w%CI@5ORD|`$WZop#s>On&xO5vn3j}n7jwW7M65zzw=eYBUs8u8*pMy{3=VHY7A zfRGJo6EtW0*FAvDO`1>qQkNC|t-9=gF-HAo9Q|jT_dmv{ivKjIQ`Bg#v@8RKdvw$@xny(4)K$Q3$rF@Ul8{9H*Q=&IMncCnsvzNE!6DmO zo?kIEt!=^9Gp7eARZvEqNeV|?ROiv z0J;=S3*pG4N`CKcI>tQUEqE0%nXX=EX)2I1vBb!HfN#4%qK zaH?RdMXOMaG!Nw?cyLQP z9qtv@A~?|}3Lx`36gmgQ86GM6RP8e8-^mB}lTO93;Y5gQTqoKNNp;Mv6Kp_5VQ?&oYj3eo1i;BU%d!_}hcZ!kcU z9)r_LkWP4Rf}K--nCMNEL~G<4$Of0^!$+EClLmsS0`j1lHwl$KWg$w~N`peCUZ+1d zenMKTtx=RJAKM1g+ho3T?5I2!?}1HeXEK<?(?#_q_Jh9@80{mAi@dI`h&E*L50 zWR5lr4zF1bJ(Nt5Xpn)+M1B`skYv042GG*43~acj{{Tg*2&&W8(x=<$Q~GfTM>c$N zfq%c>o!$!cFa#NCGc5GIY)PVA{Y|+$a*h9ijNQ{O^ECeMSD2RU=mM2;Q)L=srGd>W zT^1?*Pr^}Cd!!b``9&XL6z@WU?$6?zYZtHSeOTXzR43HOynpI>Pzuo&T)u*x$(N?* zUsss_Yd-xiW*JJ-Uwj(P$E4lZSeA|w=h`9RxqtmvbkaE z3dI*Hv(vp6l{VJQfa|p&^4f|LJSdjLgpu=lrSpz!X7Z|LXUFH8+CV$R^vyh+MYLJC zg*FF_JyFsfJ^WNLbY}T6hj7mv{8{b=#j^@X9yE$;M%;OY#Lw1pk+ouQp@Sb^52DWr z+7n^PmZmp!_>xt`g?$emV_Of`nrSOFMO$@B!nCF*Vp!t_D>%UwgMxrwqq7geFM8ZG zXZ~Y+BXi*yuits08q`7xr24v*i?Fm6yX*YiGfPI7y0lubJdySrG*8Ajk2RiJJ+(J6 zY)lf!sh*|SKBd(|VJY8ZvdL%a^k>z(tf#M z60Be^-$b+{TXq5h2X1RF!g&Y`@s|S_yLan2RhcASnW3hDoaEi@9BiXq6 zsRu%yp2qWewMV%RNarhUAi~N$)C4KD&S~0X;W6Ja-MBS;g!J?W56D;j8J-brLYy?3Ob=hn<_&Z z8&!q;wT6-O2V{6Goj-fpF!}86{x$0z_^;s3T6pa4`*rWb{`TJU{rgG&zo++vDVqhp zpFuOyn;g!H@n9I^zIndlga&oQ$aund_`=pjO6s*4Yz2c!+|k1}2-kzbFk)10-*6!9 zM_^-_))Ljy+Z-=Fd2<-kpP$cP;l9z@84-%Qp`Yea4)UG_a zM}uTOd6LM6z?SSv39=zTG#?;Hq#aDM1I`P+GXE6tfY5(fRrUED9$T~I2~vR#Qy9p# z9InVn}BhBM(PE5#4g5~<5s~_chQZ_ZMtAOS6fU3AXU2aF|{QfxdnvBklbxTbdve z3js5sUL#*GkT@%I_O9t=jp!MXg0GKP^XPN@D`r_)e2WF2o3m}hsYe#2xxcD55esFr zDs3W;ts2W)a2Ja9ON+sh!!KNU*Z%b;-=<%iQ;VDgzi~8r;gB|app6ns&)%P7%T$N( zG81BS5^d{!oKXMATRc$SK|&u{w>MR7!YOwx)n@LZNCu-dGcN}7&EwRJ)pd0%%9QAm z(>Lf2EXvOHf$tW{>>xi%HK+eU8(y6h124X4L+#&=-lYHio26`H`8UJ2A?5!l!&KE! z$iyi3V33=%A)&Tu0b<}35d4UZaNXGJ3H9q&EF07}XP>wHh%{kvAKyN{>)?3Z4xzpS z53_H;C_$!&hrw-qDb*)g8Fwbxx1MkRVEfr`h7k(69kdE7Dhc_hryJK1SUWBuCxtsr zd5tlYWI3p>2C+{hL-kilxSpiF&%3Bxk|KXeBwx1kNx9HP0XMnAwIn~kjD}K>9H|YM zb2mu=L7GJO(_QeC{ZtSgW;7nv#;DJwggw`iXs_N_xJ|S0QQr-alC9kn0IH`ZZz{Xd ziY6qYp;DZnW%!Xc*w|w5Ci7JSEYAM^8+{k2p#{?B-W=}|!RmDz=iS)19!l&FQOXMwTl#(0!DkY3F?i+P${SQZn z>8wQ_ylK-|A(~MU*o*exkHOOo8|;gSmh^rb@c!w?N3yG|1+XCGTDAr_VW~nCR81vD zLqiLcko`1S=2ATnDjN zhj^*9mUL|=DpyvI+F#EZwdu#d$NG)3O|<#yDe)BbV!Y(5G=_E%Fz}WhGFNd$Y}s`- z{St3WHGw7&4%d8spq!?PkSre9JZB7Tn?EvYvj?EB4?Ez%1=z*+kb?nV5=dO<&HZp% z5WSxxy}wJ7|Iteh{jIwgD88mF7GGC!4B-CAG!2s1K$WCS_S=J2M(T3b6j!w-2dov) zz=rCU?r*xd+W*Y@BeREzP4qF7c2D7#SBmio*>O#`W4TSOs{BtuAK^k9-*1d#EEGch zt9s2WvkgHLRPnE&K8N=(*!NxdMMBuj-);Ks{E|$-(sUcq2iD@OjaJ}{n$BNC-gZhq zndyfe{Lo?Ua33E+gE6UWg%>+x_E{Qp{))}ATxWA5U$J@VZws)0XRYI(LHR$pwg!}! z(o))|@0iimL{KV2m$^<9$BgQH^FaFv3^kkzyX^?tYf`KB-2uUPd>Tx6mnI_ z0n6jfEKM57Nq|(yWDUwImzIt#8|^L8vMZaVD(}{-^`)Tq98OoAF+Av}*EGj#_MKO+ zolmc0SX&&=XFR_MqncnUeqp*rd2=Vssw@-Zl)5o!B^y}utu2$%`laiNN+=g+bpK}K zZojsE@Sv$!un;`kz`C*0drbf6@k46N$>mTzt6I)ID6oDg&kO>5-P}ON399n!&>7t| zJttMDP3i7vKZH|=w1DEprF3J6IQj2O6EaJ+hsj50&x>@^H?M&im|mKb%_mlBcom@g zRnO-@n81Bg{!bD<8Ee=KnlKOT0UU%TVsh|JgQ%5_#VSEwnZ+l!3sH3MEo!LG{UQ-N zR!FTDW>USVX|$s>X>AI>N}-z5a3V$hgB#ER08M8f$XWSNN01 zRmA#r7=P6fG&d#qQ$0gj)d=)2F=yf2L_k%rZxO=pGFQ)ebwl*LU9P^t)b|Nbf-P{?&(w%y>xJUz@p8Sg3*I&xrXs0AF!J`i>SUErT2D#?;!k0S}V zs+_V?3G@b#By3Sip*R^_?XH_dRQoexjwE;G} zokgl3()>m7j}*P){0^y*=mxZ?I1@;R_9TVAy?EvVMaLu544bu$l|G7Y>MmCU^Y zq~nw;ur>1wAXOcx#0^SK$&`GuqI=q^!c?yW((xcwN)(g9AdXCJ{DO^11DoZgFfc@o zl@rj%h9JGf2bcpI6!0WPk*bYrS9 zlJnK&5vr%fd!Q(pwP@86)z_qS&6?FjNgnxe^LK zpGp^hw$aU&=LM0maqw`g_bNG-0kIJ93%Ki#M=x&NEw!Ea$qJfENsO0A!>o!$LZ)h} z@tKYg5uwHgS1WyMs$UGmvss*_E*+oUILVdy_5u&2`2Uo46;M?zZJUFFfJk?Dcej9m z(%s#C=nxSEq`MKMOH#T8qy^~~P`X>Q=bf27d*;l3 z=Xt2#_pDkobE5`{Akqrki{oHvYzc=G6*NrWd(5#h;jKfF_1rLg4l!IW5dG5P5$D5v zR<_crGkVzsj2c(xp}-Th8r^$3TnX2&dDu-0WLX#t{qQ5n)zqUM<2Zv zv+9(2#Cu=$Z5|!IZmW+sa}{lX4|P{yO`g8g5zT|!=XB)r)`o9j z954Z@f9*@u57-!0NDaCqQ1(1r^J&BnUjtDP2xdK+^TInOaF zZ5OJmHdbXQwYtDe!aD8ASlLmb8Ts#8D^bnvjPLO)D}l4gky|lg7};O2yQd=Y4-NV9 z3wS1B+s|QFy`}5Q-7TbvT9J{fqeX8Qz-OElb-{6`pwu?<1bNmshlL z0897JHXSJm!WXt9VeE%Gn5~bE152ZX8^+Nde0Id0$gIkLyN+4hlT-auLD>|Cv9{dl&YG)S(J( zTo!2#Ya9VIIg<=AXF{-N3-4r@0%Hv z#tzvIQQ}{QUdrnYj;U-MdkQgrV!h~ReqAoREM2m=OMt4+MkZ-{Xzu5dB}Lnn8vG<4&yJ-(z;W*_e*6#uOWJ5+jlzCUooF6! z!ITD%5==qEV zD+-%M7eI>XM>2|So@k#cHB6nbF`e8x^lv6C<2i12>$$+8;|6ZZONoRdh$IdL7mfTi zQa%BIx)aX>*~u8FHT|?4$BR5-FA7}~kI=r)N6rL3WfC7paD;2M`io?81K7%B$hmD! zjB_V+XeP&2s?H@^J;t=NSQE*7*O;YeDy49}#b*q}^*mpaOy=8}k z%>;8#C|us^$r3g|3GN`6u%AYrzs^cQRZ~`NW~rsXJyIqf$+=)XM34xJdVWt4O{%Ob zP1I{b$}*IY2G?8aU`3WtGZkS;^yzxVcjeV?YjJlYC4{jV|2DALWNq z3!5pQAGgxWa)%v`jJF)%GutdvTQs%XcgtCK@0d^*%Ww^%^sQJk;tC>^jo2`|4};>M zFfUio2i*E9JKPU(;UhSO&ga{YnX|Bi`Rqd!A1m4`U556=0~U9@mRJ2rPer9BOal|T znq-2r;8rwPXQ#5bPWiYBM@zF?6Q<7~bKo;vk;q|9!rRLNG%z(G52(0J(m!J;TLjmo zWT0(j4GG04Y8ihgV)zn1>E4j_e2i#$yE7ZM_D2?U@!~fNiTa?j4f-Fx^4L}Ghgl2u z7B$fbN)APS4&95-tA40se5iLE1JGeL&#ydnorxhE!N*+BX>ltKK9TpIia^4dd0h~@ zyO}(lpmhS8WQyZJ%uKI6ApcCzV*in-7`H7R*RM3Ewn7TJw90M_X-rW=rm11Q7hAY~ zRvA=)x1`ev0r_$t|2^qrmRHg*yKpFulcEn4s{fEgtnO)d--4NBdI@AT2i`jOMU_ZVw}jmGvc-mjdKEJ)7MLs zQ?V!QFYoK~jlR7+Ao56~N+oQTw3t>|+OLDQO+);lqxxC(ix<9yTMe^maeb-+9@I8u z^}dNqETwovC&psnLmc4rdK_eR(C)_%o!>S)Q8ethCgqX}35|8lA~SZ?cn>u)zci%6 z9pM#F6E-Iq(CnZ9WG^>r68s&LK?p9#h&?yCZ5YaZl4esgXs{Pr~as-$x*b$aIza+WKUl*kK=) zkHoWsp#VNb-kd(Qz5A7{>YS<6DeOMO1&(KHTPlUk+zLqZM2q}%*98RCL(-;j^5V;O zmaO6~A_zT|z_d4?JJe(CXg$|8_3JsL%n91k!0I6J8R-h!iK7quN=7B{bPYeLk6a*b zb}D8wCtPtCftwGMi}gj#`J!C8_|n_6a?k{!NExa)mgi_;JUg&19C9qPaHcZ~LNfTC z3eXMN|7oDBH_1^B`W|irE_x(?FacEn7fZ-7TE{sr^IUaX5MrYG+$DFwH_o_DAww9? z?v{Lg_imh1UEx+F0a635(>+vWLkt6VomkYR$6_8(tWDM3yrzO>*1*BU2kpHLqZyki z9&E1scFlG#W(uJ@Q4{8!$Q8cgOaCC=lM{JNF=-hoZz&PgeX-F0TzkQI6M+3Ra70IL zY0pjPX>op|&x__t*td4BGe!}ClSYnTLgG#Od-r(!7WtOmv&e09)^@8+37QIp<@fqL zSae6R;24*0XCV#DrtKifhO6|;`fN~LAq&nQhz(PXn0Z^IGNH!#sEhnyql{%~%(XVe z1ASmUe~*Q6U0Vjusk}ALY_C0ZLY;%?;Zgg;3kI~ko#Qu!vaLNsZ$2rcgql7F^7)Ut zrWiMd8=pZ|&57~_#6PP|MZ4#k?C{w1Q&XruUOVC*vd+g&whOKlbWVieFv1?G1_}o* zhUN`>{>sxmziD4md&I9X@!ZEa9uzhuWXk(nvx7bNgE6;|G1i20QZ;pAdDPpK4!ggX zsl^R!PnJm*8%gk(EkS&ZDoAK4*vlGmP|mF6U9wQ`KGMfs?qR`rlca(n>2v6awj!QM z!>0iXLMz8|Fv|s^XGW_OM=zCmmnp032-nINjki^F$G>CGFQ6{o(1>%6`OahSHnQ^g zi)C_Y*--vR-MyJlc55Yt1}2=GT+96@)NB+JWkbY=jM1)H2Kg_uRJbX2yy~sSUKMI9 zi#5a@u2aA8fUgK|>ZNBxNzhKWh&Ed$iFzKuW*ET!qwU?p0P$?cb73AuQKP)MX_S_h z+A2%7ry5zK7U*#jxc8c;FxK11P)y|&iW~)JE*J&V9Z~A>U<=E*IBgrE=e}Wy!eYzD z$40*_Pb{!U=yNjaS5;18eeUc7>CbXHaexq~5&~)1#t=wh4$YMcOL+XOF zR17{|Q^)ZGZjWU2eWuQ*cy_cXuU6Zvf;#OOcAtFM^ue%})*QZc{c3if!9(bMJgF^riCR!pzC zEP}o&LJzmP%Eu=UGbm3Rb;u|J`guSha%QAYd8PE!re!>xqwd-R0kVF@w3ytUY600( z!bF=N*yi6}n3743alx|bvbk^{Paij|@IO7)O}$vMfk2oIN4|gmgq}BkEZEIt@EPM; zj~MZJOc7?VUvl4K?*a7;1NJ1drMWnx5oND8vd}ZV@1if&WmgV`8TcJ8G9vBnbEu1U zR={Ns#;SL6JmfBxqA4gdTpm_*vy;ikxa{+ zPZvw^LB>1d%jRUl{;2QO>O$H=g^*6#4fg`9z{^F0)QeqUU{&x?zPl94Cn}TJE7S=W z8Gd{;kv12@9X%5{WJH}c6sKdC300de+SeZ?l#Hzy#6XqIiQ!Q9>JRQ2j-_c$ce_VPfgnn$Go5`^RiqIuVBb{V`*F847$9smDGW# zF*hb?+=ZnU!#Ff$co(Zt6vz-u--$(SB&$JDD-Jq-*Abk-}q;&QlXT(fE=+{WEuao$3(z#Lf&X_FhtZ#kcNR0lbtV+&rY)Hp}Lm< z;dE%V>vk7&APWBws-z9Uw=8$b?hG;~4QD0%jxl~&hjKRH7jg~QoPxYX{=agNvdDFrEz-18ovbap3B3K zHF4da4EZ*&OYzkp+9)1#Vx!aR+v6c#$a)KwUQ2toMuqfFMpJm-a_q+_6Uzo+$D7oL zZ4cv6`*AQ6LaaQ|Y!)c@2`^@x?lo;Egh+ z4CvD^<~0mSDpPmiz}t$~D~(Qs+dsz>sduNh!7x0ro$XXmG;w+)>E!&4}5LB4(@3u{FzfASkUA^`R!`G?@P$b zX0N;{uC|#6(877RT=oy|LEe*AE#)6uRL8@Gp}c&pMu+!srbX2|A)dDrs`%`9LBPiL zoD_ZVzUOHmUITiT)B?&G`BFr;)Skz#nlt|f7r zixzM7f*>B14}G?pzIF#XB_&>jWo!kqMunU)A3iKP$_^@Xm|xC!utOw89@rF&nXlv_ zTkGkYiPZGL@#JN8UlFRA* zQSzY*@xgwEDr}mM9(jVG^oacqJ5;!*lgUL3e589u!&8D?k~1{rEb& zYUa4@=pK5*hUi{NJg+o?3EJaFdKa1O<}adndd9P)3r+@iX`zrn>>dlx>0H;=^B0UY z@>{lunwHRC7I0}Q4`4KjF0V$_QLeeY`#PL$q46$KbFXOAF)E5k^)k&}`<(FV1w#Pw zir@w`1cV!SLjC)s2cBQOqWjZi`g>x}?^9~>RZ7oQ&Tt%%TG9(rxiJeyShIpQ=r9vq zwC!~ka&#h&EAAKZdZT*lDowP@K!GICyblG95X#{sp>acbeTAwUpk9B& zNm1yPzH2I>ZSSm!=+xIKO!6dR*XGbTvmy1b+*Xry=}U(>$;PXUtLV{F^pZ?;Z+ZKc zc-5e$hI%EOLnXYqXGXTzp*=~|jbN-sdPv|OLn|MU7U~7)SKO-rs-MYa_#%JQdyJJq zbAn`q74(^I__#p!BdL$zD8# zL(LXPS4_y6rm=CWWt%ScrUmD+2hq18p##yZfWf_-TpM7wfeXF(fN zUvFzo=1ZOsY^~Un&#WPoZsKIY_iFsep@QKf%d68!yvRNcjC`xBs1jB)N!aH>S#T{f zo^WeL0+yR-7|h5;`iViYHE!09^4D#I6ne+9`q_Popq3#goyp8ibgDNgrF8XDg?6oN z04i&+>wk$F)kZ(IO;wXWdy7JlyU3t@?^q&>+|8D}?+Lo)ehb=%T6N0_fA~4!55}== ztlDlSCtxjKa;)Xmj`M>F7;TJ zylFy|T;HIE#-VGU*txvUjoFIEE_3~Xg-xjAv`!`0K(tYjJHAZ3 z%*P|I>VwzHOLY+rb6+5Np=!FF{>$=F*C)3##H-;m%TN4d9Q=Mx@Ta*G&we?4>}@QJ z4ITdp8Ekf>v@D4blHWI!`yyw;+D;4l4PLNPe5a9ACbCB4A6;$y?9EtN`OR_QFC}Ct z2+jZ(REGVpI!dCoqqm-X7>zy7lX3dc$lnCP)zVLb0fQ-VS{&fZsmF83R{o;k9;WWVTA7%?pylx}VIA%e@O zPl)&)mLL-r5(K?tuf;`clVd%@!LM2cMoxRXu-Q+O=+dkViRpm`lO5e7dQRl+s-+x0 zxU5{N+MWqdTpH7AWh)ZBfPI;#Z&sb5PYen8y98nD^DKApuX=w+*xEk;oZrCy)3nH^ zzv}(F!f!WzliXoV%#Q4l!DUACyL=*y0!bfXpBuc!kQWnDXYf%}I8ZIkHd4WOM@ojW z*rbCr95tqzhL#M2PmmC94jWQ<4Tg9$*x~yn{^^7#;SM3hS+frI!`F)0FIC%Ga7%H6 zV4rS>V%UBDatOJ0loL3&t3j*D6ct?s_XPP-JHb)p^|FTS7^6qilch1}hwn5ryMx&2H0$^2 zN-h-rJUiCmfqk>W)?58}b%)UO4H=bq2R(dsslmYIG|lhtqJ2=DSw^=}uX_Kz^Lkkz zxc4c3>wN$u8Uz{y3=EhS8iE-74*>ywEc|9*F`GLkEvh6$FD@enCTO``49+Ik{T=wr zv|zVrw+eBCKmAcmT1ZA*44i|)AT4&+!&RYcV8mM8mHje^j-u`und#zNt%oB~pKF zAb>X*>u*E;N0Rk-D{@n6^-Agd+CYFj=6{Oy*R{AQJa{G5dTk(pF!-_ax3#!?bKVke zy&L^ajkDWhE)3joe~a;-9ssWg?g|>-1-PjzawU~^Z6JWZ@UH=G3#a|&`){VTD`k@Z znj<(b{%?c7(kZzc=1m=iD z*qbccx9cdU^b6P@{ z+b2dcyU8 z1ipLV`7Y$mbAnf=Laz-3z;gc|A%7pqH%K||V%|Kqb$jFzdHj!dnOSs{1_G%1{cE(JAJ@3s!Z+XV-kxLL z1pZs-zh39v-1NSUP?Y!!gg77lfS?EeDT03^v*%rX`af|( Be7*nx diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 9e0264d04..44f3cf2c1 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip \ No newline at end of file +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/com.microsoft.java.debug.core/.classpath b/com.microsoft.java.debug.core/.classpath index 9ba41a249..b6fe6b96c 100644 --- a/com.microsoft.java.debug.core/.classpath +++ b/com.microsoft.java.debug.core/.classpath @@ -13,7 +13,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index de9b5e366..b2be945c8 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 442e9bb17..f2ceffb53 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true Bundle-Version: 0.53.2 -Bundle-RequiredExecutionEnvironment: JavaSE-11 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin Bundle-Vendor: Microsoft diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index baf2807df..deabb9f3e 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -1,4 +1,6 @@ - + + + @@ -14,4 +16,5 @@ + diff --git a/mvnw b/mvnw index e96ccd5fb..e9cf8d330 100755 --- a/mvnw +++ b/mvnw @@ -19,209 +19,277 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.3 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac -fi +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 fi fi - ;; -esac + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" +} - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - saveddir=`pwd` +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac - M2_HOME=`dirname "$PRG"`/.. +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} - cd "$saveddir" - # echo Using m2 at $M2_HOME +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" fi -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" fi -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true fi else - JAVACMD="`which java`" + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 fi fi -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" fi +fi - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi fi - # end of workaround done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; + set -f fi -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" fi -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 019bd74d7..3fd2be860 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,143 +1,189 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.3 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 9f59485d9..b8320ef8f 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.6 + 5.0.0 ${basedir} From dabcc49979f45e34216a83dd9fbd126852f6ec1f Mon Sep 17 00:00:00 2001 From: mozhuanzuojing <63572041+mozhuanzuojing@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:14:04 +0800 Subject: [PATCH 201/206] Update build.yml add distribution: 'temurin' of `JDK` (#600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update build.yml add distribution: 'temurin' of `JDK` add distribution: 'temurin' of JavaSE-21 * upgrade actions v4 、v5 * format yml --------- Co-authored-by: Changyong Gong Co-authored-by: wenyt <75360946+wenytang-ms@users.noreply.github.com> --- .github/workflows/build.yml | 115 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71d726f63..553d95a9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,80 +12,83 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - - name: Set up JDK 21 - uses: actions/setup-java@v1 - with: - java-version: '21' + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + - name: Cache local Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - - name: Verify - run: ./mvnw clean verify -U + - name: Verify + run: ./mvnw clean verify -U - - name: Checkstyle - run: ./mvnw checkstyle:check + - name: Checkstyle + run: ./mvnw checkstyle:check windows: name: Windows runs-on: windows-latest timeout-minutes: 30 steps: - - name: Set git to use LF - run: | - git config --global core.autocrlf false - git config --global core.eol lf + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - - name: Set up JDK 21 - uses: actions/setup-java@v1 - with: - java-version: '21' + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: $HOME/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + - name: Cache local Maven repository + uses: actions/cache@v4 + with: + path: $HOME/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - - name: Verify - run: ./mvnw.cmd clean verify + - name: Verify + run: ./mvnw.cmd clean verify - - name: Checkstyle - run: ./mvnw.cmd checkstyle:check + - name: Checkstyle + run: ./mvnw.cmd checkstyle:check darwin: name: macOS runs-on: macos-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v2 - - - name: Set up JDK 21 - uses: actions/setup-java@v1 - with: - java-version: '21' - - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - - name: Verify - run: ./mvnw clean verify -U - - - name: Checkstyle - run: ./mvnw checkstyle:check + - uses: actions/checkout@v5 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + + - name: Cache local Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw clean verify -U + + - name: Checkstyle + run: ./mvnw checkstyle:check From 905c7ba292bf52e7ed324a8fab80669b3806a6e5 Mon Sep 17 00:00:00 2001 From: Changyong Gong Date: Tue, 11 Nov 2025 13:46:33 +0800 Subject: [PATCH 202/206] chore: add triage open issues workflow (#613) --- .github/workflows/triage-agent.yml | 5 +- .github/workflows/triage-all-open-issues.yml | 145 +++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/triage-all-open-issues.yml diff --git a/.github/workflows/triage-agent.yml b/.github/workflows/triage-agent.yml index 185cf8f32..f1df6e117 100644 --- a/.github/workflows/triage-agent.yml +++ b/.github/workflows/triage-agent.yml @@ -1,4 +1,4 @@ -name: AI Triage - Label and Comment on New Issues +name: AI Triage on: issues: types: [opened] @@ -8,6 +8,9 @@ on: description: 'Issue number to triage (manual run). e.g. 123' required: true +run-name: >- + AI Triage for Issue #${{ github.event.issue.number || github.event.inputs.issue_number }} + permissions: issues: write contents: read diff --git a/.github/workflows/triage-all-open-issues.yml b/.github/workflows/triage-all-open-issues.yml new file mode 100644 index 000000000..092f4ef70 --- /dev/null +++ b/.github/workflows/triage-all-open-issues.yml @@ -0,0 +1,145 @@ +name: AI Triage - Process All Open Issues +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Dry run mode - only list issues without processing' + required: false + default: false + type: boolean + max_issues: + description: 'Maximum number of issues to process (0 = all)' + required: false + default: '0' + type: string + +permissions: + issues: write + contents: read + actions: write + +jobs: + get_open_issues: + runs-on: ubuntu-latest + outputs: + issue_numbers: ${{ steps.get_issues.outputs.issue_numbers }} + total_count: ${{ steps.get_issues.outputs.total_count }} + + steps: + - name: Get all open issues + id: get_issues + uses: actions/github-script@v6 + with: + script: | + // Use Search API to filter issues at API level + const { data } = await github.rest.search.issuesAndPullRequests({ + q: `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open -label:ai-triaged -label:invalid`, + sort: 'created', + order: 'asc', + per_page: 100 + }); + + const actualIssues = data.items; + + let issuesToProcess = actualIssues; + const maxIssues = parseInt('${{ inputs.max_issues }}' || '0'); + + if (maxIssues > 0 && actualIssues.length > maxIssues) { + issuesToProcess = actualIssues.slice(0, maxIssues); + console.log(`Limiting to first ${maxIssues} issues out of ${actualIssues.length} total`); + } + + const issueNumbers = issuesToProcess.map(issue => issue.number); + const totalCount = issuesToProcess.length; + + console.log(`Found ${actualIssues.length} open issues, processing ${totalCount}:`); + issuesToProcess.forEach(issue => { + console.log(` #${issue.number}: ${issue.title}`); + }); + + core.setOutput('issue_numbers', JSON.stringify(issueNumbers)); + core.setOutput('total_count', totalCount); + + process_issues: + runs-on: ubuntu-latest + needs: get_open_issues + if: needs.get_open_issues.outputs.total_count > 0 + + strategy: + # Process issues one by one (max-parallel: 1) + max-parallel: 1 + matrix: + issue_number: ${{ fromJSON(needs.get_open_issues.outputs.issue_numbers) }} + + steps: + - name: Log current issue being processed + run: | + echo "🔄 Processing issue #${{ matrix.issue_number }}" + echo "Total issues to process: ${{ needs.get_open_issues.outputs.total_count }}" + + - name: Check if dry run mode + if: inputs.dry_run == true + run: | + echo "🔍 DRY RUN MODE: Would process issue #${{ matrix.issue_number }}" + echo "Skipping actual triage processing" + + - name: Trigger triage workflow for issue + if: inputs.dry_run != true + uses: actions/github-script@v6 + with: + script: | + const issueNumber = '${{ matrix.issue_number }}'; + + try { + console.log(`Triggering triage workflow for issue #${issueNumber}`); + + const response = await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'triage-agent.yml', + ref: 'main', + inputs: { + issue_number: issueNumber + } + }); + + console.log(`✅ Successfully triggered triage workflow for issue #${issueNumber}`); + + } catch (error) { + console.error(`❌ Failed to trigger triage workflow for issue #${issueNumber}:`, error); + core.setFailed(`Failed to process issue #${issueNumber}: ${error.message}`); + } + + - name: Wait for workflow completion + if: inputs.dry_run != true + run: | + echo "⏳ Waiting for triage workflow to complete for issue #${{ matrix.issue_number }}..." + echo "Timeout: ${{ vars.TRIAGE_AGENT_TIMEOUT }} seconds" + sleep ${{ vars.TRIAGE_AGENT_TIMEOUT }} # Wait for triage workflow completion + + summary: + runs-on: ubuntu-latest + needs: [get_open_issues, process_issues] + if: always() + + steps: + - name: Print summary + run: | + echo "## Triage Processing Summary" + echo "Total open issues found: ${{ needs.get_open_issues.outputs.total_count }}" + + if [ "${{ inputs.dry_run }}" == "true" ]; then + echo "Mode: DRY RUN (no actual processing performed)" + else + echo "Mode: FULL PROCESSING" + fi + + if [ "${{ needs.process_issues.result }}" == "success" ]; then + echo "✅ All issues processed successfully" + elif [ "${{ needs.process_issues.result }}" == "failure" ]; then + echo "❌ Some issues failed to process" + elif [ "${{ needs.process_issues.result }}" == "skipped" ]; then + echo "⏭️ Processing was skipped (no open issues found)" + else + echo "⚠️ Processing completed with status: ${{ needs.process_issues.result }}" + fi From b62897e444a5b522b68dfb2c73aae21614d1fe30 Mon Sep 17 00:00:00 2001 From: Karl-Erik Enkelmann <110300169+playdohface@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:49:45 +0100 Subject: [PATCH 203/206] Set source to null when unavailable in a StackTrace (#614) --- .../debug/core/adapter/handler/StackTraceRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 3fa0a9a9b..3bb319a01 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -220,7 +220,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra } else { // For other unavailable method, such as lambda expression's built-in methods run/accept/apply, // display "Unknown Source" in the Call Stack View. - clientSource = new Types.Source("Unknown Source", "unknown", 0); + clientSource = null; } // DAP specifies lineNumber to be set to 0 when unavailable clientLineNumber = 0; From 81339de8108b50dfc93d2dc47dc48c72d8904902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Tue, 9 Dec 2025 02:13:35 +0100 Subject: [PATCH 204/206] Fix lspFrame.source NPE on stackTrace request (#616) * Fix lspFrame.source NPE on stackTrace request Follow up to: - https://github.com/microsoft/java-debug/pull/614 - https://github.com/microsoft/java-debug/pull/609 With the change to set the line number to 0 the jdiLineNumber != lspFrame.line comparison can evaluate to true: dap> lspFrame Types$StackFrame@78 column: 1 id: 6 line: 0 name: "0x000000002f0bc000.invokeVirtual(Object,Object)" presentationHint: "subtle" source: null dap> jdiLineNumber -1 `source` being null caused an NPE * Fix line number comparison in StackTraceRequestHandler --------- Co-authored-by: Changyong Gong --- .../core/adapter/handler/StackTraceRequestHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 3bb319a01..f63d1b24e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -29,9 +29,9 @@ import com.google.gson.JsonObject; import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IBreakpoint; -import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; @@ -40,13 +40,13 @@ import com.microsoft.java.debug.core.adapter.SourceType; import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.protocol.Events.TelemetryEvent; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.StackTraceArguments; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; -import com.microsoft.java.debug.core.protocol.Events.TelemetryEvent; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.LocalVariable; @@ -114,7 +114,7 @@ public CompletableFuture handle(Command command, Arguments arguments, result.add(lspFrame); frameReference.setSource(lspFrame.source); int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); - if (jdiLineNumber != lspFrame.line) { + if (jdiLineNumber >= 0 && jdiLineNumber != lspFrame.line) { decompiledClasses.add(lspFrame.source.path); } } From 9623b8ff69b188c5d0325110a5977a2623a67ea1 Mon Sep 17 00:00:00 2001 From: Changyong Gong Date: Tue, 16 Dec 2025 15:42:19 +0800 Subject: [PATCH 205/206] fix: lspFrame.source.path is null (#618) * fix: lspFrame.source.path is null * fix: update bundle location --- .../debug/core/adapter/handler/StackTraceRequestHandler.java | 2 +- .../com.microsoft.java.debug.tp.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index f63d1b24e..6f54bcedb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -114,7 +114,7 @@ public CompletableFuture handle(Command command, Arguments arguments, result.add(lspFrame); frameReference.setSource(lspFrame.source); int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); - if (jdiLineNumber >= 0 && jdiLineNumber != lspFrame.line) { + if (jdiLineNumber != lspFrame.line && lspFrame.source != null && lspFrame.source.path != null) { decompiledClasses.add(lspFrame.source.path); } } diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index deabb9f3e..31462a770 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -4,7 +4,7 @@ - + From 31dd8ee33403f7365937cf77c653f2f5ec0960ba Mon Sep 17 00:00:00 2001 From: Changyong Gong Date: Thu, 29 Jan 2026 12:35:31 +0800 Subject: [PATCH 206/206] feat: support suspend all setting (#619) * feat: support suspend all setting * fix: handle stoppedEvent for suspend policy change * fix: not allow change policy during a live debug session * fix: format issue * fix: let step command execute for current thread * fix: remove unused import * fix: apply suspend policy to exception breakpoint * fix: improve naming --- .../microsoft/java/debug/core/Breakpoint.java | 41 +++++++++++++++---- .../java/debug/core/DebugSession.java | 20 ++++++--- .../java/debug/core/DebugSettings.java | 1 + .../java/debug/core/DebugUtility.java | 3 +- .../debug/core/EvaluatableBreakpoint.java | 20 ++++----- .../java/debug/core/IBreakpoint.java | 7 ++++ .../java/debug/core/IDebugSession.java | 6 +++ .../java/debug/core/MethodBreakpoint.java | 6 ++- .../microsoft/java/debug/core/Watchpoint.java | 15 ++++--- .../ConfigurationDoneRequestHandler.java | 5 ++- .../adapter/handler/RestartFrameHandler.java | 4 +- .../handler/SetBreakpointsRequestHandler.java | 8 +++- .../SetDataBreakpointsRequestHandler.java | 9 +++- .../SetFunctionBreakpointsRequestHandler.java | 9 +++- .../handler/ThreadsRequestHandler.java | 5 +++ 15 files changed, 117 insertions(+), 42 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 82859b268..d8316c2e3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -42,24 +42,28 @@ public class Breakpoint implements IBreakpoint { private String condition = null; private String logMessage = null; private HashMap propertyMap = new HashMap<>(); + private final boolean suspendAllThreads; private boolean async = false; - Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { - this(vm, eventHub, className, lineNumber, 0, null); + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, boolean suspendAllThreads) { + this(vm, eventHub, className, lineNumber, 0, null, suspendAllThreads); } - Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount) { - this(vm, eventHub, className, lineNumber, hitCount, null); + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, boolean suspendAllThreads) { + this(vm, eventHub, className, lineNumber, hitCount, null, suspendAllThreads); } - Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) { - this(vm, eventHub, className, lineNumber, hitCount, condition, null); + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, + String condition, boolean suspendAllThreads) { + this(vm, eventHub, className, lineNumber, hitCount, condition, null, suspendAllThreads); } - Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) { + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, + String condition, String logMessage, boolean suspendAllThreads) { this.vm = vm; this.eventHub = eventHub; + this.suspendAllThreads = suspendAllThreads; String contextClass = className; String methodName = null; String methodSignature = null; @@ -79,13 +83,15 @@ public class Breakpoint implements IBreakpoint { this.logMessage = logMessage; } - Breakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) { + Breakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, + String condition, String logMessage, boolean suspendAllThreads) { this.vm = vm; this.eventHub = eventHub; this.sourceLocation = sourceLocation; this.hitCount = hitCount; this.condition = condition; this.logMessage = logMessage; + this.suspendAllThreads = suspendAllThreads; } // IDebugResource @@ -203,6 +209,19 @@ public void setAsync(boolean async) { this.async = async; } + @Override + public void setSuspendPolicy(String policy) { + } + + @Override + public String getSuspendPolicy() { + return suspendAllThreads ? "SUSPEND_ALL" : "SUSPEND_EVENT_THREAD"; + } + + protected boolean suspendAllThreads() { + return suspendAllThreads; + } + @Override public CompletableFuture install() { // It's possible that different class loaders create new class with the same name. @@ -412,7 +431,11 @@ private CompletableFuture> createBreakpointRequests(List newLocations.forEach(location -> { BreakpointRequest request = vm.eventRequestManager().createBreakpointRequest(location); - request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD); + if ("SUSPEND_ALL".equals(getSuspendPolicy())) { + request.setSuspendPolicy(BreakpointRequest.SUSPEND_ALL); + } else { + request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD); + } if (hitCount > 0) { request.addCountFilter(hitCount); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 38a234fa9..d5b8ceb47 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -36,9 +36,12 @@ public class DebugSession implements IDebugSession { private EventHub eventHub = new EventHub(); private List eventRequests = new ArrayList<>(); private List subscriptions = new ArrayList<>(); + private final boolean suspendAllThreads; public DebugSession(VirtualMachine virtualMachine) { vm = virtualMachine; + // Capture suspend policy at session start - this persists for the session lifetime + this.suspendAllThreads = DebugSettings.getCurrent().suspendAllThreads; } @Override @@ -128,17 +131,17 @@ public void terminate() { @Override public IBreakpoint createBreakpoint(JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) { - return new EvaluatableBreakpoint(vm, this.getEventHub(), sourceLocation, hitCount, condition, logMessage); + return new EvaluatableBreakpoint(vm, this.getEventHub(), sourceLocation, hitCount, condition, logMessage, suspendAllThreads); } @Override public IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage) { - return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage); + return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage, suspendAllThreads); } @Override public IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount) { - return new Watchpoint(vm, this.getEventHub(), className, fieldName, accessType, condition, hitCount); + return new Watchpoint(vm, this.getEventHub(), className, fieldName, accessType, condition, hitCount, suspendAllThreads); } @Override @@ -185,7 +188,7 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught if (exceptionTypes == null || exceptionTypes.length == 0) { ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD); if (classFilters != null) { for (String classFilter : classFilters) { request.addClassFilter(classFilter); @@ -260,17 +263,22 @@ public VirtualMachine getVM() { return vm; } + @Override + public boolean shouldSuspendAllThreads() { + return suspendAllThreads; + } + @Override public IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount) { - return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount); + return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount, suspendAllThreads); } private void createExceptionBreakpoint(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters) { EventRequestManager manager = vm.eventRequestManager(); ExceptionRequest request = manager.createExceptionRequest(refType, notifyCaught, notifyUncaught); - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD); if (classFilters != null) { for (String classFilter : classFilters) { request.addClassFilter(classFilter); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index 0a3e05ec8..b422f6801 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -45,6 +45,7 @@ public final class DebugSettings { public int jdwpRequestTimeout = 3000; public AsyncMode asyncJDWP = AsyncMode.OFF; public Switch debugSupportOnDecompiledSource = Switch.OFF; + public boolean suspendAllThreads = false; public static DebugSettings getCurrent() { return current; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index 4a2a49e9b..8a31792f0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -394,6 +394,7 @@ private static StepRequest createStepRequest(ThreadReference thread, int stepSiz request.addClassExclusionFilter(exclusionFilter); } } + // Note: suspend policy will be set by the caller (StepRequestHandler) based on session settings request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); request.addCountFilter(1); @@ -415,7 +416,7 @@ public static CompletableFuture stopOnEntry(IDebugSession debugSession, St EventRequestManager manager = debugSession.getVM().eventRequestManager(); MethodEntryRequest request = manager.createMethodEntryRequest(); request.addClassFilter(mainClass); - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + request.setSuspendPolicy(debugSession.shouldSuspendAllThreads() ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD); debugSession.getEventHub().events().filter(debugEvent -> { return debugEvent.event instanceof MethodEntryEvent && request.equals(debugEvent.event.request()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java index 723e2cadf..d5a909f0b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java @@ -29,28 +29,28 @@ public class EvaluatableBreakpoint extends Breakpoint implements IEvaluatableBre private Object compiledLogpointExpression = null; private Map compiledExpressions = new ConcurrentHashMap<>(); - EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { - this(vm, eventHub, className, lineNumber, 0, null); + EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, boolean suspendAllThreads) { + this(vm, eventHub, className, lineNumber, 0, null, suspendAllThreads); } - EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount) { - this(vm, eventHub, className, lineNumber, hitCount, null); + EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, boolean suspendAllThreads) { + this(vm, eventHub, className, lineNumber, hitCount, null, suspendAllThreads); } EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, - String condition) { - this(vm, eventHub, className, lineNumber, hitCount, condition, null); + String condition, boolean suspendAllThreads) { + this(vm, eventHub, className, lineNumber, hitCount, condition, null, suspendAllThreads); } EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, - String condition, String logMessage) { - super(vm, eventHub, className, lineNumber, hitCount, condition, logMessage); + String condition, String logMessage, boolean suspendAllThreads) { + super(vm, eventHub, className, lineNumber, hitCount, condition, logMessage, suspendAllThreads); this.eventHub = eventHub; } EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, - String condition, String logMessage) { - super(vm, eventHub, sourceLocation, hitCount, condition, logMessage); + String condition, String logMessage, boolean suspendAllThreads) { + super(vm, eventHub, sourceLocation, hitCount, condition, logMessage, suspendAllThreads); this.eventHub = eventHub; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 40995e9dd..ec3ea818a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -55,4 +55,11 @@ default void setAsync(boolean async) { default boolean async() { return false; } + + default void setSuspendPolicy(String policy) { + } + + default String getSuspendPolicy() { + return null; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 6cc3f3a46..afc2283f0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -52,4 +52,10 @@ void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, Strin IEventHub getEventHub(); VirtualMachine getVM(); + + /** + * Returns whether breakpoints should suspend all threads or just the event thread. + * This value is captured at session start and persists for the session lifetime. + */ + boolean shouldSuspendAllThreads(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java index 7a6e74c6b..bc46fd96f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java @@ -44,6 +44,7 @@ public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoi private String condition; private int hitCount; private boolean async = false; + private final boolean suspendAllThreads; private HashMap propertyMap = new HashMap<>(); private Object compiledConditionalExpression = null; @@ -53,7 +54,7 @@ public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoi private List subscriptions = new ArrayList<>(); public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, String functionName, - String condition, int hitCount) { + String condition, int hitCount, boolean suspendAllThreads) { Objects.requireNonNull(vm); Objects.requireNonNull(eventHub); Objects.requireNonNull(className); @@ -64,6 +65,7 @@ public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, this.functionName = functionName; this.condition = condition; this.hitCount = hitCount; + this.suspendAllThreads = suspendAllThreads; } @Override @@ -262,7 +264,7 @@ private Optional createMethodEntryRequest0(ReferenceType typ MethodEntryRequest request = vm.eventRequestManager().createMethodEntryRequest(); request.addClassFilter(type); - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD); if (hitCount > 0) { request.addCountFilter(hitCount); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java index 3de321ec8..fdb2354a9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Watchpoint.java @@ -46,20 +46,22 @@ public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint { private HashMap propertyMap = new HashMap<>(); private Object compiledConditionalExpression = null; private Map compiledExpressions = new ConcurrentHashMap<>(); + private final boolean suspendAllThreads; // IDebugResource private List requests = new ArrayList<>(); private List subscriptions = new ArrayList<>(); - Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName) { - this(vm, eventHub, className, fieldName, "write"); + Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, boolean suspendAllThreads) { + this(vm, eventHub, className, fieldName, "write", suspendAllThreads); } - Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType) { - this(vm, eventHub, className, fieldName, accessType, null, 0); + Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, boolean suspendAllThreads) { + this(vm, eventHub, className, fieldName, accessType, null, 0, suspendAllThreads); } - Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, String condition, int hitCount) { + Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, + String condition, int hitCount, boolean suspendAllThreads) { Objects.requireNonNull(vm); Objects.requireNonNull(eventHub); Objects.requireNonNull(className); @@ -71,6 +73,7 @@ public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint { this.accessType = accessType; this.condition = condition; this.hitCount = hitCount; + this.suspendAllThreads = suspendAllThreads; } @Override @@ -212,7 +215,7 @@ private List createWatchpointRequests(ReferenceType type) { } watchpointRequests.forEach(request -> { - request.setSuspendPolicy(WatchpointRequest.SUSPEND_EVENT_THREAD); + request.setSuspendPolicy(suspendAllThreads ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD); if (hitCount > 0) { request.addCountFilter(hitCount); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 1c543bce4..308adddb6 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -40,6 +40,7 @@ import com.sun.jdi.event.ThreadStartEvent; import com.sun.jdi.event.VMDeathEvent; import com.sun.jdi.event.VMDisconnectEvent; +import com.sun.jdi.request.EventRequest; import com.sun.jdi.event.VMStartEvent; public class ConfigurationDoneRequestHandler implements IDebugRequestHandler { @@ -119,7 +120,9 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, ((ExceptionEvent) event).catchLocation() == null); context.getExceptionManager().setException(thread.uniqueID(), jdiException); context.getThreadCache().addEventThread(thread, "exception"); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID())); + boolean allThreadsStopped = event.request() != null + && event.request().suspendPolicy() == EventRequest.SUSPEND_ALL; + context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID(), allThreadsStopped)); debugEvent.shouldResume = false; } else { isImportantEvent = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java index 164909656..26ebefaee 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java @@ -33,6 +33,7 @@ import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; +import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.StepRequest; /** @@ -121,7 +122,8 @@ private void stepInto(IDebugAdapterContext context, ThreadReference thread) { debugEvent.shouldResume = false; // Have to send two events to keep the UI sync with the step in operations: context.getProtocolServer().sendEvent(new Events.ContinuedEvent(thread.uniqueID())); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID())); + boolean allThreadsStopped = request.suspendPolicy() == EventRequest.SUSPEND_ALL; + context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID(), allThreadsStopped)); context.getThreadCache().setThreadStoppedReason(thread.uniqueID(), "restartframe"); }); request.enable(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 09dafd1b0..0f171486e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -212,15 +212,19 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { debugEvent.eventSet.resume(); } else { context.getThreadCache().addEventThread(bpThread, breakpointName); + boolean allThreadsStopped = event.request() != null + && event.request().suspendPolicy() == EventRequest.SUSPEND_ALL; context.getProtocolServer().sendEvent(new Events.StoppedEvent( - breakpointName, bpThread.uniqueID())); + breakpointName, bpThread.uniqueID(), allThreadsStopped)); } }); }); } else { context.getThreadCache().addEventThread(bpThread, breakpointName); + boolean allThreadsStopped = event.request() != null + && event.request().suspendPolicy() == EventRequest.SUSPEND_ALL; context.getProtocolServer().sendEvent(new Events.StoppedEvent( - breakpointName, bpThread.uniqueID())); + breakpointName, bpThread.uniqueID(), allThreadsStopped)); } debugEvent.shouldResume = false; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java index 373b1c31b..c7c0995fe 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java @@ -41,6 +41,7 @@ import com.sun.jdi.ThreadReference; import com.sun.jdi.event.Event; import com.sun.jdi.event.WatchpointEvent; +import com.sun.jdi.request.EventRequest; public class SetDataBreakpointsRequestHandler implements IDebugRequestHandler { private boolean registered = false; @@ -152,13 +153,17 @@ private void registerWatchpointHandler(IDebugAdapterContext context) { debugEvent.eventSet.resume(); } else { context.getThreadCache().addEventThread(bpThread, "data breakpoint"); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); + boolean allThreadsStopped = event.request() != null + && event.request().suspendPolicy() == EventRequest.SUSPEND_ALL; + context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID(), allThreadsStopped)); } }); }); } else { context.getThreadCache().addEventThread(bpThread, "data breakpoint"); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); + boolean allThreadsStopped = event.request() != null + && event.request().suspendPolicy() == EventRequest.SUSPEND_ALL; + context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID(), allThreadsStopped)); } debugEvent.shouldResume = false; }); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index 96a0e395b..01b5e9619 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -40,6 +40,7 @@ import com.microsoft.java.debug.core.protocol.Types.FunctionBreakpoint; import com.sun.jdi.ThreadReference; import com.sun.jdi.event.MethodEntryEvent; +import com.sun.jdi.request.EventRequest; public class SetFunctionBreakpointsRequestHandler implements IDebugRequestHandler { private boolean registered = false; @@ -166,16 +167,20 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { debugEvent.eventSet.resume(); } else { context.getThreadCache().addEventThread(bpThread, "function breakpoint"); + boolean allThreadsStopped = methodEntryEvent.request() != null + && methodEntryEvent.request().suspendPolicy() == EventRequest.SUSPEND_ALL; context.getProtocolServer().sendEvent(new Events.StoppedEvent( - "function breakpoint", bpThread.uniqueID())); + "function breakpoint", bpThread.uniqueID(), allThreadsStopped)); } }); }); } else { context.getThreadCache().addEventThread(bpThread, "function breakpoint"); + boolean allThreadsStopped = methodEntryEvent.request() != null + && methodEntryEvent.request().suspendPolicy() == EventRequest.SUSPEND_ALL; context.getProtocolServer() - .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); + .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID(), allThreadsStopped)); } debugEvent.shouldResume = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index 6573f11da..5c4770391 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -149,6 +149,11 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, if (thread == null) { thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); } + + if (context.getDebugSession().shouldSuspendAllThreads()) { + thread = null; + } + /** * See the jdi doc https://docs.oracle.com/javase/7/docs/jdk/api/jpda/jdi/com/sun/jdi/ThreadReference.html#resume(), * suspends of both the virtual machine and individual threads are counted. Before a thread will run again, it must