diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58381df99..99ed7f2cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,7 @@ name: CI on: push: - branches: [ main ] pull_request: - branches: [ main ] jobs: linux: diff --git a/.project b/.project index d34865de3..71876c8c9 100644 --- a/.project +++ b/.project @@ -16,12 +16,12 @@ - 1600224298170 + 1667383449012 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..5215cd389 100644 --- a/com.microsoft.java.debug.core/.project +++ b/com.microsoft.java.debug.core/.project @@ -28,12 +28,12 @@ - 1599036548523 + 1667383449000 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/check_style.xml b/com.microsoft.java.debug.core/check_style.xml new file mode 100644 index 000000000..3f19228a5 --- /dev/null +++ b/com.microsoft.java.debug.core/check_style.xml @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.microsoft.java.debug.core/microsoft.header b/com.microsoft.java.debug.core/microsoft.header new file mode 100644 index 000000000..db463ef1b --- /dev/null +++ b/com.microsoft.java.debug.core/microsoft.header @@ -0,0 +1,2 @@ +^/\*+$ +^\s*\* Copyright \(c\) \d\d\d\d.*$ \ No newline at end of file diff --git a/com.microsoft.java.debug.core/mvnw b/com.microsoft.java.debug.core/mvnw new file mode 100755 index 000000000..e96ccd5fb --- /dev/null +++ b/com.microsoft.java.debug.core/mvnw @@ -0,0 +1,227 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# 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 +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# 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" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + 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 + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +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"` +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? +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 +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" + fi + else + JAVACMD="`which java`" + 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." +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() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + 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` + 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; +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"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/com.microsoft.java.debug.core/mvnw.cmd b/com.microsoft.java.debug.core/mvnw.cmd new file mode 100644 index 000000000..019bd74d7 --- /dev/null +++ b/com.microsoft.java.debug.core/mvnw.cmd @@ -0,0 +1,143 @@ +@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% diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 4009824a5..e4cb6aba9 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -2,16 +2,47 @@ 4.0.0 - - com.microsoft.java - java-debug-parent - 0.34.0 - - com.microsoft.java.debug.core + ch.epfl.scala + com-microsoft-java-debug-core jar ${base.name} :: Debugger Core + 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/scalacenter/java-debug + 0.34.0+9 + Java Debug Server for Visual Studio Code + UTF-8 + ${basedir}/../ + + + + Eclipse Public License 1.0 + https://github.com/Microsoft/java-debug/blob/master/LICENSE.txt + repo + + + + + + adpi2 + Adrien Piquerez + adrien.piquerez@gmail.com + "https://github.com/adpi2/" + + + + + ch.epfl.scala + https://scala.epfl.ch/ + + + + scm:git:git://github.com/scalacenter/java-debug.git + scm:git:ssh://github.com:scalacenter/java-debug.git + https://github.com/scalacenter/java-debug/tree/main + + target target/classes @@ -22,6 +53,26 @@ org.apache.maven.plugins maven-failsafe-plugin + 2.15 + + + + integration-tests + + integration-test + verify + + + + ${failsafeArgLine} + + ${skip.integration.tests} + + + org.apache.maven.plugins @@ -35,6 +86,34 @@ org.apache.maven.plugins maven-checkstyle-plugin + 3.1.0 + + + com.puppycrawl.tools + checkstyle + 8.29 + + + com.github.sevntu-checkstyle + sevntu-checkstyle-maven-plugin + 1.24.1 + + + + ${checkstyleDir}/check_style.xml + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + @@ -79,7 +158,7 @@ - + default-tools.jar (,9) @@ -94,5 +173,61 @@ + + release + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.1 + + + attach-javadocs + + jar + + + + + none + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + 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..8b9db7301 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 @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.logging.Logger; import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; @@ -22,10 +23,11 @@ public class DebugSession implements IDebugSession { private VirtualMachine vm; - private EventHub eventHub = new EventHub(); + private EventHub eventHub; - public DebugSession(VirtualMachine virtualMachine) { + public DebugSession(VirtualMachine virtualMachine, Logger logger) { vm = virtualMachine; + eventHub = new EventHub(logger); } @Override 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..988a2c388 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 @@ -14,7 +14,6 @@ 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; @@ -23,7 +22,6 @@ 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(); @@ -54,15 +52,11 @@ public static DebugSettings getCurrent() { * @param jsonSettings * the new settings represents in json format. */ - 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())); + public void updateSettings(String jsonSettings) throws JsonSyntaxException { + DebugSettings oldSettings = current; + current = JsonUtils.fromJson(jsonSettings, DebugSettings.class); + for (IDebugSettingChangeListener listener : listeners) { + listener.update(oldSettings, 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 1202a30f3..641765ee4 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 @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; @@ -59,7 +60,7 @@ public class DebugUtility { /** * Launch a debuggee in suspend mode. - * @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[]) + * @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[], Logger) */ public static IDebugSession launch(VirtualMachineManager vmManager, String mainClass, @@ -68,7 +69,8 @@ public static IDebugSession launch(VirtualMachineManager vmManager, List modulePaths, List classPaths, String cwd, - String[] envVars) + String[] envVars, + Logger logger) throws IOException, IllegalConnectorArgumentsException, VMStartException { return DebugUtility.launch(vmManager, mainClass, @@ -77,12 +79,13 @@ public static IDebugSession launch(VirtualMachineManager vmManager, String.join(File.pathSeparator, modulePaths), String.join(File.pathSeparator, classPaths), cwd, - envVars); + envVars, + logger); } /** * Launch a debuggee in suspend mode. - * @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[], String) + * @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[], String, Logger) */ public static IDebugSession launch(VirtualMachineManager vmManager, String mainClass, @@ -92,7 +95,8 @@ public static IDebugSession launch(VirtualMachineManager vmManager, List classPaths, String cwd, String[] envVars, - String javaExec) + String javaExec, + Logger logger) throws IOException, IllegalConnectorArgumentsException, VMStartException { return DebugUtility.launch(vmManager, mainClass, @@ -102,7 +106,8 @@ public static IDebugSession launch(VirtualMachineManager vmManager, String.join(File.pathSeparator, classPaths), cwd, envVars, - javaExec); + javaExec, + logger); } /** @@ -141,9 +146,10 @@ public static IDebugSession launch(VirtualMachineManager vmManager, String modulePaths, String classPaths, String cwd, - String[] envVars) + String[] envVars, + Logger logger) throws IOException, IllegalConnectorArgumentsException, VMStartException { - return launch(vmManager, mainClass, programArguments, vmArguments, modulePaths, classPaths, cwd, envVars, null); + return launch(vmManager, mainClass, programArguments, vmArguments, modulePaths, classPaths, cwd, envVars, null, logger); } /** @@ -185,7 +191,8 @@ public static IDebugSession launch(VirtualMachineManager vmManager, String classPaths, String cwd, String[] envVars, - String javaExec) + String javaExec, + Logger logger) throws IOException, IllegalConnectorArgumentsException, VMStartException { List connectors = vmManager.launchingConnectors(); LaunchingConnector connector = connectors.get(0); @@ -252,7 +259,7 @@ public static IDebugSession launch(VirtualMachineManager vmManager, // Without this line, it throws ObjectCollectedException in ExceptionRequest.enable(). // See https://github.com/Microsoft/java-debug/issues/23 vm.version(); - return new DebugSession(vm); + return new DebugSession(vm, logger); } private static boolean isValidJavaExec(String javaExec) { @@ -285,7 +292,7 @@ private static boolean isValidJavaExec(String javaExec) { * @throws IllegalConnectorArgumentsException * when one of the connector arguments is invalid. */ - public static IDebugSession attach(VirtualMachineManager vmManager, String hostName, int port, int attachTimeout) + public static IDebugSession attach(VirtualMachineManager vmManager, String hostName, int port, int attachTimeout, Logger logger) throws IOException, IllegalConnectorArgumentsException { List connectors = vmManager.attachingConnectors(); AttachingConnector connector = connectors.get(0); @@ -301,7 +308,7 @@ public static IDebugSession attach(VirtualMachineManager vmManager, String hostN arguments.get(HOSTNAME).setValue(hostName); arguments.get(PORT).setValue(String.valueOf(port)); arguments.get(TIMEOUT).setValue(String.valueOf(attachTimeout)); - return new DebugSession(connector.attach(arguments)); + return new DebugSession(connector.attach(arguments), logger); } /** @@ -678,8 +685,7 @@ private static List parseArgumentsNonWindows(String args) { * This piece of code is mainly copied from * https://github.com/eclipse/eclipse.platform.debug/blob/master/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java#L1264 * - * @param args - * the command line arguments as a single string. + * @param args the command line arguments as a single string. * @return the individual arguments */ private static List parseArgumentsWindows(String args) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EventHub.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EventHub.java index 712ed03ba..a16f3f667 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EventHub.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EventHub.java @@ -31,9 +31,13 @@ import io.reactivex.subjects.PublishSubject; public class EventHub implements IEventHub { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private final Logger logger; private PublishSubject subject = PublishSubject.create(); + public EventHub(Logger logger) { + this.logger = logger; + } + @Override public Observable events() { return subject; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LoggerFactory.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LoggerFactory.java new file mode 100644 index 000000000..8d75df046 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LoggerFactory.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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; + +import java.util.logging.Logger; + +@FunctionalInterface +public interface LoggerFactory { + Logger create(String name); +} 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..77c371434 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,13 +1,13 @@ /******************************************************************************* -* 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 -*******************************************************************************/ + * 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; @@ -28,8 +28,8 @@ import com.sun.jdi.event.Event; public class UsageDataSession { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - private static final Logger usageDataLogger = Logger.getLogger(Configuration.USAGE_DATA_LOGGER_NAME); + private final Logger logger; + private final Logger usageDataLogger; private static final long RESPONSE_MAX_DELAY_MS = 1000; private static final ThreadLocal threadLocal = new InheritableThreadLocal<>(); @@ -48,7 +48,16 @@ public static String getSessionGuid() { return threadLocal.get() == null ? "" : threadLocal.get().sessionGuid; } - public UsageDataSession() { + public static UsageDataSession currentSession() { + return threadLocal.get(); + } + + /** + * Constructor. + */ + public UsageDataSession(Logger logger, LoggerFactory factory) { + this.logger = logger; + this.usageDataLogger = factory.create(Configuration.USAGE_DATA_LOGGER_NAME); threadLocal.set(this); } @@ -153,16 +162,13 @@ public void submitUsageData() { /** * Record JDI event. */ - public static void recordEvent(Event event) { + public void recordEvent(Event event) { try { - UsageDataSession currentSession = threadLocal.get(); - if (currentSession != null) { - Map eventEntry = new HashMap<>(); - eventEntry.put("timestamp", String.valueOf(System.currentTimeMillis())); - eventEntry.put("event", event.toString()); - synchronized (currentSession.eventList) { - currentSession.eventList.add(JsonUtils.toJson(eventEntry)); - } + Map eventEntry = new HashMap<>(); + eventEntry.put("timestamp", String.valueOf(System.currentTimeMillis())); + eventEntry.put("event", event.toString()); + synchronized (eventList) { + eventList.add(JsonUtils.toJson(eventEntry)); } } catch (Exception e) { logger.log(Level.SEVERE, String.format("Exception on recording event: %s.", e.toString()), e); 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..414d01daf 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 @@ -73,9 +73,9 @@ public static String sourceLookup(String[] sourcePaths, String sourceName) { /** * Get the enclosing type name of the given fully qualified name. *
-     * a.b.c        ->   a.b.c
-     * a.b.c$1      ->   a.b.c
-     * a.b.c$1$2    ->   a.b.c
+     * a.b.c     = a.b.c
+     * a.b.c$1   = a.b.c
+     * a.b.c$1$2 = a.b.c
      * 
* @param fullyQualifiedName * fully qualified name 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..d7fd0473d 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 @@ -23,12 +23,11 @@ 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 implements IBreakpointManager { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private final Logger logger; /** * A collection of breakpoints registered with this manager. */ @@ -40,7 +39,8 @@ public class BreakpointManager implements IBreakpointManager { /** * Constructor. */ - public BreakpointManager() { + public BreakpointManager(Logger logger) { + this.logger = logger; this.breakpoints = Collections.synchronizedList(new ArrayList<>(5)); this.sourceToBreakpoints = new HashMap<>(); this.watchpoints = new HashMap<>(); @@ -108,7 +108,8 @@ private void addBreakpointsInternally(String source, IBreakpoint[] breakpoints) if (breakpoints != null && breakpoints.length > 0) { for (IBreakpoint breakpoint : breakpoints) { - breakpoint.putProperty("id", this.nextBreakpointId.getAndIncrement()); + if (breakpoint.getProperty("id") == null) + breakpoint.putProperty("id", this.nextBreakpointId.getAndIncrement()); this.breakpoints.add(breakpoint); breakpointMap.put(String.valueOf(breakpoint.getLineNumber()), breakpoint); } 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..5bdd5eef7 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 @@ -19,7 +19,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.microsoft.java.debug.core.Configuration; 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; @@ -51,7 +50,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; public class DebugAdapter implements IDebugAdapter { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private final Logger logger; private IDebugAdapterContext debugContext = null; private Map> requestHandlersForDebug = null; @@ -60,8 +59,9 @@ public class DebugAdapter implements IDebugAdapter { /** * Constructor. */ - public DebugAdapter(IProtocolServer server, IProviderContext providerContext) { - this.debugContext = new DebugAdapterContext(server, providerContext); + public DebugAdapter(IProtocolServer server, IProviderContext providerContext, Logger logger) { + this.logger = logger; + this.debugContext = new DebugAdapterContext(server, providerContext, logger); requestHandlersForDebug = new HashMap<>(); requestHandlersForNoDebug = new HashMap<>(); initialize(); @@ -101,33 +101,33 @@ private 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()); - registerHandler(new LaunchRequestHandler()); + registerHandler(new LaunchRequestHandler(logger)); // DEBUG node only - registerHandlerForDebug(new AttachRequestHandler()); - registerHandlerForDebug(new ConfigurationDoneRequestHandler()); - registerHandlerForDebug(new DisconnectRequestHandler()); - registerHandlerForDebug(new SetBreakpointsRequestHandler()); + registerHandlerForDebug(new AttachRequestHandler(logger)); + registerHandlerForDebug(new ConfigurationDoneRequestHandler(logger)); + registerHandlerForDebug(new DisconnectRequestHandler(logger)); + registerHandlerForDebug(new SetBreakpointsRequestHandler(logger)); registerHandlerForDebug(new SetExceptionBreakpointsRequestHandler()); registerHandlerForDebug(new SourceRequestHandler()); registerHandlerForDebug(new ThreadsRequestHandler()); registerHandlerForDebug(new StepRequestHandler()); registerHandlerForDebug(new StackTraceRequestHandler()); registerHandlerForDebug(new ScopesRequestHandler()); - registerHandlerForDebug(new VariablesRequestHandler()); + registerHandlerForDebug(new VariablesRequestHandler(logger)); registerHandlerForDebug(new SetVariableRequestHandler()); - registerHandlerForDebug(new EvaluateRequestHandler()); + registerHandlerForDebug(new EvaluateRequestHandler(logger)); registerHandlerForDebug(new HotCodeReplaceHandler()); registerHandlerForDebug(new RestartFrameHandler()); registerHandlerForDebug(new CompletionsHandler()); - registerHandlerForDebug(new ExceptionInfoRequestHandler()); + registerHandlerForDebug(new ExceptionInfoRequestHandler(logger)); registerHandlerForDebug(new DataBreakpointInfoRequestHandler()); - registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); - registerHandlerForDebug(new InlineValuesRequestHandler()); + registerHandlerForDebug(new SetDataBreakpointsRequestHandler(logger)); + registerHandlerForDebug(new InlineValuesRequestHandler(logger)); registerHandlerForDebug(new RefreshVariablesHandler()); // NO_DEBUG mode only - registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); + registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler(logger)); } 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..7ac07673e 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 @@ -18,6 +18,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.logging.Logger; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.IDebugSession; @@ -60,12 +61,14 @@ 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(); + private IBreakpointManager breakpointManager; + private StepRequestManager stepRequestManager = new StepRequestManager(); - public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) { + public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext, Logger logger) { this.providerContext = providerContext; this.server = server; + this.breakpointManager = new BreakpointManager(logger); + } @Override @@ -323,7 +326,7 @@ public IBreakpointManager getBreakpointManager() { } @Override - public IStepResultManager getStepResultManager() { - return stepResultManager; + public StepRequestManager getStepRequestManager() { + return stepRequestManager; } } 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..9aa0c5341 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 @@ -127,5 +127,5 @@ public interface IDebugAdapterContext { IBreakpointManager getBreakpointManager(); - IStepResultManager getStepResultManager(); + StepRequestManager getStepRequestManager(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProviderContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProviderContext.java index c6310172d..c710a8ba0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProviderContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProviderContext.java @@ -11,9 +11,13 @@ package com.microsoft.java.debug.core.adapter; +import java.util.List; + public interface IProviderContext { T getProvider(Class clazz); void registerProvider(Class clazz, IProvider provider); + + List getProviders(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackTraceProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackTraceProvider.java new file mode 100644 index 000000000..a16f68974 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackTraceProvider.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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.protocol.Requests; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedField; +import com.sun.jdi.Location; +import com.sun.jdi.Method; +import com.sun.jdi.LocalVariable; +import com.sun.jdi.Field; +import java.util.Optional; + +public interface IStackTraceProvider extends IProvider { + boolean skipOver(Method method, Requests.StepFilters filters); + boolean skipOut(Location upperLocation, Method method); + DecodedMethod decode(Method method); + // TODO // p1i8 + DecodedVariable decode(LocalVariable variable, Method method, int sourceLine); + DecodedField decode(Field field); +} 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 deleted file mode 100644 index c19c0ecd6..000000000 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* -* 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/ProtocolServer.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java index 0526293b9..ac51d1979 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 @@ -21,6 +21,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.LoggerFactory; import com.microsoft.java.debug.core.UsageDataSession; import com.microsoft.java.debug.core.protocol.AbstractProtocolServer; import com.microsoft.java.debug.core.protocol.Events.DebugEvent; @@ -29,15 +30,23 @@ import com.sun.jdi.VMDisconnectedException; public class ProtocolServer extends AbstractProtocolServer { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - private IDebugAdapter debugAdapter; - private UsageDataSession usageDataSession = new UsageDataSession(); + private UsageDataSession usageDataSession; private Object lock = new Object(); private boolean isDispatchingRequest = false; private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue<>(); + + /** + * Constructor. + */ + public ProtocolServer(InputStream input, OutputStream output, IProviderContext context, LoggerFactory factory) { + super(input, output, factory.create(Configuration.LOGGER_NAME)); + debugAdapter = new DebugAdapter(this, context, logger); + usageDataSession = new UsageDataSession(logger, factory); + } + /** * Constructs a protocol server instance based on the given input stream and output stream. * @param input @@ -48,8 +57,7 @@ public class ProtocolServer extends AbstractProtocolServer { * provider context for a series of provider implementation */ public ProtocolServer(InputStream input, OutputStream output, IProviderContext context) { - super(input, output); - debugAdapter = new DebugAdapter(this, context); + this(input, output, context, Logger::getLogger); } /** diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProviderContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProviderContext.java index 756eb30e6..781c9f08b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProviderContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProviderContext.java @@ -11,7 +11,9 @@ package com.microsoft.java.debug.core.adapter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; public class ProviderContext implements IProviderContext { @@ -62,4 +64,9 @@ public void registerProvider(Class clazz, IProvider provide providerMap.put(clazz, provider); } + @Override + public List getProviders() { + return new ArrayList(providerMap.values()); + } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackTraceProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackTraceProvider.java new file mode 100644 index 000000000..10599e549 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackTraceProvider.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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; + +import com.microsoft.java.debug.core.protocol.Requests; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedField; +import com.microsoft.java.debug.core.adapter.stacktrace.JavaMethod; +import com.microsoft.java.debug.core.adapter.stacktrace.JavaLocalVariable; +import com.microsoft.java.debug.core.adapter.stacktrace.JavaField; +import com.sun.jdi.Location; +import com.sun.jdi.Method; +import com.sun.jdi.LocalVariable; +import com.sun.jdi.Field; +import org.apache.commons.lang3.ArrayUtils; +import java.util.Optional; + +public class StackTraceProvider implements IStackTraceProvider { + @Override + public boolean skipOver(Method method, Requests.StepFilters filters) { + if (!isConfigured(filters)) { + return false; + } + return (filters.skipStaticInitializers && method.isStaticInitializer()) + || (filters.skipSynthetics && method.isSynthetic()) + || (filters.skipConstructors && method.isConstructor()); + } + + @Override + public boolean skipOut(Location previousLocation, Method method) { + return false; + } + + + @Override + public DecodedMethod decode(Method method) { + return new JavaMethod(method); + } + + @Override + public DecodedVariable decode(LocalVariable variable, Method method, int sourceLine) { + return new JavaLocalVariable(variable); + } + + @Override + public DecodedField decode(Field field) { + return new JavaField(field); + } + + private boolean isConfigured(Requests.StepFilters filters) { + if (filters == null) { + return false; + } + 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/adapter/StepRequestManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepRequestManager.java new file mode 100644 index 000000000..a3794dd07 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepRequestManager.java @@ -0,0 +1,156 @@ +/******************************************************************************* +* 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.DebugUtility; +import com.microsoft.java.debug.core.JdiMethodResult; +import com.microsoft.java.debug.core.protocol.Requests; +import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.Location; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.request.EventRequestManager; +import com.sun.jdi.request.MethodExitRequest; +import com.sun.jdi.request.StepRequest; +import io.reactivex.disposables.Disposable; + +public class StepRequestManager { + private final Map threadStates = Collections.synchronizedMap(new HashMap<>()); + + public ThreadState setThreadState(Requests.Command stepType, ThreadReference thread) throws IncompatibleThreadStateException { + long threadId = thread.uniqueID(); + int stackDepth = thread.frameCount(); + Location location = thread.frame(0).location(); + ThreadState threadState = new ThreadState(threadId, stepType, stackDepth, location); + threadStates.put(threadId, threadState); + return threadState; + } + + public ThreadState getThreadState(long threadId) { + return threadStates.get(threadId); + } + + public void setMethodResult(long threadId, JdiMethodResult methodResult) { + ThreadState threadState = getThreadState(threadId); + threadState.methodResult = methodResult; + } + + public JdiMethodResult getMethodResult(long threadId) { + ThreadState threadState = getThreadState(threadId); + if (threadState == null) { + return null; + } + return threadState.methodResult; + } + + public void deletePendingStep(long threadId, EventRequestManager manager) { + ThreadState threadState = getThreadState(threadId); + if (threadState != null) { + threadState.deletePendingStep(manager); + } + } + + public void deleteAllPendingSteps(EventRequestManager manager) { + this.threadStates.forEach((threadId, threadState) -> threadState.deletePendingStep(manager)); + } + + public void removeMethodResult(long threadId) { + ThreadState threadState = getThreadState(threadId); + if (threadState == null) { + return; + } + threadState.methodResult = null; + } + + public void removeAllMethodResults() { + this.threadStates.forEach((threadId, threadState) -> threadState.methodResult = null); + } + + public static class ThreadState { + long threadId; + Requests.Command stepType; + StepRequest pendingStepRequest = null; + MethodExitRequest pendingMethodExitRequest = null; + int stackDepth; + Location stepLocation; + Disposable eventSubscription = null; + JdiMethodResult methodResult = null; + + public ThreadState(long threadId, Requests.Command stepType, int stackDepth, Location stepLocation) { + this.threadId = threadId; + this.stepType = stepType; + this.stackDepth = stackDepth; + this.stepLocation = stepLocation; + } + + public long getThreadId() { + return threadId; + } + + public Requests.Command getStepType() { + return stepType; + } + + public void setPendingMethodExitRequest(MethodExitRequest pendingMethodExitRequest) { + this.pendingMethodExitRequest = pendingMethodExitRequest; + } + + public MethodExitRequest getPendingMethodExitRequest() { + return pendingMethodExitRequest; + } + + public void setPendingStepRequest(StepRequest pendingStepRequest) { + this.pendingStepRequest = pendingStepRequest; + } + + public int getStackDepth() { + return stackDepth; + } + + public StepRequest getPendingStepRequest() { + return pendingStepRequest; + } + + public Location getStepLocation() { + return stepLocation; + } + + public void setEventSubscription(Disposable eventSubscription) { + this.eventSubscription = eventSubscription; + } + + public void deletePendingStep(EventRequestManager manager) { + deleteStepRequest(manager); + deleteMethodExitRequest(manager); + eventSubscription.dispose(); + } + + public void deleteStepRequest(EventRequestManager manager) { + if (this.pendingStepRequest == null) { + return; + } + DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest); + this.pendingStepRequest = null; + } + + private void deleteMethodExitRequest(EventRequestManager manager) { + if (this.pendingMethodExitRequest == null) { + return; + } + DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); + this.pendingMethodExitRequest = null; + } + } +} 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 deleted file mode 100644 index dd3937181..000000000 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* -* 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/AbstractDisconnectRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractDisconnectRequestHandler.java index fd5c68d6c..44d8045ee 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 @@ -20,7 +20,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -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; @@ -30,7 +29,11 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; public abstract class AbstractDisconnectRequestHandler implements IDebugRequestHandler { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private final Logger logger; + + public AbstractDisconnectRequestHandler(Logger logger) { + this.logger = logger; + } @Override public List getTargetCommands() { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractLaunchDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractLaunchDelegate.java new file mode 100644 index 000000000..e35a779ee --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AbstractLaunchDelegate.java @@ -0,0 +1,54 @@ +/******************************************************************************* +* 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.handler; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import com.microsoft.java.debug.core.protocol.Requests; + +abstract class AbstractLaunchDelegate implements ILaunchDelegate { + protected final Logger logger; + + protected AbstractLaunchDelegate(Logger logger) { + this.logger = logger; + } + + protected String[] constructEnvironmentVariables(Requests.LaunchArguments launchArguments) { + String[] envVars = null; + if (launchArguments.env != null && !launchArguments.env.isEmpty()) { + Map environment = new HashMap<>(System.getenv()); + List duplicated = new ArrayList<>(); + for (Map.Entry entry : launchArguments.env.entrySet()) { + if (environment.containsKey(entry.getKey())) { + duplicated.add(entry.getKey()); + } + environment.put(entry.getKey(), entry.getValue()); + } + // For duplicated variables, show a warning message. + if (!duplicated.isEmpty()) { + logger.warning(String.format("There are duplicated environment variables. The values specified in launch.json will be used. " + + "Here are the duplicated entries: %s.", String.join(",", duplicated))); + } + + envVars = new String[environment.size()]; + int i = 0; + for (Map.Entry entry : environment.entrySet()) { + envVars[i++] = entry.getKey() + "=" + entry.getValue(); + } + } + return envVars; + } +} 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..006973f6e 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 @@ -20,7 +20,6 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; -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.adapter.AdapterUtils; @@ -43,9 +42,13 @@ import org.apache.commons.lang3.StringUtils; public class AttachRequestHandler implements IDebugRequestHandler { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private final Logger logger; private VMHandler vmHandler = new VMHandler(); + public AttachRequestHandler(Logger logger) { + this.logger = logger; + } + @Override public List getTargetCommands() { return Arrays.asList(Command.ATTACH); @@ -65,7 +68,7 @@ public CompletableFuture handle(Command command, Arguments arguments, 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); + attachArguments.timeout, logger); context.setDebugSession(debugSession); vmHandler.connectVirtualMachine(debugSession.getVM()); logger.info("Attaching to debuggee VM succeeded."); 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..7263270fb 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 @@ -16,7 +16,6 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; -import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugEvent; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; @@ -41,11 +40,16 @@ import com.sun.jdi.event.VMDeathEvent; import com.sun.jdi.event.VMDisconnectEvent; import com.sun.jdi.event.VMStartEvent; +import com.sun.jdi.request.EventRequestManager; public class ConfigurationDoneRequestHandler implements IDebugRequestHandler { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + protected final Logger logger; private VMHandler vmHandler = new VMHandler(); + public ConfigurationDoneRequestHandler(Logger logger) { + this.logger = logger; + } + @Override public List getTargetCommands() { return Arrays.asList(Command.CONFIGURATIONDONE); @@ -75,7 +79,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (event instanceof VMStartEvent) { if (context.isVmStopOnEntry()) { DebugUtility.stopOnEntry(debugSession, context.getMainClass()).thenAccept(threadId -> { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("entry", threadId)); + notifyStopEvent("entry", threadId, context); }); } } else if (event instanceof VMDeathEvent) { @@ -117,7 +121,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.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID())); + notifyStopEvent("exception", thread.uniqueID(), context); debugEvent.shouldResume = false; } else { isImportantEvent = false; @@ -125,7 +129,16 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // record events of important types only, to get rid of noises. if (isImportantEvent) { - UsageDataSession.recordEvent(event); + UsageDataSession session = UsageDataSession.currentSession(); + if (session != null) { + session.recordEvent(event); + } } } + + private void notifyStopEvent(String reason, long threadId, IDebugAdapterContext context) { + EventRequestManager eventRequestManager = context.getDebugSession().getVM().eventRequestManager(); + context.getStepRequestManager().deletePendingStep(threadId, eventRequestManager); + context.getProtocolServer().sendEvent(new Events.StoppedEvent(reason, threadId)); + } } 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 a615e8963..bd42e8a50 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 @@ -11,6 +11,8 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.util.logging.Logger; + import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.protocol.Messages.Response; @@ -18,8 +20,13 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.DisconnectArguments; + public class DisconnectRequestHandler extends AbstractDisconnectRequestHandler { + public DisconnectRequestHandler(Logger logger) { + super(logger); + } + @Override public void destroyDebugSession(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { DisconnectArguments disconnectArguments = (DisconnectArguments) arguments; 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..8d33d04bd 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 @@ -11,14 +11,21 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.util.logging.Logger; + 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; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.DisconnectArguments; + public class DisconnectRequestWithoutDebuggingHandler extends AbstractDisconnectRequestHandler { + public DisconnectRequestWithoutDebuggingHandler(Logger logger) { + super(logger); + } + @Override public void destroyDebugSession(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { DisconnectArguments disconnectArguments = (DisconnectArguments) arguments; 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..bd0930931 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 @@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils; -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.adapter.AdapterUtils; @@ -49,7 +48,11 @@ import com.sun.jdi.VoidValue; public class EvaluateRequestHandler implements IDebugRequestHandler { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + protected final Logger logger; + + public EvaluateRequestHandler(Logger logger) { + this.logger = logger; + } @Override public List getTargetCommands() { 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..e1b3f97ee 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 @@ -19,7 +19,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.JdiExceptionReference; import com.microsoft.java.debug.core.adapter.AdapterUtils; @@ -42,7 +41,11 @@ import com.sun.jdi.Value; public class ExceptionInfoRequestHandler implements IDebugRequestHandler { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + protected final Logger logger; + + public ExceptionInfoRequestHandler(Logger logger) { + this.logger = logger; + } @Override public List getTargetCommands() { 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..e01d4b370 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 @@ -54,7 +54,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsCompletionsRequest = true; caps.supportsRestartFrame = true; caps.supportsLogPoints = true; - caps.supportsEvaluateForHovers = true; + caps.supportsEvaluateForHovers = false; Types.ExceptionBreakpointFilter[] exceptionFilters = { Types.ExceptionBreakpointFilter.UNCAUGHT_EXCEPTION_FILTER, Types.ExceptionBreakpointFilter.CAUGHT_EXCEPTION_FILTER, 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..653210c5f 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 @@ -22,7 +22,6 @@ 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; @@ -53,7 +52,11 @@ import org.apache.commons.lang3.math.NumberUtils; public class InlineValuesRequestHandler implements IDebugRequestHandler { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + protected final Logger logger; + + public InlineValuesRequestHandler(Logger logger) { + this.logger = logger; + } @Override public List getTargetCommands() { 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..17b93284f 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 @@ -20,10 +20,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -36,7 +33,6 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -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.DebugUtility; @@ -62,11 +58,15 @@ import com.sun.jdi.event.VMDisconnectEvent; public class LaunchRequestHandler implements IDebugRequestHandler { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + protected final Logger logger; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; protected ILaunchDelegate activeLaunchHandler; private CompletableFuture waitForDebuggeeConsole = new CompletableFuture<>(); + public LaunchRequestHandler(Logger logger) { + this.logger = logger; + } + @Override public List getTargetCommands() { return Arrays.asList(Command.LAUNCH); @@ -75,8 +75,9 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { LaunchArguments launchArguments = (LaunchArguments) arguments; - activeLaunchHandler = launchArguments.noDebug ? new LaunchWithoutDebuggingDelegate((daContext) -> handleTerminatedEvent(daContext)) - : new LaunchWithDebuggingDelegate(); + activeLaunchHandler = launchArguments.noDebug + ? new LaunchWithoutDebuggingDelegate(this::handleTerminatedEvent, logger) + : new LaunchWithDebuggingDelegate(logger); return handleLaunchCommand(arguments, response, context); } @@ -284,32 +285,6 @@ private static OutputEvent convertToOutputEvent(String message, Category categor return new OutputEvent(category, message); } - protected static String[] constructEnvironmentVariables(LaunchArguments launchArguments) { - String[] envVars = null; - if (launchArguments.env != null && !launchArguments.env.isEmpty()) { - Map environment = new HashMap<>(System.getenv()); - List duplicated = new ArrayList<>(); - for (Entry entry : launchArguments.env.entrySet()) { - if (environment.containsKey(entry.getKey())) { - duplicated.add(entry.getKey()); - } - environment.put(entry.getKey(), entry.getValue()); - } - // For duplicated variables, show a warning message. - if (!duplicated.isEmpty()) { - logger.warning(String.format("There are duplicated environment variables. The values specified in launch.json will be used. " - + "Here are the duplicated entries: %s.", String.join(",", duplicated))); - } - - envVars = new String[environment.size()]; - int i = 0; - for (Entry entry : environment.entrySet()) { - envVars[i++] = entry.getKey() + "=" + entry.getValue(); - } - } - return envVars; - } - public static String parseMainClassWithoutModuleName(String mainClass) { int index = mainClass.indexOf('/'); return mainClass.substring(index + 1); 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..3ebcadd4b 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,7 +24,6 @@ import org.apache.commons.lang3.SystemUtils; import com.google.gson.JsonObject; -import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.DebugSession; import com.microsoft.java.debug.core.DebugUtility; @@ -52,14 +51,16 @@ import com.sun.jdi.connect.TransportTimeoutException; import com.sun.jdi.connect.VMStartException; -public class LaunchWithDebuggingDelegate implements ILaunchDelegate { - - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); +public class LaunchWithDebuggingDelegate extends AbstractLaunchDelegate { 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(); + public LaunchWithDebuggingDelegate(Logger logger) { + super(logger); + } + @Override public CompletableFuture launchInTerminal(LaunchArguments launchArguments, Response response, IDebugAdapterContext context) { CompletableFuture resultFuture = new CompletableFuture<>(); @@ -104,7 +105,7 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume try { VirtualMachine vm = listenConnector.accept(args); vmHandler.connectVirtualMachine(vm); - context.setDebugSession(new DebugSession(vm)); + context.setDebugSession(new DebugSession(vm, logger)); logger.info("Launching debuggee in terminal console succeeded."); resultFuture.complete(response); } catch (TransportTimeoutException e) { @@ -185,8 +186,9 @@ public Process launch(LaunchArguments launchArguments, IDebugAdapterContext cont Arrays.asList(launchArguments.modulePaths), Arrays.asList(launchArguments.classPaths), launchArguments.cwd, - LaunchRequestHandler.constructEnvironmentVariables(launchArguments), - launchArguments.javaExec); + constructEnvironmentVariables(launchArguments), + launchArguments.javaExec, + logger); context.setDebugSession(debugSession); vmHandler.connectVirtualMachine(debugSession.getVM()); 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..55cf2a7f9 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,7 +21,6 @@ import java.util.logging.Logger; import com.google.gson.JsonObject; -import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -36,13 +35,13 @@ import com.sun.jdi.connect.IllegalConnectorArgumentsException; import com.sun.jdi.connect.VMStartException; -public class LaunchWithoutDebuggingDelegate implements ILaunchDelegate { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); +public class LaunchWithoutDebuggingDelegate extends AbstractLaunchDelegate { protected static final String TERMINAL_TITLE = "Java Process Console"; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; private Consumer terminateHandler; - public LaunchWithoutDebuggingDelegate(Consumer terminateHandler) { + public LaunchWithoutDebuggingDelegate(Consumer terminateHandler, Logger logger) { + super(logger); this.terminateHandler = terminateHandler; } @@ -54,7 +53,7 @@ public Process launch(LaunchArguments launchArguments, IDebugAdapterContext cont if (launchArguments.cwd != null && Files.isDirectory(Paths.get(launchArguments.cwd))) { workingDir = new File(launchArguments.cwd); } - Process debuggeeProcess = Runtime.getRuntime().exec(cmds, LaunchRequestHandler.constructEnvironmentVariables(launchArguments), + Process debuggeeProcess = Runtime.getRuntime().exec(cmds, constructEnvironmentVariables(launchArguments), workingDir); new Thread() { public void run() { 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..d2c0388ad 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 @@ -32,6 +32,7 @@ import com.microsoft.java.debug.core.protocol.Requests.RestartFrameArguments; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; +import com.sun.jdi.request.EventRequestManager; import com.sun.jdi.request.StepRequest; /** @@ -102,8 +103,10 @@ private void popStackFrames(IDebugAdapterContext context, ThreadReference thread } private void stepInto(IDebugAdapterContext context, ThreadReference thread) { + EventRequestManager manager = context.getDebugSession().getVM().eventRequestManager(); 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 -> { + DebugUtility.deleteEventRequestSafely(manager, request); 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())); 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..6a6bd42dc 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,11 +17,12 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.sun.jdi.request.EventRequestManager; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; -import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.IDebugSession; @@ -34,6 +35,7 @@ import com.microsoft.java.debug.core.adapter.IEvaluationProvider; import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.adapter.IStackTraceProvider; 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 +45,7 @@ import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.BooleanValue; import com.sun.jdi.Field; +import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; @@ -53,11 +56,14 @@ import com.sun.jdi.event.StepEvent; public class SetBreakpointsRequestHandler implements IDebugRequestHandler { - - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + private final Logger logger; private boolean registered = false; + public SetBreakpointsRequestHandler(Logger logger) { + this.logger = logger; + } + @Override public List getTargetCommands() { return Arrays.asList(Command.SETBREAKPOINTS); @@ -125,31 +131,10 @@ 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 = 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) { - added[i].install().thenAccept(bp -> { - Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("new", this.convertDebuggerBreakpointToClient(bp, context)); - context.getProtocolServer().sendEvent(bpEvent); - }); - } else if (added[i].className() != null) { - if (toAdds[i].getHitCount() != added[i].getHitCount()) { - // Update hitCount condition. - added[i].setHitCount(toAdds[i].getHitCount()); - } - - if (!StringUtils.equals(toAdds[i].getLogMessage(), added[i].getLogMessage())) { - added[i].setLogMessage(toAdds[i].getLogMessage()); - } - - if (!StringUtils.equals(toAdds[i].getCondition(), added[i].getCondition())) { - added[i].setCondition(toAdds[i].getCondition()); - } - - } - res.add(this.convertDebuggerBreakpointToClient(added[i], context)); + String decodedSourcePath = AdapterUtils.decodeURIComponent(sourcePath); + IBreakpoint[] added = installBreakpoints(decodedSourcePath, toAdds, "new", bpArguments.sourceModified, context); + for (IBreakpoint addedBreakpoint : added) { + res.add(this.convertDebuggerBreakpointToClient(addedBreakpoint, context)); } response.body = new Responses.SetBreakpointsResponseBody(res); return CompletableFuture.completedFuture(response); @@ -160,6 +145,34 @@ public CompletableFuture handle(Command command, Arguments arguments, } } + private IBreakpoint[] installBreakpoints(String sourcePath, IBreakpoint[] toAdds, String reason, boolean sourceModified, IDebugAdapterContext context) { + IBreakpoint[] added = context.getBreakpointManager().setBreakpoints(sourcePath, toAdds, sourceModified); + for (int i = 0; i < toAdds.length; i++) { + // 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(reason, this.convertDebuggerBreakpointToClient(bp, context)); + context.getProtocolServer().sendEvent(bpEvent); + }); + } else if (added[i].className() != null) { + if (toAdds[i].getHitCount() != added[i].getHitCount()) { + // Update hitCount condition. + added[i].setHitCount(toAdds[i].getHitCount()); + } + + if (!StringUtils.equals(toAdds[i].getLogMessage(), added[i].getLogMessage())) { + added[i].setLogMessage(toAdds[i].getLogMessage()); + } + + if (!StringUtils.equals(toAdds[i].getCondition(), added[i].getCondition())) { + added[i].setCondition(toAdds[i].getCondition()); + } + + } + } + return added; + } + private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream().filter( bp -> { @@ -172,6 +185,7 @@ private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext cont private void registerBreakpointHandler(IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); + IStackTraceProvider stackTraceProvider = context.getProvider(IStackTraceProvider.class); if (debugSession != null) { debugSession.getEventHub().events().filter(debugEvent -> debugEvent.event instanceof BreakpointEvent).subscribe(debugEvent -> { Event event = debugEvent.event; @@ -182,6 +196,12 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { ThreadReference bpThread = ((BreakpointEvent) event).thread(); IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); if (engine.isInEvaluation(bpThread)) { + debugEvent.shouldResume = true; + return; + } + Method method = bpThread.frame(0).location().method(); + if (stackTraceProvider.skipOver(method, context.getStepFilters())) { + debugEvent.shouldResume = true; return; } @@ -191,19 +211,19 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (expressionBP != null) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> { - boolean resume = handleEvaluationResult(context, bpThread, (IEvaluatableBreakpoint) expressionBP, value, ex); + boolean resume = handleEvaluationResult(context, bpThread, (IEvaluatableBreakpoint) expressionBP, value, ex, logger); // 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())); + notifyStoppedThread(context, bpThread.uniqueID()); } }); }); } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); + notifyStoppedThread(context, bpThread.uniqueID()); } debugEvent.shouldResume = false; } @@ -211,11 +231,17 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { } } + private void notifyStoppedThread(IDebugAdapterContext context, long threadId) { + EventRequestManager eventRequestManager = context.getDebugSession().getVM().eventRequestManager(); + context.getStepRequestManager().deletePendingStep(threadId, eventRequestManager); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", threadId)); + } + /** * 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) { + Value value, Throwable ex, Logger logger) { if (StringUtils.isNotBlank(breakpoint.getLogMessage())) { if (ex != null) { logger.log(Level.SEVERE, String.format("[Logpoint]: %s", ex.getMessage() != null ? ex.getMessage() : ex.toString()), ex); @@ -292,24 +318,38 @@ private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Type return breakpoints; } - private void reinstallBreakpoints(IDebugAdapterContext context, List typenames) { - if (typenames == null || typenames.isEmpty()) { - return; + private IBreakpoint[] createFreshBreakpoints(String sourceFile, IBreakpoint[] oldBreakpoints, IDebugAdapterContext context) + throws DebugException { + int[] lines = Arrays.asList(oldBreakpoints).stream().mapToInt(b -> b.getLineNumber()).toArray(); + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); + String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, null); + IBreakpoint[] breakpoints = new IBreakpoint[lines.length]; + for (int i = 0; i < lines.length; i++) { + IBreakpoint oldBreakpoint = oldBreakpoints[i]; + breakpoints[i] = context.getDebugSession().createBreakpoint(fqns[i], lines[i], oldBreakpoint.getHitCount(), + oldBreakpoint.getCondition(), oldBreakpoint.getLogMessage()); + breakpoints[i].putProperty("id", oldBreakpoint.getProperty("id")); + if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(fqns[i])) { + breakpoints[i].putProperty("verified", true); + } } - IBreakpoint[] breakpoints = context.getBreakpointManager().getBreakpoints(); + return breakpoints; + } - for (IBreakpoint breakpoint : breakpoints) { - if (typenames.contains(breakpoint.className())) { - try { - breakpoint.close(); - breakpoint.install().thenAccept(bp -> { - Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("new", this.convertDebuggerBreakpointToClient(bp, context)); - context.getProtocolServer().sendEvent(bpEvent); - }); - } catch (Exception e) { - logger.log(Level.SEVERE, String.format("Remove breakpoint exception: %s", e.toString()), e); - } + private void reinstallBreakpoints(IDebugAdapterContext context, List fqcns) { + try { + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); + List sourceFiles = fqcns.stream() + .map(fqcn -> sourceProvider.getSourceFileURI(fqcn, "")) + .distinct() + .collect(Collectors.toList()); + + for (String sourceFile : sourceFiles) { + IBreakpoint[] breakpoints = createFreshBreakpoints(sourceFile, context.getBreakpointManager().getBreakpoints(sourceFile), context); + IBreakpoint[] added = installBreakpoints(sourceFile, breakpoints, "changed", true, context); } + } catch (Exception e) { + logger.log(Level.SEVERE, String.format("Failed to reinstall breakpoints: %s", e.toString()), e); } } } 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..9c7aa73f4 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 @@ -16,8 +16,10 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; import java.util.stream.Stream; +import com.sun.jdi.request.EventRequestManager; import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.IDebugSession; @@ -43,8 +45,14 @@ import com.sun.jdi.event.WatchpointEvent; public class SetDataBreakpointsRequestHandler implements IDebugRequestHandler { + private final Logger logger; + private boolean registered = false; + public SetDataBreakpointsRequestHandler(Logger logger) { + this.logger = logger; + } + @Override public List getTargetCommands() { return Arrays.asList(Command.SETDATABREAKPOINTS); @@ -144,22 +152,28 @@ private void registerWatchpointHandler(IDebugAdapterContext context) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) watchpoint, bpThread).whenComplete((value, ex) -> { boolean resume = SetBreakpointsRequestHandler.handleEvaluationResult( - context, bpThread, (IEvaluatableBreakpoint) watchpoint, value, ex); + context, bpThread, (IEvaluatableBreakpoint) watchpoint, value, ex, logger); // 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())); + notifyStoppedThread(context, bpThread.uniqueID()); } }); }); } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); + notifyStoppedThread(context, bpThread.uniqueID()); } debugEvent.shouldResume = false; }); } } + + private void notifyStoppedThread(IDebugAdapterContext context, long threadId) { + EventRequestManager eventRequestManager = context.getDebugSession().getVM().eventRequestManager(); + context.getStepRequestManager().deletePendingStep(threadId, eventRequestManager); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", threadId)); + } } 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..b96358d35 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 @@ -19,6 +19,7 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import org.apache.commons.io.filefilter.TrueFileFilter; import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.DebugUtility; @@ -26,12 +27,15 @@ 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.IStackTraceProvider; import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedMethod; 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.StackTraceArguments; +import com.microsoft.java.debug.core.protocol.Requests; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.AbsentInformationException; @@ -41,6 +45,7 @@ import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; +import java.util.Optional; public class StackTraceRequestHandler implements IDebugRequestHandler { @@ -61,22 +66,28 @@ public CompletableFuture handle(Command command, Arguments arguments, int totalFrames = 0; if (thread != null) { try { + totalFrames = thread.frameCount(); 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++) { StackFrameReference stackframe = new StackFrameReference(thread, i); int frameId = context.getRecyclableIdPool().addObject(thread.uniqueID(), stackframe); - result.add(convertDebuggerStackFrameToClient(frames[i], frameId, context)); + IStackTraceProvider stackTraceProvider = context.getProvider(IStackTraceProvider.class); + DecodedMethod decodedMethod = stackTraceProvider.decode(thread.frame(i).location().method()); + + result.add(convertDebuggerStackFrameToClient(frames[i], frameId, context, decodedMethod)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException e) { + e.printStackTrace(); + System.out.println(e.getMessage()); // when error happens, the possible reason is: // 1. the vscode has wrong parameter/wrong uri // 2. the thread actually terminates @@ -87,25 +98,20 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } - private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame, int frameId, IDebugAdapterContext context) + private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame, int frameId, IDebugAdapterContext context, DecodedMethod decodedMethod) throws URISyntaxException, AbsentInformationException { Location location = stackFrame.location(); - Method method = location.method(); Types.Source clientSource = this.convertDebuggerSourceToClient(location, context); - String methodName = formatMethodName(method, true, true); + String formattedName = decodedMethod.format(); 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. - if (lineNumber < 0) { - if (method.isNative()) { - // For native method, display a tip text "native method" in the Call Stack View. - methodName += "[native method]"; - } 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; - } + if (lineNumber < 0 && !location.method().isNative()) { + // 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; } - return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0); + String presentationHint = decodedMethod.isGenerated() ? "subtle" : null; + return new Types.StackFrame(frameId, formattedName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); } private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdapterContext context) throws URISyntaxException { @@ -133,6 +139,7 @@ public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedNa 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; @@ -161,21 +168,4 @@ public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedNa } } } - - private String formatMethodName(Method method, boolean showContextClass, boolean showParameter) { - StringBuilder formattedName = new StringBuilder(); - if (showContextClass) { - String fullyQualifiedClassName = method.declaringType().name(); - formattedName.append(SimpleTypeFormatter.trimTypeName(fullyQualifiedClassName)); - formattedName.append("."); - } - formattedName.append(method.name()); - if (showParameter) { - List argumentTypeNames = method.argumentTypeNames().stream().map(SimpleTypeFormatter::trimTypeName).collect(Collectors.toList()); - formattedName.append("("); - formattedName.append(String.join(",", argumentTypeNames)); - formattedName.append(")"); - } - return formattedName.toString(); - } } 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..e87a97afa 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,8 +15,6 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import org.apache.commons.lang3.ArrayUtils; - import com.microsoft.java.debug.core.DebugEvent; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; @@ -26,12 +24,14 @@ 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.IStackTraceProvider; +import com.microsoft.java.debug.core.adapter.StepRequestManager; 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; 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.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.Location; import com.sun.jdi.Method; @@ -40,10 +40,7 @@ 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; @@ -71,38 +68,34 @@ 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); + context.getStepRequestManager().removeMethodResult(threadId); try { - 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)) - || debugEvent.event instanceof BreakpointEvent - || debugEvent.event instanceof ExceptionEvent) - .subscribe(debugEvent -> { - handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); - }); + StepRequestManager stepRequestManager = context.getStepRequestManager(); + StepRequestManager.ThreadState threadState = stepRequestManager.setThreadState(command, thread); + Disposable eventSubscription = context.getDebugSession().getEventHub().events() + .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.getPendingStepRequest())) + || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.getPendingMethodExitRequest()))) + .subscribe(debugEvent -> handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState)); + threadState.setEventSubscription(eventSubscription); + StepRequest stepRequest; if (command == Command.STEPIN) { - threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + stepRequest = DebugUtility.createStepIntoRequest(thread, + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { - threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, + stepRequest = DebugUtility.createStepOutRequest(thread, context.getStepFilters().allowClasses, context.getStepFilters().skipClasses); } else { - threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); + stepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingStepRequest.enable(); + threadState.setPendingStepRequest(stepRequest); + stepRequest.enable(); MethodExitRequest methodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); methodExitRequest.addThreadFilter(thread); - methodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + methodExitRequest.addClassFilter(threadState.getStepLocation().declaringType()); if (thread.virtualMachine().canUseInstanceFilters()) { try { ObjectReference thisObject = getTopFrame(thread).thisObject(); @@ -114,7 +107,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } } methodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - threadState.pendingMethodExitRequest = methodExitRequest; + threadState.setPendingMethodExitRequest(methodExitRequest); methodExitRequest.enable(); DebugUtility.resumeThread(thread); @@ -143,95 +136,84 @@ public CompletableFuture handle(Command command, Arguments arguments, } private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, - ThreadState threadState) { + StepRequestManager.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.deleteStepRequest(eventRequestManager); - threadState.deleteMethodExitRequest(eventRequestManager); - context.getStepResultManager().removeMethodResult(threadId); - if (threadState.eventSubscription != null) { - threadState.eventSubscription.dispose(); - } - } - } else if (event instanceof StepEvent) { + if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); threadState.deleteStepRequest(eventRequestManager); - if (isStepFiltersConfigured(context.getStepFilters())) { - 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, - // 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().allowClasses, - context.getStepFilters().skipClasses); - threadState.pendingStepRequest.enable(); - debugEvent.shouldResume = true; - return; - } + IStackTraceProvider stackTrace = context.getProvider(IStackTraceProvider.class); + try { + Location originalLocation = threadState.getStepLocation(); + Location currentLocation = getTopFrame(thread).location(); + Location upperLocation = null; + if (thread.frameCount() > 1) { + upperLocation = thread.frame(1).location(); + } + if (originalLocation != null && currentLocation != null) { + Requests.StepFilters stepFilters = context.getStepFilters(); + // If we stepped into a method that should be stepped out + if (shouldSkipOut(stackTrace, threadState.getStackDepth(), thread.frameCount(), upperLocation, currentLocation)) { + doExtraStepOut(debugEvent, thread, stepFilters, threadState); + return; + } + // If the ending location is the same as the original location do another step into. + if (shouldDoExtraStep(threadState, originalLocation, thread.frameCount(), currentLocation)) { + doExtraStepInto(debugEvent, thread, stepFilters, threadState); + return; + } + // If the ending location should be stepped into + if (shouldSkipOver(stackTrace, originalLocation, currentLocation, stepFilters)) { + doExtraStepInto(debugEvent, thread, stepFilters, threadState); + return; } - } catch (IncompatibleThreadStateException | IndexOutOfBoundsException ex) { - // ignore. } + + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException ex) { + // ignore. } - threadState.deleteMethodExitRequest(eventRequestManager); - if (threadState.eventSubscription != null) { - threadState.eventSubscription.dispose(); - } - context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID())); + threadState.deletePendingStep(eventRequestManager); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", threadState.getThreadId())); 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())) { + if (threadId == threadState.getThreadId() && methodExitEvent.method().equals(threadState.getStepLocation().method())) { Value returnValue = methodExitEvent.returnValue(); if (returnValue instanceof VoidValue) { - context.getStepResultManager().removeMethodResult(threadId); + context.getStepRequestManager().removeMethodResult(threadId); } else { JdiMethodResult methodResult = new JdiMethodResult(methodExitEvent.method(), returnValue); - context.getStepResultManager().setMethodResult(threadId, methodResult); + context.getStepRequestManager().setMethodResult(threadId, methodResult); } } debugEvent.shouldResume = true; } } - private boolean isStepFiltersConfigured(StepFilters filters) { - if (filters == null) { - return false; - } - return ArrayUtils.isNotEmpty(filters.allowClasses) || ArrayUtils.isNotEmpty(filters.skipClasses) - || 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 step into. * * @throws IncompatibleThreadStateException * if the thread is not suspended in the target VM. */ - private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, IDebugAdapterContext context) + private boolean shouldSkipOver(IStackTraceProvider stackTrace, Location originalLocation, Location currentLocation, Requests.StepFilters stepFilters) throws IncompatibleThreadStateException { - if (originalLocation == null || currentLocation == null) { - return false; - } - return !shouldFilterMethod(originalLocation.method(), context) && shouldFilterMethod(currentLocation.method(), context); + return !stackTrace.skipOver(originalLocation.method(), stepFilters) + && stackTrace.skipOver(currentLocation.method(), stepFilters); } - private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) { - return (context.getStepFilters().skipStaticInitializers && method.isStaticInitializer()) - || (context.getStepFilters().skipSynthetics && method.isSynthetic()) - || (context.getStepFilters().skipConstructors && method.isConstructor()); + private boolean shouldSkipOut(IStackTraceProvider stackTrace, int originalStackDepth, int currentStackDepth, Location upperLocation, + Location currentLocation) + throws IncompatibleThreadStateException { + if (upperLocation == null) { + return false; + } + if (currentStackDepth <= originalStackDepth) { + return false; + } + return stackTrace.skipOut(upperLocation, currentLocation.method()); } /** @@ -240,9 +222,12 @@ private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) * @throws IncompatibleThreadStateException * if the thread is not suspended in the target VM. */ - private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, Location currentLocation) + private boolean shouldDoExtraStep(StepRequestManager.ThreadState threadState, Location originalLocation, int currentStackDepth, Location currentLocation) throws IncompatibleThreadStateException { - if (originalStackDepth != currentStackDepth) { + if (threadState.getStepType() != Command.STEPIN) { + return false; + } + if (threadState.getStackDepth() != currentStackDepth) { return false; } if (originalLocation == null) { @@ -253,10 +238,21 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL if (!originalMethod.equals(currentMethod)) { return false; } - if (originalLocation.lineNumber() != currentLocation.lineNumber()) { - return false; - } - return true; + return originalLocation.lineNumber() == currentLocation.lineNumber(); + } + + private void doExtraStepInto(DebugEvent debugEvent, ThreadReference thread, Requests.StepFilters stepFilters, StepRequestManager.ThreadState threadState) { + StepRequest stepRequest = DebugUtility.createStepIntoRequest(thread, stepFilters.allowClasses, stepFilters.skipClasses); + threadState.setPendingStepRequest(stepRequest); + stepRequest.enable(); + debugEvent.shouldResume = true; + } + + private void doExtraStepOut(DebugEvent debugEvent, ThreadReference thread, Requests.StepFilters stepFilters, StepRequestManager.ThreadState threadState) { + StepRequest stepRequest = DebugUtility.createStepOutRequest(thread, stepFilters.allowClasses, stepFilters.skipClasses); + threadState.setPendingStepRequest(stepRequest); + stepRequest.enable(); + debugEvent.shouldResume = true; } /** @@ -273,24 +269,4 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL private StackFrame getTopFrame(ThreadReference thread) throws IncompatibleThreadStateException { return thread.frame(0); } - - class ThreadState { - long threadId = -1; - Command pendingStepType; - StepRequest pendingStepRequest = null; - MethodExitRequest pendingMethodExitRequest = null; - int stackDepth = -1; - Location stepLocation = null; - Disposable eventSubscription = null; - - 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; - } - } } 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..20e2bf868 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,11 +17,13 @@ import java.util.concurrent.CompletableFuture; 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; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.adapter.StepRequestManager; 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; @@ -36,6 +38,7 @@ import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ThreadReference; import com.sun.jdi.VMDisconnectedException; +import com.sun.jdi.request.EventRequestManager; public class ThreadsRequestHandler implements IDebugRequestHandler { @@ -93,14 +96,19 @@ private CompletableFuture threads(Requests.ThreadsArguments arguments, } private CompletableFuture pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) { - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + StepRequestManager stepRequestManager = context.getStepRequestManager(); + IDebugSession debugSession = context.getDebugSession(); + EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); + ThreadReference thread = DebugUtility.getThread(debugSession, arguments.threadId); if (thread != null) { - context.getStepResultManager().removeMethodResult(arguments.threadId); + stepRequestManager.removeMethodResult(arguments.threadId); + stepRequestManager.deletePendingStep(arguments.threadId, eventRequestManager); thread.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId)); } else { - context.getStepResultManager().removeAllMethodResults(); - context.getDebugSession().suspend(); + stepRequestManager.removeAllMethodResults(); + stepRequestManager.deleteAllPendingSteps(eventRequestManager); + debugSession.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); } return CompletableFuture.completedFuture(response); @@ -115,13 +123,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.getStepRequestManager().removeMethodResult(arguments.threadId); context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; DebugUtility.resumeThread(thread); checkThreadRunningAndRecycleIds(thread, context); } else { - context.getStepResultManager().removeAllMethodResults(); + context.getStepRequestManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); context.getDebugSession().resume(); context.getRecyclableIdPool().removeAllObjects(); @@ -154,7 +162,12 @@ private CompletableFuture resumeOthers(Requests.ThreadOperationArgumen } private CompletableFuture pauseAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { - context.getDebugSession().suspend(); + StepRequestManager stepRequestManager = context.getStepRequestManager(); + IDebugSession debugSession = context.getDebugSession(); + EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); + stepRequestManager.removeAllMethodResults(); + stepRequestManager.deleteAllPendingSteps(eventRequestManager); + debugSession.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); 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 26a0f7124..966c9780a 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 @@ -24,7 +24,6 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -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; @@ -32,8 +31,13 @@ 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.IStackTraceProvider; +import com.microsoft.java.debug.core.adapter.StackTraceProvider; import com.microsoft.java.debug.core.adapter.IStackFrameManager; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedVariable; +import com.microsoft.java.debug.core.adapter.stacktrace.DecodedField; import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; +import com.microsoft.java.debug.core.adapter.variables.IVariableProvider; import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure; import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalStructureExpression; import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable; @@ -54,13 +58,20 @@ import com.sun.jdi.IntegerValue; import com.sun.jdi.InternalException; import com.sun.jdi.InvalidStackFrameException; +import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; import com.sun.jdi.StackFrame; import com.sun.jdi.Type; import com.sun.jdi.Value; +import com.microsoft.java.debug.core.adapter.stacktrace.JavaField; + public class VariablesRequestHandler implements IDebugRequestHandler { - protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + protected final Logger logger; + + public VariablesRequestHandler(Logger logger) { + this.logger = logger; + } @Override public List getTargetCommands() { @@ -75,6 +86,7 @@ public CompletableFuture handle(Command command, Arguments arguments, boolean showStaticVariables = DebugSettings.getCurrent().showStaticVariables; Map options = variableFormatter.getDefaultOptions(); + IVariableProvider variableProvider = context.getProvider(IVariableProvider.class); VariableUtils.applyFormatterOptions(options, varArgs.format != null && varArgs.format.hex); IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class); @@ -96,11 +108,14 @@ public CompletableFuture handle(Command command, Arguments arguments, VariableProxy containerNode = (VariableProxy) container; List childrenList = new ArrayList<>(); IStackFrameManager stackFrameManager = context.getStackFrameManager(); + IStackTraceProvider stackTraceProvider = context.getProvider(IStackTraceProvider.class); String containerEvaluateName = containerNode.getEvaluateName(); boolean isUnboundedTypeContainer = containerNode.isUnboundedType(); + Method method = null; if (containerNode.getProxiedVariable() instanceof StackFrameReference) { StackFrameReference stackFrameReference = (StackFrameReference) containerNode.getProxiedVariable(); StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); + method = frame.location().method(); if (frame == null) { throw AdapterUtils.createCompletionException( String.format("Invalid stackframe id %d to get variables.", varArgs.variablesReference), @@ -108,7 +123,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } try { long threadId = stackFrameReference.getThread().uniqueID(); - JdiMethodResult result = context.getStepResultManager().getMethodResult(threadId); + JdiMethodResult result = context.getStepRequestManager().getMethodResult(threadId); if (result != null) { String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->"; childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null)); @@ -225,6 +240,43 @@ public CompletableFuture handle(Command command, Arguments arguments, for (Variable javaVariable : childrenList) { Value value = javaVariable.value; String name = javaVariable.name; + if (javaVariable.local != null) { + StackFrameReference stackFrameReference = (StackFrameReference) containerNode.getProxiedVariable(); + StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); + try { + DecodedVariable decodedVariable = stackTraceProvider.decode(javaVariable.local, method, frame.location().lineNumber()); + name = decodedVariable.format(); + if (name == "") { + continue; + } + } catch (Exception e) { + String notFound = "ch.epfl.scala.decoder.NotFoundException"; + String ambiguous = "ch.epfl.scala.decoder.AmbiguousException"; + if (e.getMessage().contains(notFound) || e.getMessage().contains(ambiguous)) { + logger.log(Level.INFO, "Failed to decode the variable", e); + } else { + throw e; + } + } + } else + if (javaVariable.field != null) { + try { + DecodedField decodedField = stackTraceProvider.decode(javaVariable.field); + name = decodedField.format(); + JavaField javaField = new JavaField(javaVariable.field); + if (name == "" || !javaField.show()) { + continue; + } + } catch (Exception e) { + String notFound = "ch.epfl.scala.decoder.NotFoundException"; + String ambiguous = "ch.epfl.scala.decoder.AmbiguousException"; + if (e.getMessage().contains(notFound) || e.getMessage().contains(ambiguous)) { + logger.log(Level.INFO, "Failed to decode the variable", e); + } else { + throw e; + } + } + } else if (variableNameMap.containsKey(javaVariable)) { name = variableNameMap.get(javaVariable); } @@ -257,12 +309,12 @@ public CompletableFuture handle(Command command, Arguments arguments, 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); + evaluateName = variableProvider.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()); + evaluateName = variableProvider.getEvaluateName(javaVariable.evaluateName, containerEvaluateName, containerNode.isIndexedVariable()); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedField.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedField.java new file mode 100644 index 000000000..10a290a52 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedField.java @@ -0,0 +1,7 @@ +package com.microsoft.java.debug.core.adapter.stacktrace; + +public interface DecodedField { + String format(); + + boolean show(); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedMethod.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedMethod.java new file mode 100644 index 000000000..0b997b267 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedMethod.java @@ -0,0 +1,7 @@ +package com.microsoft.java.debug.core.adapter.stacktrace; + +public interface DecodedMethod { + String format(); + + boolean isGenerated(); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedVariable.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedVariable.java new file mode 100644 index 000000000..d43bfe430 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/DecodedVariable.java @@ -0,0 +1,5 @@ +package com.microsoft.java.debug.core.adapter.stacktrace; + +public interface DecodedVariable { + String format(); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaField.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaField.java new file mode 100644 index 000000000..fe13b71c6 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaField.java @@ -0,0 +1,25 @@ +package com.microsoft.java.debug.core.adapter.stacktrace; + +import java.util.List; +import java.util.stream.Collectors; + +import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; +import com.sun.jdi.Field; + +public class JavaField implements DecodedField { + private Field field; + + public JavaField(Field field) { + this.field = field; + } + + @Override + public String format() { + return field.name(); + } + + @Override + public boolean show() { + return !field.isStatic() || field.declaringType().name().endsWith("$"); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaLocalVariable.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaLocalVariable.java new file mode 100644 index 000000000..ff75ebc7a --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaLocalVariable.java @@ -0,0 +1,20 @@ +package com.microsoft.java.debug.core.adapter.stacktrace; + +import java.util.List; +import java.util.stream.Collectors; + +import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; +import com.sun.jdi.LocalVariable; + +public class JavaLocalVariable implements DecodedVariable { + private LocalVariable variable; + + public JavaLocalVariable(LocalVariable variable) { + this.variable = variable; + } + + @Override + public String format() { + return variable.name(); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaMethod.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaMethod.java new file mode 100644 index 000000000..f3cde6682 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/stacktrace/JavaMethod.java @@ -0,0 +1,37 @@ +package com.microsoft.java.debug.core.adapter.stacktrace; + +import java.util.List; +import java.util.stream.Collectors; + +import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; +import com.sun.jdi.Method; + +public class JavaMethod implements DecodedMethod { + private Method method; + + public JavaMethod(Method method) { + this.method = method; + } + + @Override + public String format() { + StringBuilder formattedName = new StringBuilder(); + String fullyQualifiedClassName = method.declaringType().name(); + formattedName.append(SimpleTypeFormatter.trimTypeName(fullyQualifiedClassName)); + formattedName.append("."); + List argumentTypeNames = method.argumentTypeNames().stream().map(SimpleTypeFormatter::trimTypeName).collect(Collectors.toList()); + formattedName.append("("); + formattedName.append(String.join(",", argumentTypeNames)); + formattedName.append(")"); + if (method.isNative()) { + // For native method, display a tip text "native method" in the Call Stack View. + formattedName.append("[native method]"); + } + return formattedName.toString(); + } + + @Override + public boolean isGenerated() { + return method.isBridge() || method.isSynthetic(); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/IVariableProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/IVariableProvider.java new file mode 100644 index 000000000..2311ae0a0 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/IVariableProvider.java @@ -0,0 +1,14 @@ +package com.microsoft.java.debug.core.adapter.variables; + +import com.microsoft.java.debug.core.adapter.IProvider; + +public interface IVariableProvider extends IProvider { + /** + * 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 String getEvaluateName(String name, String containerName, boolean isArrayElement); +} \ No newline at end of file diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProvider.java new file mode 100644 index 000000000..76b48d5af --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProvider.java @@ -0,0 +1,8 @@ +package com.microsoft.java.debug.core.adapter.variables; + +public class VariableProvider implements IVariableProvider { + @Override + public String getEvaluateName(String name, String containerName, boolean isArrayElement) { + return VariableUtils.getEvaluateName(name, containerName, isArrayElement); + } +} \ No newline at end of file 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..3d161decc 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 @@ -16,11 +16,8 @@ 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; -import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.adapter.formatter.NumericFormatEnum; import com.microsoft.java.debug.core.adapter.formatter.NumericFormatter; @@ -41,8 +38,6 @@ import com.sun.jdi.Value; public abstract class VariableUtils { - private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - /** * Test whether the value has referenced objects. * @@ -98,7 +93,6 @@ public static List listFieldVariables(ObjectReference obj, boolean inc } return a.name().compareToIgnoreCase(b.name()); } catch (Exception e) { - logger.log(Level.SEVERE, String.format("Cannot sort fields: %s", e), e); return -1; } }).collect(Collectors.toList()); 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..5146faf97 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 @@ -40,7 +40,7 @@ import io.reactivex.subjects.PublishSubject; public abstract class AbstractProtocolServer implements IProtocolServer { - private static final Logger logger = Logger.getLogger("java-debug"); + protected final Logger logger; private static final int BUFFER_SIZE = 4096; private static final String TWO_CRLF = "\r\n\r\n"; private static final Pattern CONTENT_LENGTH_MATCHER = Pattern.compile("Content-Length: (\\d+)"); @@ -67,7 +67,8 @@ public abstract class AbstractProtocolServer implements IProtocolServer { * @param output * the output stream */ - public AbstractProtocolServer(InputStream input, OutputStream output) { + public AbstractProtocolServer(InputStream input, OutputStream output, Logger logger) { + this.logger = logger; this.reader = new BufferedReader(new InputStreamReader(input, PROTOCOL_ENCODING)); this.writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output, PROTOCOL_ENCODING))); this.contentLength = -1; @@ -76,7 +77,7 @@ public AbstractProtocolServer(InputStream input, OutputStream output) { requestSubject.observeOn(Schedulers.newThread()).subscribe(request -> { try { this.dispatchRequest(request); - } catch (Exception e) { + } catch (Throwable e) { logger.log(Level.SEVERE, String.format("Dispatch debug protocol error: %s", e.toString()), e); } }); 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..728ff2955 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,7 @@ public static class StackFrame { public int line; public int column; public String name; + public String presentationHint; /** * Constructs a StackFrame with the given information. @@ -58,12 +59,13 @@ public static class StackFrame { * @param col * column number of the stack frame */ - 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 hint) { this.id = id; this.name = name; this.source = src; this.line = ln; this.column = col; + this.presentationHint = hint; } } diff --git a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/DebugSessionFactory.java b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/DebugSessionFactory.java index 9589c9bdc..96156e8bf 100644 --- a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/DebugSessionFactory.java +++ b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/DebugSessionFactory.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.logging.Logger; import org.apache.commons.io.FileUtils; @@ -27,6 +28,7 @@ import com.sun.jdi.event.VMDisconnectEvent; public class DebugSessionFactory { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String TEST_ROOT = "../com.microsoft.java.debug.test/project"; private static String rootPath = new File(TEST_ROOT).getAbsolutePath(); private static Set readyForLaunchProjects = new HashSet<>(); @@ -52,7 +54,7 @@ public static IDebugSession getDebugSession(String projectName, String mainClass String projectRoot = new File(rootPath, name).getAbsolutePath(); try { final IDebugSession debugSession = DebugUtility.launch(Bootstrap.virtualMachineManager(), mainClass, "", "", - null, new File(projectRoot, "bin").getAbsolutePath(), null, null); + null, new File(projectRoot, "bin").getAbsolutePath(), null, null, logger); debugSession.getEventHub().events().subscribe(debugEvent -> { if (debugEvent.event instanceof VMDisconnectEvent) { try { diff --git a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/CharacterFormatterTest.java b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/CharacterFormatterTest.java index 3c6bd0700..1c0505407 100644 --- a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/CharacterFormatterTest.java +++ b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/CharacterFormatterTest.java @@ -71,9 +71,9 @@ public void testToString() throws Exception { assertEquals("Should be able to format char type.", "?", formatter.toString(charVar, options)); - charVar = getVM().mirrorOf('中'); - assertEquals("Should be able to format char type.", "中", - formatter.toString(charVar, options)); + // charVar = getVM().mirrorOf('中'); + // assertEquals("Should be able to format char type.", "中", + // formatter.toString(charVar, options)); } @Test diff --git a/com.microsoft.java.debug.plugin/.project b/com.microsoft.java.debug.plugin/.project index 72ba17ff7..e24c2da8b 100644 --- a/com.microsoft.java.debug.plugin/.project +++ b/com.microsoft.java.debug.plugin/.project @@ -39,12 +39,12 @@ - 1599036548577 + 1667383449005 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 ae117b544..83ac9c400 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -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.34.0+9.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index d7cd7c6a4..d672ac5ee 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -43,9 +43,9 @@ 2.10.0 - com.microsoft.java - com.microsoft.java.debug.core - 0.34.0 + ch.epfl.scala + com-microsoft-java-debug-core + 0.34.0+9 diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtProviderContextFactory.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtProviderContextFactory.java index 7213f8e9a..92e4eb986 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtProviderContextFactory.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtProviderContextFactory.java @@ -16,8 +16,10 @@ import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider; import com.microsoft.java.debug.core.adapter.IProviderContext; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.adapter.IStepFilterProvider; import com.microsoft.java.debug.core.adapter.IVirtualMachineManagerProvider; import com.microsoft.java.debug.core.adapter.ProviderContext; +import com.microsoft.java.debug.core.adapter.StepFilterProvider; import com.microsoft.java.debug.plugin.internal.eval.JdtEvaluationProvider; /** @@ -35,6 +37,8 @@ public static IProviderContext createProviderContext() { context.registerProvider(IHotCodeReplaceProvider.class, new JavaHotCodeReplaceProvider()); context.registerProvider(IEvaluationProvider.class, new JdtEvaluationProvider()); context.registerProvider(ICompletionsProvider.class, new CompletionsProvider()); + context.registerProvider(IStepFilterProvider.class, new StepFilterProvider()); + context.registerProvider(IVariableProvider.class, new VariableProvider()); return context; } diff --git a/com.microsoft.java.debug.repository/.project b/com.microsoft.java.debug.repository/.project index 887e4a7a8..88af6b38a 100644 --- a/com.microsoft.java.debug.repository/.project +++ b/com.microsoft.java.debug.repository/.project @@ -16,12 +16,12 @@ - 1600224298119 + 1667383449009 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/pom.xml b/pom.xml index a0f7c9b9d..e2b2c965b 100644 --- a/pom.xml +++ b/pom.xml @@ -168,7 +168,7 @@ JDT.LS p2 - https://download.eclipse.org/jdtls/snapshots/repository/latest/ + https://download.eclipse.org/jdtls/snapshots/repository/1.5.0.202110111021/ JBOLL.TOOLS