From 66feca936e8dd0bfbb7f6af3c3f4c4ce9833a95e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 1 Feb 2021 13:19:52 +0800 Subject: [PATCH 001/144] Bump version to 0.30.0 (#361) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 24ec1b741..e29d730d1 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.29.0 + 0.30.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3291a02bb..08383fa97 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 905946192..44401979f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.29.0 +Bundle-Version: 0.30.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.29.0.jar + lib/com.microsoft.java.debug.core-0.30.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 28901c18d..0150d3470 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.29.0 + 0.30.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.29.0 + 0.30.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index e873f4578..c437e34f3 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index ce56bf3a5..09cc52182 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.29.0 + 0.30.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 994191f7b..64805772b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.29.0 + 0.30.0 pom Java Debug Server for Visual Studio Code From 64d42435b9913ad39f49f8700e0c046abdc12dd5 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Tue, 2 Mar 2021 03:58:11 -0800 Subject: [PATCH 002/144] Enable GitHub Actions (#363) Signed-off-by: Sheng Chen --- .github/workflows/build.yml | 91 +++++++++++++++++++++++++++++++++++++ .travis.yml | 13 ------ 2 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..afa1e2185 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,91 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + linux: + name: Linux + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw clean verify + + - name: Checkstyle + run: ./mvnw checkstyle:check + + windows: + name: Windows + runs-on: windows-latest + timeout-minutes: 30 + steps: + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: $HOME/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw.cmd clean verify + + - name: Checkstyle + run: ./mvnw.cmd checkstyle:check + + darwin: + name: macOS + runs-on: macos-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw clean verify + + - name: Checkstyle + run: ./mvnw checkstyle:check diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f331372be..000000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: java - -os: - - linux - - osx - -script: - - ./mvnw clean verify - - ./mvnw checkstyle:check - -cache: - directories: - - $HOME/.m2 From e55f86413b05446f1cb99c30ce2b04c4363e9525 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 23 Mar 2021 12:20:05 +0800 Subject: [PATCH 003/144] Remove the usage of JDK internal API to avoid breaking JDK 16 (#364) --- com.microsoft.java.debug.core/pom.xml | 2 +- .../core/adapter/handler/LaunchUtils.java | 19 ++++++------------- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- .../category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 15 insertions(+), 22 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index e29d730d1..7c20a8aae 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.0 + 0.30.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index be7f95126..17a4f7359 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -16,15 +16,14 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.file.Files; -import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; @@ -33,8 +32,6 @@ import org.apache.commons.lang3.ArrayUtils; -import sun.security.action.GetPropertyAction; - public class LaunchUtils { private static Set tempFilesInUse = new HashSet<>(); @@ -109,16 +106,12 @@ public static void releaseTempLaunchFile(Path tempFile) { private static synchronized Path getTmpDir() throws IOException { if (tmpdir == null) { + Path tmpfile = Files.createTempFile("", UUID.randomUUID().toString()); + tmpdir = tmpfile.getParent(); try { - tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); - } catch (NullPointerException | InvalidPathException e) { - Path tmpfile = Files.createTempFile("", ".tmp"); - tmpdir = tmpfile.getParent(); - try { - Files.deleteIfExists(tmpfile); - } catch (Exception ex) { - // do nothing - } + Files.deleteIfExists(tmpfile); + } catch (Exception ex) { + // do nothing } } diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 08383fa97..e9e2f6c74 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 44401979f..95fc9aeeb 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.30.0 +Bundle-Version: 0.30.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -24,4 +24,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.30.0.jar + lib/com.microsoft.java.debug.core-0.30.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 0150d3470..2a1985e15 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.0 + 0.30.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.30.0 + 0.30.1 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index c437e34f3..d351c135e 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 09cc52182..09bd08f1b 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.30.0 + 0.30.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 64805772b..fe8385811 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.30.0 + 0.30.1 pom Java Debug Server for Visual Studio Code From e521ed1d53faee58fe3a868a3de1fbc21f58b5a0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Apr 2021 12:35:44 +0800 Subject: [PATCH 004/144] Leverage AST and Java Runtime to resolve inline values (#368) * Leverage AST and Java Runtime to resolve inline values --- .../java/debug/core/adapter/DebugAdapter.java | 2 + .../handler/InlineValuesRequestHandler.java | 250 +++++++ .../java/debug/core/protocol/Requests.java | 29 + .../java/debug/core/protocol/Responses.java | 9 + .../java/debug/core/protocol/Types.java | 8 + .../META-INF/MANIFEST.MF | 1 + com.microsoft.java.debug.plugin/plugin.xml | 1 + .../plugin/internal/InlineValueHandler.java | 644 ++++++++++++++++++ .../JavaDebugDelegateCommandHandler.java | 4 + 9 files changed, 948 insertions(+) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 798200e97..db2b28adf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -30,6 +30,7 @@ import com.microsoft.java.debug.core.adapter.handler.ExceptionInfoRequestHandler; import com.microsoft.java.debug.core.adapter.handler.HotCodeReplaceHandler; import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; @@ -121,6 +122,7 @@ private void initialize() { registerHandlerForDebug(new ExceptionInfoRequestHandler()); registerHandlerForDebug(new DataBreakpointInfoRequestHandler()); registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); + registerHandlerForDebug(new InlineValuesRequestHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java new file mode 100644 index 000000000..e9697f899 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java @@ -0,0 +1,250 @@ +/******************************************************************************* +* Copyright (c) 2021 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.adapter.IStackFrameManager; +import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; +import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure; +import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager; +import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.adapter.variables.Variable; +import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Types; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.InlineVariable; +import com.microsoft.java.debug.core.protocol.Requests.InlineValuesArguments; +import com.sun.jdi.ArrayReference; +import com.sun.jdi.Field; +import com.sun.jdi.IntegerValue; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.StackFrame; +import com.sun.jdi.Value; + +import org.apache.commons.lang3.math.NumberUtils; + +public class InlineValuesRequestHandler implements IDebugRequestHandler { + protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.INLINEVALUES); + } + + /** + * This request only resolves the values for those non-local variables, such as + * field variables and captured variables from outer scope. Because the values + * of local variables in current stackframe are usually expanded by Variables View + * by default, inline values can reuse these values directly. However, for field + * variables and variables captured from external scopes, they are hidden as properties + * of 'this' variable and require additional evaluation to get their values. + */ + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + InlineValuesArguments inlineValuesArgs = (InlineValuesArguments) arguments; + int variableCount = inlineValuesArgs == null || inlineValuesArgs.variables == null ? 0 : inlineValuesArgs.variables.length; + InlineVariable[] inlineVariables = inlineValuesArgs.variables; + StackFrameReference stackFrameReference = (StackFrameReference) context.getRecyclableIdPool().getObjectById(inlineValuesArgs.frameId); + if (stackFrameReference == null) { + logger.log(Level.SEVERE, String.format("InlineValues failed: invalid stackframe id %d.", inlineValuesArgs.frameId)); + response.body = new Responses.InlineValuesResponse(null); + return CompletableFuture.completedFuture(response); + } + + IStackFrameManager stackFrameManager = context.getStackFrameManager(); + StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); + if (frame == null) { + logger.log(Level.SEVERE, String.format("InlineValues failed: stale stackframe id %d.", inlineValuesArgs.frameId)); + response.body = new Responses.InlineValuesResponse(null); + return CompletableFuture.completedFuture(response); + } + + Variable[] values = new Variable[variableCount]; + try { + if (isLambdaFrame(frame)) { + // Lambda expression stores the captured variables from 'outer' scope in a synthetic stackframe below the lambda frame. + StackFrame syntheticLambdaFrame = stackFrameReference.getThread().frame(stackFrameReference.getDepth() + 1); + resolveValuesFromThisVariable(syntheticLambdaFrame.thisObject(), inlineVariables, values, true); + } + + resolveValuesFromThisVariable(frame.thisObject(), inlineVariables, values, false); + } catch (Exception ex) { + // do nothig + } + + Types.Variable[] result = new Types.Variable[variableCount]; + IVariableFormatter variableFormatter = context.getVariableFormatter(); + Map formatterOptions = variableFormatter.getDefaultOptions(); + Map calculatedValues = new HashMap<>(); + IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class); + for (int i = 0; i < variableCount; i++) { + if (values[i] == null) { + continue; + } + + if (calculatedValues.containsKey(inlineVariables[i])) { + result[i] = calculatedValues.get(inlineVariables[i]); + continue; + } + + Value value = values[i].value; + String name = values[i].name; + int indexedVariables = -1; + Value sizeValue = null; + if (value instanceof ArrayReference) { + indexedVariables = ((ArrayReference) value).length(); + } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { + try { + JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); + if (structure != null && structure.getSizeExpression() != null) { + sizeValue = structure.getSize((ObjectReference) value, frame.thread(), evaluationEngine); + if (sizeValue != null && sizeValue instanceof IntegerValue) { + indexedVariables = ((IntegerValue) sizeValue).value(); + } + } + } catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) { + logger.log(Level.INFO, + String.format("Failed to get the logical size for the type %s.", value.type().name()), e); + } + } + + Types.Variable formattedVariable = new Types.Variable(name, variableFormatter.valueToString(value, formatterOptions)); + formattedVariable.indexedVariables = Math.max(indexedVariables, 0); + String detailsValue = null; + if (sizeValue != null) { + detailsValue = "size=" + variableFormatter.valueToString(sizeValue, formatterOptions); + } else if (DebugSettings.getCurrent().showToString) { + detailsValue = VariableDetailUtils.formatDetailsValue(value, frame.thread(), variableFormatter, formatterOptions, evaluationEngine); + } + + if (detailsValue != null) { + formattedVariable.value = formattedVariable.value + " " + detailsValue; + } + + result[i] = formattedVariable; + calculatedValues.put(inlineVariables[i], formattedVariable); + } + + response.body = new Responses.InlineValuesResponse(result); + return CompletableFuture.completedFuture(response); + } + + private static boolean isCapturedLocalVariable(String fieldName, String variableName) { + String capturedVariableName = "val$" + variableName; + return Objects.equals(fieldName, capturedVariableName) + || (fieldName.startsWith(capturedVariableName + "$") && NumberUtils.isDigits(fieldName.substring(capturedVariableName.length() + 1))); + } + + private static boolean isCapturedThisVariable(String fieldName) { + if (fieldName.startsWith("this$")) { + String suffix = fieldName.substring(5).replaceAll("\\$+$", ""); + return NumberUtils.isDigits(suffix); + } + + return false; + } + + private static boolean isLambdaFrame(StackFrame frame) { + Method method = frame.location().method(); + return method.isSynthetic() && method.name().startsWith("lambda$"); + } + + private void resolveValuesFromThisVariable(ObjectReference thisObj, InlineVariable[] unresolvedVariables, Variable[] result, + boolean isSyntheticLambdaFrame) { + if (thisObj == null) { + return; + } + + int unresolved = 0; + for (Variable item : result) { + if (item == null) { + unresolved++; + } + } + + try { + ReferenceType type = thisObj.referenceType(); + String typeName = type.name(); + ObjectReference enclosingInstance = null; + for (Field field : type.allFields()) { + String fieldName = field.name(); + boolean isSyntheticField = field.isSynthetic(); + Value fieldValue = null; + for (int i = 0; i < unresolvedVariables.length; i++) { + if (result[i] != null) { + continue; + } + + InlineVariable inlineVariable = unresolvedVariables[i]; + boolean isInlineFieldVariable = (inlineVariable.declaringClass != null); + boolean isMatch = false; + if (isSyntheticLambdaFrame) { + isMatch = !isInlineFieldVariable && Objects.equals(fieldName, inlineVariable.expression); + } else { + boolean isMatchedField = isInlineFieldVariable + && Objects.equals(fieldName, inlineVariable.expression) + && Objects.equals(typeName, inlineVariable.declaringClass); + boolean isMatchedCapturedVariable = !isInlineFieldVariable + && isSyntheticField + && isCapturedLocalVariable(fieldName, inlineVariable.expression); + isMatch = isMatchedField || isMatchedCapturedVariable; + + if (!isMatch && isSyntheticField && enclosingInstance == null && isCapturedThisVariable(fieldName)) { + Value value = thisObj.getValue(field); + if (value instanceof ObjectReference) { + enclosingInstance = (ObjectReference) value; + break; + } + } + } + + if (isMatch) { + fieldValue = fieldValue == null ? thisObj.getValue(field) : fieldValue; + result[i] = new Variable(inlineVariable.expression, fieldValue); + unresolved--; + } + } + + if (unresolved <= 0) { + break; + } + } + + if (unresolved > 0 && enclosingInstance != null) { + resolveValuesFromThisVariable(enclosingInstance, unresolvedVariables, result, isSyntheticLambdaFrame); + } + } catch (Exception ex) { + // do nothing + } + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index f62a241a1..2afb02bd8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.Map; +import java.util.Objects; import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.Types.DataBreakpoint; @@ -340,6 +341,33 @@ public static class SetDataBreakpointsArguments extends Arguments { public DataBreakpoint[] breakpoints; } + public static class InlineValuesArguments extends Arguments { + public int frameId; + public InlineVariable[] variables; + } + + public static class InlineVariable { + public String expression; + public String declaringClass; + + @Override + public int hashCode() { + return Objects.hash(declaringClass, expression); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof InlineVariable)) { + return false; + } + InlineVariable other = (InlineVariable) obj; + return Objects.equals(declaringClass, other.declaringClass) && Objects.equals(expression, other.expression); + } + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -372,6 +400,7 @@ public static enum Command { CONTINUEOTHERS("continueOthers", ThreadOperationArguments.class), PAUSEALL("pauseAll", ThreadOperationArguments.class), PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), + INLINEVALUES("inlineValues", InlineValuesArguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index 1b4bc7b4a..cc349b6e7 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -16,6 +16,7 @@ import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; +import com.microsoft.java.debug.core.protocol.Types.Variable; /** * The response content types defined by VSCode Debug Protocol. @@ -316,4 +317,12 @@ public RedefineClassesResponse(String[] changedClasses, String errorMessage) { this.errorMessage = errorMessage; } } + + public static class InlineValuesResponse extends ResponseBody { + public Types.Variable[] variables; + + public InlineValuesResponse(Variable[] variables) { + this.variables = variables; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 3ce41869c..0b9a0494a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -101,6 +101,14 @@ public Variable(String name, String val, String type, int rf, String evaluateNam this.variablesReference = rf; this.evaluateName = evaluateName; } + + /** + * Constructor. + */ + public Variable(String name, String value) { + this.name = name; + this.value = value; + } } public static class Thread { diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 95fc9aeeb..40f78351f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.debug.core, org.eclipse.jdt.debug, org.eclipse.jdt.core, + org.eclipse.jdt.core.manipulation, org.eclipse.jdt.ls.core, org.eclipse.jdt.launching, com.google.gson, diff --git a/com.microsoft.java.debug.plugin/plugin.xml b/com.microsoft.java.debug.plugin/plugin.xml index 361cdcbda..69d76830d 100644 --- a/com.microsoft.java.debug.plugin/plugin.xml +++ b/com.microsoft.java.debug.plugin/plugin.xml @@ -20,6 +20,7 @@ + diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java new file mode 100644 index 000000000..5c2cf8834 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java @@ -0,0 +1,644 @@ +/******************************************************************************* +* Copyright (c) 2021 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.plugin.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.ISourceRange; +import org.eclipse.jdt.core.ISourceReference; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; +import org.eclipse.jdt.core.dom.Block; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.DoStatement; +import org.eclipse.jdt.core.dom.ForStatement; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.SwitchStatement; +import org.eclipse.jdt.core.dom.TypeDeclarationStatement; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.IfStatement; +import org.eclipse.jdt.core.dom.ImportDeclaration; +import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.ModuleDeclaration; +import org.eclipse.jdt.core.dom.PackageDeclaration; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.QualifiedType; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.handlers.JsonRpcHelpers; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; + +public class InlineValueHandler { + + /** + * Find the valid inline variables belonging to the visible view port. + */ + public static InlineVariable[] resolveInlineVariables(InlineParams params, IProgressMonitor monitor) { + ITypeRoot root = JDTUtils.resolveTypeRoot(params.uri); + try { + if (root == null || root.getBuffer() == null) { + return new InlineVariable[0]; + } + + Position stoppedLocation = params.stoppedLocation.getStart(); + int stoppedOffset = JsonRpcHelpers.toOffset(root.getBuffer(), stoppedLocation.getLine(), stoppedLocation.getCharacter()); + IMethod enclosingMethod = findEnclosingMethod(root, stoppedOffset); + if (enclosingMethod == null) { + return new InlineVariable[0]; + } + + Position startLocation = getPosition(root.getBuffer(), enclosingMethod.getSourceRange().getOffset()); + Range stoppedRange = new Range(startLocation, stoppedLocation); + if (params.viewPort != null + && (params.viewPort.getEnd().getLine() < startLocation.getLine() || params.viewPort.getStart().getLine() > stoppedLocation.getLine())) { + return new InlineVariable[0]; + } + + CompilationUnit astRoot = CoreASTProvider.getInstance().getAST(root, CoreASTProvider.WAIT_YES, monitor); + VariableVisitor visitor = new VariableVisitor(astRoot, stoppedRange, params.viewPort, Flags.isStatic(enclosingMethod.getFlags())); + astRoot.accept(visitor); + InlineVariable[] result = visitor.getInlineVariables(); + return result; + } catch (JavaModelException e) { + return new InlineVariable[0]; + } + } + + private static IMethod findEnclosingMethod(ITypeRoot root, int stoppedOffset) throws JavaModelException { + IType enclosingType = null; + if (root instanceof ICompilationUnit) { + IType[] types = ((ICompilationUnit) root).getAllTypes(); + for (IType type : types) { + if (isEnclosed(type, stoppedOffset)) { + enclosingType = type; + } + } + } else if (root instanceof IClassFile) { + enclosingType = ((IClassFile) root).getType(); + } + + if (enclosingType == null) { + return null; + } + + IMethod enclosingMethod = null; + for (IMethod method : enclosingType.getMethods()) { + if (isEnclosed(method, stoppedOffset)) { + enclosingMethod = method; + break; + } + } + + if (enclosingMethod == null) { + return null; + } + + // Deal with the scenario that the stopped location is inside the local types defined in method. + return findMethodInLocalTypes(enclosingMethod, stoppedOffset); + } + + private static boolean isEnclosed(ISourceReference sourceReference, int offset) throws JavaModelException { + ISourceRange sourceRange = sourceReference.getSourceRange(); + return sourceRange != null && offset >= sourceRange.getOffset() + && offset < sourceRange.getOffset() + sourceRange.getLength(); + } + + private static IMethod findMethodInLocalTypes(IMethod enclosingMethod, int stoppedOffset) throws JavaModelException { + if (enclosingMethod == null) { + return null; + } + + for (IJavaElement element : enclosingMethod.getChildren()) { + if (element instanceof IType) { + if (isEnclosed((IType) element, stoppedOffset)) { + for (IMethod method : ((IType) element).getMethods()) { + if (isEnclosed(method, stoppedOffset)) { + IMethod nearerMethod = findMethodInLocalTypes(method, stoppedOffset); + return nearerMethod == null ? enclosingMethod : nearerMethod; + } + } + + break; + } + } + } + + return enclosingMethod; + } + + /** + * Returns the zero based line and column number. + */ + private static Position getPosition(IBuffer buffer, int offset) { + int[] result = JsonRpcHelpers.toLine(buffer, offset); + if (result == null && result.length < 1) { + return new Position(-1, -1); + } + + return new Position(result[0], result[1]); + } + + static class VariableVisitor extends ASTVisitor { + private CompilationUnit unit = null; + private Range stoppedSourceRange; + private Range viewPort; + private boolean isStoppingAtStaticMethod; + private int baseLine; + private Set[] tokens; + private List localVarDecls = new ArrayList<>(); + private List localVarDeclPositions = new ArrayList<>(); + private boolean isStoppingAtLambda = false; + private Set varDeclsAtLastLine = new HashSet<>(); + private Range visibleInlineRange = null; + + public VariableVisitor(CompilationUnit unit, Range stoppedSourceRange, Range viewPort, boolean stopAtStaticMethod) { + this.unit = unit; + this.stoppedSourceRange = stoppedSourceRange; + this.viewPort = viewPort; + this.isStoppingAtStaticMethod = stopAtStaticMethod; + this.baseLine = stoppedSourceRange.getStart().getLine(); + this.tokens = new Set[stoppedSourceRange.getEnd().getLine() - stoppedSourceRange.getStart().getLine() + 1]; + updateVisibleRange(); + } + + private void updateVisibleRange() { + if (viewPort == null) { + visibleInlineRange = stoppedSourceRange; + } else if (compare(viewPort.getStart(), stoppedSourceRange.getEnd()) > 0 + || compare(viewPort.getEnd(), stoppedSourceRange.getStart()) < 0) { + visibleInlineRange = null; + } else { + Position start = compare(viewPort.getStart(), stoppedSourceRange.getStart()) >= 0 ? viewPort.getStart() : stoppedSourceRange.getStart(); + Position end = compare(viewPort.getEnd(), stoppedSourceRange.getEnd()) <= 0 ? viewPort.getEnd() : stoppedSourceRange.getEnd(); + visibleInlineRange = new Range(start, end); + } + } + + /** + * Handle the variables in the visible source ranges. + */ + @Override + public boolean visit(SimpleName node) { + if (visibleInlineRange == null) { + return false; + } + + Position startPosition = getStartPosition(node); + boolean isAtLastLine = isAtStopLocation(startPosition); + if (isEnclosed(visibleInlineRange, startPosition) || isAtLastLine) { + IBinding binding = node.resolveBinding(); + if (!(binding instanceof IVariableBinding)) { + return false; + } else if (isAtLastLine && this.varDeclsAtLastLine.contains(binding.getKey())) { + return false; + } + + String declaringClass = null; + if (((IVariableBinding) binding).isField()) { + ITypeBinding typeBinding = ((IVariableBinding) binding).getDeclaringClass(); + if (typeBinding == null) { + return false; + } + + declaringClass = typeBinding.getBinaryName(); + } + + Token token = new Token(node.getIdentifier(), startPosition, declaringClass); + int index = startPosition.getLine() - baseLine; + if (tokens[index] == null) { + tokens[index] = new LinkedHashSet<>(); + } + + if (!tokens[index].contains(token)) { + tokens[index].add(token); + } + } + + return false; + } + + /** + * Handle local variable declarations happening in current method. + */ + @Override + public boolean visit(VariableDeclarationFragment node) { + SimpleName name = node.getName(); + Position startPosition = getStartPosition(name); + if (isEnclosed(stoppedSourceRange, startPosition)) { + this.localVarDecls.add(name.getIdentifier()); + this.localVarDeclPositions.add(startPosition); + } + + if (isAtStopLocation(startPosition)) { + IVariableBinding binding = node.resolveBinding(); + if (binding != null) { + this.varDeclsAtLastLine.add(binding.getKey()); + } + } + + return true; + } + + /** + * Handle formal parameter declarations happening in current method. + */ + @Override + public boolean visit(SingleVariableDeclaration node) { + SimpleName name = node.getName(); + Position startPosition = getStartPosition(name); + if (isEnclosed(stoppedSourceRange, startPosition)) { + this.localVarDecls.add(name.getIdentifier()); + this.localVarDeclPositions.add(startPosition); + } + + return false; + } + + /** + * Handle the lambda expression containing the stopped location. + * If the execution instruction stops on a lambda expression, then + * crop the visible source ranges to the lambda expression body. + */ + @Override + public boolean visit(LambdaExpression node) { + Position startPosition = getStartPosition(node); + Position endPosition = getEndPosition(node); + if (compare(startPosition, stoppedSourceRange.getStart()) >= 0 + && isEnclosed(new Range(startPosition, endPosition), stoppedSourceRange.getEnd())) { + stoppedSourceRange.setStart(startPosition); + updateVisibleRange(); + isStoppingAtLambda = true; + localVarDecls.clear(); + localVarDeclPositions.clear(); + return true; + } + + return super.visit(node); + } + + /** + * Handle the method containing the stopped location. + */ + @Override + public boolean visit(MethodDeclaration node) { + Position startPosition = getStartPosition(node); + Position endPosition = getEndPosition(node); + if (compare(startPosition, stoppedSourceRange.getStart()) <= 0 && compare(endPosition, stoppedSourceRange.getEnd()) >= 0) { + return true; + } + + return false; + } + + @Override + public boolean visit(Block node) { + if (isUnreachableNode(node)) { + return false; + } + + return true; + } + + @Override + public boolean visit(DoStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(ForStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(IfStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(SwitchStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(WhileStatement node) { + if (isUnreachableNode(node) && !isAtStopLocation(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + if (isUnreachableNode(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + if (isUnreachableNode(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(TypeDeclarationStatement node) { + if (isUnreachableNode(node)) { + return false; + } + + return super.visit(node); + } + + @Override + public boolean visit(ImportDeclaration node) { + return false; + } + + @Override + public boolean visit(ModuleDeclaration node) { + return false; + } + + @Override + public boolean visit(PackageDeclaration node) { + return false; + } + + @Override + public boolean visit(QualifiedName node) { + return Objects.equals("length", node.getName().getIdentifier()); + } + + @Override + public boolean visit(QualifiedType node) { + return false; + } + + /** + * Return the valid inline variables in the visible source ranges. + * + *

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

+ * + *

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

+ * + *

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

+ */ + public InlineVariable[] getInlineVariables() { + if (visibleInlineRange == null) { + return new InlineVariable[0]; + } + + // Adding the local variable declarations to the token list. + for (int i = 0; i < localVarDecls.size(); i++) { + String name = localVarDecls.get(i); + Position position = localVarDeclPositions.get(i); + if (isEnclosed(visibleInlineRange, position)) { + int index = position.getLine() - baseLine; + if (tokens[index] == null) { + tokens[index] = new LinkedHashSet<>(); + } + + Token token = new Token(name, position, null); + if (!tokens[index].contains(token)) { + tokens[index].add(token); + } + } + } + + // For lambda expression in non static method, the captured variable 'arg$1' + // points to 'this' object of the enclosing method, and the index of other + // captured variables starts with 2. + int capturedArgIndexInLambda = isStoppingAtStaticMethod ? 1 : 2; + Map capturedVarsInLambda = new HashMap<>(); + List result = new ArrayList<>(); + for (int i = 0; i < tokens.length; i++) { + int line = baseLine + i; + if (tokens[i] == null || line < visibleInlineRange.getStart().getLine()) { + continue; + } + + for (Token token : tokens[i]) { + if (!isEnclosed(visibleInlineRange, token.position) && !isAtLastVisibleLine(token.position)) { + continue; + } + + // Local Variables + if (token.declaringClass == null && localVarDecls.contains(token.name)) { + int declIndex = localVarDecls.lastIndexOf(token.name); + Position declPosition = localVarDeclPositions.get(declIndex); + if (compare(token.position, declPosition) >= 0) { + result.add(new InlineVariable(new Range(token.position, token.position), token.name, InlineKind.VariableLookup)); + continue; + } + } + + InlineVariable value = new InlineVariable( + new Range(token.position, token.position), token.name, InlineKind.Evaluation, token.declaringClass); + // Captured variables by lambda expression + if (isStoppingAtLambda && token.declaringClass == null) { + /** + * When the lambda body accesses variables from its "outer" scope such as + * its enclosing method, these variables will be captured as properties of + * 'this' object of a synthetic lambda instance by Java runtime. However, + * when the compiler parses the lambda expression, it erases the specific + * variable name but keeps the captured variable names with format like + * 'arg$'. In order to evaluate the correct value from Java runtime, + * we have to encode the variable name using the same rule 'arg$' as + * the compiler. + */ + if (capturedVarsInLambda.containsKey(token.name)) { + value.expression = capturedVarsInLambda.get(token.name); + } else { + value.expression = "arg$" + capturedArgIndexInLambda++; + capturedVarsInLambda.put(token.name, value.expression); + } + } + + result.add(value); + } + } + + return result.toArray(new InlineVariable[0]); + } + + private Position getStartPosition(ASTNode node) { + // Line number returned by AST unit is one based, converts it to zero based. + int lineNumber = unit.getLineNumber(node.getStartPosition()) - 1; + int columnNumber = unit.getColumnNumber(node.getStartPosition()); + return new Position(lineNumber, columnNumber); + } + + private Position getEndPosition(ASTNode node) { + // Line number returned by AST unit is one based, converts it to zero based. + int lineNumber = unit.getLineNumber(node.getStartPosition() + node.getLength() - 1) - 1; + int columnNumber = unit.getColumnNumber(node.getStartPosition() + node.getLength() - 1); + return new Position(lineNumber, columnNumber); + } + + private boolean isUnreachableNode(ASTNode node) { + Position startPosition = getStartPosition(node); + Position endPosition = getEndPosition(node); + return compare(startPosition, stoppedSourceRange.getEnd()) > 0 + || compare(endPosition, stoppedSourceRange.getEnd()) < 0; + } + + private boolean isEnclosed(Range range, Position position) { + return compare(range.getStart(), position) <= 0 && compare(range.getEnd(), position) >= 0; + } + + private int compare(Position p1, Position p2) { + if (p1.getLine() < p2.getLine()) { + return -1; + } else if (p1.getLine() == p2.getLine()) { + return p1.getCharacter() - p2.getCharacter(); + } + + return 1; + } + + private boolean isAtStopLocation(Position position) { + return position.getLine() == stoppedSourceRange.getEnd().getLine(); + } + + private boolean isAtStopLocation(ASTNode node) { + Position startPosition = getStartPosition(node); + return isAtStopLocation(startPosition); + } + + private boolean isAtLastVisibleLine(Position position) { + return visibleInlineRange != null && visibleInlineRange.getEnd().getLine() == position.getLine(); + } + } + + static class InlineVariable { + Range range; + String name; + InlineKind kind; + String expression; + String declaringClass; + + public InlineVariable(Range range, String name, InlineKind kind) { + this.range = range; + this.name = name; + this.kind = kind; + } + + public InlineVariable(Range range, String name, InlineKind kind, String declaringClass) { + this.range = range; + this.name = name; + this.kind = kind; + this.declaringClass = declaringClass; + } + } + + static enum InlineKind { + VariableLookup, + Evaluation + } + + static class InlineParams { + String uri; + Range viewPort; + Range stoppedLocation; + } + + static class Token { + String name; + Position position; + String declaringClass = null; + + public Token(String name, Position position) { + this.name = name; + this.position = position; + } + + public Token(String name, Position position, String declaringClass) { + this.name = name; + this.position = position; + this.declaringClass = declaringClass; + } + + @Override + public int hashCode() { + return Objects.hash(declaringClass, name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Token)) { + return false; + } + Token other = (Token) obj; + return Objects.equals(declaringClass, other.declaringClass) && Objects.equals(name, other.name); + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 032d0420c..1875d514e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -28,6 +28,7 @@ import com.microsoft.java.debug.core.UsageDataStore; import com.microsoft.java.debug.core.protocol.JsonUtils; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; +import com.microsoft.java.debug.plugin.internal.InlineValueHandler.InlineParams; public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler { public static final String FETCH_USER_DATA = "vscode.java.fetchUsageData"; @@ -47,6 +48,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler public static final String FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; public static final String RESOLVE_CLASSFILTERS = "vscode.java.resolveClassFilters"; public static final String RESOLVE_SOURCEURI = "vscode.java.resolveSourceUri"; + public static final String RESOLVE_INLINEVARIABLES = "vscode.java.resolveInlineVariables"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { @@ -90,6 +92,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return JavaClassFilter.resolveClassFilters(arguments); case RESOLVE_SOURCEURI: return ResolveSourceMappingHandler.resolveSourceUri(arguments); + case RESOLVE_INLINEVARIABLES: + return InlineValueHandler.resolveInlineVariables(JsonUtils.fromJson((String) arguments.get(0), InlineParams.class), progress); default: break; } From f9aa9649b8c4a33b418a662b4516b2a14c977473 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Apr 2021 17:26:12 +0800 Subject: [PATCH 005/144] Fix bug#973: breakpoints inside record don't work (#370) --- .../internal/JdtSourceLookUpProvider.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 65ba6074a..d24d2abdd 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -61,6 +61,17 @@ public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private ISourceContainer[] sourceContainers = null; private HashMap options = new HashMap(); + private String latestJavaVersion = null; + private int latestASTLevel; + + public JdtSourceLookUpProvider() { + // Get the latest supported Java version by JDT tooling. + this.latestJavaVersion = JavaCore.latestSupportedJavaVersion(); + // Get the mapped AST level for the latest Java version. + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, latestJavaVersion); + this.latestASTLevel = new AST(javaOptions).apiLevel(); + } @Override public void initialize(IDebugAdapterContext context, Map props) { @@ -101,8 +112,7 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return new String[0]; } - // Currently the highest version the debugger supports is JavaSE-13 Edition (JLS13). - final ASTParser parser = ASTParser.newParser(AST.JLS13); + final ASTParser parser = ASTParser.newParser(this.latestASTLevel); parser.setResolveBindings(true); parser.setBindingsRecovery(true); parser.setStatementsRecovery(true); @@ -131,9 +141,10 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th * the user need specify the compiler options explicitly. */ Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_13); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_13); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_13); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); parser.setCompilerOptions(javaOptions); astUnit = (CompilationUnit) parser.createAST(null); } else { From 6fc0528cff69403e470367973dbd93f9929d695e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 22 Apr 2021 15:07:14 +0800 Subject: [PATCH 006/144] Refresh variables with new variable formatters (#369) --- .vscode/settings.json | 1 - .../java/debug/core/adapter/DebugAdapter.java | 2 + .../handler/RefreshVariablesHandler.java | 50 +++++++++++++++++++ .../java/debug/core/protocol/Events.java | 39 +++++++++++++++ .../java/debug/core/protocol/Requests.java | 9 ++++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java diff --git a/.vscode/settings.json b/.vscode/settings.json index fbaf055a0..2c67a2d4b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "java.configuration.updateBuildConfiguration": "automatic", "files.exclude": { "**/.git": true, "**/*.class": true, diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index db2b28adf..4342c48d3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -32,6 +32,7 @@ import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler; import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetBreakpointsRequestHandler; @@ -123,6 +124,7 @@ private void initialize() { registerHandlerForDebug(new DataBreakpointInfoRequestHandler()); registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); registerHandlerForDebug(new InlineValuesRequestHandler()); + registerHandlerForDebug(new RefreshVariablesHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java new file mode 100644 index 000000000..01214b615 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshVariablesHandler.java @@ -0,0 +1,50 @@ +/******************************************************************************* +* Copyright (c) 2021 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Events.InvalidatedAreas; +import com.microsoft.java.debug.core.protocol.Events.InvalidatedEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.RefreshVariablesArguments; + +public class RefreshVariablesHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.REFRESHVARIABLES); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + RefreshVariablesArguments refreshArgs = (RefreshVariablesArguments) arguments; + if (refreshArgs != null) { + DebugSettings.getCurrent().showHex = refreshArgs.showHex; + DebugSettings.getCurrent().showQualifiedNames = refreshArgs.showQualifiedNames; + DebugSettings.getCurrent().showStaticVariables = refreshArgs.showStaticVariables; + DebugSettings.getCurrent().showLogicalStructure = refreshArgs.showLogicalStructure; + DebugSettings.getCurrent().showToString = refreshArgs.showToString; + } + + context.getProtocolServer().sendEvent(new InvalidatedEvent(InvalidatedAreas.VARIABLES)); + return CompletableFuture.completedFuture(response); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index ea18f183c..5397c418e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -11,6 +11,7 @@ package com.microsoft.java.debug.core.protocol; +import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.Types.Source; /** @@ -244,4 +245,42 @@ public UserNotificationEvent(NotificationType notifyType, String message) { this.message = message; } } + + public static enum InvalidatedAreas { + @SerializedName("all") + ALL, + @SerializedName("stacks") + STACKS, + @SerializedName("threads") + THREADS, + @SerializedName("variables") + VARIABLES; + } + + public static class InvalidatedEvent extends DebugEvent { + public InvalidatedAreas[] areas; + public long threadId; + public int frameId; + + public InvalidatedEvent() { + super("invalidated"); + } + + public InvalidatedEvent(InvalidatedAreas area) { + super("invalidated"); + this.areas = new InvalidatedAreas[]{area}; + } + + public InvalidatedEvent(InvalidatedAreas area, long threadId) { + super("invalidated"); + this.areas = new InvalidatedAreas[]{area}; + this.threadId = threadId; + } + + public InvalidatedEvent(InvalidatedAreas area, int frameId) { + super("invalidated"); + this.areas = new InvalidatedAreas[]{area}; + this.frameId = frameId; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 2afb02bd8..e31a65261 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -297,6 +297,14 @@ public static class SetVariableArguments extends Arguments { public ValueFormat format; } + public static class RefreshVariablesArguments extends Arguments { + public boolean showStaticVariables = false; + public boolean showQualifiedNames = false; + public boolean showHex = false; + public boolean showLogicalStructure = true; + public boolean showToString = true; + } + public static class SourceArguments extends Arguments { public int sourceReference; } @@ -401,6 +409,7 @@ public static enum Command { PAUSEALL("pauseAll", ThreadOperationArguments.class), PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), INLINEVALUES("inlineValues", InlineValuesArguments.class), + REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), UNSUPPORTED("", Arguments.class); private String command; From a0e31fc8bda7dbe604ef94aabd769bfb0a26bbbc Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 26 Apr 2021 09:46:49 +0800 Subject: [PATCH 007/144] Bump version to 0.31.0 (#371) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 7c20a8aae..04099cf90 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.1 + 0.31.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index e9e2f6c74..3fd9659f9 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 40f78351f..be9168b0c 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.30.1 +Bundle-Version: 0.31.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.30.1.jar + lib/com.microsoft.java.debug.core-0.31.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 2a1985e15..0fefc2fea 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.30.1 + 0.31.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.30.1 + 0.31.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index d351c135e..9a33da2cf 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 09bd08f1b..1d0b490e9 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.30.1 + 0.31.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index fe8385811..a27138fb9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.30.1 + 0.31.0 pom Java Debug Server for Visual Studio Code From 5bb368e35911e2220e691623de592895b851048e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 29 Apr 2021 16:10:09 +0800 Subject: [PATCH 008/144] Update CI branch (#373) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afa1e2185..58381df99 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: linux: From 71e9ac5a85c9712452f639b5f4708ebb86d7b76a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 30 Apr 2021 15:29:38 +0800 Subject: [PATCH 009/144] Fix the broken due to the changes from the upstream JDI impl class ConnectorImpl$StringArgumentImpl (#374) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 4 +- com.microsoft.java.debug.plugin/pom.xml | 4 +- .../internal/AdvancedLaunchingConnector.java | 80 +++++++++++++++++-- .../category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 83 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 04099cf90..e0260e727 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.0 + 0.31.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3fd9659f9..c00d65ed3 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index be9168b0c..84bd01f8f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.31.0 +Bundle-Version: 0.31.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.31.0.jar + lib/com.microsoft.java.debug.core-0.31.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 0fefc2fea..f9a19badc 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.0 + 0.31.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.31.0 + 0.31.1 diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index d025c9c1d..607d26a4e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2021 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -44,11 +44,11 @@ public AdvancedLaunchingConnector(VirtualMachineManagerImpl virtualMachineManage public Map defaultArguments() { Map defaultArgs = super.defaultArguments(); - Argument cwdArg = new AdvancedStringArgumentImpl(DebugUtility.CWD, "Current working directory", DebugUtility.CWD, false); + Argument cwdArg = new JDIStringArgumentImpl(DebugUtility.CWD, "Current working directory", DebugUtility.CWD, false); cwdArg.setValue(null); defaultArgs.put(DebugUtility.CWD, cwdArg); - Argument envArg = new AdvancedStringArgumentImpl(DebugUtility.ENV, "Environment variables", DebugUtility.ENV, false); + Argument envArg = new JDIStringArgumentImpl(DebugUtility.ENV, "Environment variables", DebugUtility.ENV, false); envArg.setValue(null); defaultArgs.put(DebugUtility.ENV, envArg); @@ -117,11 +117,79 @@ private static String[] constructLaunchCommand(Map l return DebugUtility.parseArguments(execString.toString()).toArray(new String[0]); } - class AdvancedStringArgumentImpl extends StringArgumentImpl implements StringArgument { - private static final long serialVersionUID = 1L; + abstract class JDIArgumentImpl implements Argument { + private static final long serialVersionUID = 8850533280769854833L; + private String name; + private String description; + private String label; + private boolean mustSpecify; + + protected JDIArgumentImpl(String name, String description, String label, boolean mustSpecify) { + this.name = name; + this.description = description; + this.label = label; + this.mustSpecify = mustSpecify; + } + + @Override + public String name() { + return name; + } + + @Override + public String description() { + return description; + } + + @Override + public String label() { + return label; + } + + @Override + public boolean mustSpecify() { + return mustSpecify; + } + + @Override + public abstract String value(); + + @Override + public abstract void setValue(String value); + + @Override + public abstract boolean isValid(String value); - protected AdvancedStringArgumentImpl(String name, String description, String label, boolean mustSpecify) { + @Override + public abstract String toString(); + } + + class JDIStringArgumentImpl extends JDIArgumentImpl implements StringArgument { + private static final long serialVersionUID = 6009335074727417445L; + private String value; + + protected JDIStringArgumentImpl(String name, String description, String label, boolean mustSpecify) { super(name, description, label, mustSpecify); } + + @Override + public String value() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean isValid(String value) { + return true; + } + + @Override + public String toString() { + return value; + } } } diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 9a33da2cf..3520fec6d 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 1d0b490e9..f3ee87766 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.31.0 + 0.31.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index a27138fb9..4e5d368b8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.31.0 + 0.31.1 pom Java Debug Server for Visual Studio Code From a024806d513674ba4cb1e5dfa2eb381667f0780d Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 21 May 2021 13:39:55 +0800 Subject: [PATCH 010/144] Support customizing the classpaths and modulepaths (#376) --- .../internal/ResolveClasspathsHandler.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java index be37285e3..5e7017128 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveClasspathsHandler.java @@ -70,6 +70,9 @@ public class ResolveClasspathsHandler { */ public String[][] resolveClasspaths(List arguments) throws Exception { try { + if (arguments.size() == 3) { + return computeClassPath((String) arguments.get(0), (String) arguments.get(1), (String) arguments.get(2)); + } return computeClassPath((String) arguments.get(0), (String) arguments.get(1)); } catch (CoreException e) { logger.log(Level.SEVERE, "Failed to resolve classpath: " + e.getMessage(), e); @@ -159,6 +162,23 @@ public void acceptSearchMatch(SearchMatch match) { * CoreException */ private static String[][] computeClassPath(String mainClass, String projectName) throws CoreException { + return computeClassPath(mainClass, projectName, null); + } + + /** + * Accord to the project name and the main class, compute runtime classpath. + * + * @param mainClass + * fully qualified class name + * @param projectName + * project name + * @param scope + * scope of the classpath + * @return class path + * @throws CoreException + * CoreException + */ + private static String[][] computeClassPath(String mainClass, String projectName, String scope) throws CoreException { IJavaProject project = null; // if type exists in multiple projects, debug configuration need provide // project name. @@ -179,6 +199,12 @@ private static String[][] computeClassPath(String mainClass, String projectName) project = projects.get(0); } + if ("test".equals(scope)) { + return computeClassPath(project, mainClass, false /*excludeTestCode*/, Collections.EMPTY_LIST); + } else if ("runtime".equals(scope)) { + return computeClassPath(project, mainClass, true /*excludeTestCode*/, Collections.EMPTY_LIST); + } + IJavaElement testElement = findMainClassInTestFolders(project, mainClass); List mappedResources = (testElement != null && testElement.getResource() != null) ? Arrays.asList(testElement.getResource()) : Collections.EMPTY_LIST; @@ -275,7 +301,7 @@ public void acceptSearchMatch(SearchMatch match) { private static class JavaApplicationLaunchConfiguration extends LaunchConfiguration { public static final String JAVA_APPLICATION_LAUNCH = "\n" - + "\n" + + "\n" + "\n" + "\n" + "\n" @@ -302,7 +328,18 @@ protected JavaApplicationLaunchConfiguration(IProject project, String mainType, } else if (ProjectUtils.isGradleProject(project)) { classpathProvider = "org.eclipse.buildship.core.classpathprovider"; } - this.launchInfo = new JavaLaunchConfigurationInfo(JAVA_APPLICATION_LAUNCH); + + // Since MavenRuntimeClasspathProvider will only including test entries when: + // 1. Launch configuration is JUnit/TestNG type + // 2. Mapped resource is in test path. + // That's why we use JUnit launch configuration here to make sure the result is right when excludeTestCode is false. + String launchXml = null; + if (!excludeTestCode && mappedResources.isEmpty()) { + launchXml = String.format(JAVA_APPLICATION_LAUNCH, "org.eclipse.jdt.junit.launchconfig"); + } else { + launchXml = String.format(JAVA_APPLICATION_LAUNCH, "org.eclipse.jdt.launching.localJavaApplication"); + } + this.launchInfo = new JavaLaunchConfigurationInfo(launchXml); } @Override From 79430673710a84a6be59f524368e1fd7ba11c5a2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 21 May 2021 16:16:11 +0800 Subject: [PATCH 011/144] Bump version to 0.32.0 (#377) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index e0260e727..5b6f0c2c3 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.1 + 0.32.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c00d65ed3..8c76f8dd3 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 84bd01f8f..2245a3854 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.31.1 +Bundle-Version: 0.32.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.5.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.31.1.jar + lib/com.microsoft.java.debug.core-0.32.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index f9a19badc..f5e0003e3 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.31.1 + 0.32.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.31.1 + 0.32.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 3520fec6d..88e7c7041 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index f3ee87766..6e9099962 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.31.1 + 0.32.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4e5d368b8..4f7abb14c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.31.1 + 0.32.0 pom Java Debug Server for Visual Studio Code From f15f84bca99b6cb0f4573540a9f9fb3a1579769d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Thu, 17 Jun 2021 09:05:03 +0200 Subject: [PATCH 012/144] Ensure CompletionsResponse contains targets (#362) If a client sent a completion request without a frameId then the server returned a completion response without targets. According to the specification the response of a completions request must always contain a `targets` property. It is not optional. See https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Completions --- .../java/debug/core/adapter/handler/CompletionsHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java index 9f660644a..eba7d5153 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/CompletionsHandler.java @@ -11,8 +11,8 @@ package com.microsoft.java.debug.core.adapter.handler; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -46,7 +46,7 @@ public CompletableFuture handle(Command command, Arguments arguments, // completions should be illegal when frameId is zero, it is sent when the program is running, while during running we cannot resolve // the completion candidates if (completionsArgs.frameId == 0) { - response.body = new ArrayList<>(); + response.body = new Responses.CompletionsResponseBody(Collections.emptyList()); return CompletableFuture.completedFuture(response); } From 7a2d6debdcfc5b9af95c1ceb6edd82f4b6eab145 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 13 Jul 2021 13:41:39 +0800 Subject: [PATCH 013/144] Fix vulnerabilities (#380) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 5b6f0c2c3..b2b73793d 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -62,7 +62,7 @@ commons-io commons-io - 2.5 + 2.10.0 diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index f5e0003e3..cd27e1bce 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -40,7 +40,7 @@ commons-io commons-io - 2.5 + 2.10.0 com.microsoft.java From 900975382105e900c57bd12eb8c5573c2134bfac Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 7 Sep 2021 20:47:41 +0800 Subject: [PATCH 014/144] Fix the commons-io version issue in project file (#382) --- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 8c76f8dd3..bcc62dabd 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -2,7 +2,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 2245a3854..e6b08d24a 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -21,7 +21,7 @@ Require-Bundle: org.eclipse.core.runtime, org.apache.commons.lang3, org.eclipse.lsp4j, com.google.guava -Bundle-ClassPath: lib/commons-io-2.5.jar, +Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, From 3f50bfc9e994df8f435e02fdf70239f1ef297112 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 13 Sep 2021 13:22:38 +0800 Subject: [PATCH 015/144] Allow mainClass to be configured with unicode characters (#383) --- .../internal/ResolveMainClassHandler.java | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index 9d3b1014e..a554a6e67 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2021 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -22,6 +22,8 @@ import java.util.logging.Logger; import java.util.stream.Collectors; +import javax.lang.model.SourceVersion; + import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -47,8 +49,10 @@ public class ResolveMainClassHandler { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - // Java command line supports two kinds of main class format: and [/] - private static final String CLASSNAME_REGX = "([$\\w]+\\.)*[$\\w]+(/([$\\w]+\\.)*[$\\w]+)?"; + private static final int CONFIGERROR_INVALID_CLASS_NAME = 1; + private static final int CONFIGERROR_MAIN_CLASS_NOT_EXIST = 2; + private static final int CONFIGERROR_MAIN_CLASS_NOT_UNIQUE = 3; + private static final int CONFIGERROR_INVALID_JAVA_PROJECT = 4; /** * resolve main class and project name. @@ -185,23 +189,41 @@ private ValidationResponse validateLaunchConfigCore(List arguments) thro private ValidationResult validateMainClass(final String mainClass, final String projectName, boolean containsExternalClasspaths) throws CoreException { if (StringUtils.isEmpty(mainClass)) { return new ValidationResult(true); - } else if (!mainClass.matches(CLASSNAME_REGX)) { - return new ValidationResult(false, String.format("ConfigError: '%s' is not a valid class name.", mainClass)); + } else if (!isValidMainClassName(mainClass)) { + return new ValidationResult(false, String.format("ConfigError: '%s' is not a valid class name.", mainClass), + CONFIGERROR_INVALID_CLASS_NAME); } if (!containsExternalClasspaths && StringUtils.isEmpty(projectName)) { List javaProjects = searchClassInProjectClasspaths(mainClass); if (javaProjects.size() == 0) { - return new ValidationResult(false, String.format("ConfigError: Main class '%s' doesn't exist in the workspace.", mainClass)); + return new ValidationResult(false, String.format("ConfigError: Main class '%s' doesn't exist in the workspace.", mainClass), + CONFIGERROR_MAIN_CLASS_NOT_EXIST); } if (javaProjects.size() > 1) { - return new ValidationResult(false, String.format("ConfigError: Main class '%s' isn't unique in the workspace.", mainClass)); + return new ValidationResult(false, String.format("ConfigError: Main class '%s' isn't unique in the workspace.", mainClass), + CONFIGERROR_MAIN_CLASS_NOT_UNIQUE); } } return new ValidationResult(true); } + // Java command line supports two kinds of main class format: and [/] + private boolean isValidMainClassName(String mainClass) { + if (StringUtils.isEmpty(mainClass)) { + return true; + } + + int index = mainClass.indexOf('/'); + if (index == -1) { + return SourceVersion.isName(mainClass); + } + + return SourceVersion.isName(mainClass.substring(0, index)) + && SourceVersion.isName(mainClass.substring(index + 1)); + } + private List searchClassInProjectClasspaths(String fullyQualifiedClassName) throws CoreException { return ResolveClasspathsHandler.getJavaProjectFromType(fullyQualifiedClassName); } @@ -212,7 +234,8 @@ private ValidationResult validateProjectName(final String mainClass, final Strin } if (JdtUtils.getJavaProject(projectName) == null) { - return new ValidationResult(false, String.format("ConfigError: The project '%s' is not a valid java project.", projectName)); + return new ValidationResult(false, String.format("ConfigError: The project '%s' is not a valid java project.", projectName), + CONFIGERROR_INVALID_JAVA_PROJECT); } return new ValidationResult(true); @@ -302,14 +325,16 @@ class ValidationResponse { class ValidationResult { boolean isValid; String message; + int kind; ValidationResult(boolean isValid) { this.isValid = isValid; } - ValidationResult(boolean isValid, String message) { + ValidationResult(boolean isValid, String message, int kind) { this.isValid = isValid; this.message = message; + this.kind = kind; } } } From 64b54e8c781e96e6e8fe1572a2e014bc091247d6 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 13 Sep 2021 15:30:35 +0800 Subject: [PATCH 016/144] Fix: classpath is not generated into argfile when using shortCommanLine=argfile (#384) --- .../java/debug/core/adapter/handler/LaunchUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 17a4f7359..d0d99af40 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -73,11 +73,11 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws public static synchronized Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { String argfile = ""; if (ArrayUtils.isNotEmpty(classPaths)) { - argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\""; + argfile = "-cp \"" + String.join(File.pathSeparator, classPaths) + "\""; } if (ArrayUtils.isNotEmpty(modulePaths)) { - argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; + argfile += " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; } argfile = argfile.replace("\\", "\\\\"); From 9a84e7c5733db35e4b60bf1c64335870bf0192e4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 23 Sep 2021 12:27:09 +0800 Subject: [PATCH 017/144] if listing the variable value of a large object throws OOM or timeout exception, just display its basic info (#385) --- .../handler/EvaluateRequestHandler.java | 48 ++++++++++---- .../handler/VariablesRequestHandler.java | 64 ++++++++++++++----- 2 files changed, 84 insertions(+), 28 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 2a498608e..9cf99742d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2021 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,7 +14,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; @@ -103,10 +102,8 @@ public CompletableFuture handle(Command command, Arguments arguments, indexedVariables = ((IntegerValue) sizeValue).value(); } } - } catch (CancellationException | IllegalArgumentException | InterruptedException - | ExecutionException | UnsupportedOperationException e) { - logger.log(Level.INFO, - String.format("Failed to get the logical size for the type %s.", value.type().name()), e); + } catch (Exception e) { + logger.log(Level.INFO, "Failed to get the logical size of the variable", e); } } int referenceId = 0; @@ -114,20 +111,49 @@ public CompletableFuture handle(Command command, Arguments arguments, referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy); } - String valueString = variableFormatter.valueToString(value, options); + boolean hasErrors = false; + String valueString = null; + try { + valueString = variableFormatter.valueToString(value, options); + } catch (OutOfMemoryError e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to convert the value of a large object to a string", e); + valueString = ""; + } catch (Exception e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to resolve the variable value", e); + valueString = ""; + } + String detailsString = null; - if (sizeValue != null) { + if (hasErrors) { + // If failed to resolve the variable value, skip the details info as well. + } else if (sizeValue != null) { detailsString = "size=" + variableFormatter.valueToString(sizeValue, options); } else if (DebugSettings.getCurrent().showToString) { - detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); + try { + detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); + } catch (OutOfMemoryError e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); + detailsString = ""; + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value", e); + detailsString = ""; + } } if ("clipboard".equals(evalArguments.context) && detailsString != null) { response.body = new Responses.EvaluateResponseBody(detailsString, -1, "String", 0); } else { + String typeString = ""; + try { + typeString = variableFormatter.typeToString(value == null ? null : value.type(), options); + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to resolve the variable type", e); + typeString = ""; + } response.body = new Responses.EvaluateResponseBody((detailsString == null) ? valueString : valueString + " " + detailsString, - referenceId, variableFormatter.typeToString(value == null ? null : value.type(), options), - Math.max(indexedVariables, 0)); + referenceId, typeString, Math.max(indexedVariables, 0)); } return response; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 70d5dae31..26a0f7124 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2021 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,9 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -133,7 +131,12 @@ public CompletableFuture handle(Command command, Arguments arguments, try { ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable(); if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { - JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); + JavaLogicalStructure logicalStructure = null; + try { + logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to get the logical structure for the variable, fall back to the Object view.", e); + } if (isUnboundedTypeContainer && logicalStructure != null && containerEvaluateName != null) { containerEvaluateName = "((" + logicalStructure.getFullyQualifiedName() + ")" + containerEvaluateName + ")"; isUnboundedTypeContainer = false; @@ -162,11 +165,8 @@ public CompletableFuture handle(Command command, Arguments arguments, childrenList.add(variable); } } - } catch (IllegalArgumentException | CancellationException | InterruptedException | ExecutionException e) { - logger.log(Level.WARNING, - String.format("Failed to get the logical structure for the type %s, fall back to the Object view.", - containerObj.type().name()), - e); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to get the logical structure for the variable, fall back to the Object view.", e); } logicalStructure = null; @@ -241,9 +241,8 @@ public CompletableFuture handle(Command command, Arguments arguments, indexedVariables = ((IntegerValue) sizeValue).value(); } } - } catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) { - logger.log(Level.INFO, - String.format("Failed to get the logical size for the type %s.", value.type().name()), e); + } catch (Exception e) { + logger.log(Level.INFO, "Failed to get the logical size of the variable", e); } } @@ -275,15 +274,46 @@ public CompletableFuture handle(Command command, Arguments arguments, varProxy.setUnboundedType(javaVariable.isUnboundedType()); } - Types.Variable typedVariables = new Types.Variable(name, variableFormatter.valueToString(value, options), - variableFormatter.typeToString(value == null ? null : value.type(), options), - referenceId, evaluateName); + boolean hasErrors = false; + String valueString = null; + try { + valueString = variableFormatter.valueToString(value, options); + } catch (OutOfMemoryError e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to convert the value of a large object to a string", e); + valueString = ""; + } catch (Exception e) { + hasErrors = true; + logger.log(Level.SEVERE, "Failed to resolve the variable value", e); + valueString = ""; + } + + String typeString = ""; + try { + typeString = variableFormatter.typeToString(value == null ? null : value.type(), options); + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to resolve the variable type", e); + typeString = ""; + } + + Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); typedVariables.indexedVariables = Math.max(indexedVariables, 0); + String detailsValue = null; - if (sizeValue != null) { + if (hasErrors) { + // If failed to resolve the variable value, skip the details info as well. + } else if (sizeValue != null) { detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options); } else if (DebugSettings.getCurrent().showToString) { - detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); + try { + detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); + } catch (OutOfMemoryError e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); + detailsValue = ""; + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value", e); + detailsValue = ""; + } } if (detailsValue != null) { From 83fe25d3d6257e72848466b5a7a37365c1d1d512 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 23 Sep 2021 12:56:29 +0800 Subject: [PATCH 018/144] Bump version to 0.33.0 (#386) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index b2b73793d..c9610d0a5 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.32.0 + 0.33.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index bcc62dabd..47284619a 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index e6b08d24a..96e3a626d 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.32.0 +Bundle-Version: 0.33.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.32.0.jar + lib/com.microsoft.java.debug.core-0.33.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index cd27e1bce..84dcc6f50 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.32.0 + 0.33.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.32.0 + 0.33.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 88e7c7041..0fffc8012 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 6e9099962..9426a408a 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.32.0 + 0.33.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4f7abb14c..b4db6d530 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.32.0 + 0.33.0 pom Java Debug Server for Visual Studio Code From a5b88da1ec7af905293421437f1e0d3e6c331dff Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Tue, 12 Oct 2021 09:20:31 +0200 Subject: [PATCH 019/144] Fix step filter: keep method exit request (#387) --- .../core/adapter/handler/StepRequestHandler.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 94e0d3078..72d14eb5d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -145,12 +145,14 @@ public CompletableFuture handle(Command command, Arguments arguments, private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; + EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); // When a breakpoint occurs, abort any pending step requests from the same thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { - threadState.deleteStepRequests(debugSession.getVM().eventRequestManager()); + threadState.deleteStepRequest(eventRequestManager); + threadState.deleteMethodExitRequest(eventRequestManager); context.getStepResultManager().removeMethodResult(threadId); if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); @@ -158,7 +160,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); - threadState.deleteStepRequests(debugSession.getVM().eventRequestManager()); + threadState.deleteStepRequest(eventRequestManager); if (isStepFiltersConfigured(context.getStepFilters())) { try { if (threadState.pendingStepType == Command.STEPIN) { @@ -181,6 +183,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // ignore. } } + threadState.deleteMethodExitRequest(eventRequestManager); if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } @@ -280,10 +283,13 @@ class ThreadState { Location stepLocation = null; Disposable eventSubscription = null; - public void deleteStepRequests(EventRequestManager manager) { - DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest); + public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); this.pendingMethodExitRequest = null; + } + + public void deleteStepRequest(EventRequestManager manager) { + DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest); this.pendingStepRequest = null; } } From da61636dcbc4970e73fa6adcb124f75c7269208e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 19 Oct 2021 10:55:12 +0800 Subject: [PATCH 020/144] Add ThirdPartyNotices.txt (#389) Signed-off-by: Jinbo Wang --- ThirdPartyNotices.txt | 437 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 ThirdPartyNotices.txt diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt new file mode 100644 index 000000000..b9fc26420 --- /dev/null +++ b/ThirdPartyNotices.txt @@ -0,0 +1,437 @@ +java-debug + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. + +1. ReactiveX/RxJava (https://github.com/ReactiveX/RxJava) +2. reactive-streams/reactive-streams-jvm (https://github.com/reactive-streams/reactive-streams-jvm) +3. apache/commons-io (https://github.com/apache/commons-io) + + +%% ReactiveX/RxJava NOTICES AND INFORMATION BEGIN HERE +========================================= + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF ReactiveX/RxJava NOTICES AND INFORMATION + +%% reactive-streams/reactive-streams-jvm NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF reactive-streams/reactive-streams-jvm NOTICES AND INFORMATION + +%% apache/commons-io NOTICES AND INFORMATION BEGIN HERE +========================================= + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF apache/commons-io NOTICES AND INFORMATION \ No newline at end of file From d5fc1fd33528876f2732cec94adc82902c8ae83e Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 15 Nov 2021 10:52:28 +0800 Subject: [PATCH 021/144] Update to gson@2.8.9 (#390) --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index c9610d0a5..9fac8bde1 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -47,7 +47,7 @@ com.google.code.gson gson - 2.7 + 2.8.9 io.reactivex.rxjava2 From 934d6cb0b8ffb3fb2ee9db4702c494ea02b2df03 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 19 Nov 2021 15:32:13 +0800 Subject: [PATCH 022/144] Make DAP implementation to be encoding-neutral (#391) --- .../java/debug/core/adapter/ProcessConsole.java | 2 +- .../adapter/handler/LaunchRequestHandler.java | 17 +++++++---------- .../internal/JdtSourceLookUpProvider.java | 13 ++++--------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java index 6b384c628..3d823df91 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProcessConsole.java @@ -129,7 +129,7 @@ public void stop() { } private void monitor(InputStream input, PublishSubject subject) { - BufferedReader reader = new BufferedReader(new InputStreamReader(input, encoding)); + BufferedReader reader = new BufferedReader(encoding == null ? new InputStreamReader(input) : new InputStreamReader(input, encoding)); final int BUFFERSIZE = 4096; char[] buffer = new char[BUFFERSIZE]; while (true) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 7d76e1ffd..031a4826b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -16,7 +16,6 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -90,23 +89,21 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R "Failed to launch debuggee VM. Missing mainClass or modulePaths/classPaths options in launch configuration.", ErrorCode.ARGUMENT_MISSING); } - if (StringUtils.isBlank(launchArguments.encoding)) { - context.setDebuggeeEncoding(StandardCharsets.UTF_8); - } else { + if (StringUtils.isNotBlank(launchArguments.encoding)) { if (!Charset.isSupported(launchArguments.encoding)) { throw AdapterUtils.createCompletionException( "Failed to launch debuggee VM. 'encoding' options in the launch configuration is not recognized.", ErrorCode.INVALID_ENCODING); } context.setDebuggeeEncoding(Charset.forName(launchArguments.encoding)); + if (StringUtils.isBlank(launchArguments.vmArgs)) { + launchArguments.vmArgs = String.format("-Dfile.encoding=%s", context.getDebuggeeEncoding().name()); + } else { + // if vmArgs already has the file.encoding settings, duplicate options for jvm will not cause an error, the right most value wins + launchArguments.vmArgs = String.format("%s -Dfile.encoding=%s", launchArguments.vmArgs, context.getDebuggeeEncoding().name()); + } } - if (StringUtils.isBlank(launchArguments.vmArgs)) { - launchArguments.vmArgs = String.format("-Dfile.encoding=%s", context.getDebuggeeEncoding().name()); - } else { - // if vmArgs already has the file.encoding settings, duplicate options for jvm will not cause an error, the right most value wins - launchArguments.vmArgs = String.format("%s -Dfile.encoding=%s", launchArguments.vmArgs, context.getDebuggeeEncoding().name()); - } context.setLaunchMode(launchArguments.noDebug ? LaunchMode.NO_DEBUG : LaunchMode.DEBUG); activeLaunchHandler.preLaunch(launchArguments, context); diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index d24d2abdd..efc7ce20c 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2021 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -17,7 +17,6 @@ import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; @@ -120,11 +119,7 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th String filePath = AdapterUtils.toPath(uri); // For file uri, read the file contents directly and pass them to the ast parser. if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { - Charset cs = (Charset) this.options.get(Constants.DEBUGGEE_ENCODING); - if (cs == null) { - cs = Charset.defaultCharset(); - } - String source = readFile(filePath, cs); + String source = readFile(filePath); parser.setSource(source.toCharArray()); /** * See the java doc for { @link ASTParser#setResolveBindings(boolean) }. @@ -293,10 +288,10 @@ private static IClassFile resolveClassFile(String uriString) { return null; } - private static String readFile(String filePath, Charset cs) { + private static String readFile(String filePath) { StringBuilder builder = new StringBuilder(); try (BufferedReader bufferReader = - new BufferedReader(new InputStreamReader(new FileInputStream(filePath), cs))) { + new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) { final int BUFFER_SIZE = 4096; char[] buffer = new char[BUFFER_SIZE]; while (true) { From 719eb8c3efd3098b75e91c5b5f866c3b1c984ce2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 22 Nov 2021 13:19:59 +0800 Subject: [PATCH 023/144] Bump version to 0.34.0 (#392) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 9fac8bde1..4009824a5 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.33.0 + 0.34.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 47284619a..51b0645e0 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -6,6 +6,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 96e3a626d..ae117b544 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.33.0 +Bundle-Version: 0.34.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.33.0.jar + lib/com.microsoft.java.debug.core-0.34.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 84dcc6f50..d7cd7c6a4 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.33.0 + 0.34.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -45,7 +45,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.33.0 + 0.34.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 0fffc8012..88c8461b3 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9426a408a..87692aa25 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.33.0 + 0.34.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index b4db6d530..a0f7c9b9d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.33.0 + 0.34.0 pom Java Debug Server for Visual Studio Code From f652c8d109c782f22fddec153e47bd49acee0410 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 23 Dec 2021 10:40:19 +0800 Subject: [PATCH 024/144] Migrate to Java 11 (#393) * Migrate to Java 11 --- com.microsoft.java.debug.core/.classpath | 2 +- com.microsoft.java.debug.core/pom.xml | 21 ++----------------- com.microsoft.java.debug.plugin/.classpath | 7 ++++++- .../META-INF/MANIFEST.MF | 2 +- com.microsoft.java.debug.plugin/pom.xml | 11 ++++++++++ .../internal/ResolveMainMethodHandler.java | 5 +++-- java.debug.target | 15 ++++++++++--- pom.xml | 6 +++--- 8 files changed, 39 insertions(+), 30 deletions(-) diff --git a/com.microsoft.java.debug.core/.classpath b/com.microsoft.java.debug.core/.classpath index f0257c5a5..9ba41a249 100644 --- a/com.microsoft.java.debug.core/.classpath +++ b/com.microsoft.java.debug.core/.classpath @@ -13,7 +13,7 @@ - + diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 4009824a5..09cb040af 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -28,8 +28,8 @@ maven-compiler-plugin 3.7.0 - 1.8 - 1.8 + 11 + 11 @@ -78,21 +78,4 @@ test - - - default-tools.jar - - (,9) - - - - com.sun - tools - 1.8 - system - ${java.home}/../lib/tools.jar - - - - diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 51b0645e0..a305d1f0b 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -1,6 +1,11 @@ - + + + + + + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index ae117b544..27f1ad0c0 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true Bundle-Version: 0.34.0 -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin Bundle-Vendor: Microsoft diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index d7cd7c6a4..273f12875 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -17,6 +17,17 @@ tycho-maven-plugin ${tycho-version} true + + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho-version} + + + --limit-modules + java.base,java.logging,java.xml,jdk.jdi,java.compiler + + org.apache.maven.plugins diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index 85ff58978..a3c61e814 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018-2020 Microsoft Corporation and others. + * Copyright (c) 2018-2021 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -26,6 +26,7 @@ import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.IType; @@ -167,7 +168,7 @@ private static MainMethod extractMainMethodInfo(ICompilationUnit typeRoot, IMeth private static Range getRange(ICompilationUnit typeRoot, IJavaElement element) throws JavaModelException { ISourceRange r = ((ISourceReference) element).getNameRange(); - return JDTUtils.toRange(typeRoot, r.getOffset(), r.getLength()); + return JDTUtils.toRange((IOpenable) typeRoot, r.getOffset(), r.getLength()); } static class MainMethod { diff --git a/java.debug.target b/java.debug.target index 835c63801..dcb0b8c5e 100644 --- a/java.debug.target +++ b/java.debug.target @@ -6,17 +6,26 @@ - + + + + + + + + + + - + - + diff --git a/pom.xml b/pom.xml index a0f7c9b9d..de00b34b0 100644 --- a/pom.xml +++ b/pom.xml @@ -154,9 +154,9 @@ - 201912 + 202112 p2 - https://download.eclipse.org/releases/2019-12/ + https://download.eclipse.org/releases/2021-12/202112081000/ oss.sonatype.org @@ -173,7 +173,7 @@ JBOLL.TOOLS p2 - https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.0-2018-05-16_00-46-30-H11 + https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.3-2019-11-08_11-04-22-H22/ orbit From 810d955d329e853a6e949d949e0f0f52028241a5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Jan 2022 10:20:38 +0800 Subject: [PATCH 025/144] Limit search scope of main class to source files and the specified project or paths (#395) * Limit search scope of main class to source files and the specified project or paths --- .../internal/ResolveMainClassHandler.java | 96 ++++++++++++++++--- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index a554a6e67..f70d0c044 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2021 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,6 +12,7 @@ package com.microsoft.java.debug.plugin.internal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -21,6 +22,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.lang.model.SourceVersion; @@ -79,15 +81,26 @@ public Object validateLaunchConfig(List arguments) throws Exception { } private List resolveMainClassCore(List arguments) { - IPath rootPath = null; if (arguments != null && arguments.size() > 0 && arguments.get(0) != null) { - rootPath = ResourceUtils.filePathFromURI((String) arguments.get(0)); - } - final ArrayList targetProjectPath = new ArrayList<>(); - if (rootPath != null) { - targetProjectPath.add(rootPath); + String argument = (String) arguments.get(0); + IProject[] projects = ProjectUtils.getAllProjects(); + if (Stream.of(projects).anyMatch(project -> Objects.equals(project.getName(), argument))) { + return resolveMainClassUnderProject(argument); + } + + IPath rootPath = ResourceUtils.filePathFromURI(argument); + if (rootPath != null) { + return resolveMainClassUnderPaths(Arrays.asList(rootPath)); + } } - IJavaSearchScope searchScope = SearchEngine.createWorkspaceScope(); + + return resolveMainClassUnderPaths(Collections.emptyList()); + } + + private List resolveMainClassUnderPaths(List parentPaths) { + // Limit to search main method from source code only. + IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), + IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); final List res = new ArrayList<>(); @@ -112,10 +125,9 @@ public void acceptSearchMatch(SearchMatch match) { } } String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); - if (projectName == null - || targetProjectPath.isEmpty() - || ResourceUtils.isContainedIn(project.getLocation(), targetProjectPath) - || isContainedInInvisibleProject(project, targetProjectPath)) { + if (parentPaths.isEmpty() + || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) + || isContainedInInvisibleProject(project, parentPaths)) { String filePath = null; if (match.getResource() instanceof IFile) { @@ -149,6 +161,66 @@ public void acceptSearchMatch(SearchMatch match) { return resolutions; } + private List resolveMainClassUnderProject(final String projectName) { + // Limit to search main method from source code only. + IJavaProject javaProject = ProjectUtils.getJavaProject(projectName); + IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(javaProject == null ? new IJavaProject[0] : new IJavaProject[] {javaProject}, + IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); + SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + final List res = new ArrayList<>(); + SearchRequestor requestor = new SearchRequestor() { + @Override + public void acceptSearchMatch(SearchMatch match) { + Object element = match.getElement(); + if (element instanceof IMethod) { + IMethod method = (IMethod) element; + try { + if (method.isMainMethod()) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; + } + } + + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore + } + } + res.add(new ResolutionItem(mainClass, projectName, filePath)); + } + } + } + } catch (JavaModelException e) { + // ignore + } + } + } + }; + SearchEngine searchEngine = new SearchEngine(); + try { + searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, + searchScope, requestor, null /* progress monitor */); + } catch (Exception e) { + logger.log(Level.SEVERE, String.format("Searching the main class failure: %s", e.toString()), e); + } + + List resolutions = res.stream().distinct().collect(Collectors.toList()); + Collections.sort(resolutions); + return resolutions; + } + private boolean isContainedInInvisibleProject(IProject project, Collection rootPaths) { if (project == null) { return false; From e6655ead412ceed5afa37b3781ed84e0b8ba425a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Jan 2022 11:08:30 +0800 Subject: [PATCH 026/144] Bump version to 0.35.0 (#396) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 09cb040af..f081491ae 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.34.0 + 0.35.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index a305d1f0b..de236212a 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 27f1ad0c0..1c0eaf78c 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.34.0 +Bundle-Version: 0.35.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.10.0.jar, ., lib/rxjava-2.1.1.jar, lib/reactive-streams-1.0.0.jar, - lib/com.microsoft.java.debug.core-0.34.0.jar + lib/com.microsoft.java.debug.core-0.35.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 273f12875..ffc3ded41 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.34.0 + 0.35.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.34.0 + 0.35.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 88c8461b3..782edf279 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 87692aa25..b21f4ba96 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.34.0 + 0.35.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index de00b34b0..007cf78be 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.34.0 + 0.35.0 pom Java Debug Server for Visual Studio Code From fca1d23dd000ef6e5885047f82097b76edf8fd3f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 16 Mar 2022 19:45:53 +0800 Subject: [PATCH 027/144] Bump java deps version (#403) --- com.microsoft.java.debug.core/pom.xml | 6 +++--- com.microsoft.java.debug.plugin/.classpath | 6 +++--- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 6 +++--- com.microsoft.java.debug.plugin/pom.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index f081491ae..811a32a64 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -52,17 +52,17 @@ io.reactivex.rxjava2 rxjava - 2.1.1 + 2.2.21 org.reactivestreams reactive-streams - 1.0.0 + 1.0.3 commons-io commons-io - 2.10.0 + 2.11.0 diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index de236212a..0362be354 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -7,10 +7,10 @@ - + - - + + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 1c0eaf78c..925c853aa 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -21,8 +21,8 @@ Require-Bundle: org.eclipse.core.runtime, org.apache.commons.lang3, org.eclipse.lsp4j, com.google.guava -Bundle-ClassPath: lib/commons-io-2.10.0.jar, +Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., - lib/rxjava-2.1.1.jar, - lib/reactive-streams-1.0.0.jar, + lib/rxjava-2.2.21.jar, + lib/reactive-streams-1.0.3.jar, lib/com.microsoft.java.debug.core-0.35.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index ffc3ded41..44eecc5fb 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -41,17 +41,17 @@ io.reactivex.rxjava2 rxjava - 2.1.1 + 2.2.21 org.reactivestreams reactive-streams - 1.0.0 + 1.0.3 commons-io commons-io - 2.10.0 + 2.11.0 com.microsoft.java From 7f6f76be4fab5c94b6bc5b090f2d8a85b96bfb80 Mon Sep 17 00:00:00 2001 From: Karl von Randow Date: Thu, 17 Mar 2022 21:36:39 +1300 Subject: [PATCH 028/144] Report more detail in VM start errors (#398) * Report more detail in VM start errors Co-authored-by: Jinbo Wang --- .../java/debug/core/LaunchException.java | 51 +++++++++ .../adapter/handler/LaunchRequestHandler.java | 17 +++ .../internal/AdvancedLaunchingConnector.java | 104 ++++++++++++++++-- 3 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java new file mode 100644 index 000000000..6c1e1a513 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/LaunchException.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2018-2021 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core; + +import com.sun.jdi.connect.VMStartException; + +/** + * Extends {@link VMStartException} to provide more detail about the failed process + * from before it is destroyed. + */ +public class LaunchException extends VMStartException { + + boolean exited; + int exitStatus; + String stdout; + String stderr; + + public LaunchException(String message, Process process, boolean exited, int exitStatus, String stdout, String stderr) { + super(message, process); + this.exited = exited; + this.exitStatus = exitStatus; + this.stdout = stdout; + this.stderr = stderr; + } + + public boolean isExited() { + return exited; + } + + public int getExitStatus() { + return exitStatus; + } + + public String getStdout() { + return stdout; + } + + public String getStderr() { + return stderr; + } + +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 031a4826b..37182e9fd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -41,6 +41,7 @@ import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.LaunchException; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -245,6 +246,22 @@ protected CompletableFuture launch(LaunchArguments launchArguments, Re .subscribe((event) -> context.getProtocolServer().sendEvent(event)); debuggeeConsole.start(); resultFuture.complete(response); + } catch (LaunchException e) { + if (StringUtils.isNotBlank(e.getStdout())) { + OutputEvent event = convertToOutputEvent(e.getStdout(), Category.stdout, context); + context.getProtocolServer().sendEvent(event); + } + if (StringUtils.isNotBlank(e.getStderr())) { + OutputEvent event = convertToOutputEvent(e.getStderr(), Category.stderr, context); + context.getProtocolServer().sendEvent(event); + } + + resultFuture.completeExceptionally( + new DebugException( + String.format("Failed to launch debuggee VM. Reason: %s", e.getMessage()), + ErrorCode.LAUNCH_FAILURE.getId() + ) + ); } catch (IOException | IllegalConnectorArgumentsException | VMStartException e) { resultFuture.completeExceptionally( new DebugException( diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index 607d26a4e..3551ab2e6 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -13,16 +13,22 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import org.apache.commons.io.IOUtils; import org.eclipse.jdi.internal.VirtualMachineImpl; import org.eclipse.jdi.internal.VirtualMachineManagerImpl; import org.eclipse.jdi.internal.connect.SocketLaunchingConnectorImpl; import org.eclipse.jdi.internal.connect.SocketListeningConnectorImpl; import com.microsoft.java.debug.core.DebugUtility; +import com.microsoft.java.debug.core.LaunchException; import com.sun.jdi.VirtualMachine; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.IllegalConnectorArgumentsException; @@ -83,18 +89,100 @@ public VirtualMachine launch(Map connectionArgs) String address = listenConnector.startListening(args); String[] cmds = constructLaunchCommand(connectionArgs, address); - Process process = Runtime.getRuntime().exec(cmds, envVars, workingDir); - VirtualMachineImpl vm; + /* Launch the Java process */ + final Process process = Runtime.getRuntime().exec(cmds, envVars, workingDir); + + /* A Future that will be completed if we successfully connect to the launched process, or + will fail with an Exception if we do not. + */ + final CompletableFuture result = new CompletableFuture<>(); + + /* Listen for the debug connection from the Java process */ + CompletableFuture.runAsync(() -> { + try { + VirtualMachineImpl vm = (VirtualMachineImpl) listenConnector.accept(args); + vm.setLaunchedProcess(process); + result.complete(vm); + } catch (IllegalConnectorArgumentsException e) { + result.completeExceptionally(e); + } catch (IOException e) { + if (result.isDone()) { + /* The result Future has already been completed by the Process onExit hook */ + return; + } + + final String stdout = streamToString(process.getInputStream()); + final String stderr = streamToString(process.getErrorStream()); + + process.destroy(); + + result.completeExceptionally(new LaunchException( + String.format("VM did not connect within given time: %d ms", ACCEPT_TIMEOUT), + process, + false, + -1, + stdout, + stderr + )); + } catch (RuntimeException e) { + result.completeExceptionally(e); + } + }); + + /* Wait for the Java process to exit; if it exits before the debug connection is made, report it as an error. */ + process.onExit().thenAcceptAsync(theProcess -> { + if (result.isDone()) { + /* The result Future has already been completed by successfully connecting to the debug connection */ + return; + } + + final int exitStatus = theProcess.exitValue(); + final String stdout = streamToString(process.getInputStream()); + final String stderr = streamToString(process.getErrorStream()); + + result.completeExceptionally(new LaunchException( + String.format("VM exited with status %d", exitStatus), + theProcess, + true, + exitStatus, + stdout, + stderr + )); + + /* Stop the debug connection attempt */ + try { + listenConnector.stopListening(args); + } catch (IOException e) { + /* Ignore */ + } + }); + try { - vm = (VirtualMachineImpl) listenConnector.accept(args); - } catch (IOException | IllegalConnectorArgumentsException e) { - process.destroy(); - throw new VMStartException(String.format("VM did not connect within given time: %d ms", ACCEPT_TIMEOUT), process); + return result.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } else if (e.getCause() instanceof IllegalConnectorArgumentsException) { + throw (IllegalConnectorArgumentsException) e.getCause(); + } else if (e.getCause() instanceof VMStartException) { + throw (VMStartException) e.getCause(); + } else if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } else { + throw new IllegalStateException("Unexpected exception thrown when launching VM", e.getCause()); + } + } catch (InterruptedException e) { + throw new VMStartException("VM start interrupted", process); } + } - vm.setLaunchedProcess(process); - return vm; + private String streamToString(final InputStream inputStream) { + try { + return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } catch (IOException ioe) { + return null; + } } private static String[] constructLaunchCommand(Map launchingOptions, String address) { From 798d259d5078ccb81c9d233df67c70ca1553c259 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 22 Mar 2022 22:59:28 -0400 Subject: [PATCH 029/144] Send logpoint messages to the debug console (#402) * Send logpoint messages as protocol events Instead of printing them to `System.out` of the process being debugged. This should be paired with a change in vscode-java-debug to receive the message and send it to the vscode debug console. --- .../core/adapter/handler/SetBreakpointsRequestHandler.java | 7 +++++++ .../debug/plugin/internal/eval/JdtEvaluationProvider.java | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index fe246a61e..2bc2b5f05 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -44,6 +44,7 @@ import com.sun.jdi.BooleanValue; import com.sun.jdi.Field; import com.sun.jdi.ObjectReference; +import com.sun.jdi.StringReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; @@ -222,6 +223,12 @@ public static boolean handleEvaluationResult(IDebugAdapterContext context, Threa context.getProtocolServer().sendEvent(new Events.UserNotificationEvent( Events.UserNotificationEvent.NotificationType.ERROR, String.format("[Logpoint] Log message '%s' error: %s", breakpoint.getLogMessage(), ex.getMessage()))); + } else if (value != null) { + if (value instanceof StringReference) { + String message = ((StringReference) value).value(); + context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput( + message + System.lineSeparator())); + } } return true; } else { diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index 21e2dff6e..e014552a8 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -226,9 +226,9 @@ private String logMessageToExpression(String logMessage) { } if (arguments.size() > 0) { - return "System.out.println(String.format(\"" + format + "\"," + String.join(",", arguments) + "))"; + return "String.format(\"" + format + "\"," + String.join(",", arguments) + ")"; } else { - return "System.out.println(\"" + format + "\")"; + return "\"" + format + "\""; } } From f704199702097475e268a7b7aa25c84d046c5b38 Mon Sep 17 00:00:00 2001 From: Shi Chen Date: Thu, 24 Mar 2022 19:12:42 +0800 Subject: [PATCH 030/144] Support lazy loading object values from toString() (#401) --- .../handler/VariablesRequestHandler.java | 68 +++++++++++++++---- .../variables/VariableDetailUtils.java | 14 ++++ .../core/adapter/variables/VariableProxy.java | 13 +++- .../java/debug/core/protocol/Types.java | 9 +++ 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 26a0f7124..8f37ef574 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -47,6 +47,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.VariablesArguments; +import com.microsoft.java.debug.core.protocol.Types.VariablePresentationHint; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.AbsentInformationException; @@ -94,6 +95,15 @@ public CompletableFuture handle(Command command, Arguments arguments, } VariableProxy containerNode = (VariableProxy) container; + + if (containerNode.isLazyVariable() && DebugSettings.getCurrent().showToString) { + Types.Variable typedVariable = this.resolveLazyVariable(context, containerNode, variableFormatter, options, evaluationEngine); + if (typedVariable != null) { + list.add(typedVariable); + response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); + } + } List childrenList = new ArrayList<>(); IStackFrameManager stackFrameManager = context.getStackFrameManager(); String containerEvaluateName = containerNode.getEvaluateName(); @@ -266,10 +276,9 @@ public CompletableFuture handle(Command command, Arguments arguments, } } - int referenceId = 0; + VariableProxy varProxy = null; if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) { - VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName); - referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); + varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName); varProxy.setIndexedVariable(indexedVariables >= 0); varProxy.setUnboundedType(javaVariable.isUnboundedType()); } @@ -296,26 +305,38 @@ public CompletableFuture handle(Command command, Arguments arguments, typeString = ""; } - Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); - typedVariables.indexedVariables = Math.max(indexedVariables, 0); - String detailsValue = null; if (hasErrors) { // If failed to resolve the variable value, skip the details info as well. } else if (sizeValue != null) { detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options); } else if (DebugSettings.getCurrent().showToString) { - try { - detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); - } catch (OutOfMemoryError e) { - logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); - detailsValue = ""; - } catch (Exception e) { - logger.log(Level.SEVERE, "Failed to compute the toString() value", e); - detailsValue = ""; + if (VariableDetailUtils.isLazyLoadingSupported(value) && varProxy != null) { + varProxy.setLazyVariable(true); + } else { + try { + detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine); + } catch (OutOfMemoryError e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e); + detailsValue = ""; + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to compute the toString() value", e); + detailsValue = ""; + } } } + int referenceId = 0; + if (varProxy != null) { + referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); + } + + Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); + typedVariables.indexedVariables = Math.max(indexedVariables, 0); + if (varProxy != null && varProxy.isLazyVariable()) { + typedVariables.presentationHint = new VariablePresentationHint(true); + } + if (detailsValue != null) { typedVariables.value = typedVariables.value + " " + detailsValue; } @@ -331,6 +352,25 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter, + Map options, IEvaluationProvider evaluationEngine) { + VariableProxy valueReferenceProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), + containerNode.getProxiedVariable(), null /** container */, containerNode.getEvaluateName()); + valueReferenceProxy.setIndexedVariable(containerNode.isIndexedVariable()); + valueReferenceProxy.setUnboundedType(containerNode.isUnboundedType()); + int referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), valueReferenceProxy); + // this proxiedVariable is intermediate object, see https://github.com/microsoft/vscode/issues/135147#issuecomment-1076240074 + Object proxiedVariable = containerNode.getProxiedVariable(); + if (proxiedVariable instanceof ObjectReference) { + ObjectReference variable = (ObjectReference) proxiedVariable; + String valueString = variableFormatter.valueToString(variable, options); + String detailString = VariableDetailUtils.formatDetailsValue(variable, containerNode.getThread(), variableFormatter, options, + evaluationEngine); + return new Types.Variable("", valueString + " " + detailString, "", referenceId, containerNode.getEvaluateName()); + } + return null; + } + private Set getDuplicateNames(Collection list) { Set result = new HashSet<>(); Set set = new HashSet<>(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java index e82ab79f9..c6d0d38e0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableDetailUtils.java @@ -154,4 +154,18 @@ private static boolean isClassType(Value value, String typeName) { return Objects.equals(((ObjectReference) value).type().name(), typeName); } + + public static boolean isLazyLoadingSupported(Value value) { + if (isClassType(value, STRING_TYPE)) { + return false; + } + if (!(value instanceof ObjectReference)) { + return false; + } + String inheritedType = findInheritedType(value, COLLECTION_TYPES); + if (inheritedType == null && !containsToStringMethod((ObjectReference) value)) { + return false; + } + return true; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java index 9f208464c..5ebf93735 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableProxy.java @@ -24,6 +24,7 @@ public class VariableProxy { private final String evaluateName; private boolean isIndexedVariable; private boolean isUnboundedType = false; + private boolean isLazyVariable = false; /** * Create a variable reference. @@ -75,7 +76,8 @@ public boolean equals(Object obj) { } VariableProxy other = (VariableProxy) obj; return Objects.equals(scopeName, other.scopeName) && Objects.equals(getThreadId(), other.getThreadId()) - && Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName); + && Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName) + && Objects.equals(isLazyVariable, other.isLazyVariable); } public long getThreadId() { @@ -109,4 +111,13 @@ public boolean isUnboundedType() { public void setUnboundedType(boolean isUnboundedType) { this.isUnboundedType = isUnboundedType; } + + public boolean isLazyVariable() { + return isLazyVariable; + } + + public void setLazyVariable(boolean isLazyVariable) { + this.isLazyVariable = isLazyVariable; + } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 0b9a0494a..f0a91ce88 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -90,6 +90,7 @@ public static class Variable { public int namedVariables; public int indexedVariables; public String evaluateName; + public VariablePresentationHint presentationHint; /** * Constructor. @@ -343,6 +344,14 @@ public static class ExceptionDetails { public ExceptionDetails[] innerException; } + public static class VariablePresentationHint { + public boolean lazy; + + public VariablePresentationHint(boolean lazy) { + this.lazy = lazy; + } + } + public static class Capabilities { public boolean supportsConfigurationDoneRequest; public boolean supportsHitConditionalBreakpoints; From 8a1c9752e0df8ef241ba27fba1dedad8796691fa Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Mar 2022 12:48:22 +0800 Subject: [PATCH 031/144] customRequest 'processId' to get the debugging Java process id (#399) * customRequest 'processId' to get Java process id * send a custom notification when processId/shellProcessId is ready --- .../java/debug/core/adapter/DebugAdapter.java | 4 +- .../core/adapter/DebugAdapterContext.java | 23 ++++++++++ .../core/adapter/IDebugAdapterContext.java | 8 ++++ .../adapter/handler/LaunchRequestHandler.java | 11 +++++ .../handler/LaunchWithDebuggingDelegate.java | 10 +++++ .../LaunchWithoutDebuggingDelegate.java | 17 +++++++- .../adapter/handler/ProcessIdHandler.java | 43 +++++++++++++++++++ .../java/debug/core/protocol/Events.java | 22 ++++++++++ .../java/debug/core/protocol/Requests.java | 1 + .../java/debug/core/protocol/Responses.java | 29 +++++++++++-- 10 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 4342c48d3..afdb5759c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -32,6 +32,7 @@ import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler; import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.ProcessIdHandler; import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; @@ -125,10 +126,11 @@ private void initialize() { registerHandlerForDebug(new SetDataBreakpointsRequestHandler()); registerHandlerForDebug(new InlineValuesRequestHandler()); registerHandlerForDebug(new RefreshVariablesHandler()); + registerHandlerForDebug(new ProcessIdHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); - + registerHandlerForNoDebug(new ProcessIdHandler()); } private void registerHandlerForDebug(IDebugRequestHandler handler) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 395d39ec6..30fb12200 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -54,6 +54,9 @@ public class DebugAdapterContext implements IDebugAdapterContext { private Path classpathJar = null; private Path argsfile = null; + private long shellProcessId = -1; + private long processId = -1; + private IdCollection sourceReferences = new IdCollection<>(); private RecyclableObjectPool recyclableIdPool = new RecyclableObjectPool<>(); private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter(); @@ -326,4 +329,24 @@ public IBreakpointManager getBreakpointManager() { public IStepResultManager getStepResultManager() { return stepResultManager; } + + @Override + public long getProcessId() { + return this.processId; + } + + @Override + public long getShellProcessId() { + return this.shellProcessId; + } + + @Override + public void setProcessId(long processId) { + this.processId = processId; + } + + @Override + public void setShellProcessId(long shellProcessId) { + this.shellProcessId = shellProcessId; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 4f843fd09..a1b21ecee 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -128,4 +128,12 @@ public interface IDebugAdapterContext { IBreakpointManager getBreakpointManager(); IStepResultManager getStepResultManager(); + + void setShellProcessId(long shellProcessId); + + long getShellProcessId(); + + void setProcessId(long processId); + + long getProcessId(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 37182e9fd..fec967b84 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -138,6 +138,17 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } return launch(launchArguments, response, context).thenCompose(res -> { + long processId = context.getProcessId(); + long shellProcessId = context.getShellProcessId(); + if (context.getDebuggeeProcess() != null) { + processId = context.getDebuggeeProcess().pid(); + } + + // If processId or shellProcessId exist, send a notification to client. + if (processId > 0 || shellProcessId > 0) { + context.getProtocolServer().sendEvent(new Events.ProcessIdNotification(processId, shellProcessId)); + } + LaunchUtils.releaseTempLaunchFile(context.getClasspathJar()); LaunchUtils.releaseTempLaunchFile(context.getArgsfile()); if (res.success) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 138455025..11935fcbe 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.SystemUtils; import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.DebugSession; @@ -45,6 +46,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; import com.microsoft.java.debug.core.protocol.Requests.RunInTerminalRequestArguments; +import com.microsoft.java.debug.core.protocol.Responses.RunInTerminalResponseBody; import com.sun.jdi.VirtualMachine; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.IllegalConnectorArgumentsException; @@ -102,6 +104,14 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume if (runResponse != null) { if (runResponse.success) { try { + try { + RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson( + JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class); + context.setProcessId(terminalResponse.processId); + context.setShellProcessId(terminalResponse.shellProcessId); + } catch (JsonSyntaxException e) { + logger.severe("Failed to resolve runInTerminal response: " + e.toString()); + } VirtualMachine vm = listenConnector.accept(args); vmHandler.connectVirtualMachine(vm); context.setDebugSession(new DebugSession(vm)); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index c993dc5f1..7b9a5be10 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -21,6 +21,7 @@ import java.util.logging.Logger; import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.adapter.ErrorCode; @@ -33,6 +34,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; import com.microsoft.java.debug.core.protocol.Requests.RunInTerminalRequestArguments; +import com.microsoft.java.debug.core.protocol.Responses.RunInTerminalResponseBody; import com.sun.jdi.connect.IllegalConnectorArgumentsException; import com.sun.jdi.connect.VMStartException; @@ -112,8 +114,19 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume context.getProtocolServer().sendRequest(request, RUNINTERMINAL_TIMEOUT).whenComplete((runResponse, ex) -> { if (runResponse != null) { if (runResponse.success) { - // Without knowing the pid, debugger has lost control of the process. - // So simply send `terminated` event to end the session. + try { + RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson( + JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class); + context.setProcessId(terminalResponse.processId); + context.setShellProcessId(terminalResponse.shellProcessId); + } catch (JsonSyntaxException e) { + logger.severe("Failed to resolve runInTerminal response: " + e.toString()); + } + + // TODO: Since the RunInTerminal request will return the pid or parent shell + // pid now, the debugger is able to use this pid to monitor the lifecycle + // of the running Java process. There is no need to terminate the debug + // session early here. context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); resultFuture.complete(response); } else { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java new file mode 100644 index 000000000..d3eb5ad13 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ProcessIdHandler.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Responses.ProcessIdResponseBody; + +public class ProcessIdHandler implements IDebugRequestHandler { + @Override + public List getTargetCommands() { + return Arrays.asList(Command.PROCESSID); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + long processId = context.getProcessId(); + long shellProcessId = context.getShellProcessId(); + if (context.getDebuggeeProcess() != null) { + processId = context.getDebuggeeProcess().pid(); + } + + response.body = new ProcessIdResponseBody(processId, shellProcessId); + return CompletableFuture.completedFuture(response); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index 5397c418e..681ec543d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -283,4 +283,26 @@ public InvalidatedEvent(InvalidatedAreas area, int frameId) { this.frameId = frameId; } } + + public static class ProcessIdNotification extends DebugEvent { + /** + * The process ID. + */ + public long processId = -1; + /** + * The process ID of the terminal shell if the process is running in a terminal shell. + */ + public long shellProcessId = -1; + + public ProcessIdNotification(long processId) { + super("processid"); + this.processId = processId; + } + + public ProcessIdNotification(long processId, long shellProcessId) { + super("processid"); + this.processId = processId; + this.shellProcessId = shellProcessId; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index e31a65261..9b444155c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -410,6 +410,7 @@ public static enum Command { PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class), INLINEVALUES("inlineValues", InlineValuesArguments.class), REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), + PROCESSID("processId", Arguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index cc349b6e7..2210c8e93 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -39,11 +39,34 @@ public InitializeResponseBody(Types.Capabilities capabilities) { } } - public static class RunInTerminalResponseBody extends ResponseBody { - public int processId; + public static class ProcessIdResponseBody extends ResponseBody { + /** + * The process ID. + */ + public long processId = -1; + /** + * The process ID of the terminal shell if the process is running in a terminal shell. + */ + public long shellProcessId = -1; + + public ProcessIdResponseBody(long processId) { + this.processId = processId; + } - public RunInTerminalResponseBody(int processId) { + public ProcessIdResponseBody(long processId, long shellProcessId) { this.processId = processId; + this.shellProcessId = shellProcessId; + } + } + + public static class RunInTerminalResponseBody extends ProcessIdResponseBody { + + public RunInTerminalResponseBody(long processId) { + super(processId); + } + + public RunInTerminalResponseBody(long processId, long shellProcessId) { + super(processId, shellProcessId); } } From 625fe84cef4825a4b6b476d04383f5168e81708d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Mar 2022 15:06:44 +0800 Subject: [PATCH 032/144] Up version to 0.36.0 (#406) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 811a32a64..4c67c37f4 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.35.0 + 0.36.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 0362be354..94b396327 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 925c853aa..0f59e7eb9 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.35.0 +Bundle-Version: 0.36.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.3.jar, - lib/com.microsoft.java.debug.core-0.35.0.jar + lib/com.microsoft.java.debug.core-0.36.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 44eecc5fb..1e8e8c5d0 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.35.0 + 0.36.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.35.0 + 0.36.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 782edf279..ab60e9b37 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index b21f4ba96..9e02c6804 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.35.0 + 0.36.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 007cf78be..b60bc4cf5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.35.0 + 0.36.0 pom Java Debug Server for Visual Studio Code From e3cf474244163561d74c9a2dec3047fa0ed025ab Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 24 May 2022 14:41:36 +0800 Subject: [PATCH 033/144] Capture Java process pid and display debug toolbar when running Java in terminal (#413) * Capture Java process pid and display debug toolbar when running Java in terminal --- ...connectRequestWithoutDebuggingHandler.java | 9 +- .../core/adapter/handler/LaunchUtils.java | 190 +++++++++++++++++- .../handler/LaunchWithDebuggingDelegate.java | 8 +- .../LaunchWithoutDebuggingDelegate.java | 24 ++- 4 files changed, 222 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java index bb56d4052..59d750bcd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/DisconnectRequestWithoutDebuggingHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,6 +11,8 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.util.Optional; + import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; @@ -25,6 +27,11 @@ public void destroyDebugSession(Command command, Arguments arguments, Response r Process debuggeeProcess = context.getDebuggeeProcess(); if (debuggeeProcess != null && disconnectArguments.terminateDebuggee) { debuggeeProcess.destroy(); + } else if (context.getProcessId() > 0 && disconnectArguments.terminateDebuggee) { + Optional debuggeeHandle = ProcessHandle.of(context.getProcessId()); + if (debuggeeHandle.isPresent()) { + debuggeeHandle.get().destroy(); + } } } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index d0d99af40..fc89bfa7c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2021 Microsoft Corporation and others. +* Copyright (c) 2021-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,28 +11,39 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.adapter.AdapterUtils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; public class LaunchUtils { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static Set tempFilesInUse = new HashSet<>(); /** @@ -102,6 +113,171 @@ public static void releaseTempLaunchFile(Path tempFile) { } } + public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String javaCommand, int timeout/*ms*/) { + ProcessHandle shellProcess = ProcessHandle.of(shellPid).orElse(null); + if (shellProcess != null) { + int retry = 0; + final int INTERVAL = 100; + final int maxRetries = timeout / INTERVAL; + final boolean isCygwinShell = isCygwinShell(shellProcess.info().command().orElse(null)); + while (retry <= maxRetries) { + Optional subProcessHandle = shellProcess.descendants().filter(proc -> { + String command = proc.info().command().orElse(""); + return Objects.equals(command, javaCommand) || command.endsWith("\\java.exe") || command.endsWith("/java"); + }).findFirst(); + + if (subProcessHandle.isPresent()) { + logger.info("shellPid: " + shellPid + ", javaPid: " + subProcessHandle.get().pid()); + return subProcessHandle.get(); + } else if (isCygwinShell) { + long javaPid = findJavaProcessByCygwinPsCommand(shellProcess, javaCommand); + if (javaPid > 0) { + logger.info("[Cygwin Shell] shellPid: " + shellPid + ", javaPid: " + javaPid); + return ProcessHandle.of(javaPid).orElse(null); + } + } + + retry++; + if (retry > maxRetries) { + break; + } + + try { + Thread.sleep(INTERVAL); + } catch (InterruptedException e) { + // do nothing + } + logger.info("Retry to find Java subProcess of shell pid " + shellPid); + } + } + + return null; + } + + private static long findJavaProcessByCygwinPsCommand(ProcessHandle shellProcess, String javaCommand) { + String psCommand = detectPsCommandPath(shellProcess.info().command().orElse(null)); + if (psCommand == null) { + return -1; + } + + BufferedReader psReader = null; + List psProcs = new ArrayList<>(); + List javaCandidates = new ArrayList<>(); + try { + String[] headers = null; + int pidIndex = -1; + int ppidIndex = -1; + int winpidIndex = -1; + String line; + String javaExeName = Paths.get(javaCommand).toFile().getName().replaceFirst("\\.exe$", ""); + + Process p = Runtime.getRuntime().exec(new String[] {psCommand, "-l"}); + psReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + /** + * Here is a sample output when running ps command in Cygwin/MINGW64 shell. + * PID PPID PGID WINPID TTY UID STIME COMMAND + * 1869 1 1869 7852 cons2 4096 15:29:27 /usr/bin/bash + * 2271 1 2271 30820 cons4 4096 19:38:30 /usr/bin/bash + * 1812 1 1812 21540 cons1 4096 15:05:03 /usr/bin/bash + * 2216 1 2216 11328 cons3 4096 19:38:18 /usr/bin/bash + * 1720 1 1720 5404 cons0 4096 13:46:42 /usr/bin/bash + * 2269 2216 2269 6676 cons3 4096 19:38:21 /c/Program Files/Microsoft/jdk-11.0.14.9-hotspot/bin/java + * 1911 1869 1869 29708 cons2 4096 15:29:31 /c/Program Files/nodejs/node + * 2315 2271 2315 18064 cons4 4096 19:38:34 /usr/bin/ps + */ + while ((line = psReader.readLine()) != null) { + String[] cols = line.strip().split("\\s+"); + if (headers == null) { + headers = cols; + pidIndex = ArrayUtils.indexOf(headers, "PID"); + ppidIndex = ArrayUtils.indexOf(headers, "PPID"); + winpidIndex = ArrayUtils.indexOf(headers, "WINPID"); + if (pidIndex < 0 || ppidIndex < 0 || winpidIndex < 0) { + logger.warning("Failed to find Java process because ps command is not the standard Cygwin ps command."); + return -1; + } + } else if (cols.length >= headers.length) { + long pid = Long.parseLong(cols[pidIndex]); + long ppid = Long.parseLong(cols[ppidIndex]); + long winpid = Long.parseLong(cols[winpidIndex]); + PsProcess process = new PsProcess(pid, ppid, winpid); + psProcs.add(process); + if (cols[cols.length - 1].endsWith("/" + javaExeName) || cols[cols.length - 1].endsWith("/java")) { + javaCandidates.add(process); + } + } + } + } catch (Exception err) { + logger.log(Level.WARNING, "Failed to find Java process by Cygwin ps command.", err); + } finally { + if (psReader != null) { + try { + psReader.close(); + } catch (IOException e) { + // ignore + } + } + } + + if (!javaCandidates.isEmpty()) { + Set descendantWinpids = shellProcess.descendants().map(proc -> proc.pid()).collect(Collectors.toSet()); + long shellWinpid = shellProcess.pid(); + for (PsProcess javaCandidate: javaCandidates) { + if (descendantWinpids.contains(javaCandidate.winpid)) { + return javaCandidate.winpid; + } + + for (PsProcess psProc : psProcs) { + if (javaCandidate.ppid != psProc.pid) { + continue; + } + + if (descendantWinpids.contains(psProc.winpid) || psProc.winpid == shellWinpid) { + return javaCandidate.winpid; + } + + break; + } + } + } + + return -1; + } + + private static boolean isCygwinShell(String shellPath) { + if (!SystemUtils.IS_OS_WINDOWS || shellPath == null) { + return false; + } + + String lowerShellPath = shellPath.toLowerCase(); + return lowerShellPath.endsWith("git\\bin\\bash.exe") + || lowerShellPath.endsWith("git\\usr\\bin\\bash.exe") + || lowerShellPath.endsWith("mintty.exe") + || lowerShellPath.endsWith("cygwin64\\bin\\bash.exe") + || (lowerShellPath.endsWith("bash.exe") && detectPsCommandPath(shellPath) != null) + || (lowerShellPath.endsWith("sh.exe") && detectPsCommandPath(shellPath) != null); + } + + private static String detectPsCommandPath(String shellPath) { + if (shellPath == null) { + return null; + } + + Path psPath = Paths.get(shellPath, "..\\ps.exe"); + if (!Files.exists(psPath)) { + psPath = Paths.get(shellPath, "..\\..\\usr\\bin\\ps.exe"); + if (!Files.exists(psPath)) { + psPath = null; + } + } + + if (psPath == null) { + return null; + } + + return psPath.normalize().toString(); + } + private static Path tmpdir = null; private static synchronized Path getTmpDir() throws IOException { @@ -156,4 +332,16 @@ private static String getMd5(String input) { return Integer.toString(input.hashCode(), Character.MAX_RADIX); } } + + private static class PsProcess { + long pid; + long ppid; + long winpid; + + public PsProcess(long pid, long ppid, long winpid) { + this.pid = pid; + this.ppid = ppid; + this.winpid = winpid; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 11935fcbe..6506e93f0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -116,6 +116,12 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume vmHandler.connectVirtualMachine(vm); context.setDebugSession(new DebugSession(vm)); logger.info("Launching debuggee in terminal console succeeded."); + if (context.getShellProcessId() > 0) { + ProcessHandle debuggeeProcess = LaunchUtils.findJavaProcessInTerminalShell(context.getShellProcessId(), cmds[0], 0); + if (debuggeeProcess != null) { + context.setProcessId(debuggeeProcess.pid()); + } + } resultFuture.complete(response); } catch (TransportTimeoutException e) { int commandLength = StringUtils.length(launchArguments.cwd) + 1; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index 7b9a5be10..1718e6a36 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -114,20 +114,32 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume context.getProtocolServer().sendRequest(request, RUNINTERMINAL_TIMEOUT).whenComplete((runResponse, ex) -> { if (runResponse != null) { if (runResponse.success) { + ProcessHandle debuggeeProcess = null; try { RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson( JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class); context.setProcessId(terminalResponse.processId); context.setShellProcessId(terminalResponse.shellProcessId); + + if (terminalResponse.processId > 0) { + debuggeeProcess = ProcessHandle.of(terminalResponse.processId).orElse(null); + } else if (terminalResponse.shellProcessId > 0) { + debuggeeProcess = LaunchUtils.findJavaProcessInTerminalShell(terminalResponse.shellProcessId, cmds[0], 3000); + } + + if (debuggeeProcess != null) { + context.setProcessId(debuggeeProcess.pid()); + debuggeeProcess.onExit().thenAcceptAsync(proc -> { + context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); + }); + } } catch (JsonSyntaxException e) { logger.severe("Failed to resolve runInTerminal response: " + e.toString()); } - // TODO: Since the RunInTerminal request will return the pid or parent shell - // pid now, the debugger is able to use this pid to monitor the lifecycle - // of the running Java process. There is no need to terminate the debug - // session early here. - context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); + if (debuggeeProcess == null || !debuggeeProcess.isAlive()) { + context.getProtocolServer().sendEvent(new Events.TerminatedEvent()); + } resultFuture.complete(response); } else { resultFuture.completeExceptionally( From 9e017f3382b5319dc8ba918d60df01736af4afd5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 24 May 2022 16:10:52 +0800 Subject: [PATCH 034/144] Naming Java terminal with mainClass name (#414) * Naming Java terminal with mainClass name --- .../core/adapter/handler/LaunchWithDebuggingDelegate.java | 7 ++++--- .../adapter/handler/LaunchWithoutDebuggingDelegate.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java index 6506e93f0..2962294a0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java @@ -58,7 +58,6 @@ public class LaunchWithDebuggingDelegate implements ILaunchDelegate { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final int ATTACH_TERMINAL_TIMEOUT = 20 * 1000; - private static final String TERMINAL_TITLE = "Java Debug Console"; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; private VMHandler vmHandler = new VMHandler(); @@ -77,6 +76,8 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume ((Connector.IntegerArgument) args.get("timeout")).setValue(ATTACH_TERMINAL_TIMEOUT); String address = listenConnector.startListening(args); + final String[] names = launchArguments.mainClass.split("[/\\.]"); + final String terminalName = "Debug: " + names[names.length - 1]; String[] cmds = LaunchRequestHandler.constructLaunchCommands(launchArguments, false, address); RunInTerminalRequestArguments requestArgs = null; if (launchArguments.console == CONSOLE.integratedTerminal) { @@ -84,13 +85,13 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume cmds, launchArguments.cwd, launchArguments.env, - TERMINAL_TITLE); + terminalName); } else { requestArgs = RunInTerminalRequestArguments.createExternalTerminal( cmds, launchArguments.cwd, launchArguments.env, - TERMINAL_TITLE); + terminalName); } Request request = new Request(Command.RUNINTERMINAL.getName(), (JsonObject) JsonUtils.toJsonTree(requestArgs, RunInTerminalRequestArguments.class)); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java index 1718e6a36..82a481718 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java @@ -40,7 +40,6 @@ public class LaunchWithoutDebuggingDelegate implements ILaunchDelegate { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - protected static final String TERMINAL_TITLE = "Java Process Console"; protected static final long RUNINTERMINAL_TIMEOUT = 10 * 1000; private Consumer terminateHandler; @@ -92,14 +91,16 @@ public CompletableFuture launchInTerminal(LaunchArguments launchArgume final String launchInTerminalErrorFormat = "Failed to launch debuggee in terminal. Reason: %s"; + final String[] names = launchArguments.mainClass.split("[/\\.]"); + final String terminalName = "Run: " + names[names.length - 1]; String[] cmds = LaunchRequestHandler.constructLaunchCommands(launchArguments, false, null); RunInTerminalRequestArguments requestArgs = null; if (launchArguments.console == CONSOLE.integratedTerminal) { requestArgs = RunInTerminalRequestArguments.createIntegratedTerminal(cmds, launchArguments.cwd, - launchArguments.env, TERMINAL_TITLE); + launchArguments.env, terminalName); } else { requestArgs = RunInTerminalRequestArguments.createExternalTerminal(cmds, launchArguments.cwd, - launchArguments.env, TERMINAL_TITLE); + launchArguments.env, terminalName); } Request request = new Request(Command.RUNINTERMINAL.getName(), (JsonObject) JsonUtils.toJsonTree(requestArgs, RunInTerminalRequestArguments.class)); From 4dacc167891c0a60e6db0644f67513afeb26ae7b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 30 May 2022 12:59:05 +0800 Subject: [PATCH 035/144] bump version to 0.37.0 (#415) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 4c67c37f4..25f51106c 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.36.0 + 0.37.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 94b396327..c8b5582ff 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 0f59e7eb9..e28ec94a2 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.36.0 +Bundle-Version: 0.37.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.3.jar, - lib/com.microsoft.java.debug.core-0.36.0.jar + lib/com.microsoft.java.debug.core-0.37.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 1e8e8c5d0..5f73e6b7b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.36.0 + 0.37.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.36.0 + 0.37.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index ab60e9b37..1fc613e25 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9e02c6804..d7770f079 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.36.0 + 0.37.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index b60bc4cf5..3a37847b8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.36.0 + 0.37.0 pom Java Debug Server for Visual Studio Code From f8f34858446e8d7cd34aad31ac8f5cd80ba525fc Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 31 May 2022 15:17:57 +0800 Subject: [PATCH 036/144] Increase polling frequency to ensure that Java processes are detected (#416) * Increase polling frequency to ensure that Java processes are detected --- .../java/debug/core/adapter/handler/LaunchUtils.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index fc89bfa7c..ca7d69a99 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -117,7 +117,7 @@ public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String ProcessHandle shellProcess = ProcessHandle.of(shellPid).orElse(null); if (shellProcess != null) { int retry = 0; - final int INTERVAL = 100; + final int INTERVAL = 20/*ms*/; final int maxRetries = timeout / INTERVAL; final boolean isCygwinShell = isCygwinShell(shellProcess.info().command().orElse(null)); while (retry <= maxRetries) { @@ -127,11 +127,17 @@ public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String }).findFirst(); if (subProcessHandle.isPresent()) { + if (retry > 0) { + logger.info("Retried " + retry + " times to find Java subProcess."); + } logger.info("shellPid: " + shellPid + ", javaPid: " + subProcessHandle.get().pid()); return subProcessHandle.get(); } else if (isCygwinShell) { long javaPid = findJavaProcessByCygwinPsCommand(shellProcess, javaCommand); if (javaPid > 0) { + if (retry > 0) { + logger.info("Retried " + retry + " times to find Java subProcess."); + } logger.info("[Cygwin Shell] shellPid: " + shellPid + ", javaPid: " + javaPid); return ProcessHandle.of(javaPid).orElse(null); } @@ -147,8 +153,9 @@ public static ProcessHandle findJavaProcessInTerminalShell(long shellPid, String } catch (InterruptedException e) { // do nothing } - logger.info("Retry to find Java subProcess of shell pid " + shellPid); } + + logger.info("Retried " + retry + " times but failed to find Java subProcess of shell pid " + shellPid); } return null; From f47241fcad3ebf0f1e8a1e32b6a0c880eee97238 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 7 Jun 2022 17:13:43 +0800 Subject: [PATCH 037/144] Update Java deps (#417) * Update Java deps --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 2 +- com.microsoft.java.debug.plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 25f51106c..275da47c4 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -57,7 +57,7 @@ org.reactivestreams reactive-streams - 1.0.3 + 1.0.4 commons-io diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c8b5582ff..837ca9ac0 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -10,7 +10,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index e28ec94a2..9a8f02795 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -24,5 +24,5 @@ Require-Bundle: org.eclipse.core.runtime, Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, - lib/reactive-streams-1.0.3.jar, + lib/reactive-streams-1.0.4.jar, lib/com.microsoft.java.debug.core-0.37.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 5f73e6b7b..944690f7e 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -46,7 +46,7 @@ org.reactivestreams reactive-streams - 1.0.3 + 1.0.4 commons-io From d1f2da599ac2e033618d28afab61a475784a5289 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 8 Jun 2022 17:44:06 +0800 Subject: [PATCH 038/144] Fix CI to support JDK 17 as build JDK (#420) --- .github/workflows/build.yml | 12 ++++++------ pom.xml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58381df99..41c85cb62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Cache local Maven repository uses: actions/cache@v2 @@ -45,10 +45,10 @@ jobs: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Cache local Maven repository uses: actions/cache@v2 @@ -71,10 +71,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Cache local Maven repository uses: actions/cache@v2 diff --git a/pom.xml b/pom.xml index 3a37847b8..e8ff76899 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 1.5.1 + 2.7.3 ${basedir} From 57a98e267c94e9354c51efbc6c9b378215dd1eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Wed, 8 Jun 2022 17:29:02 +0200 Subject: [PATCH 039/144] Mark native frames and unavailable methods as subtle (#409) This causes some clients to display the frames in a different way, e.g by graying them out. --- .../core/adapter/handler/StackTraceRequestHandler.java | 4 +++- .../com/microsoft/java/debug/core/protocol/Types.java | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 089a55b9d..021bae609 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -95,7 +95,9 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame String methodName = formatMethodName(method, true, true); int lineNumber = AdapterUtils.convertLineNumber(location.lineNumber(), context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); // Line number returns -1 if the information is not available; specifically, always returns -1 for native methods. + String presentationHint = null; if (lineNumber < 0) { + presentationHint = "subtle"; if (method.isNative()) { // For native method, display a tip text "native method" in the Call Stack View. methodName += "[native method]"; @@ -105,7 +107,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame clientSource = null; } } - return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0); + return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); } private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdapterContext context) throws URISyntaxException { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index f0a91ce88..e59f65f74 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -43,6 +43,8 @@ public static class StackFrame { public int line; public int column; public String name; + public String presentationHint; + /** * Constructs a StackFrame with the given information. @@ -57,13 +59,17 @@ public static class StackFrame { * line number of the stack frame * @param col * column number of the stack frame + * @param presentationHint + * An optional hint for how to present this frame in the UI. + * Values: 'normal', 'label', 'subtle' */ - public StackFrame(int id, String name, Source src, int ln, int col) { + public StackFrame(int id, String name, Source src, int ln, int col, String presentationHint) { this.id = id; this.name = name; this.source = src; this.line = ln; this.column = col; + this.presentationHint = presentationHint; } } From 9bb983200e3176beb579238f68662fa4317c76b5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 28 Jun 2022 17:32:01 +0800 Subject: [PATCH 040/144] Support method breakpoints (#424) * Add support for method breakpoints * Handle the duplicated MethodEntryEvent better Co-authored-by: Gayan Perera --- .../java/debug/core/DebugSession.java | 6 + .../java/debug/core/IDebugSession.java | 2 +- .../java/debug/core/IMethodBreakpoint.java | 33 +++ .../java/debug/core/MethodBreakpoint.java | 258 ++++++++++++++++++ .../debug/core/adapter/BreakpointManager.java | 60 ++++ .../java/debug/core/adapter/DebugAdapter.java | 3 +- .../core/adapter/IBreakpointManager.java | 20 ++ .../handler/InitializeRequestHandler.java | 1 + .../SetFunctionBreakpointsRequestHandler.java | 189 +++++++++++++ .../java/debug/core/protocol/Types.java | 1 + 10 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index a56eaf695..ab4341f51 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -146,4 +146,10 @@ public IEventHub getEventHub() { public VirtualMachine getVM() { return vm; } + + @Override + public IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, + int hitCount) { + return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 24138fef1..03780c2d9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -36,7 +36,7 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters); - // TODO: createFunctionBreakpoint + IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount); Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java new file mode 100644 index 000000000..668884567 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.core; + +import java.util.concurrent.CompletableFuture; + +public interface IMethodBreakpoint extends IDebugResource { + String methodName(); + + String className(); + + int getHitCount(); + + String getCondition(); + + void setHitCount(int hitCount); + + void setCondition(String condition); + + CompletableFuture install(); + + Object getProperty(Object key); + + void putProperty(Object key, Object value); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java new file mode 100644 index 000000000..82c5b75e2 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java @@ -0,0 +1,258 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.lang3.StringUtils; + +import com.sun.jdi.ReferenceType; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.VMDisconnectedException; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ClassPrepareEvent; +import com.sun.jdi.event.ThreadDeathEvent; +import com.sun.jdi.request.ClassPrepareRequest; +import com.sun.jdi.request.EventRequest; +import com.sun.jdi.request.MethodEntryRequest; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; + +public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoint { + + private VirtualMachine vm; + private IEventHub eventHub; + private String className; + private String functionName; + private String condition; + private int hitCount; + + private HashMap propertyMap = new HashMap<>(); + private Object compiledConditionalExpression = null; + private Map compiledExpressions = new ConcurrentHashMap<>(); + + private List requests = new ArrayList<>(); + private List subscriptions = new ArrayList<>(); + + public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, String functionName, + String condition, int hitCount) { + Objects.requireNonNull(vm); + Objects.requireNonNull(eventHub); + Objects.requireNonNull(className); + Objects.requireNonNull(functionName); + this.vm = vm; + this.eventHub = eventHub; + this.className = className; + this.functionName = functionName; + this.condition = condition; + this.hitCount = hitCount; + } + + @Override + public List requests() { + return requests; + } + + @Override + public List subscriptions() { + return subscriptions; + } + + @Override + public void close() throws Exception { + try { + vm.eventRequestManager().deleteEventRequests(requests()); + } catch (VMDisconnectedException ex) { + // ignore since removing breakpoints is meaningless when JVM is terminated. + } + subscriptions().forEach(Disposable::dispose); + requests.clear(); + subscriptions.clear(); + } + + @Override + public boolean containsEvaluatableExpression() { + return containsConditionalExpression() || containsLogpointExpression(); + } + + @Override + public boolean containsConditionalExpression() { + return StringUtils.isNotBlank(getCondition()); + } + + @Override + public boolean containsLogpointExpression() { + return false; + } + + @Override + public String getCondition() { + return condition; + } + + @Override + public void setCondition(String condition) { + this.condition = condition; + setCompiledConditionalExpression(null); + compiledExpressions.clear(); + } + + @Override + public String getLogMessage() { + return null; + } + + @Override + public void setLogMessage(String logMessage) { + // for future implementation + } + + @Override + public void setCompiledConditionalExpression(Object compiledExpression) { + this.compiledConditionalExpression = compiledExpression; + } + + @Override + public Object getCompiledConditionalExpression() { + return compiledConditionalExpression; + } + + @Override + public void setCompiledLogpointExpression(Object compiledExpression) { + // for future implementation + } + + @Override + public Object getCompiledLogpointExpression() { + return null; + } + + @Override + public void setCompiledExpression(long threadId, Object compiledExpression) { + compiledExpressions.put(threadId, compiledExpression); + } + + @Override + public Object getCompiledExpression(long threadId) { + return compiledExpressions.get(threadId); + } + + @Override + public int getHitCount() { + return hitCount; + } + + @Override + public void setHitCount(int hitCount) { + this.hitCount = hitCount; + Observable.fromIterable(this.requests()) + .filter(request -> request instanceof MethodEntryRequest) + .subscribe(request -> { + request.addCountFilter(hitCount); + request.enable(); + }); + } + + @Override + public CompletableFuture install() { + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ThreadDeathEvent) + .subscribe(debugEvent -> { + ThreadReference deathThread = ((ThreadDeathEvent) debugEvent.event).thread(); + compiledExpressions.remove(deathThread.uniqueID()); + }); + + subscriptions.add(subscription); + + // It's possible that different class loaders create new class with the same + // name. + // Here to listen to future class prepare events to handle such case. + ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); + classPrepareRequest.addClassFilter(className); + classPrepareRequest.enable(); + requests.add(classPrepareRequest); + + CompletableFuture future = new CompletableFuture<>(); + subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent + && (classPrepareRequest.equals(debugEvent.event.request()))) + .subscribe(debugEvent -> { + ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; + Optional createdRequest = createMethodEntryRequest(event.referenceType()); + if (createdRequest.isPresent()) { + MethodEntryRequest methodEntryRequest = createdRequest.get(); + requests.add(methodEntryRequest); + if (!future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + } + }); + subscriptions.add(subscription); + + List types = vm.classesByName(className); + for (ReferenceType type : types) { + Optional createdRequest = createMethodEntryRequest(type); + if (createdRequest.isPresent()) { + MethodEntryRequest methodEntryRequest = createdRequest.get(); + requests.add(methodEntryRequest); + if (!future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + } + } + return future; + } + + private Optional createMethodEntryRequest(ReferenceType type) { + return type.methodsByName(functionName).stream().findFirst().map(method -> { + MethodEntryRequest request = vm.eventRequestManager().createMethodEntryRequest(); + + request.addClassFilter(type); + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (hitCount > 0) { + request.addCountFilter(hitCount); + } + request.enable(); + return request; + }); + } + + @Override + public Object getProperty(Object key) { + return propertyMap.get(key); + } + + @Override + public void putProperty(Object key, Object value) { + propertyMap.put(key, value); + } + + @Override + public String methodName() { + return functionName; + } + + @Override + public String className() { + return className; + } + +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java index 78898df73..4b0a7ae89 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java @@ -25,6 +25,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.IMethodBreakpoint; import com.microsoft.java.debug.core.IWatchpoint; public class BreakpointManager implements IBreakpointManager { @@ -35,6 +36,7 @@ public class BreakpointManager implements IBreakpointManager { private List breakpoints; private Map> sourceToBreakpoints; private Map watchpoints; + private Map methodBreakpoints; private AtomicInteger nextBreakpointId = new AtomicInteger(1); /** @@ -44,6 +46,7 @@ public BreakpointManager() { this.breakpoints = Collections.synchronizedList(new ArrayList<>(5)); this.sourceToBreakpoints = new HashMap<>(); this.watchpoints = new HashMap<>(); + this.methodBreakpoints = new HashMap<>(); } @Override @@ -208,4 +211,61 @@ private String getWatchpointKey(IWatchpoint watchpoint) { public IWatchpoint[] getWatchpoints() { return this.watchpoints.values().stream().filter(wp -> wp != null).toArray(IWatchpoint[]::new); } + + @Override + public IMethodBreakpoint[] getMethodBreakpoints() { + return this.methodBreakpoints.values().stream().filter(Objects::nonNull).toArray(IMethodBreakpoint[]::new); + } + + @Override + public IMethodBreakpoint[] setMethodBreakpoints(IMethodBreakpoint[] breakpoints) { + List result = new ArrayList<>(); + List toAdds = new ArrayList<>(); + List toRemoves = new ArrayList<>(); + + Set visitedKeys = new HashSet<>(); + for (IMethodBreakpoint change : breakpoints) { + if (change == null) { + result.add(change); + continue; + } + + String key = getMethodBreakpointKey(change); + IMethodBreakpoint cache = methodBreakpoints.get(key); + if (cache != null) { + visitedKeys.add(key); + result.add(cache); + } else { + toAdds.add(change); + result.add(change); + } + } + + for (IMethodBreakpoint cache : methodBreakpoints.values()) { + if (!visitedKeys.contains(getMethodBreakpointKey(cache))) { + toRemoves.add(cache); + } + } + + for (IMethodBreakpoint toRemove : toRemoves) { + try { + // Destroy the method breakpoint on the debugee VM. + toRemove.close(); + this.methodBreakpoints.remove(getMethodBreakpointKey(toRemove)); + } catch (Exception e) { + logger.log(Level.SEVERE, String.format("Remove the method breakpoint exception: %s", e.toString()), e); + } + } + + for (IMethodBreakpoint toAdd : toAdds) { + toAdd.putProperty("id", this.nextBreakpointId.getAndIncrement()); + this.methodBreakpoints.put(getMethodBreakpointKey(toAdd), toAdd); + } + + return result.toArray(new IMethodBreakpoint[0]); + } + + private String getMethodBreakpointKey(IMethodBreakpoint breakpoint) { + return breakpoint.className() + "#" + breakpoint.methodName(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index afdb5759c..0bf5d2683 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -39,6 +39,7 @@ import com.microsoft.java.debug.core.adapter.handler.SetBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetDataBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetExceptionBreakpointsRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.SetFunctionBreakpointsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SetVariableRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SourceRequestHandler; import com.microsoft.java.debug.core.adapter.handler.StackTraceRequestHandler; @@ -127,7 +128,7 @@ private void initialize() { registerHandlerForDebug(new InlineValuesRequestHandler()); registerHandlerForDebug(new RefreshVariablesHandler()); registerHandlerForDebug(new ProcessIdHandler()); - + registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); registerHandlerForNoDebug(new ProcessIdHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java index 9ba609221..196714d52 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java @@ -12,6 +12,7 @@ package com.microsoft.java.debug.core.adapter; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.IMethodBreakpoint; import com.microsoft.java.debug.core.IWatchpoint; public interface IBreakpointManager { @@ -69,4 +70,23 @@ public interface IBreakpointManager { * Returns all registered watchpoints. */ IWatchpoint[] getWatchpoints(); + + /** + * Returns all the registered method breakpoints. + */ + IMethodBreakpoint[] getMethodBreakpoints(); + + /** + * Update the method breakpoints list. If the requested method breakpoints + * already registered in the breakpoint + * manager, reuse the cached one. Otherwise register the requested method + * breakpoints as a new method breakpoints. + * Besides, delete those not existed any more. + * + * @param methodBreakpoints + * the method breakpoints requested by client + * @return the full registered method breakpoints list + */ + IMethodBreakpoint[] setMethodBreakpoints(IMethodBreakpoint[] methodBreakpoints); + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index 4733170eb..ae92f356f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -62,6 +62,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.exceptionBreakpointFilters = exceptionFilters; caps.supportsExceptionInfoRequest = true; caps.supportsDataBreakpoints = true; + caps.supportsFunctionBreakpoints = true; caps.supportsClipboardContext = true; response.body = caps; return CompletableFuture.completedFuture(response); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java new file mode 100644 index 000000000..fa4d23388 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -0,0 +1,189 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; + +import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.IEvaluatableBreakpoint; +import com.microsoft.java.debug.core.IMethodBreakpoint; +import com.microsoft.java.debug.core.MethodBreakpoint; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.IEvaluationProvider; +import com.microsoft.java.debug.core.protocol.Events; +import com.microsoft.java.debug.core.protocol.Events.BreakpointEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.SetFunctionBreakpointsArguments; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Types.Breakpoint; +import com.microsoft.java.debug.core.protocol.Types.FunctionBreakpoint; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.event.MethodEntryEvent; + +public class SetFunctionBreakpointsRequestHandler implements IDebugRequestHandler { + private boolean registered = false; + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.SETFUNCTIONBREAKPOINTS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + if (context.getDebugSession() == null) { + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, + "Empty debug session."); + } + + if (!registered) { + registered = true; + registerMethodBreakpointHandler(context); + } + + SetFunctionBreakpointsArguments funcBpArgs = (SetFunctionBreakpointsArguments) arguments; + IMethodBreakpoint[] requestedMethodBreakpoints = (funcBpArgs.breakpoints == null) ? new IMethodBreakpoint[0] + : new MethodBreakpoint[funcBpArgs.breakpoints.length]; + for (int i = 0; i < requestedMethodBreakpoints.length; i++) { + FunctionBreakpoint funcBreakpoint = funcBpArgs.breakpoints[i]; + if (funcBreakpoint.name != null) { + String[] segments = funcBreakpoint.name.split("#"); + if (segments.length == 2 && StringUtils.isNotBlank(segments[0]) + && StringUtils.isNotBlank(segments[1])) { + int hitCount = 0; + try { + hitCount = Integer.parseInt(funcBreakpoint.hitCondition); + } catch (NumberFormatException e) { + hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. + } + requestedMethodBreakpoints[i] = context.getDebugSession().createFunctionBreakpoint(segments[0], + segments[1], + funcBreakpoint.condition, hitCount); + } + } + } + + IMethodBreakpoint[] currentMethodBreakpoints = context.getBreakpointManager() + .setMethodBreakpoints(requestedMethodBreakpoints); + List breakpoints = new ArrayList<>(); + for (int i = 0; i < currentMethodBreakpoints.length; i++) { + if (currentMethodBreakpoints[i] == null) { + breakpoints.add(new Breakpoint(false)); + continue; + } + + // If the requested method breakpoint exists in the manager, it will reuse + // the cached breakpoint exists object. + // Otherwise add the requested method breakpoint to the cache. + // So if the returned method breakpoint from the manager is same as the + // requested method breakpoint, this means it's a new method breakpoint, need + // install it. + if (currentMethodBreakpoints[i] == requestedMethodBreakpoints[i]) { + currentMethodBreakpoints[i].install().thenAccept(wp -> { + BreakpointEvent bpEvent = new BreakpointEvent("changed", convertDebuggerMethodToClient(wp)); + context.getProtocolServer().sendEvent(bpEvent); + }); + } else { + if (currentMethodBreakpoints[i].getHitCount() != requestedMethodBreakpoints[i].getHitCount()) { + currentMethodBreakpoints[i].setHitCount(requestedMethodBreakpoints[i].getHitCount()); + } + + if (!Objects.equals(currentMethodBreakpoints[i].getCondition(), + requestedMethodBreakpoints[i].getCondition())) { + currentMethodBreakpoints[i].setCondition(requestedMethodBreakpoints[i].getCondition()); + } + } + + breakpoints.add(convertDebuggerMethodToClient(currentMethodBreakpoints[i])); + } + + response.body = new Responses.SetDataBreakpointsResponseBody(breakpoints); + return CompletableFuture.completedFuture(response); + } + + private Breakpoint convertDebuggerMethodToClient(IMethodBreakpoint methodBreakpoint) { + return new Breakpoint((int) methodBreakpoint.getProperty("id"), + methodBreakpoint.getProperty("verified") != null && (boolean) methodBreakpoint.getProperty("verified")); + } + + private void registerMethodBreakpointHandler(IDebugAdapterContext context) { + IDebugSession debugSession = context.getDebugSession(); + if (debugSession != null) { + debugSession.getEventHub().events().filter(debugEvent -> debugEvent.event instanceof MethodEntryEvent) + .subscribe(debugEvent -> { + MethodEntryEvent methodEntryEvent = (MethodEntryEvent) debugEvent.event; + ThreadReference bpThread = methodEntryEvent.thread(); + IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); + + // Find the method breakpoint related to this method entry event + IMethodBreakpoint methodBreakpoint = Stream + .of(context.getBreakpointManager().getMethodBreakpoints()) + .filter(mp -> { + return mp.requests().contains(methodEntryEvent.request()) + && matches(methodEntryEvent, mp); + }) + .findFirst().orElse(null); + + if (methodBreakpoint != null) { + if (methodBreakpoint instanceof IEvaluatableBreakpoint + && ((IEvaluatableBreakpoint) methodBreakpoint).containsConditionalExpression()) { + if (engine.isInEvaluation(bpThread)) { + return; + } + CompletableFuture.runAsync(() -> { + engine.evaluateForBreakpoint((IEvaluatableBreakpoint) methodBreakpoint, bpThread) + .whenComplete((value, ex) -> { + boolean resume = SetBreakpointsRequestHandler.handleEvaluationResult( + context, bpThread, (IEvaluatableBreakpoint) methodBreakpoint, + value, + ex); + // Clear the evaluation environment caused by above evaluation. + engine.clearState(bpThread); + + if (resume) { + debugEvent.eventSet.resume(); + } else { + context.getProtocolServer().sendEvent(new Events.StoppedEvent( + "function breakpoint", bpThread.uniqueID())); + } + }); + }); + + } else { + context.getProtocolServer() + .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); + } + + debugEvent.shouldResume = false; + } + }); + } + } + + private boolean matches(MethodEntryEvent methodEntryEvent, IMethodBreakpoint breakpoint) { + return breakpoint.className().equals(methodEntryEvent.location().declaringType().name()) + && breakpoint.methodName().equals(methodEntryEvent.method().name()); + } + +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index e59f65f74..cd20faf4f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -374,5 +374,6 @@ public static class Capabilities { public ExceptionBreakpointFilter[] exceptionBreakpointFilters = new ExceptionBreakpointFilter[0]; public boolean supportsDataBreakpoints; public boolean supportsClipboardContext; + public boolean supportsFunctionBreakpoints; } } From 2461b037178f16aa0801386d78e3a9400f6fdfbe Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 29 Jun 2022 09:39:55 +0800 Subject: [PATCH 041/144] Up version to 0.38.0 (#425) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 275da47c4..b866771cc 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.37.0 + 0.38.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 837ca9ac0..94fa67e10 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 9a8f02795..a082aef62 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.37.0 +Bundle-Version: 0.38.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.37.0.jar + lib/com.microsoft.java.debug.core-0.38.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 944690f7e..8a65029aa 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.37.0 + 0.38.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.37.0 + 0.38.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 1fc613e25..2b3229b2d 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index d7770f079..5e3c1ed1d 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.37.0 + 0.38.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index e8ff76899..0e0c0ffee 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.37.0 + 0.38.0 pom Java Debug Server for Visual Studio Code From 1fcbe706dbdea171b0b1ab1348d46e5f9a6a1a31 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 19 Jul 2022 04:23:08 +0200 Subject: [PATCH 042/144] Improve support for method breakpoints (#426) Now method breakpoints can added by adding a line breakpoint at method header. --- .../microsoft/java/debug/core/Breakpoint.java | 35 ++++++- .../java/debug/core/IBreakpoint.java | 3 + .../handler/SetBreakpointsRequestHandler.java | 17 +++- .../java/debug/BreakpointLocationLocator.java | 91 +++++++++++++++++++ .../internal/JdtSourceLookUpProvider.java | 24 +++-- 5 files changed, 153 insertions(+), 17 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 790eff801..3a8d88316 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -18,6 +18,7 @@ import java.util.concurrent.CompletableFuture; import com.sun.jdi.Location; +import com.sun.jdi.Method; import com.sun.jdi.ReferenceType; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; @@ -38,6 +39,7 @@ public class Breakpoint implements IBreakpoint { private String condition = null; private String logMessage = null; private HashMap propertyMap = new HashMap<>(); + private String methodSignature = null; Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { this(vm, eventHub, className, lineNumber, 0, null); @@ -50,7 +52,12 @@ public class Breakpoint implements IBreakpoint { Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) { this.vm = vm; this.eventHub = eventHub; - this.className = className; + if (className != null && className.contains("#")) { + this.className = className.substring(0, className.indexOf("#")); + this.methodSignature = className.substring(className.indexOf("#") + 1); + } else { + this.className = className; + } this.lineNumber = lineNumber; this.hitCount = hitCount; this.condition = condition; @@ -234,6 +241,24 @@ private static List collectLocations(List refTypes, int return locations; } + private static List collectLocations(List refTypes, String nameAndSignature) { + List locations = new ArrayList<>(); + String[] segments = nameAndSignature.split("#"); + + for (ReferenceType refType : refTypes) { + List methods = refType.methods(); + for (Method method : methods) { + if (!method.isAbstract() && !method.isNative() + && segments[0].equals(method.name()) + && (segments[1].equals(method.genericSignature()) || segments[1].equals(method.signature()))) { + locations.add(method.location()); + break; + } + } + } + return locations; + } + private List createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount, boolean includeNestedTypes) { List refTypes = new ArrayList<>(); @@ -243,7 +268,12 @@ private List createBreakpointRequests(ReferenceType refType, private List createBreakpointRequests(List refTypes, int lineNumber, int hitCount, boolean includeNestedTypes) { - List locations = collectLocations(refTypes, lineNumber, includeNestedTypes); + List locations; + if (this.methodSignature != null) { + locations = collectLocations(refTypes, this.methodSignature); + } else { + locations = collectLocations(refTypes, lineNumber, includeNestedTypes); + } // find out the existing breakpoint locations List existingLocations = new ArrayList<>(requests.size()); @@ -268,6 +298,7 @@ private List createBreakpointRequests(List ref request.addCountFilter(hitCount); } request.enable(); + request.putProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL, Boolean.valueOf(this.methodSignature != null)); newRequests.add(request); } catch (VMDisconnectedException ex) { // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index bb9549635..485a13739 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -14,6 +14,9 @@ import java.util.concurrent.CompletableFuture; public interface IBreakpoint extends IDebugResource { + + String REQUEST_TYPE_FUNCTIONAL = "functional"; + String className(); int getLineNumber(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 2bc2b5f05..2e7dbf35a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -44,11 +44,11 @@ import com.sun.jdi.BooleanValue; import com.sun.jdi.Field; import com.sun.jdi.ObjectReference; -import com.sun.jdi.StringReference; import com.sun.jdi.ReferenceType; +import com.sun.jdi.StringReference; import com.sun.jdi.ThreadReference; -import com.sun.jdi.Value; import com.sun.jdi.VMDisconnectedException; +import com.sun.jdi.Value; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.StepEvent; @@ -171,6 +171,11 @@ private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext cont ).findFirst().orElse(null); } + private IBreakpoint getAssociatedBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { + return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream() + .filter(bp -> bp.requests().contains(event.request())).findFirst().orElse(null); + } + private void registerBreakpointHandler(IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); if (debugSession != null) { @@ -188,7 +193,7 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { // find the breakpoint related to this breakpoint event IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint(context, (BreakpointEvent) event); - + boolean functional = (boolean) event.request().getProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL); if (expressionBP != null) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> { @@ -199,12 +204,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); + context.getProtocolServer().sendEvent(new Events.StoppedEvent( + functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); } }); }); } else { - context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); + context.getProtocolServer().sendEvent(new Events.StoppedEvent( + functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); } debugEvent.shouldResume = false; } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java new file mode 100644 index 000000000..198b1eb67 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -0,0 +1,91 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera (gayanper@gmail.com) - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug; + +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; + +@SuppressWarnings("restriction") +public class BreakpointLocationLocator + extends org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator { + + private IMethodBinding methodBinding; + + public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, + boolean bestMatch) { + super(compilationUnit, lineNumber, bindingsResolved, bestMatch); + } + + @Override + public boolean visit(MethodDeclaration node) { + boolean result = super.visit(node); + if (methodBinding == null && getLocationType() == LOCATION_METHOD) { + this.methodBinding = node.resolveBinding(); + } + return result; + } + + /** + * Returns the signature of method found if the + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#getLocationType()} + * is + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#LOCATION_METHOD}. + * Otherwise return null. + */ + public String getMethodSignature() { + if (this.methodBinding == null) { + return null; + } + return toSignature(this.methodBinding); + } + + /** + * Returns the name of method found if the + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#getLocationType()} + * is + * {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#LOCATION_METHOD}. + * Otherwise return null. + */ + public String getMethodName() { + if (this.methodBinding == null) { + return null; + } + return this.methodBinding.getName(); + } + + @Override + public String getFullyQualifiedTypeName() { + if (this.methodBinding != null) { + return this.methodBinding.getDeclaringClass().getQualifiedName(); + } + return super.getFullyQualifiedTypeName(); + } + + private String toSignature(IMethodBinding binding) { + // use key for now until JDT core provides a public API for this. + // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" + // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" + String signatureString = binding.getKey(); + if (signatureString != null) { + String name = binding.getName(); + int index = signatureString.indexOf(name); + if (index > -1) { + int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); + if (exceptionIndex > -1) { + return signatureString.substring(index + name.length(), exceptionIndex); + } + return signatureString.substring(index + name.length()); + } + } + return null; + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index efc7ce20c..eb51faaeb 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -26,13 +26,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.microsoft.java.debug.core.Configuration; -import com.microsoft.java.debug.core.DebugException; -import com.microsoft.java.debug.core.adapter.AdapterUtils; -import com.microsoft.java.debug.core.adapter.Constants; -import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; -import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; - import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.sourcelookup.ISourceContainer; @@ -48,11 +41,18 @@ import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; -import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import com.microsoft.java.debug.BreakpointLocationLocator; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.Constants; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; + public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; @@ -162,12 +162,16 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th // In current stage, we don't support to move the invalid breakpoint down to the next valid location, and just // mark it as "unverified". // In future, we could consider supporting to update the breakpoint to a valid location. - ValidBreakpointLocationLocator locator = new ValidBreakpointLocationLocator(astUnit, lines[i], true, true); + BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, true); astUnit.accept(locator); // When the final valid line location is same as the original line, that represents it's a valid breakpoint. // Add location type check to avoid breakpoint on method/field which will never be hit in current implementation. - if (lines[i] == locator.getLineLocation() && locator.getLocationType() == ValidBreakpointLocationLocator.LOCATION_LINE) { + if (lines[i] == locator.getLineLocation() + && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { fqns[i] = locator.getFullyQualifiedTypeName(); + } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { + fqns[i] = locator.getFullyQualifiedTypeName().concat("#").concat(locator.getMethodName()) + .concat("#").concat(locator.getMethodSignature()); } } } From 93963b9582887fb3f62bd4bf05fc3ad2a526a122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Fri, 22 Jul 2022 06:51:55 +0200 Subject: [PATCH 043/144] Show target VM exceptions as result in evaluate requests (#428) This changes the result of an expression like `Long.parseLong("foo")` sent to the evaluate handler from: org.eclipse.debug.core.DebugException: com.sun.jdi.InvocationException: Exception occurred in target VM occurred invoking method. to the actual exception: NumberFormatException@76 "java.lang.NumberFormatException: For input string: "foo"" backtrace: Object[6]@82 cause: NumberFormatException@76 depth: 46 detailMessage: "For input string: "foo"" stackTrace: StackTraceElement[0]@84 suppressedExceptions: Collections$EmptyList@85 size=0 --- .../internal/eval/JdtEvaluationProvider.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index e014552a8..d4fac9054 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -29,6 +29,7 @@ import org.apache.commons.lang3.reflect.FieldUtils; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; @@ -328,9 +329,22 @@ private void internalEvaluate(ASTEvaluationEngine engine, ICompiledExpression co try { engine.evaluateExpression(compiledExpression, stackframe, evaluateResult -> { if (evaluateResult == null || evaluateResult.hasErrors()) { - Exception ex = evaluateResult.getException() != null ? evaluateResult.getException() - : new RuntimeException(StringUtils.join(evaluateResult.getErrorMessages())); - completableFuture.completeExceptionally(ex); + DebugException debugException = evaluateResult.getException(); + if (debugException == null) { + completableFuture.completeExceptionally( + new RuntimeException(String.join(" ", evaluateResult.getErrorMessages()))); + return; + } + IStatus status = debugException.getStatus(); + if (status.getCode() == DebugException.TARGET_REQUEST_FAILED) { + Throwable innerException = status.getException(); + if (innerException instanceof com.sun.jdi.InvocationException) { + ObjectReference objectReference = ((com.sun.jdi.InvocationException) innerException).exception(); + completableFuture.complete(objectReference); + return; + } + } + completableFuture.completeExceptionally(debugException); return; } try { From 8c0e87defedc5bc2a17a9e2980824f8573c08386 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 25 Jul 2022 15:15:44 +0800 Subject: [PATCH 044/144] Bump version to 0.39.0 (#430) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index b866771cc..287c599fe 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.38.0 + 0.39.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 94fa67e10..c3d2cffbb 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index a082aef62..c39f5a9ac 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.38.0 +Bundle-Version: 0.39.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.38.0.jar + lib/com.microsoft.java.debug.core-0.39.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 8a65029aa..7e86528b5 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.38.0 + 0.39.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.38.0 + 0.39.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 2b3229b2d..d4f44d697 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 5e3c1ed1d..4b76e45fb 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.38.0 + 0.39.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 0e0c0ffee..30c6ed253 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.38.0 + 0.39.0 pom Java Debug Server for Visual Studio Code From fe77989c0c5b286121dd2fac9d6a2f334da2e54f Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Thu, 4 Aug 2022 04:02:25 +0200 Subject: [PATCH 045/144] Add support for lambda breakpoints (#427) * Add support for lambda breakpoints The support for method header breakpoints was extended to support lambda breakpoints using the vscode inline breakpoint feature. * Optimize lambda search Only search for lambda when the breakpoing is an inline breakpoint. Add support for any column position within lambda expression. * Improve for multi line and multi lambdas * Improve lambda breakpoints - Only support for expressions. - The inline breakpoint should be before the expression start. * Remove the unnecessary nodeType check Co-authored-by: Jinbo Wang --- .../microsoft/java/debug/core/Breakpoint.java | 27 ++++- .../debug/core/EvaluatableBreakpoint.java | 6 +- .../java/debug/core/IBreakpoint.java | 8 +- .../java/debug/core/adapter/AdapterUtils.java | 18 +++ .../debug/core/adapter/BreakpointManager.java | 12 +- .../handler/SetBreakpointsRequestHandler.java | 26 ++++- .../java/debug/core/protocol/Types.java | 11 ++ .../java/debug/BreakpointLocationLocator.java | 8 +- .../java/debug/LambdaExpressionLocator.java | 105 ++++++++++++++++++ .../internal/JdtSourceLookUpProvider.java | 41 +++++-- 10 files changed, 231 insertions(+), 31 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 3a8d88316..a81968ad3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -115,12 +115,19 @@ public String getCondition() { @Override public boolean equals(Object obj) { - if (!(obj instanceof IBreakpoint)) { + if (!(obj instanceof Breakpoint)) { return super.equals(obj); } - IBreakpoint breakpoint = (IBreakpoint) obj; - return Objects.equals(this.className(), breakpoint.className()) && this.getLineNumber() == breakpoint.getLineNumber(); + Breakpoint breakpoint = (Breakpoint) obj; + return Objects.equals(this.className(), breakpoint.className()) + && this.getLineNumber() == breakpoint.getLineNumber() + && Objects.equals(this.methodSignature, breakpoint.methodSignature); + } + + @Override + public int hashCode() { + return Objects.hash(this.className, this.lineNumber, this.methodSignature); } @Override @@ -298,7 +305,7 @@ private List createBreakpointRequests(List ref request.addCountFilter(hitCount); } request.enable(); - request.putProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL, Boolean.valueOf(this.methodSignature != null)); + request.putProperty(IBreakpoint.REQUEST_TYPE, computeRequestType()); newRequests.add(request); } catch (VMDisconnectedException ex) { // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be @@ -310,6 +317,18 @@ private List createBreakpointRequests(List ref return newRequests; } + private Object computeRequestType() { + if (this.methodSignature == null) { + return IBreakpoint.REQUEST_TYPE_LINE; + } + + if (this.methodSignature.startsWith("lambda$")) { + return IBreakpoint.REQUEST_TYPE_LAMBDA; + } else { + return IBreakpoint.REQUEST_TYPE_METHOD; + } + } + @Override public void putProperty(Object key, Object value) { propertyMap.put(key, value); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java index 1a0647411..9b3fdb2dd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java @@ -11,15 +11,15 @@ package com.microsoft.java.debug.core; -import org.apache.commons.lang3.StringUtils; - import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import com.sun.jdi.event.ThreadDeathEvent; +import org.apache.commons.lang3.StringUtils; + import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ThreadDeathEvent; import io.reactivex.disposables.Disposable; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 485a13739..3db16b577 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -15,7 +15,13 @@ public interface IBreakpoint extends IDebugResource { - String REQUEST_TYPE_FUNCTIONAL = "functional"; + String REQUEST_TYPE = "request_type"; + + int REQUEST_TYPE_LINE = 0; + + int REQUEST_TYPE_METHOD = 1; + + int REQUEST_TYPE_LAMBDA = 2; String className(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 74d3fb292..5c1272102 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -111,6 +111,24 @@ public static int convertLineNumber(int line, boolean sourceLinesStartAt1, boole } } + /** + * Convert the source platform's column number to the target platform's column + * number. + * + * @param column + * the column number from the source platform + * @param sourceColumnsStartAt1 + * the source platform's column starts at 1 or not + * @return the new column number + */ + public static int convertColumnNumber(int column, boolean sourceColumnsStartAt1) { + if (sourceColumnsStartAt1) { + return column - 1; + } else { + return column; + } + } + /** * Convert the source platform's path format to the target platform's path format. * diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java index 4b0a7ae89..eaf1bb56f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/BreakpointManager.java @@ -79,12 +79,12 @@ public IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, bo // Compute the breakpoints that are newly added. List toAdd = new ArrayList<>(); - List visitedLineNumbers = new ArrayList<>(); + List visitedBreakpoints = new ArrayList<>(); for (IBreakpoint breakpoint : breakpoints) { - IBreakpoint existed = breakpointMap.get(String.valueOf(breakpoint.getLineNumber())); + IBreakpoint existed = breakpointMap.get(String.valueOf(breakpoint.hashCode())); if (existed != null) { result.add(existed); - visitedLineNumbers.add(existed.getLineNumber()); + visitedBreakpoints.add(existed.hashCode()); continue; } else { result.add(breakpoint); @@ -95,7 +95,7 @@ public IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, bo // Compute the breakpoints that are no longer listed. List toRemove = new ArrayList<>(); for (IBreakpoint breakpoint : breakpointMap.values()) { - if (!visitedLineNumbers.contains(breakpoint.getLineNumber())) { + if (!visitedBreakpoints.contains(breakpoint.hashCode())) { toRemove.add(breakpoint); } } @@ -113,7 +113,7 @@ private void addBreakpointsInternally(String source, IBreakpoint[] breakpoints) for (IBreakpoint breakpoint : breakpoints) { breakpoint.putProperty("id", this.nextBreakpointId.getAndIncrement()); this.breakpoints.add(breakpoint); - breakpointMap.put(String.valueOf(breakpoint.getLineNumber()), breakpoint); + breakpointMap.put(String.valueOf(breakpoint.hashCode()), breakpoint); } } } @@ -133,7 +133,7 @@ private void removeBreakpointsInternally(String source, IBreakpoint[] breakpoint // Destroy the breakpoint on the debugee VM. breakpoint.close(); this.breakpoints.remove(breakpoint); - breakpointMap.remove(String.valueOf(breakpoint.getLineNumber())); + breakpointMap.remove(String.valueOf(breakpoint.hashCode())); } catch (Exception e) { logger.log(Level.SEVERE, String.format("Remove breakpoint exception: %s", e.toString()), e); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 2e7dbf35a..6dc74a0bd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -52,6 +52,7 @@ import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.StepEvent; +import com.sun.jdi.request.EventRequest; public class SetBreakpointsRequestHandler implements IDebugRequestHandler { @@ -193,7 +194,8 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { // find the breakpoint related to this breakpoint event IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint(context, (BreakpointEvent) event); - boolean functional = (boolean) event.request().getProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL); + String breakpointName = computeBreakpointName(event.request()); + if (expressionBP != null) { CompletableFuture.runAsync(() -> { engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> { @@ -205,13 +207,13 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { debugEvent.eventSet.resume(); } else { context.getProtocolServer().sendEvent(new Events.StoppedEvent( - functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); + breakpointName, bpThread.uniqueID())); } }); }); } else { context.getProtocolServer().sendEvent(new Events.StoppedEvent( - functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID())); + breakpointName, bpThread.uniqueID())); } debugEvent.shouldResume = false; } @@ -219,6 +221,17 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { } } + private String computeBreakpointName(EventRequest request) { + switch ((int) request.getProperty(IBreakpoint.REQUEST_TYPE)) { + case IBreakpoint.REQUEST_TYPE_LAMBDA: + return "lambda breakpoint"; + case IBreakpoint.REQUEST_TYPE_METHOD: + return "function breakpoint"; + default: + return "breakpoint"; + } + } + /** * Check whether the condition expression is satisfied, and return a boolean value to determine to resume the thread or not. */ @@ -287,8 +300,13 @@ private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Type int[] lines = Arrays.asList(sourceBreakpoints).stream().map(sourceBreakpoint -> { return AdapterUtils.convertLineNumber(sourceBreakpoint.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); }).mapToInt(line -> line).toArray(); + + int[] columns = Arrays.asList(sourceBreakpoints).stream().map(b -> { + return AdapterUtils.convertColumnNumber(b.column, context.isClientColumnsStartAt1()); + }).mapToInt(b -> b).toArray(); + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); - String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, null); + String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, columns); IBreakpoint[] breakpoints = new IBreakpoint[lines.length]; for (int i = 0; i < lines.length; i++) { int hitCount = 0; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index cd20faf4f..979c7d632 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -202,6 +202,7 @@ public Breakpoint(int id, boolean verified, int line, String message) { public static class SourceBreakpoint { public int line; + public int column; public String hitCondition; public String condition; public String logMessage; @@ -217,6 +218,16 @@ public SourceBreakpoint(int line, String condition, String hitCondition) { this.condition = condition; this.hitCondition = hitCondition; } + + /** + * Constructor. + */ + public SourceBreakpoint(int line, String condition, String hitCondition, int column) { + this.line = line; + this.column = column; + this.condition = condition; + this.hitCondition = hitCondition; + } } public static class FunctionBreakpoint { diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index 198b1eb67..00cffb766 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -20,7 +20,8 @@ public class BreakpointLocationLocator private IMethodBinding methodBinding; - public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, + public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, + boolean bindingsResolved, boolean bestMatch) { super(compilationUnit, lineNumber, bindingsResolved, bestMatch); } @@ -45,7 +46,7 @@ public String getMethodSignature() { if (this.methodBinding == null) { return null; } - return toSignature(this.methodBinding); + return toSignature(this.methodBinding, getMethodName()); } /** @@ -70,13 +71,12 @@ public String getFullyQualifiedTypeName() { return super.getFullyQualifiedTypeName(); } - private String toSignature(IMethodBinding binding) { + static String toSignature(IMethodBinding binding, String name) { // use key for now until JDT core provides a public API for this. // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" String signatureString = binding.getKey(); if (signatureString != null) { - String name = binding.getName(); int index = signatureString.indexOf(name); if (index > -1) { int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java new file mode 100644 index 000000000..351be8cb3 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -0,0 +1,105 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera (gayanper@gmail.com) - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.LambdaExpression; + +public class LambdaExpressionLocator extends ASTVisitor { + private CompilationUnit compilationUnit; + private int line; + private int column; + private boolean found; + + private IMethodBinding lambdaMethodBinding; + private LambdaExpression lambdaExpression; + + public LambdaExpressionLocator(CompilationUnit compilationUnit, int line, int column) { + this.compilationUnit = compilationUnit; + this.line = line; + this.column = column; + } + + @Override + public boolean visit(LambdaExpression node) { + // we only support inline breakpoints which are added before the expression part of the + // lambda. And we don't support lambda blocks since they can be debugged using line + // breakpoints. + if (column > -1) { + int startPosition = node.getStartPosition(); + int endPosition = node.getBody().getStartPosition(); + int offset = this.compilationUnit.getPosition(line, column); + // lambda on same line: + // list.stream().map(i -> i + 1); + // + // lambda on multiple lines: + // list.stream().map(user + // -> user.isSystem() ? new SystemUser(user) : new EndUser(user)); + + if (offset >= startPosition && offset <= endPosition) { + this.lambdaMethodBinding = node.resolveMethodBinding(); + this.found = true; + this.lambdaExpression = node; + return false; + } + } + return super.visit(node); + } + + /** + * Returns true if a lambda is found at given location. + */ + public boolean isFound() { + return found; + } + + /** + * Returns the signature of lambda method otherwise return null. + */ + public String getMethodSignature() { + if (!this.found) { + return null; + } + return BreakpointLocationLocator.toSignature(this.lambdaMethodBinding, getMethodName()); + } + + /** + * Returns the name of lambda method otherwise return null. + */ + public String getMethodName() { + if (!this.found) { + return null; + } + String key = this.lambdaMethodBinding.getKey(); + return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + } + + /** + * Returns the name of the type which the lambda method is found. + */ + public String getFullyQualifiedTypeName() { + if (this.found) { + ASTNode parent = lambdaExpression.getParent(); + while (parent != null) { + if (parent instanceof AbstractTypeDeclaration) { + AbstractTypeDeclaration declaration = (AbstractTypeDeclaration) parent; + return declaration.resolveBinding().getBinaryName(); + } + parent = parent.getParent(); + } + } + return null; + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index eb51faaeb..1dc1c0e64 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -46,6 +46,7 @@ import org.eclipse.jdt.launching.LibraryLocation; import com.microsoft.java.debug.BreakpointLocationLocator; +import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.adapter.AdapterUtils; @@ -155,22 +156,44 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th String[] fqns = new String[lines.length]; if (astUnit != null) { for (int i = 0; i < lines.length; i++) { + if (columns[i] > -1) { + // if we have a column, try to find the lambda expression at that column + LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, lines[i], + columns[i]); + astUnit.accept(lambdaExpressionLocator); + if (lambdaExpressionLocator.isFound()) { + fqns[i] = lambdaExpressionLocator.getFullyQualifiedTypeName().concat("#") + .concat(lambdaExpressionLocator.getMethodName()) + .concat("#").concat(lambdaExpressionLocator.getMethodSignature()); + continue; + } + } + // TODO - // The ValidBreakpointLocationLocator will verify if the current line is a valid location or not. - // If so, it will return the fully qualified name of the class type that contains the current line. - // Otherwise, it will try to find a valid location from the next lines and return it's fully qualified name. - // In current stage, we don't support to move the invalid breakpoint down to the next valid location, and just + // The ValidBreakpointLocationLocator will verify if the current line is a valid + // location or not. + // If so, it will return the fully qualified name of the class type that + // contains the current line. + // Otherwise, it will try to find a valid location from the next lines and + // return it's fully qualified name. + // In current stage, we don't support to move the invalid breakpoint down to the + // next valid location, and just // mark it as "unverified". - // In future, we could consider supporting to update the breakpoint to a valid location. - BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, true); + // In future, we could consider supporting to update the breakpoint to a valid + // location. + BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, + true); astUnit.accept(locator); - // When the final valid line location is same as the original line, that represents it's a valid breakpoint. - // Add location type check to avoid breakpoint on method/field which will never be hit in current implementation. + // When the final valid line location is same as the original line, that + // represents it's a valid breakpoint. + // Add location type check to avoid breakpoint on method/field which will never + // be hit in current implementation. if (lines[i] == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { fqns[i] = locator.getFullyQualifiedTypeName(); } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - fqns[i] = locator.getFullyQualifiedTypeName().concat("#").concat(locator.getMethodName()) + fqns[i] = locator.getFullyQualifiedTypeName().concat("#") + .concat(locator.getMethodName()) .concat("#").concat(locator.getMethodSignature()); } } From 0723ee0b69bf42390310964cd6bdadc53298298b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 29 Aug 2022 16:55:04 +0800 Subject: [PATCH 046/144] Provide an option to dump the DAP perf (#435) * Provide an option to dump the DAP perf --- .../java/debug/core/UsageDataSession.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java index e54727b66..e292026c8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,6 +12,7 @@ package com.microsoft.java.debug.core; import java.util.ArrayList; +import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -32,6 +33,7 @@ public class UsageDataSession { private static final Logger usageDataLogger = Logger.getLogger(Configuration.USAGE_DATA_LOGGER_NAME); private static final long RESPONSE_MAX_DELAY_MS = 1000; private static final ThreadLocal threadLocal = new InheritableThreadLocal<>(); + private static final boolean TRACE_DAP_PERF = Boolean.getBoolean("debug.dap.perf"); private final String sessionGuid = UUID.randomUUID().toString(); private boolean jdiEventSequenceEnabled = false; @@ -43,6 +45,7 @@ public class UsageDataSession { private Map userErrorCount = new HashMap<>(); private Map commandPerfCountMap = new HashMap<>(); private List eventList = new ArrayList<>(); + private List dapPerf = new ArrayList<>(); public static String getSessionGuid() { return threadLocal.get() == null ? "" : threadLocal.get().sessionGuid; @@ -117,6 +120,12 @@ public void recordResponse(Response response) { long duration = responseMillis - requestMillis; commandPerfCountMap.compute(command, (k, v) -> (v == null ? 0 : v.intValue()) + (int) duration); + if (TRACE_DAP_PERF) { + synchronized (dapPerf) { + dapPerf.add(new String[]{command, String.valueOf(duration)}); + } + } + if (!response.success || duration > RESPONSE_MAX_DELAY_MS) { Map props = new HashMap<>(); props.put("duration", duration); @@ -148,6 +157,18 @@ public void submitUsageData() { } } usageDataLogger.log(Level.INFO, "session usage data summary", props); + + if (TRACE_DAP_PERF) { + Formatter fmt = new Formatter(); + fmt.format("\nDAP Performance Metrics:\n"); + fmt.format("%30s %10s(ms)\n", "Request", "Duration"); + synchronized (dapPerf) { + dapPerf.forEach((event) -> { + fmt.format("%30s %14s\n", event[0], event[1]); + }); + } + logger.info(String.valueOf(fmt)); + } } /** From 9ce0aef47c71de9263ad6f3f179e1b80e7b83678 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 31 Aug 2022 12:15:39 +0800 Subject: [PATCH 047/144] Perf: support sending JDWP commands asynchronously (#436) * Perf: support sending JDWP commands asynchronously --- .../java/debug/core/AsyncJdwpUtils.java | 143 +++++++++++ .../microsoft/java/debug/core/Breakpoint.java | 239 ++++++++++++------ .../java/debug/core/DebugSession.java | 9 +- .../java/debug/core/DebugSettings.java | 12 +- .../java/debug/core/DebugUtility.java | 23 +- .../java/debug/core/IBreakpoint.java | 7 + .../java/debug/core/IMethodBreakpoint.java | 7 + .../java/debug/core/MethodBreakpoint.java | 62 ++++- .../java/debug/core/UsageDataSession.java | 6 + .../core/adapter/DebugAdapterContext.java | 29 ++- .../core/adapter/IDebugAdapterContext.java | 10 + .../core/adapter/IStackFrameManager.java | 10 + .../debug/core/adapter/StackFrameManager.java | 37 ++- .../java/debug/core/adapter/ThreadCache.java | 72 ++++++ .../adapter/handler/AttachRequestHandler.java | 20 ++ .../ConfigurationDoneRequestHandler.java | 2 + .../handler/EvaluateRequestHandler.java | 5 + .../handler/InlineValuesRequestHandler.java | 8 +- .../adapter/handler/RestartFrameHandler.java | 26 +- .../handler/SetBreakpointsRequestHandler.java | 3 +- .../SetFunctionBreakpointsRequestHandler.java | 1 + .../handler/StackTraceRequestHandler.java | 159 +++++++++--- .../adapter/handler/StepRequestHandler.java | 105 ++++++-- .../handler/ThreadsRequestHandler.java | 158 +++++++++--- .../handler/VariablesRequestHandler.java | 110 +++++++- .../variables/StringReferenceProxy.java | 121 +++++++++ .../core/adapter/variables/VariableUtils.java | 152 ++++++++++- 27 files changed, 1315 insertions(+), 221 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java new file mode 100644 index 000000000..fad2ac223 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/AsyncJdwpUtils.java @@ -0,0 +1,143 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core; + +import static java.util.concurrent.CompletableFuture.allOf; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Supplier; + +public class AsyncJdwpUtils { + /** + * Create a the thread pool to process JDWP tasks. + * JDWP tasks are IO-bounded, so use a relatively large thread pool for JDWP tasks. + */ + public static ExecutorService jdwpThreadPool = Executors.newWorkStealingPool(100); + // public static ExecutorService jdwpThreadPool = Executors.newCachedThreadPool(); + + public static CompletableFuture runAsync(List tasks) { + return runAsync(jdwpThreadPool, tasks.toArray(new Runnable[0])); + } + + public static CompletableFuture runAsync(Runnable... tasks) { + return runAsync(jdwpThreadPool, tasks); + } + + public static CompletableFuture runAsync(Executor executor, List tasks) { + return runAsync(executor, tasks.toArray(new Runnable[0])); + } + + public static CompletableFuture runAsync(Executor executor, Runnable... tasks) { + List> promises = new ArrayList<>(); + for (Runnable task : tasks) { + if (task == null) { + continue; + } + + promises.add(CompletableFuture.runAsync(task, executor)); + } + + return CompletableFuture.allOf(promises.toArray(new CompletableFuture[0])); + } + + public static CompletableFuture supplyAsync(Supplier supplier) { + return supplyAsync(jdwpThreadPool, supplier); + } + + public static CompletableFuture supplyAsync(Executor executor, Supplier supplier) { + return CompletableFuture.supplyAsync(supplier, executor); + } + + public static U await(CompletableFuture future) { + try { + return future.join(); + } catch (CompletionException ex) { + if (ex.getCause() instanceof RuntimeException) { + throw (RuntimeException) ex.getCause(); + } + + throw ex; + } + } + + public static List await(CompletableFuture[] futures) { + List results = new ArrayList<>(); + try { + allOf(futures).join(); + for (CompletableFuture future : futures) { + results.add(await(future)); + } + } catch (CompletionException ex) { + if (ex.getCause() instanceof RuntimeException) { + throw (RuntimeException) ex.getCause(); + } + + throw ex; + } + + return results; + } + + public static List await(List> futures) { + return await((CompletableFuture[]) futures.toArray(new CompletableFuture[0])); + } + + public static CompletableFuture> all(CompletableFuture... futures) { + return allOf(futures).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture future : futures) { + results.add(future.join()); + } + + return results; + }); + } + + public static CompletableFuture> all(List> futures) { + return allOf(futures.toArray(new CompletableFuture[0])).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture future : futures) { + results.add(future.join()); + } + + return results; + }); + } + + public static CompletableFuture> flatAll(CompletableFuture>... futures) { + return allOf(futures).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture> future : futures) { + results.addAll(future.join()); + } + + return results; + }); + } + + public static CompletableFuture> flatAll(List>> futures) { + return allOf(futures.toArray(new CompletableFuture[0])).thenApply((res) -> { + List results = new ArrayList<>(); + for (CompletableFuture> future : futures) { + results.addAll(future.join()); + } + + return results; + }); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index a81968ad3..8b865d44f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,11 +12,15 @@ package com.microsoft.java.debug.core; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import com.sun.jdi.AbsentInformationException; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ReferenceType; @@ -41,6 +45,8 @@ public class Breakpoint implements IBreakpoint { private HashMap propertyMap = new HashMap<>(); private String methodSignature = null; + private boolean async = false; + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) { this(vm, eventHub, className, lineNumber, 0, null); } @@ -69,7 +75,7 @@ public class Breakpoint implements IBreakpoint { } // IDebugResource - private List requests = new ArrayList<>(); + private List requests = Collections.synchronizedList(new ArrayList<>()); private List subscriptions = new ArrayList<>(); @Override @@ -162,6 +168,16 @@ public String getLogMessage() { return this.logMessage; } + @Override + public boolean async() { + return this.async; + } + + @Override + public void setAsync(boolean async) { + this.async = async; + } + @Override public CompletableFuture install() { // It's possible that different class loaders create new class with the same name. @@ -185,8 +201,9 @@ public CompletableFuture install() { || localClassPrepareRequest.equals(debugEvent.event.request()))) .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; - List newRequests = createBreakpointRequests(event.referenceType(), lineNumber, - hitCount, false); + List newRequests = AsyncJdwpUtils.await( + createBreakpointRequests(event.referenceType(), lineNumber, hitCount, false) + ); requests.addAll(newRequests); if (!newRequests.isEmpty() && !future.isDone()) { this.putProperty("verified", true); @@ -195,126 +212,180 @@ public CompletableFuture install() { }); subscriptions.add(subscription); - List refTypes = vm.classesByName(className); - List newRequests = createBreakpointRequests(refTypes, lineNumber, hitCount, true); - requests.addAll(newRequests); + Runnable resolveRequestsFromExistingClasses = () -> { + List refTypes = vm.classesByName(className); + createBreakpointRequests(refTypes, lineNumber, hitCount, true) + .whenComplete((newRequests, ex) -> { + if (ex != null) { + return; + } + + requests.addAll(newRequests); + if (!newRequests.isEmpty() && !future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + }); + }; - if (!newRequests.isEmpty() && !future.isDone()) { - this.putProperty("verified", true); - future.complete(this); + if (async()) { + AsyncJdwpUtils.runAsync(resolveRequestsFromExistingClasses); + } else { + resolveRequestsFromExistingClasses.run(); } return future; } - private static List collectLocations(ReferenceType refType, int lineNumber) { - List locations = new ArrayList<>(); - - try { - locations.addAll(refType.locationsOfLine(lineNumber)); - } catch (Exception e) { - // could be AbsentInformationException or ClassNotPreparedException - // but both are expected so no need to further handle + private CompletableFuture> collectLocations(ReferenceType refType, int lineNumber) { + List>> futures = new ArrayList<>(); + Iterator iter = refType.methods().iterator(); + while (iter.hasNext()) { + Method method = iter.next(); + if (async()) { + futures.add(AsyncJdwpUtils.supplyAsync(() -> findLocaitonsOfLine(method, lineNumber))); + } else { + futures.add(CompletableFuture.completedFuture(findLocaitonsOfLine(method, lineNumber))); + } } - return locations; + return AsyncJdwpUtils.flatAll(futures); } - private static List collectLocations(List refTypes, int lineNumber, boolean includeNestedTypes) { - List locations = new ArrayList<>(); - try { - refTypes.forEach(refType -> { - List newLocations = collectLocations(refType, lineNumber); - if (!newLocations.isEmpty()) { - locations.addAll(newLocations); - } else if (includeNestedTypes) { - // ReferenceType.nestedTypes() will invoke vm.allClasses() to list all loaded classes, - // should avoid using nestedTypes for performance. - for (ReferenceType nestedType : refType.nestedTypes()) { - List nestedLocations = collectLocations(nestedType, lineNumber); - if (!nestedLocations.isEmpty()) { - locations.addAll(nestedLocations); - break; - } + private CompletableFuture> collectLocations(List refTypes, int lineNumber, boolean includeNestedTypes) { + List>> futures = new ArrayList<>(); + refTypes.forEach(refType -> { + futures.add(collectLocations(refType, lineNumber, includeNestedTypes)); + }); + + return AsyncJdwpUtils.flatAll(futures); + } + + private CompletableFuture> collectLocations(ReferenceType refType, int lineNumber, boolean includeNestedTypes) { + return collectLocations(refType, lineNumber).thenCompose((newLocations) -> { + if (!newLocations.isEmpty()) { + return CompletableFuture.completedFuture(newLocations); + } else if (includeNestedTypes) { + // ReferenceType.nestedTypes() will invoke vm.allClasses() to list all loaded classes, + // should avoid using nestedTypes for performance. + for (ReferenceType nestedType : refType.nestedTypes()) { + CompletableFuture> nestedLocationsFuture = collectLocations(nestedType, lineNumber); + List nestedLocations = nestedLocationsFuture.join(); + if (!nestedLocations.isEmpty()) { + return CompletableFuture.completedFuture(nestedLocations); } } - }); - } catch (VMDisconnectedException ex) { - // collect locations operation may be executing while JVM is terminating, thus the VMDisconnectedException may be - // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid - // response in vscode, causing no error log in trace. - } + } - return locations; + return CompletableFuture.completedFuture(Collections.emptyList()); + }); } - private static List collectLocations(List refTypes, String nameAndSignature) { - List locations = new ArrayList<>(); + private CompletableFuture> collectLocations(List refTypes, String nameAndSignature) { String[] segments = nameAndSignature.split("#"); - + List> futures = new ArrayList<>(); for (ReferenceType refType : refTypes) { - List methods = refType.methods(); - for (Method method : methods) { - if (!method.isAbstract() && !method.isNative() - && segments[0].equals(method.name()) - && (segments[1].equals(method.genericSignature()) || segments[1].equals(method.signature()))) { - locations.add(method.location()); - break; - } + if (async()) { + futures.add(AsyncJdwpUtils.supplyAsync(() -> findMethodLocaiton(refType, segments[0], segments[1]))); + } else { + futures.add(CompletableFuture.completedFuture(findMethodLocaiton(refType, segments[0], segments[1]))); } } - return locations; + + return AsyncJdwpUtils.all(futures); } - private List createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount, + private Location findMethodLocaiton(ReferenceType refType, String methodName, String methodSiguature) { + List methods = refType.methods(); + Location location = null; + for (Method method : methods) { + if (!method.isAbstract() && !method.isNative() + && methodName.equals(method.name()) + && (methodSiguature.equals(method.genericSignature()) || methodSiguature.equals(method.signature()))) { + location = method.location(); + break; + } + } + + return location; + } + + private List findLocaitonsOfLine(Method method, int lineNumber) { + try { + return method.locationsOfLine(lineNumber); + } catch (AbsentInformationException e) { + // could be AbsentInformationException or ClassNotPreparedException + // but both are expected so no need to further handle + } + + return Collections.emptyList(); + } + + private CompletableFuture> createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount, boolean includeNestedTypes) { - List refTypes = new ArrayList<>(); - refTypes.add(refType); - return createBreakpointRequests(refTypes, lineNumber, hitCount, includeNestedTypes); + return createBreakpointRequests(Arrays.asList(refType), lineNumber, hitCount, includeNestedTypes); } - private List createBreakpointRequests(List refTypes, int lineNumber, + private CompletableFuture> createBreakpointRequests(List refTypes, int lineNumber, int hitCount, boolean includeNestedTypes) { - List locations; + CompletableFuture> locationsFuture; if (this.methodSignature != null) { - locations = collectLocations(refTypes, this.methodSignature); + locationsFuture = collectLocations(refTypes, this.methodSignature); } else { - locations = collectLocations(refTypes, lineNumber, includeNestedTypes); + locationsFuture = collectLocations(refTypes, lineNumber, includeNestedTypes); } - // find out the existing breakpoint locations - List existingLocations = new ArrayList<>(requests.size()); - Observable.fromIterable(requests).filter(request -> request instanceof BreakpointRequest) - .map(request -> ((BreakpointRequest) request).location()).toList().subscribe(list -> { - existingLocations.addAll(list); - }); - - // remove duplicated locations - List newLocations = new ArrayList<>(locations.size()); - Observable.fromIterable(locations).filter(location -> !existingLocations.contains(location)).toList().subscribe(list -> { - newLocations.addAll(list); - }); + return locationsFuture.thenCompose((locations) -> { + // find out the existing breakpoint locations + List existingLocations = new ArrayList<>(requests.size()); + Observable.fromIterable(requests).filter(request -> request instanceof BreakpointRequest) + .map(request -> ((BreakpointRequest) request).location()).toList().subscribe(list -> { + existingLocations.addAll(list); + }); + + // remove duplicated locations + List newLocations = new ArrayList<>(locations.size()); + Observable.fromIterable(locations).filter(location -> !existingLocations.contains(location)).toList().subscribe(list -> { + newLocations.addAll(list); + }); - List newRequests = new ArrayList<>(newLocations.size()); + List newRequests = new ArrayList<>(newLocations.size()); - newLocations.forEach(location -> { - try { + newLocations.forEach(location -> { BreakpointRequest request = vm.eventRequestManager().createBreakpointRequest(location); request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD); if (hitCount > 0) { request.addCountFilter(hitCount); } - request.enable(); request.putProperty(IBreakpoint.REQUEST_TYPE, computeRequestType()); newRequests.add(request); - } catch (VMDisconnectedException ex) { - // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be - // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid - // response in vscode, causing no error log in trace. + }); + + List> futures = new ArrayList<>(); + for (BreakpointRequest request : newRequests) { + if (async()) { + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + request.enable(); + } catch (VMDisconnectedException ex) { + // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be + // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid + // response in vscode, causing no error log in trace. + } + })); + } else { + try { + request.enable(); + } catch (VMDisconnectedException ex) { + // enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be + // possible, in case of VMDisconnectedException, this method will return an empty array which turns out a valid + // response in vscode, causing no error log in trace. + } + } } - }); - return newRequests; + return AsyncJdwpUtils.all(futures).thenApply((res) -> newRequests); + }); } private Object computeRequestType() { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index ab4341f51..220688693 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.List; +import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; import com.sun.jdi.request.EventRequest; @@ -57,8 +58,12 @@ public void resume() { * all threads fully. */ for (ThreadReference tr : DebugUtility.getAllThreadsSafely(this)) { - while (!tr.isCollected() && tr.suspendCount() > 1) { - tr.resume(); + try { + while (tr.suspendCount() > 1) { + tr.resume(); + } + } catch (ObjectCollectedException ex) { + // Skip it if the thread is garbage collected. } } vm.resume(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index e3c928332..f59f10043 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -43,6 +43,7 @@ public final class DebugSettings { public boolean exceptionFiltersUpdated = false; public int limitOfVariablesPerJdwpRequest = 100; public int jdwpRequestTimeout = 3000; + public AsyncMode asyncJDWP = AsyncMode.OFF; public static DebugSettings getCurrent() { return current; @@ -87,6 +88,15 @@ public static enum HotCodeReplace { NEVER } + public static enum AsyncMode { + @SerializedName("auto") + AUTO, + @SerializedName("on") + ON, + @SerializedName("off") + OFF + } + public static interface IDebugSettingChangeListener { public void update(DebugSettings oldSettings, DebugSettings newSettings); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java index 1202a30f3..4a2a49e9b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java @@ -444,7 +444,7 @@ public static CompletableFuture stopOnEntry(IDebugSession debugSession, St */ public static ThreadReference getThread(IDebugSession debugSession, long threadId) { for (ThreadReference thread : getAllThreadsSafely(debugSession)) { - if (thread.uniqueID() == threadId && !thread.isCollected()) { + if (thread.uniqueID() == threadId) { return thread; } } @@ -477,7 +477,7 @@ public static List getAllThreadsSafely(IDebugSession debugSessi */ public static void resumeThread(ThreadReference thread) { // if thread is not found or is garbage collected, do nothing - if (thread == null || thread.isCollected()) { + if (thread == null) { return; } try { @@ -495,6 +495,25 @@ public static void resumeThread(ThreadReference thread) { } } + public static void resumeThread(ThreadReference thread, int resumeCount) { + if (thread == null) { + return; + } + + try { + for (int i = 0; i < resumeCount; i++) { + /** + * Invoking this method will decrement the count of pending suspends on this thread. + * If it is decremented to 0, the thread will continue to execute. + */ + thread.resume(); + } + } catch (ObjectCollectedException ex) { + // ObjectCollectionException can be thrown if the thread has already completed (exited) in the VM when calling suspendCount, + // the resume operation to this thread is meanness. + } + } + /** * Remove the event request from the vm. If the vm has terminated, do nothing. * @param eventManager diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 3db16b577..04fbf5005 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -44,4 +44,11 @@ public interface IBreakpoint extends IDebugResource { String getLogMessage(); void setLogMessage(String logMessage); + + default void setAsync(boolean async) { + } + + default boolean async() { + return false; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java index 668884567..68f9539c8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IMethodBreakpoint.java @@ -30,4 +30,11 @@ public interface IMethodBreakpoint extends IDebugResource { Object getProperty(Object key); void putProperty(Object key, Object value); + + default void setAsync(boolean async) { + } + + default boolean async() { + return false; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java index 82c5b75e2..7a6e74c6b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/MethodBreakpoint.java @@ -11,6 +11,7 @@ package com.microsoft.java.debug.core; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,12 +43,13 @@ public class MethodBreakpoint implements IMethodBreakpoint, IEvaluatableBreakpoi private String functionName; private String condition; private int hitCount; + private boolean async = false; private HashMap propertyMap = new HashMap<>(); private Object compiledConditionalExpression = null; private Map compiledExpressions = new ConcurrentHashMap<>(); - private List requests = new ArrayList<>(); + private List requests = Collections.synchronizedList(new ArrayList<>()); private List subscriptions = new ArrayList<>(); public MethodBreakpoint(VirtualMachine vm, IEventHub eventHub, String className, String functionName, @@ -169,6 +171,16 @@ public void setHitCount(int hitCount) { }); } + @Override + public boolean async() { + return this.async; + } + + @Override + public void setAsync(boolean async) { + this.async = async; + } + @Override public CompletableFuture install() { Disposable subscription = eventHub.events() @@ -194,7 +206,9 @@ public CompletableFuture install() { && (classPrepareRequest.equals(debugEvent.event.request()))) .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; - Optional createdRequest = createMethodEntryRequest(event.referenceType()); + Optional createdRequest = AsyncJdwpUtils.await( + createMethodEntryRequest(event.referenceType()) + ); if (createdRequest.isPresent()) { MethodEntryRequest methodEntryRequest = createdRequest.get(); requests.add(methodEntryRequest); @@ -206,22 +220,44 @@ public CompletableFuture install() { }); subscriptions.add(subscription); - List types = vm.classesByName(className); - for (ReferenceType type : types) { - Optional createdRequest = createMethodEntryRequest(type); - if (createdRequest.isPresent()) { - MethodEntryRequest methodEntryRequest = createdRequest.get(); - requests.add(methodEntryRequest); - if (!future.isDone()) { - this.putProperty("verified", true); - future.complete(this); - } + Runnable createRequestsFromLoadedClasses = () -> { + List types = vm.classesByName(className); + for (ReferenceType type : types) { + createMethodEntryRequest(type).whenComplete((createdRequest, ex) -> { + if (ex != null) { + return; + } + + if (createdRequest.isPresent()) { + MethodEntryRequest methodEntryRequest = createdRequest.get(); + requests.add(methodEntryRequest); + if (!future.isDone()) { + this.putProperty("verified", true); + future.complete(this); + } + } + }); } + }; + + if (async()) { + AsyncJdwpUtils.runAsync(createRequestsFromLoadedClasses); + } else { + createRequestsFromLoadedClasses.run(); } + return future; } - private Optional createMethodEntryRequest(ReferenceType type) { + private CompletableFuture> createMethodEntryRequest(ReferenceType type) { + if (async()) { + return CompletableFuture.supplyAsync(() -> createMethodEntryRequest0(type)); + } else { + return CompletableFuture.completedFuture(createMethodEntryRequest0(type)); + } + } + + private Optional createMethodEntryRequest0(ReferenceType type) { return type.methodsByName(functionName).stream().findFirst().map(method -> { MethodEntryRequest request = vm.eventRequestManager().createMethodEntryRequest(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java index e292026c8..911dca529 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java @@ -190,6 +190,12 @@ public static void recordEvent(Event event) { } } + public static void recordInfo(String key, Object value) { + Map map = new HashMap<>(); + map.put(key, value); + usageDataLogger.log(Level.INFO, "session info", map); + } + /** * Record counts for each user errors encountered. */ diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 30fb12200..4185d7597 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -21,6 +21,7 @@ import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.DebugSettings.AsyncMode; import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter; import com.microsoft.java.debug.core.adapter.variables.VariableFormatterFactory; import com.microsoft.java.debug.core.protocol.IProtocolServer; @@ -57,6 +58,8 @@ public class DebugAdapterContext implements IDebugAdapterContext { private long shellProcessId = -1; private long processId = -1; + private boolean localDebugging = true; + private IdCollection sourceReferences = new IdCollection<>(); private RecyclableObjectPool recyclableIdPool = new RecyclableObjectPool<>(); private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter(); @@ -65,6 +68,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private IExceptionManager exceptionManager = new ExceptionManager(); private IBreakpointManager breakpointManager = new BreakpointManager(); private IStepResultManager stepResultManager = new StepResultManager(); + private ThreadCache threadCache = new ThreadCache(); public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) { this.providerContext = providerContext; @@ -349,4 +353,27 @@ public void setProcessId(long processId) { public void setShellProcessId(long shellProcessId) { this.shellProcessId = shellProcessId; } + + @Override + public void setThreadCache(ThreadCache cache) { + this.threadCache = cache; + } + + @Override + public ThreadCache getThreadCache() { + return this.threadCache; + } + + @Override + public boolean asyncJDWP() { + return DebugSettings.getCurrent().asyncJDWP == AsyncMode.ON; + } + + public boolean isLocalDebugging() { + return localDebugging; + } + + public void setLocalDebugging(boolean local) { + this.localDebugging = local; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index a1b21ecee..bfcad8905 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -136,4 +136,14 @@ public interface IDebugAdapterContext { void setProcessId(long processId); long getProcessId(); + + void setThreadCache(ThreadCache cache); + + ThreadCache getThreadCache(); + + boolean asyncJDWP(); + + boolean isLocalDebugging(); + + void setLocalDebugging(boolean local); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java index de10319f6..f3ae95d6e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java @@ -31,4 +31,14 @@ public interface IStackFrameManager { * @return all the stackframes in the specified thread */ StackFrame[] reloadStackFrames(ThreadReference thread); + + /** + * Refersh the stackframes starting from the specified depth and length. + * + * @param thread the jdi thread + * @param start the index of the first frame to refresh. Index 0 represents the current frame. + * @param length the number of frames to refersh + * @return the refreshed stackframes + */ + StackFrame[] reloadStackFrames(ThreadReference thread, int start, int length); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java index 9e1a86970..d2f934901 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,7 +11,6 @@ package com.microsoft.java.debug.core.adapter; -import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -21,10 +20,10 @@ import com.sun.jdi.ThreadReference; public class StackFrameManager implements IStackFrameManager { - private Map threadStackFrameMap = Collections.synchronizedMap(new HashMap<>()); + private Map threadStackFrameMap = new HashMap<>(); @Override - public StackFrame getStackFrame(StackFrameReference ref) { + public synchronized StackFrame getStackFrame(StackFrameReference ref) { ThreadReference thread = ref.getThread(); int depth = ref.getDepth(); StackFrame[] frames = threadStackFrameMap.get(thread.uniqueID()); @@ -32,13 +31,39 @@ public StackFrame getStackFrame(StackFrameReference ref) { } @Override - public StackFrame[] reloadStackFrames(ThreadReference thread) { + public synchronized StackFrame[] reloadStackFrames(ThreadReference thread) { return threadStackFrameMap.compute(thread.uniqueID(), (key, old) -> { try { - return thread.frames().toArray(new StackFrame[0]); + if (old == null || old.length == 0) { + return thread.frames().toArray(new StackFrame[0]); + } else { + return thread.frames(0, old.length).toArray(new StackFrame[0]); + } } catch (IncompatibleThreadStateException e) { return new StackFrame[0]; } }); } + + @Override + public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, int start, int length) { + long threadId = thread.uniqueID(); + StackFrame[] old = threadStackFrameMap.get(threadId); + try { + StackFrame[] newFrames = thread.frames(start, length).toArray(new StackFrame[0]); + if (old == null || (start == 0 && length == old.length)) { + threadStackFrameMap.put(threadId, newFrames); + } else { + int maxLength = Math.max(old.length, start + length); + StackFrame[] totalFrames = new StackFrame[maxLength]; + System.arraycopy(old, 0, totalFrames, 0, old.length); + System.arraycopy(newFrames, 0, totalFrames, start, length); + threadStackFrameMap.put(threadId, totalFrames); + } + + return newFrames; + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException e) { + return new StackFrame[0]; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java new file mode 100644 index 000000000..3232dbf18 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java @@ -0,0 +1,72 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.sun.jdi.ThreadReference; + +public class ThreadCache { + private List allThreads = new ArrayList<>(); + private Map threadNameMap = new ConcurrentHashMap<>(); + private Map deathThreads = Collections.synchronizedMap(new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + return this.size() > 100; + } + }); + + public synchronized void resetThreads(List threads) { + allThreads.clear(); + allThreads.addAll(threads); + } + + public synchronized List getThreads() { + return allThreads; + } + + public synchronized ThreadReference getThread(long threadId) { + for (ThreadReference thread : allThreads) { + if (threadId == thread.uniqueID()) { + return thread; + } + } + + return null; + } + + public void setThreadName(long threadId, String name) { + threadNameMap.put(threadId, name); + } + + public String getThreadName(long threadId) { + return threadNameMap.get(threadId); + } + + public void addDeathThread(long threadId) { + threadNameMap.remove(threadId); + deathThreads.put(threadId, true); + } + + public void removeDeathThread(long threadId) { + deathThreads.remove(threadId); + } + + public boolean isDeathThread(long threadId) { + return deathThreads.containsKey(threadId); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index 7069bc39e..ecadbaaac 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -23,6 +23,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; +import com.microsoft.java.debug.core.UsageDataSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.ErrorCode; @@ -39,6 +40,7 @@ import com.microsoft.java.debug.core.protocol.Requests.AttachArguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.sun.jdi.connect.IllegalConnectorArgumentsException; +import com.sun.jdi.request.EventRequest; import org.apache.commons.lang3.StringUtils; @@ -58,6 +60,7 @@ public CompletableFuture handle(Command command, Arguments arguments, context.setSourcePaths(attachArguments.sourcePaths); context.setDebuggeeEncoding(StandardCharsets.UTF_8); // Use UTF-8 as debuggee's default encoding format. context.setStepFilters(attachArguments.stepFilters); + context.setLocalDebugging(isLocalHost(attachArguments.hostName)); IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); @@ -96,6 +99,14 @@ public CompletableFuture handle(Command command, Arguments arguments, logger.warning(warnMessage); context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); } + + EventRequest request = debugSession.getVM().eventRequestManager().createVMDeathRequest(); + request.setSuspendPolicy(EventRequest.SUSPEND_NONE); + long sent = System.currentTimeMillis(); + request.enable(); + long received = System.currentTimeMillis(); + logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); + UsageDataSession.recordInfo("networkLatency", (received - sent)); } IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); @@ -111,4 +122,13 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private boolean isLocalHost(String hostName) { + if (hostName == null || "localhost".equals(hostName) || "127.0.0.1".equals(hostName)) { + return true; + } + + // TODO: Check the host name of current computer as well. + return false; + } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 9cd3aa446..311db11c3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -55,6 +55,7 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); vmHandler.setVmProvider(context.getProvider(IVirtualMachineManagerProvider.class)); + UsageDataSession.recordInfo("asyncJDWP", context.asyncJDWP()); if (debugSession != null) { // This is a global event handler to handle the JDI Event from Virtual Machine. debugSession.getEventHub().events().subscribe(debugEvent -> { @@ -104,6 +105,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, ThreadReference deathThread = ((ThreadDeathEvent) event).thread(); Events.ThreadEvent threadDeathEvent = new Events.ThreadEvent("exited", deathThread.uniqueID()); context.getProtocolServer().sendEvent(threadDeathEvent); + context.getThreadCache().addDeathThread(deathThread.uniqueID()); } else if (event instanceof BreakpointEvent) { // ignore since SetBreakpointsRequestHandler has already handled } else if (event instanceof ExceptionEvent) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 9cf99742d..1a0e41003 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -64,6 +64,11 @@ public CompletableFuture handle(Command command, Arguments arguments, VariableUtils.applyFormatterOptions(options, evalArguments.format != null && evalArguments.format.hex); String expression = evalArguments.expression; + // Async mode is supposed to be performant, then disable the advanced features like hover evaluation. + if (!context.isLocalDebugging() && context.asyncJDWP() && "hover".equals(evalArguments.context)) { + return CompletableFuture.completedFuture(response); + } + if (StringUtils.isBlank(expression)) { throw new CompletionException(AdapterUtils.createUserErrorDebugException( "Failed to evaluate. Reason: Empty expression cannot be evaluated.", diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java index e9697f899..b30959cd5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java @@ -72,7 +72,7 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { InlineValuesArguments inlineValuesArgs = (InlineValuesArguments) arguments; - int variableCount = inlineValuesArgs == null || inlineValuesArgs.variables == null ? 0 : inlineValuesArgs.variables.length; + final int variableCount = inlineValuesArgs == null || inlineValuesArgs.variables == null ? 0 : inlineValuesArgs.variables.length; InlineVariable[] inlineVariables = inlineValuesArgs.variables; StackFrameReference stackFrameReference = (StackFrameReference) context.getRecyclableIdPool().getObjectById(inlineValuesArgs.frameId); if (stackFrameReference == null) { @@ -81,6 +81,12 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + // Async mode is supposed to be performant, then disable the advanced features like inline values. + if (!context.isLocalDebugging() && context.asyncJDWP()) { + response.body = new Responses.InlineValuesResponse(null); + return CompletableFuture.completedFuture(response); + } + IStackFrameManager stackFrameManager = context.getStackFrameManager(); StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference); if (frame == null) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java index 3479c9a8a..2023209f4 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java @@ -30,6 +30,7 @@ import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.RestartFrameArguments; +import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.request.StepRequest; @@ -59,7 +60,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (canRestartFrame(context, stackFrameReference)) { try { ThreadReference reference = stackFrameReference.getThread(); - popStackFrames(context, reference, stackFrameReference.getDepth()); + popStackFrames(context, stackFrameReference); stepInto(context, reference); } catch (DebugException de) { context.getProtocolServer().sendEvent(new Events.UserNotificationEvent(NotificationType.ERROR, de.getMessage())); @@ -80,10 +81,20 @@ private boolean canRestartFrame(IDebugAdapterContext context, StackFrameReferenc return false; } ThreadReference reference = frameReference.getThread(); - StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(reference); + int totalFrames; + try { + totalFrames = reference.frameCount(); + } catch (IncompatibleThreadStateException e) { + return false; + } // The frame cannot be the bottom one of the call stack: - if (frames.length <= frameReference.getDepth() + 1) { + if (totalFrames <= frameReference.getDepth() + 1) { + return false; + } + + StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(reference, 0, frameReference.getDepth() + 2); + if (frames.length == 0) { return false; } @@ -96,9 +107,12 @@ private boolean canRestartFrame(IDebugAdapterContext context, StackFrameReferenc return true; } - private void popStackFrames(IDebugAdapterContext context, ThreadReference thread, int depth) throws DebugException { - StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread); - StackFrameUtility.pop(frames[depth]); + private void popStackFrames(IDebugAdapterContext context, StackFrameReference stackFrameRef) throws DebugException { + StackFrame frame = context.getStackFrameManager().getStackFrame(stackFrameRef); + if (frame == null) { + return; + } + StackFrameUtility.pop(frame); } private void stepInto(IDebugAdapterContext context, ThreadReference thread) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 6dc74a0bd..085dfe63f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -130,10 +130,11 @@ public CompletableFuture handle(Command command, Arguments arguments, IBreakpoint[] added = context.getBreakpointManager() .setBreakpoints(AdapterUtils.decodeURIComponent(sourcePath), toAdds, bpArguments.sourceModified); for (int i = 0; i < bpArguments.breakpoints.length; i++) { + added[i].setAsync(context.asyncJDWP()); // For newly added breakpoint, should install it to debuggee first. if (toAdds[i] == added[i] && added[i].className() != null) { added[i].install().thenAccept(bp -> { - Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("new", this.convertDebuggerBreakpointToClient(bp, context)); + Events.BreakpointEvent bpEvent = new Events.BreakpointEvent("changed", this.convertDebuggerBreakpointToClient(bp, context)); context.getProtocolServer().sendEvent(bpEvent); }); } else if (added[i].className() != null) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index fa4d23388..3f370b429 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -93,6 +93,7 @@ public CompletableFuture handle(Command command, Arguments arguments, continue; } + currentMethodBreakpoints[i].setAsync(context.asyncJDWP()); // If the requested method breakpoint exists in the manager, it will reuse // the cached breakpoint exists object. // Otherwise add the requested method breakpoint to the cache. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 021bae609..ffa74206e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,11 +16,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -36,9 +39,12 @@ import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.LocalVariable; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; @@ -57,26 +63,32 @@ public CompletableFuture handle(Command command, Arguments arguments, response.body = new Responses.StackTraceResponseBody(result, 0); return CompletableFuture.completedFuture(response); } - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId); + ThreadReference thread = context.getThreadCache().getThread(stacktraceArgs.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId); + } int totalFrames = 0; if (thread != null) { try { totalFrames = thread.frameCount(); + int count = stacktraceArgs.levels == 0 ? totalFrames - stacktraceArgs.startFrame + : Math.min(totalFrames - stacktraceArgs.startFrame, stacktraceArgs.levels); if (totalFrames <= stacktraceArgs.startFrame) { response.body = new Responses.StackTraceResponseBody(result, totalFrames); return CompletableFuture.completedFuture(response); } - StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread); - int count = stacktraceArgs.levels == 0 ? totalFrames - stacktraceArgs.startFrame - : Math.min(totalFrames - stacktraceArgs.startFrame, stacktraceArgs.levels); - for (int i = stacktraceArgs.startFrame; i < frames.length && count-- > 0; i++) { + StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count); + List jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP()); + for (int i = stacktraceArgs.startFrame; i < jdiFrames.size() && count-- > 0; i++) { StackFrameReference stackframe = new StackFrameReference(thread, i); - int frameId = context.getRecyclableIdPool().addObject(thread.uniqueID(), stackframe); - result.add(convertDebuggerStackFrameToClient(frames[i], frameId, context)); + int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); + StackFrameInfo jdiFrame = jdiFrames.get(i - stacktraceArgs.startFrame); + result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, context)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException - | AbsentInformationException | ObjectCollectedException e) { + | AbsentInformationException | ObjectCollectedException + | CancellationException | CompletionException e) { // when error happens, the possible reason is: // 1. the vscode has wrong parameter/wrong uri // 2. the thread actually terminates @@ -87,18 +99,79 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } - private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame, int frameId, IDebugAdapterContext context) + private static List resolveStackFrameInfos(StackFrame[] frames, boolean async) + throws AbsentInformationException, IncompatibleThreadStateException { + List jdiFrames = new ArrayList<>(); + List> futures = new ArrayList<>(); + for (StackFrame frame : frames) { + StackFrameInfo jdiFrame = new StackFrameInfo(frame); + jdiFrame.location = jdiFrame.frame.location(); + jdiFrame.method = jdiFrame.location.method(); + jdiFrame.methodName = jdiFrame.method.name(); + jdiFrame.isNative = jdiFrame.method.isNative(); + jdiFrame.declaringType = jdiFrame.location.declaringType(); + if (async) { + // JDWP Command: M_LINE_TABLE + futures.add(AsyncJdwpUtils.runAsync(() -> { + jdiFrame.lineNumber = jdiFrame.location.lineNumber(); + })); + + // JDWP Commands: RT_SOURCE_DEBUG_EXTENSION, RT_SOURCE_FILE + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + // When the .class file doesn't contain source information in meta data, + // invoking Location#sourceName() would throw AbsentInformationException. + jdiFrame.sourceName = jdiFrame.declaringType.sourceName(); + } catch (AbsentInformationException e) { + jdiFrame.sourceName = null; + } + })); + + // JDWP Command: RT_SIGNATURE + futures.add(AsyncJdwpUtils.runAsync(() -> { + jdiFrame.typeSignature = jdiFrame.declaringType.signature(); + })); + } else { + jdiFrame.lineNumber = jdiFrame.location.lineNumber(); + jdiFrame.typeSignature = jdiFrame.declaringType.signature(); + try { + // When the .class file doesn't contain source information in meta data, + // invoking Location#sourceName() would throw AbsentInformationException. + jdiFrame.sourceName = jdiFrame.declaringType.sourceName(); + } catch (AbsentInformationException e) { + jdiFrame.sourceName = null; + } + } + + jdiFrames.add(jdiFrame); + } + + AsyncJdwpUtils.await(futures); + for (StackFrameInfo jdiFrame : jdiFrames) { + jdiFrame.typeName = jdiFrame.declaringType.name(); + jdiFrame.argumentTypeNames = jdiFrame.method.argumentTypeNames(); + if (jdiFrame.sourceName == null) { + String enclosingType = AdapterUtils.parseEnclosingType(jdiFrame.typeName); + jdiFrame.sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java"; + jdiFrame.sourcePath = enclosingType.replace('.', File.separatorChar) + ".java"; + } else { + jdiFrame.sourcePath = jdiFrame.declaringType.sourcePaths(null).get(0); + } + } + + return jdiFrames; + } + + private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFrame, int frameId, IDebugAdapterContext context) throws URISyntaxException, AbsentInformationException { - Location location = stackFrame.location(); - Method method = location.method(); - Types.Source clientSource = this.convertDebuggerSourceToClient(location, context); - String methodName = formatMethodName(method, true, true); - int lineNumber = AdapterUtils.convertLineNumber(location.lineNumber(), context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + Types.Source clientSource = convertDebuggerSourceToClient(jdiFrame.typeName, jdiFrame.sourceName, jdiFrame.sourcePath, context); + String methodName = formatMethodName(jdiFrame.methodName, jdiFrame.argumentTypeNames, jdiFrame.typeName, true, true); + int clientLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); // Line number returns -1 if the information is not available; specifically, always returns -1 for native methods. String presentationHint = null; - if (lineNumber < 0) { + if (clientLineNumber < 0) { presentationHint = "subtle"; - if (method.isNative()) { + if (jdiFrame.isNative) { // For native method, display a tip text "native method" in the Call Stack View. methodName += "[native method]"; } else { @@ -107,25 +180,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame clientSource = null; } } - return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); - } - - private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdapterContext context) throws URISyntaxException { - final String fullyQualifiedName = location.declaringType().name(); - String sourceName = ""; - String relativeSourcePath = ""; - try { - // When the .class file doesn't contain source information in meta data, - // invoking Location#sourceName() would throw AbsentInformationException. - sourceName = location.sourceName(); - relativeSourcePath = location.sourcePath(); - } catch (AbsentInformationException e) { - String enclosingType = AdapterUtils.parseEnclosingType(fullyQualifiedName); - sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java"; - relativeSourcePath = enclosingType.replace('.', File.separatorChar) + ".java"; - } - - return convertDebuggerSourceToClient(fullyQualifiedName, sourceName, relativeSourcePath, context); + return new Types.StackFrame(frameId, methodName, clientSource, clientLineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); } /** @@ -164,20 +219,42 @@ public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedNa } } - private String formatMethodName(Method method, boolean showContextClass, boolean showParameter) { + private String formatMethodName(String methodName, List argumentTypeNames, String fqn, boolean showContextClass, boolean showParameter) { StringBuilder formattedName = new StringBuilder(); if (showContextClass) { - String fullyQualifiedClassName = method.declaringType().name(); - formattedName.append(SimpleTypeFormatter.trimTypeName(fullyQualifiedClassName)); + formattedName.append(SimpleTypeFormatter.trimTypeName(fqn)); formattedName.append("."); } - formattedName.append(method.name()); + formattedName.append(methodName); if (showParameter) { - List argumentTypeNames = method.argumentTypeNames().stream().map(SimpleTypeFormatter::trimTypeName).collect(Collectors.toList()); + argumentTypeNames = argumentTypeNames.stream().map(SimpleTypeFormatter::trimTypeName).collect(Collectors.toList()); formattedName.append("("); formattedName.append(String.join(",", argumentTypeNames)); formattedName.append(")"); } return formattedName.toString(); } + + static class StackFrameInfo { + public StackFrame frame; + public Location location; + public Method method; + public String methodName; + public List argumentTypeNames = new ArrayList<>(); + public boolean isNative = false; + public int lineNumber; + public ReferenceType declaringType = null; + public String typeName; + public String typeSignature; + public String sourceName = ""; + public String sourcePath = ""; + + // variables + public List visibleVariables = null; + public ObjectReference thisObject; + + public StackFrameInfo(StackFrame frame) { + this.frame = frame; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 72d14eb5d..4f58c113f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,12 +11,15 @@ package com.microsoft.java.debug.core.adapter.handler; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import org.apache.commons.lang3.ArrayUtils; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugEvent; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; @@ -68,16 +71,18 @@ public CompletableFuture handle(Command command, Arguments arguments, } long threadId = ((StepArguments) arguments).threadId; - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), threadId); + ThreadReference thread = context.getThreadCache().getThread(threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), threadId); + } if (thread != null) { JdiExceptionReference exception = context.getExceptionManager().removeException(threadId); context.getStepResultManager().removeMethodResult(threadId); try { + final ThreadReference targetThread = thread; ThreadState threadState = new ThreadState(); threadState.threadId = threadId; threadState.pendingStepType = command; - threadState.stackDepth = thread.frameCount(); - threadState.stepLocation = getTopFrame(thread).location(); threadState.eventSubscription = context.getDebugSession().getEventHub().events() .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) @@ -98,27 +103,82 @@ public CompletableFuture handle(Command command, Arguments arguments, } else { threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingStepRequest.enable(); - MethodExitRequest methodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); - methodExitRequest.addThreadFilter(thread); - methodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); - if (thread.virtualMachine().canUseInstanceFilters()) { + threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); + threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + + if (context.asyncJDWP()) { + List> futures = new ArrayList<>(); + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + // JDWP Command: TR_FRAMES + threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + // JDWP Command: SF_THIS_OBJECT + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore + } + } + } catch (IncompatibleThreadStateException e) { + throw new CompletionException(e); + } + })); + futures.add(AsyncJdwpUtils.runAsync( + // JDWP Command: OR_IS_COLLECTED + () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread) + )); + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + // JDWP Command: TR_FRAME_COUNT + threadState.stackDepth = targetThread.frameCount(); + } catch (IncompatibleThreadStateException e) { + throw new CompletionException(e); + } + })); + futures.add( + // JDWP Command: ER_SET + AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable()) + ); + try { - ObjectReference thisObject = getTopFrame(thread).thisObject(); - if (thisObject != null) { - methodExitRequest.addInstanceFilter(thisObject); + AsyncJdwpUtils.await(futures); + } catch (CompletionException ex) { + if (ex.getCause() instanceof IncompatibleThreadStateException) { + throw (IncompatibleThreadStateException) ex.getCause(); } - } catch (Exception e) { - // ignore + throw ex; } + + // JDWP Command: ER_SET + threadState.pendingMethodExitRequest.enable(); + } else { + threadState.stackDepth = targetThread.frameCount(); + threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addThreadFilter(thread); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore + } + } + threadState.pendingStepRequest.enable(); + threadState.pendingMethodExitRequest.enable(); } - methodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - threadState.pendingMethodExitRequest = methodExitRequest; - methodExitRequest.enable(); DebugUtility.resumeThread(thread); - ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context); } catch (IncompatibleThreadStateException ex) { // Roll back the Exception info if stepping fails. @@ -136,6 +196,14 @@ public CompletableFuture handle(Command command, Arguments arguments, failureMessage, ErrorCode.STEP_FAILURE, ex); + } catch (Exception ex) { + // Roll back the Exception info if stepping fails. + context.getExceptionManager().setException(threadId, exception); + final String failureMessage = String.format("Failed to step because of the error '%s'", ex.getMessage()); + throw AdapterUtils.createCompletionException( + failureMessage, + ErrorCode.STEP_FAILURE, + ex.getCause() != null ? ex.getCause() : ex); } } @@ -280,6 +348,7 @@ class ThreadState { StepRequest pendingStepRequest = null; MethodExitRequest pendingMethodExitRequest = null; int stackDepth = -1; + StackFrame topFrame = null; Location stepLocation = null; Disposable eventSubscription = null; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index 3e4b481d5..cedbf2ae1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,9 +14,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; +import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -77,14 +81,14 @@ public CompletableFuture handle(Command command, Arguments arguments, private CompletableFuture threads(Requests.ThreadsArguments arguments, Response response, IDebugAdapterContext context) { ArrayList threads = new ArrayList<>(); try { - for (ThreadReference thread : context.getDebugSession().getAllThreads()) { - if (thread.isCollected()) { - continue; - } - Types.Thread clientThread = new Types.Thread(thread.uniqueID(), "Thread [" + thread.name() + "]"); - threads.add(clientThread); + List allThreads = context.getDebugSession().getAllThreads(); + context.getThreadCache().resetThreads(allThreads); + allThreads = allThreads.stream().filter((thread) -> !context.getThreadCache().isDeathThread(thread.uniqueID())).toList(); + List jdiThreads = resolveThreadInfos(allThreads, context); + for (ThreadInfo jdiThread : jdiThreads) { + threads.add(new Types.Thread(jdiThread.thread.uniqueID(), "Thread [" + jdiThread.name + "]")); } - } catch (ObjectCollectedException ex) { + } catch (ObjectCollectedException | CancellationException | CompletionException ex) { // allThreads may throw VMDisconnectedException when VM terminates and thread.name() may throw ObjectCollectedException // when the thread is exiting. } @@ -92,6 +96,33 @@ private CompletableFuture threads(Requests.ThreadsArguments arguments, return CompletableFuture.completedFuture(response); } + private static List resolveThreadInfos(List allThreads, IDebugAdapterContext context) { + List threadInfos = new ArrayList<>(allThreads.size()); + List> futures = new ArrayList<>(); + for (ThreadReference thread : allThreads) { + ThreadInfo threadInfo = new ThreadInfo(thread); + long threadId = thread.uniqueID(); + if (context.getThreadCache().getThreadName(threadId) != null) { + threadInfo.name = context.getThreadCache().getThreadName(threadId); + } else { + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> { + threadInfo.name = threadInfo.thread.name(); + context.getThreadCache().setThreadName(threadId, threadInfo.name); + })); + } else { + threadInfo.name = threadInfo.thread.name(); + context.getThreadCache().setThreadName(threadId, threadInfo.name); + } + } + + threadInfos.add(threadInfo); + } + + AsyncJdwpUtils.await(futures); + return threadInfos; + } + private CompletableFuture pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) { ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); if (thread != null) { @@ -108,7 +139,10 @@ private CompletableFuture pause(Requests.PauseArguments arguments, Res private CompletableFuture resume(Requests.ContinueArguments arguments, Response response, IDebugAdapterContext context) { boolean allThreadsContinued = true; - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + ThreadReference thread = context.getThreadCache().getThread(arguments.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + } /** * See the jdi doc https://docs.oracle.com/javase/7/docs/jdk/api/jpda/jdi/com/sun/jdi/ThreadReference.html#resume(), * suspends of both the virtual machine and individual threads are counted. Before a thread will run again, it must @@ -123,7 +157,11 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); - context.getDebugSession().resume(); + if (context.asyncJDWP()) { + resumeVMAsync(context.getDebugSession()); + } else { + context.getDebugSession().resume(); + } context.getRecyclableIdPool().removeAllObjects(); } response.body = new Responses.ContinueResponseBody(allThreadsContinued); @@ -132,7 +170,11 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { context.getExceptionManager().removeAllExceptions(); - context.getDebugSession().resume(); + if (context.asyncJDWP()) { + resumeVMAsync(context.getDebugSession()); + } else { + context.getDebugSession().resume(); + } context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); context.getRecyclableIdPool().removeAllObjects(); return CompletableFuture.completedFuture(response); @@ -140,16 +182,19 @@ private CompletableFuture resumeAll(Requests.ThreadOperationArguments private CompletableFuture resumeOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { - long threadId = thread.uniqueID(); - if (threadId != arguments.threadId && thread.isSuspended()) { - context.getExceptionManager().removeException(threadId); - DebugUtility.resumeThread(thread); - context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); - checkThreadRunningAndRecycleIds(thread, context); + if (thread.uniqueID() == arguments.threadId) { + continue; } - } + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> resumeThread(thread, context))); + } else { + resumeThread(thread, context); + } + } + AsyncJdwpUtils.await(futures); return CompletableFuture.completedFuture(response); } @@ -161,14 +206,19 @@ private CompletableFuture pauseAll(Requests.ThreadOperationArguments a private CompletableFuture pauseOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { - long threadId = thread.uniqueID(); - if (threadId != arguments.threadId && !thread.isCollected() && !thread.isSuspended()) { - thread.suspend(); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + if (thread.uniqueID() == arguments.threadId) { + continue; } - } + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> pauseThread(thread, context))); + } else { + pauseThread(thread, context); + } + } + AsyncJdwpUtils.await(futures); return CompletableFuture.completedFuture(response); } @@ -179,13 +229,7 @@ public static void checkThreadRunningAndRecycleIds(ThreadReference thread, IDebu try { IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); engine.clearState(thread); - boolean allThreadsRunning = !DebugUtility.getAllThreadsSafely(context.getDebugSession()).stream() - .anyMatch(ThreadReference::isSuspended); - if (allThreadsRunning) { - context.getRecyclableIdPool().removeAllObjects(); - } else { - context.getRecyclableIdPool().removeObjectsByOwner(thread.uniqueID()); - } + context.getRecyclableIdPool().removeObjectsByOwner(thread.uniqueID()); } catch (VMDisconnectedException ex) { // isSuspended may throw VMDisconnectedException when the VM terminates context.getRecyclableIdPool().removeAllObjects(); @@ -194,4 +238,58 @@ public static void checkThreadRunningAndRecycleIds(ThreadReference thread, IDebu context.getRecyclableIdPool().removeObjectsByOwner(thread.uniqueID()); } } + + private void resumeVMAsync(IDebugSession debugSession) { + List> futures = new ArrayList<>(); + for (ThreadReference tr : DebugUtility.getAllThreadsSafely(debugSession)) { + futures.add(AsyncJdwpUtils.runAsync(() -> { + try { + while (tr.suspendCount() > 1) { + tr.resume(); + } + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + })); + } + + AsyncJdwpUtils.await(futures); + debugSession.getVM().resume(); + } + + private void resumeThread(ThreadReference thread, IDebugAdapterContext context) { + try { + int suspends = thread.suspendCount(); + if (suspends > 0) { + long threadId = thread.uniqueID(); + context.getExceptionManager().removeException(threadId); + DebugUtility.resumeThread(thread, suspends); + context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); + checkThreadRunningAndRecycleIds(thread, context); + } + } catch (ObjectCollectedException ex) { + // ignore it. + } + } + + private void pauseThread(ThreadReference thread, IDebugAdapterContext context) { + try { + if (!thread.isSuspended()) { + long threadId = thread.uniqueID(); + thread.suspend(); + context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + } + } catch (ObjectCollectedException ex) { + // ignore it if the thread is garbage collected. + } + } + + static class ThreadInfo { + public ThreadReference thread; + public String name; + + public ThreadInfo(ThreadReference thread) { + this.thread = thread; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 8f37ef574..76bb5a4db 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2021 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,11 +19,14 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.JdiMethodResult; @@ -39,6 +42,7 @@ import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable; import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.adapter.variables.StringReferenceProxy; import com.microsoft.java.debug.core.adapter.variables.Variable; import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils; import com.microsoft.java.debug.core.adapter.variables.VariableProxy; @@ -57,6 +61,7 @@ import com.sun.jdi.InvalidStackFrameException; import com.sun.jdi.ObjectReference; import com.sun.jdi.StackFrame; +import com.sun.jdi.StringReference; import com.sun.jdi.Type; import com.sun.jdi.Value; @@ -96,7 +101,7 @@ public CompletableFuture handle(Command command, Arguments arguments, VariableProxy containerNode = (VariableProxy) container; - if (containerNode.isLazyVariable() && DebugSettings.getCurrent().showToString) { + if (supportsToStringView(context) && containerNode.isLazyVariable()) { Types.Variable typedVariable = this.resolveLazyVariable(context, containerNode, variableFormatter, options, evaluationEngine); if (typedVariable != null) { list.add(typedVariable); @@ -123,24 +128,29 @@ public CompletableFuture handle(Command command, Arguments arguments, String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->"; childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null)); } - childrenList.addAll(VariableUtils.listLocalVariables(frame)); - Variable thisVariable = VariableUtils.getThisVariable(frame); - if (thisVariable != null) { - childrenList.add(thisVariable); - } - if (showStaticVariables && frame.location().method().isStatic()) { - childrenList.addAll(VariableUtils.listStaticVariables(frame)); + + if (context.asyncJDWP()) { + childrenList.addAll(getVariablesOfFrameAsync(frame, showStaticVariables)); + } else { + childrenList.addAll(VariableUtils.listLocalVariables(frame)); + Variable thisVariable = VariableUtils.getThisVariable(frame); + if (thisVariable != null) { + childrenList.add(thisVariable); + } + if (showStaticVariables && frame.location().method().isStatic()) { + childrenList.addAll(VariableUtils.listStaticVariables(frame)); + } } - } catch (AbsentInformationException | InternalException | InvalidStackFrameException e) { + } catch (CompletionException | InternalException | InvalidStackFrameException | CancellationException | AbsentInformationException e) { throw AdapterUtils.createCompletionException( String.format("Failed to get variables. Reason: %s", e.toString()), ErrorCode.GET_VARIABLE_FAILURE, - e); + e.getCause() != null ? e.getCause() : e); } } else { try { ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable(); - if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { + if (supportsLogicStructureView(context) && evaluationEngine != null) { JavaLogicalStructure logicalStructure = null; try { logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj); @@ -232,6 +242,17 @@ public CompletableFuture handle(Command command, Arguments arguments, } }); } + + // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. + if (context.asyncJDWP()) { + try { + AsyncJdwpUtils.await(warmUpJDICache(childrenList)); + } catch (CompletionException | CancellationException e) { + response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); + } + } + for (Variable javaVariable : childrenList) { Value value = javaVariable.value; String name = javaVariable.name; @@ -242,7 +263,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Value sizeValue = null; if (value instanceof ArrayReference) { indexedVariables = ((ArrayReference) value).length(); - } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) { + } else if (supportsLogicStructureView(context) && value instanceof ObjectReference && evaluationEngine != null) { try { JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); if (structure != null && structure.getSizeExpression() != null) { @@ -310,7 +331,7 @@ public CompletableFuture handle(Command command, Arguments arguments, // If failed to resolve the variable value, skip the details info as well. } else if (sizeValue != null) { detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options); - } else if (DebugSettings.getCurrent().showToString) { + } else if (supportsToStringView(context)) { if (VariableDetailUtils.isLazyLoadingSupported(value) && varProxy != null) { varProxy.setLazyVariable(true); } else { @@ -352,6 +373,14 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private boolean supportsLogicStructureView(IDebugAdapterContext context) { + return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showLogicalStructure; + } + + private boolean supportsToStringView(IDebugAdapterContext context) { + return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showToString; + } + private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter, Map options, IEvaluationProvider evaluationEngine) { VariableProxy valueReferenceProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), @@ -384,4 +413,57 @@ private Set getDuplicateNames(Collection list) { } return result; } + + private List getVariablesOfFrameAsync(StackFrame frame, boolean showStaticVariables) { + CompletableFuture> localVariables = VariableUtils.listLocalVariablesAsync(frame); + CompletableFuture thisVariable = VariableUtils.getThisVariableAsync(frame); + CompletableFuture>[] staticVariables = new CompletableFuture[1]; + if (showStaticVariables && frame.location().method().isStatic()) { + staticVariables[0] = VariableUtils.listStaticVariablesAsync(frame); + } + + CompletableFuture futures = staticVariables[0] == null ? CompletableFuture.allOf(localVariables, thisVariable) + : CompletableFuture.allOf(localVariables, thisVariable, staticVariables[0]); + + AsyncJdwpUtils.await(futures); + + List result = new ArrayList<>(); + result.addAll(localVariables.join()); + Variable thisVar = thisVariable.join(); + if (thisVar != null) { + result.add(thisVar); + } + + if (staticVariables[0] != null) { + result.addAll(staticVariables[0].join()); + } + + return result; + } + + private CompletableFuture warmUpJDICache(List variables) { + List> fetchVariableInfoFutures = new ArrayList<>(); + for (Variable javaVariable : variables) { + Value value = javaVariable.value; + if (value instanceof ArrayReference) { + // JDWP Command: AR_LENGTH + fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> ((ArrayReference) value).length())); + } else if (value instanceof StringReference) { + // JDWP Command: SR_VALUE + fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + String strValue = ((StringReference) value).value(); + javaVariable.value = new StringReferenceProxy((StringReference) value, strValue); + })); + } + + if (value instanceof ObjectReference) { + // JDWP Command: OR_REFERENCE_TYPE, RT_SIGNATURE + fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + value.type().signature(); + })); + } + } + + return CompletableFuture.allOf(fetchVariableInfoFutures.toArray(new CompletableFuture[0])); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java new file mode 100644 index 000000000..82c4da56e --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StringReferenceProxy.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2022 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.variables; + +import java.util.List; +import java.util.Map; + +import com.sun.jdi.ClassNotLoadedException; +import com.sun.jdi.Field; +import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.InvalidTypeException; +import com.sun.jdi.InvocationException; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.StringReference; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.Type; +import com.sun.jdi.Value; +import com.sun.jdi.VirtualMachine; + +public class StringReferenceProxy implements StringReference { + private StringReference delegateStringRef; + private String value = null; + + public StringReferenceProxy(StringReference sr, String value) { + this.delegateStringRef = sr; + this.value = value; + } + + public String value() { + if (value != null) { + return value; + } + + return delegateStringRef.value(); + } + + public ReferenceType referenceType() { + return delegateStringRef.referenceType(); + } + + public VirtualMachine virtualMachine() { + return delegateStringRef.virtualMachine(); + } + + public String toString() { + return delegateStringRef.toString(); + } + + public Value getValue(Field sig) { + return delegateStringRef.getValue(sig); + } + + public Map getValues(List fields) { + return delegateStringRef.getValues(fields); + } + + public void setValue(Field field, Value value) throws InvalidTypeException, ClassNotLoadedException { + delegateStringRef.setValue(field, value); + } + + public Value invokeMethod(ThreadReference thread, Method method, List arguments, int options) + throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, + InvocationException { + return delegateStringRef.invokeMethod(thread, method, arguments, options); + } + + public Type type() { + return delegateStringRef.type(); + } + + public void disableCollection() { + delegateStringRef.disableCollection(); + } + + public void enableCollection() { + delegateStringRef.enableCollection(); + } + + public boolean isCollected() { + return delegateStringRef.isCollected(); + } + + public long uniqueID() { + return delegateStringRef.uniqueID(); + } + + public List waitingThreads() throws IncompatibleThreadStateException { + return delegateStringRef.waitingThreads(); + } + + public ThreadReference owningThread() throws IncompatibleThreadStateException { + return delegateStringRef.owningThread(); + } + + public int entryCount() throws IncompatibleThreadStateException { + return delegateStringRef.entryCount(); + } + + public List referringObjects(long maxReferrers) { + return delegateStringRef.referringObjects(maxReferrers); + } + + public boolean equals(Object obj) { + return delegateStringRef.equals(obj); + } + + public int hashCode() { + return delegateStringRef.hashCode(); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index d53e2705c..bd8c8036d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,11 +15,15 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.function.Consumer; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.adapter.formatter.NumericFormatEnum; @@ -213,6 +217,91 @@ public static List listLocalVariables(StackFrame stackFrame) throws Ab return res; } + public static CompletableFuture> listLocalVariablesAsync(StackFrame stackFrame) { + CompletableFuture> future = new CompletableFuture<>(); + if (stackFrame.location().method().isNative()) { + return CompletableFuture.completedFuture(new ArrayList<>()); + } + + AsyncJdwpUtils.supplyAsync(() -> { + try { + return stackFrame.visibleVariables(); + } catch (AbsentInformationException ex) { + throw new CompletionException(ex); + } + }).thenCompose((visibleVariables) -> { + // When using the API StackFrame.getValues() to batch fetch the variable values, the JDI + // probably throws timeout exception if the variables to be passed at one time are large. + // So use paging to fetch the values in chunks. + return bulkFetchValuesAsync(visibleVariables, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage) -> { + Map values = stackFrame.getValues(currentPage); + List result = new ArrayList<>(); + for (LocalVariable localVariable : currentPage) { + Variable var = new Variable(localVariable.name(), values.get(localVariable)); + var.local = localVariable; + result.add(var); + } + + return result; + }); + }).whenComplete((res, ex) -> { + if (ex instanceof CompletionException && ex.getCause() != null) { + ex = ex.getCause(); + } + + if (ex instanceof AbsentInformationException) { + // avoid listing variable on native methods + try { + if (stackFrame.location().method().argumentTypes().size() == 0) { + future.complete(new ArrayList<>()); + return; + } + } catch (ClassNotLoadedException ex2) { + // ignore since the method is hit. + } + // 1. in oracle implementations, when there is no debug information, the AbsentInformationException will be + // thrown, then we need to retrieve arguments from stackFrame#getArgumentValues. + // 2. in eclipse jdt implementations, when there is no debug information, stackFrame#visibleVariables will + // return some generated variables like arg0, arg1, and the stackFrame#getArgumentValues will return null + + // for both scenarios, we need to handle the possible null returned by stackFrame#getArgumentValues and + // we need to call stackFrame.getArgumentValues get the arguments if AbsentInformationException is thrown + int argId = 0; + try { + List arguments = stackFrame.getArgumentValues(); + if (arguments == null) { + future.complete(new ArrayList<>()); + return; + } + + List variables = new ArrayList<>(); + for (Value argValue : arguments) { + Variable var = new Variable("arg" + argId, argValue); + var.argumentIndex = argId++; + variables.add(var); + } + future.complete(variables); + } catch (InternalException ex2) { + // From Oracle's forums: + // This could be a JPDA bug. Unexpected JDWP Error: 32 means that an 'opaque' frame was + // detected at the lower JPDA levels, + // typically a native frame. + if (ex2.errorCode() != 32) { + throw ex2; + } + } + } else if (ex != null) { + future.complete(new ArrayList<>()); + } else { + future.complete(res.stream() + .flatMap(List::stream) + .collect(Collectors.toList())); + } + }); + + return future; + } + /** * Get the this variable of an stack frame. * @@ -228,6 +317,16 @@ public static Variable getThisVariable(StackFrame stackFrame) { return new Variable("this", thisObject); } + public static CompletableFuture getThisVariableAsync(StackFrame stackFrame) { + return AsyncJdwpUtils.supplyAsync(() -> { + ObjectReference thisObject = stackFrame.thisObject(); + if (thisObject == null) { + return null; + } + return new Variable("this", thisObject); + }); + } + /** * Get the static variable of an stack frame. * @@ -251,6 +350,40 @@ public static List listStaticVariables(StackFrame stackFrame) { return res; } + public static CompletableFuture> listStaticVariablesAsync(StackFrame stackFrame) { + CompletableFuture> future = new CompletableFuture<>(); + ReferenceType type = stackFrame.location().declaringType(); + AsyncJdwpUtils.supplyAsync(() -> { + return type.allFields().stream().filter(TypeComponent::isStatic).collect(Collectors.toList()); + }).thenCompose((fields) -> { + return bulkFetchValuesAsync(fields, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage) -> { + List variables = new ArrayList<>(); + Map fieldValues = type.getValues(currentPage); + for (Field currentField : currentPage) { + Variable var = new Variable(currentField.name(), fieldValues.get(currentField)); + var.field = currentField; + variables.add(var); + } + + return variables; + }); + }).whenComplete((res, ex) -> { + if (ex instanceof CompletionException && ex.getCause() != null) { + ex = ex.getCause(); + } + + if (ex != null) { + future.complete(new ArrayList<>()); + } else { + future.complete(res.stream() + .flatMap(List::stream) + .collect(Collectors.toList())); + } + }); + + return future; + } + /** * Apply the display options for variable formatter, it is used in variable and evaluate requests, controls the display content in * variable view/debug console. @@ -316,6 +449,23 @@ private static void bulkFetchValues(List elements, int numberPerPage, Con } } + private static CompletableFuture> bulkFetchValuesAsync(List elements, int numberPerPage, Function, R> function) { + int size = elements.size(); + numberPerPage = numberPerPage < 1 ? 1 : numberPerPage; + int page = size / numberPerPage + Math.min(size % numberPerPage, 1); + List> futures = new ArrayList<>(); + for (int i = 0; i < page; i++) { + int pageStart = i * numberPerPage; + int pageEnd = Math.min(pageStart + numberPerPage, size); + final List currentPage = elements.subList(pageStart, pageEnd); + futures.add(AsyncJdwpUtils.supplyAsync(() -> { + return function.apply(currentPage); + })); + } + + return AsyncJdwpUtils.all(futures); + } + private VariableUtils() { } From 216a7c650a8786b1272cb203d3edbc89dccae675 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 09:18:09 +0800 Subject: [PATCH 048/144] Support listing field variables async (#437) --- .../java/debug/core/UsageDataSession.java | 4 + .../adapter/handler/AttachRequestHandler.java | 97 ++++++++++--------- .../handler/VariablesRequestHandler.java | 64 +++++++----- .../core/adapter/variables/VariableUtils.java | 72 +++++++++++++- 4 files changed, 169 insertions(+), 68 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java index 911dca529..b645e2843 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/UsageDataSession.java @@ -196,6 +196,10 @@ public static void recordInfo(String key, Object value) { usageDataLogger.log(Level.INFO, "session info", map); } + public static void recordInfo(String description, Map data) { + usageDataLogger.log(Level.INFO, description, data); + } + /** * Record counts for each user errors encountered. */ diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index ecadbaaac..4d11ef6fb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -62,59 +62,66 @@ public CompletableFuture handle(Command command, Arguments arguments, context.setStepFilters(attachArguments.stepFilters); context.setLocalDebugging(isLocalHost(attachArguments.hostName)); + Map traceInfo = new HashMap<>(); + traceInfo.put("localAttach", context.isLocalDebugging()); + IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); IDebugSession debugSession = null; try { - logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port)); - debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, - attachArguments.timeout); - context.setDebugSession(debugSession); - vmHandler.connectVirtualMachine(debugSession.getVM()); - logger.info("Attaching to debuggee VM succeeded."); - } catch (IOException | IllegalConnectorArgumentsException e) { - throw AdapterUtils.createCompletionException( - String.format("Failed to attach to remote debuggee VM. Reason: %s", e.toString()), - ErrorCode.ATTACH_FAILURE, - e); - } + try { + logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port)); + debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port, + attachArguments.timeout); + context.setDebugSession(debugSession); + vmHandler.connectVirtualMachine(debugSession.getVM()); + logger.info("Attaching to debuggee VM succeeded."); + } catch (IOException | IllegalConnectorArgumentsException e) { + throw AdapterUtils.createCompletionException( + String.format("Failed to attach to remote debuggee VM. Reason: %s", e.toString()), + ErrorCode.ATTACH_FAILURE, + e); + } - Map options = new HashMap<>(); - options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding()); - if (attachArguments.projectName != null) { - options.put(Constants.PROJECT_NAME, attachArguments.projectName); - } - // TODO: Clean up the initialize mechanism - ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); - sourceProvider.initialize(context, options); - // If the debugger and debuggee run at the different JVM platforms, show a warning message. - if (debugSession != null) { - String debuggeeVersion = debugSession.getVM().version(); - String debuggerVersion = sourceProvider.getJavaRuntimeVersion(attachArguments.projectName); - if (StringUtils.isNotBlank(debuggerVersion) && !debuggerVersion.equals(debuggeeVersion)) { - String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. " - + "You could see wrong source mapping results.\n" - + "Debugger JVM version: %s\n" - + "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion); - logger.warning(warnMessage); - context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); + Map options = new HashMap<>(); + options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding()); + if (attachArguments.projectName != null) { + options.put(Constants.PROJECT_NAME, attachArguments.projectName); } + // TODO: Clean up the initialize mechanism + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); + sourceProvider.initialize(context, options); + // If the debugger and debuggee run at the different JVM platforms, show a warning message. + if (debugSession != null) { + String debuggeeVersion = debugSession.getVM().version(); + String debuggerVersion = sourceProvider.getJavaRuntimeVersion(attachArguments.projectName); + if (StringUtils.isNotBlank(debuggerVersion) && !debuggerVersion.equals(debuggeeVersion)) { + String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. " + + "You could see wrong source mapping results.\n" + + "Debugger JVM version: %s\n" + + "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion); + logger.warning(warnMessage); + context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage)); + } - EventRequest request = debugSession.getVM().eventRequestManager().createVMDeathRequest(); - request.setSuspendPolicy(EventRequest.SUSPEND_NONE); - long sent = System.currentTimeMillis(); - request.enable(); - long received = System.currentTimeMillis(); - logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); - UsageDataSession.recordInfo("networkLatency", (received - sent)); - } + EventRequest request = debugSession.getVM().eventRequestManager().createVMDeathRequest(); + request.setSuspendPolicy(EventRequest.SUSPEND_NONE); + long sent = System.currentTimeMillis(); + request.enable(); + long received = System.currentTimeMillis(); + logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); + traceInfo.put("networkLatency", (received - sent)); + } - IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); - evaluationProvider.initialize(context, options); - IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class); - hcrProvider.initialize(context, options); - ICompletionsProvider completionsProvider = context.getProvider(ICompletionsProvider.class); - completionsProvider.initialize(context, options); + IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); + evaluationProvider.initialize(context, options); + IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class); + hcrProvider.initialize(context, options); + ICompletionsProvider completionsProvider = context.getProvider(ICompletionsProvider.class); + completionsProvider.initialize(context, options); + } finally { + UsageDataSession.recordInfo("attach debug info", traceInfo); + } // Send an InitializedEvent to indicate that the debugger is ready to accept configuration requests // (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest). diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 76bb5a4db..c9d7cb666 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -60,6 +60,7 @@ import com.sun.jdi.InternalException; import com.sun.jdi.InvalidStackFrameException; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.StringReference; import com.sun.jdi.Type; @@ -197,7 +198,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (varArgs.count > 0) { childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count); } else { - childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables); + childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables, context.asyncJDWP()); } } } catch (AbsentInformationException e) { @@ -210,12 +211,24 @@ public CompletableFuture handle(Command command, Arguments arguments, // Find variable name duplicates Set duplicateNames = getDuplicateNames(childrenList.stream().map(var -> var.name).collect(Collectors.toList())); - Map variableNameMap = new HashMap<>(); - if (!duplicateNames.isEmpty()) { - Map> duplicateVars = childrenList.stream() - .filter(var -> duplicateNames.contains(var.name)).collect(Collectors.groupingBy(var -> var.name, Collectors.toList())); + List duplicateVars = childrenList.stream() + .filter(var -> duplicateNames.contains(var.name)) + .collect(Collectors.toList()); + // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. + if (context.asyncJDWP()) { + try { + AsyncJdwpUtils.await(warmUpJDICache(childrenList, duplicateVars)); + } catch (CompletionException | CancellationException e) { + response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); + } + } - duplicateVars.forEach((k, duplicateVariables) -> { + Map variableNameMap = new HashMap<>(); + if (!duplicateVars.isEmpty()) { + Map> duplicateVarGroups = duplicateVars.stream() + .collect(Collectors.groupingBy(var -> var.name, Collectors.toList())); + duplicateVarGroups.forEach((k, duplicateVariables) -> { Set declarationTypeNames = new HashSet<>(); boolean declarationTypeNameConflict = false; // try use type formatter to resolve name conflict @@ -243,16 +256,6 @@ public CompletableFuture handle(Command command, Arguments arguments, }); } - // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. - if (context.asyncJDWP()) { - try { - AsyncJdwpUtils.await(warmUpJDICache(childrenList)); - } catch (CompletionException | CancellationException e) { - response.body = new Responses.VariablesResponseBody(list); - return CompletableFuture.completedFuture(response); - } - } - for (Variable javaVariable : childrenList) { Value value = javaVariable.value; String name = javaVariable.name; @@ -441,16 +444,33 @@ private List getVariablesOfFrameAsync(StackFrame frame, boolean showSt return result; } - private CompletableFuture warmUpJDICache(List variables) { - List> fetchVariableInfoFutures = new ArrayList<>(); + private CompletableFuture warmUpJDICache(List variables, List duplicatedVars) { + List> futures = new ArrayList<>(); + if (duplicatedVars != null && !duplicatedVars.isEmpty()) { + Set declaringTypes = new HashSet<>(); + duplicatedVars.forEach((var) -> { + Type declarationType = var.getDeclaringType(); + if (declarationType != null) { + declaringTypes.add(declarationType); + } + }); + + for (Type type : declaringTypes) { + if (type instanceof ReferenceType) { + // JDWP Command: RT_SIGNATURE + futures.add(AsyncJdwpUtils.runAsync(() -> type.signature())); + } + } + } + for (Variable javaVariable : variables) { Value value = javaVariable.value; if (value instanceof ArrayReference) { // JDWP Command: AR_LENGTH - fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> ((ArrayReference) value).length())); + futures.add(AsyncJdwpUtils.runAsync(() -> ((ArrayReference) value).length())); } else if (value instanceof StringReference) { // JDWP Command: SR_VALUE - fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + futures.add(AsyncJdwpUtils.runAsync(() -> { String strValue = ((StringReference) value).value(); javaVariable.value = new StringReferenceProxy((StringReference) value, strValue); })); @@ -458,12 +478,12 @@ private CompletableFuture warmUpJDICache(List variables) { if (value instanceof ObjectReference) { // JDWP Command: OR_REFERENCE_TYPE, RT_SIGNATURE - fetchVariableInfoFutures.add(AsyncJdwpUtils.runAsync(() -> { + futures.add(AsyncJdwpUtils.runAsync(() -> { value.type().signature(); })); } } - return CompletableFuture.allOf(fetchVariableInfoFutures.toArray(new CompletableFuture[0])); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java index bd8c8036d..1a8139fa9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java @@ -12,9 +12,12 @@ package com.microsoft.java.debug.core.adapter.variables; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.function.Consumer; @@ -33,6 +36,8 @@ import com.sun.jdi.AbsentInformationException; import com.sun.jdi.ArrayReference; import com.sun.jdi.ArrayType; +import com.sun.jdi.ClassType; +import com.sun.jdi.InterfaceType; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.Field; import com.sun.jdi.InternalException; @@ -77,6 +82,10 @@ public static boolean hasChildren(Value value, boolean includeStatic) { * when there is any error in retrieving information */ public static List listFieldVariables(ObjectReference obj, boolean includeStatic) throws AbsentInformationException { + return listFieldVariables(obj, includeStatic, false); + } + + public static List listFieldVariables(ObjectReference obj, boolean includeStatic, boolean async) throws AbsentInformationException { List res = new ArrayList<>(); ReferenceType type = obj.referenceType(); if (type instanceof ArrayType) { @@ -89,7 +98,7 @@ public static List listFieldVariables(ObjectReference obj, boolean inc } return res; } - List fields = type.allFields().stream().filter(t -> includeStatic || !t.isStatic()) + List fields = resolveAllFields(type, async).stream().filter(t -> includeStatic || !t.isStatic()) .sorted((a, b) -> { try { boolean v1isStatic = a.isStatic(); @@ -466,6 +475,67 @@ private static CompletableFuture> bulkFetchValuesAsync(List el return AsyncJdwpUtils.all(futures); } + private static List resolveAllFields(ReferenceType type, boolean async) { + if (async) { + return resolveAllFieldsAsync(type); + } + + return type.allFields(); + } + + private static List resolveAllFieldsAsync(ReferenceType type) { + Set result = Collections.synchronizedSet(new HashSet<>()); + AsyncJdwpUtils.await(resolveAllFieldsAsync(type, result)); + List fields = new ArrayList<>(); + fields.addAll(result); + return fields; + } + + private static CompletableFuture resolveAllFieldsAsync(ReferenceType type, Set result) { + List> futures = new ArrayList<>(); + // JDWP Command: RT_FIELDS_WITH_GENERIC + futures.add( + AsyncJdwpUtils.runAsync(() -> result.addAll(type.fields())) + ); + + if (type instanceof ClassType) { + ClassType classType = (ClassType) type; + // JDWP Command: RT_INTERFACES + futures.add(AsyncJdwpUtils.supplyAsync(() -> classType.interfaces()) + .thenCompose((its) -> { + List> itFutures = new ArrayList<>(); + for (InterfaceType it : its) { + itFutures.add(resolveAllFieldsAsync(it, result)); + } + + return CompletableFuture.allOf(itFutures.toArray(new CompletableFuture[0])); + })); + + // JDWP Command: CT_SUPERCLASS + AsyncJdwpUtils.supplyAsync(() -> classType.superclass()) + .thenCompose((superclass) -> { + if (superclass != null) { + return resolveAllFieldsAsync(superclass, result); + } + return CompletableFuture.completedFuture(null); + }); + } else if (type instanceof InterfaceType) { + InterfaceType interfaceType = (InterfaceType) type; + // JDWP Command: RT_INTERFACES + futures.add(AsyncJdwpUtils.supplyAsync(() -> interfaceType.superinterfaces()) + .thenCompose((its) -> { + List> itFutures = new ArrayList<>(); + for (InterfaceType it : its) { + itFutures.add(resolveAllFieldsAsync(it, result)); + } + + return CompletableFuture.allOf(itFutures.toArray(new CompletableFuture[0])); + })); + } + + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + } + private VariableUtils() { } From fa392425f656b1ed3bab0102764d4ca7dfdcc5f4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 09:22:06 +0800 Subject: [PATCH 049/144] Bump version to 0.40.0 (#438) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 287c599fe..83fed3cc9 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.39.0 + 0.40.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c3d2cffbb..b7e330e32 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index c39f5a9ac..9667f9e51 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.39.0 +Bundle-Version: 0.40.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.39.0.jar + lib/com.microsoft.java.debug.core-0.40.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 7e86528b5..56bb813e6 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.39.0 + 0.40.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.39.0 + 0.40.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index d4f44d697..7d8519073 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 4b76e45fb..43cd80e43 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.39.0 + 0.40.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 30c6ed253..2b0497f26 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.39.0 + 0.40.0 pom Java Debug Server for Visual Studio Code From a5bbebc661414528fe0a6b8bbf5111aeb342ce63 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 14:02:44 +0800 Subject: [PATCH 050/144] Clear stackframe cache if the thread state has changed (#439) --- .../java/debug/core/adapter/IStackFrameManager.java | 7 +++++++ .../java/debug/core/adapter/StackFrameManager.java | 5 +++++ .../adapter/handler/StackTraceRequestHandler.java | 11 ++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java index f3ae95d6e..abce2ff3e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java @@ -41,4 +41,11 @@ public interface IStackFrameManager { * @return the refreshed stackframes */ StackFrame[] reloadStackFrames(ThreadReference thread, int start, int length); + + /** + * Clear the stackframes cache from the specified thread. + * + * @param thread the jdi thread + */ + void clearStackFrames(ThreadReference thread); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java index d2f934901..2a9d1e47e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java @@ -66,4 +66,9 @@ public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, int s return new StackFrame[0]; } } + + @Override + public synchronized void clearStackFrames(ThreadReference thread) { + threadStackFrameMap.remove(thread.uniqueID()); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index ffa74206e..2120cafab 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -70,6 +70,11 @@ public CompletableFuture handle(Command command, Arguments arguments, int totalFrames = 0; if (thread != null) { try { + // Thread state has changed and then invalidate the stack frame cache. + if (stacktraceArgs.startFrame == 0) { + context.getStackFrameManager().clearStackFrames(thread); + } + totalFrames = thread.frameCount(); int count = stacktraceArgs.levels == 0 ? totalFrames - stacktraceArgs.startFrame : Math.min(totalFrames - stacktraceArgs.startFrame, stacktraceArgs.levels); @@ -80,10 +85,10 @@ public CompletableFuture handle(Command command, Arguments arguments, StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count); List jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP()); - for (int i = stacktraceArgs.startFrame; i < jdiFrames.size() && count-- > 0; i++) { - StackFrameReference stackframe = new StackFrameReference(thread, i); + for (int i = 0; i < count; i++) { + StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i); int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); - StackFrameInfo jdiFrame = jdiFrames.get(i - stacktraceArgs.startFrame); + StackFrameInfo jdiFrame = jdiFrames.get(i); result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, context)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException From 5bac075002154f2c8441a39026db53bdc67aef56 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 1 Sep 2022 15:28:26 +0800 Subject: [PATCH 051/144] refine the debug trace (#440) --- .../debug/core/adapter/handler/AttachRequestHandler.java | 1 + .../adapter/handler/ConfigurationDoneRequestHandler.java | 1 - .../debug/core/adapter/handler/LaunchRequestHandler.java | 7 +++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index 4d11ef6fb..cee6e64a3 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -64,6 +64,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Map traceInfo = new HashMap<>(); traceInfo.put("localAttach", context.isLocalDebugging()); + traceInfo.put("asyncJDWP", context.asyncJDWP()); IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class); vmHandler.setVmProvider(vmProvider); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 311db11c3..737d5d858 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -55,7 +55,6 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); vmHandler.setVmProvider(context.getProvider(IVirtualMachineManagerProvider.class)); - UsageDataSession.recordInfo("asyncJDWP", context.asyncJDWP()); if (debugSession != null) { // This is a global event handler to handle the JDI Event from Virtual Machine. debugSession.getEventHub().events().subscribe(debugEvent -> { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index fec967b84..e5b580f50 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -42,6 +42,7 @@ import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.LaunchException; +import com.microsoft.java.debug.core.UsageDataSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -76,6 +77,12 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { LaunchArguments launchArguments = (LaunchArguments) arguments; + Map traceInfo = new HashMap<>(); + traceInfo.put("asyncJDWP", context.asyncJDWP()); + traceInfo.put("noDebug", launchArguments.noDebug); + traceInfo.put("console", launchArguments.console); + UsageDataSession.recordInfo("launch debug info", traceInfo); + activeLaunchHandler = launchArguments.noDebug ? new LaunchWithoutDebuggingDelegate((daContext) -> handleTerminatedEvent(daContext)) : new LaunchWithDebuggingDelegate(); return handleLaunchCommand(arguments, response, context); From f9592bffae91ee713896af84ab2fc46404e3c67b Mon Sep 17 00:00:00 2001 From: crissNb <83137305+crissNb472@users.noreply.github.com> Date: Tue, 13 Sep 2022 09:49:14 +0200 Subject: [PATCH 052/144] Fixes NumericFormatter failure on systems with other decimal separator (#443) --- .../debug/core/adapter/formatter/NumericFormatterTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java index 001b928e9..372eb8163 100644 --- a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java +++ b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/adapter/formatter/NumericFormatterTest.java @@ -29,6 +29,8 @@ import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; +import java.util.Locale; + import static com.microsoft.java.debug.core.adapter.formatter.NumericFormatter.NUMERIC_FORMAT_OPTION; import static com.microsoft.java.debug.core.adapter.formatter.NumericFormatter.NUMERIC_PRECISION_OPTION; import static org.junit.Assert.*; @@ -38,6 +40,7 @@ public class NumericFormatterTest extends BaseJdiTestCase { @Before public void setup() throws Exception { super.setup(); + Locale.setDefault(Locale.US); formatter = new NumericFormatter(); } From 050e59125dc8ee3726f9fb880345cb1fe0b1b659 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Sep 2022 16:28:32 +0800 Subject: [PATCH 053/144] Support for debugging virtual threads (#441) * Support for debugging virtual threads --- .../java/debug/core/DebugSession.java | 35 +++++++- .../java/debug/core/adapter/ThreadCache.java | 52 ++++++++++- .../ConfigurationDoneRequestHandler.java | 6 +- .../handler/ExceptionInfoRequestHandler.java | 8 +- .../handler/SetBreakpointsRequestHandler.java | 4 +- .../SetDataBreakpointsRequestHandler.java | 4 +- .../SetFunctionBreakpointsRequestHandler.java | 2 + .../adapter/handler/StepRequestHandler.java | 2 + .../handler/ThreadsRequestHandler.java | 87 +++++++++++-------- 9 files changed, 154 insertions(+), 46 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 220688693..ed711bf82 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,6 +11,8 @@ package com.microsoft.java.debug.core; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -31,18 +33,49 @@ public DebugSession(VirtualMachine virtualMachine) { @Override public void start() { + boolean supportsVirtualThreads = mayCreateVirtualThreads(); + // request thread events by default EventRequest threadStartRequest = vm.eventRequestManager().createThreadStartRequest(); threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + if (supportsVirtualThreads) { + addPlatformThreadsOnlyFilter(threadStartRequest); + } threadStartRequest.enable(); EventRequest threadDeathRequest = vm.eventRequestManager().createThreadDeathRequest(); threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + if (supportsVirtualThreads) { + addPlatformThreadsOnlyFilter(threadDeathRequest); + } threadDeathRequest.enable(); eventHub.start(vm); } + private boolean mayCreateVirtualThreads() { + try { + Method method = vm.getClass().getMethod("mayCreateVirtualThreads"); + return (boolean) method.invoke(vm); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // ignore + } + + return false; + } + + /** + * For thread start and thread death events, restrict the events so they are only sent for platform threads. + */ + private void addPlatformThreadsOnlyFilter(EventRequest threadLifecycleRequest) { + try { + Method method = threadLifecycleRequest.getClass().getMethod("addPlatformThreadsOnlyFilter"); + method.invoke(threadLifecycleRequest); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // ignore + } + } + @Override public void suspend() { vm.suspend(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java index 3232dbf18..ce1c17282 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java @@ -13,9 +13,11 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.sun.jdi.ThreadReference; @@ -29,6 +31,7 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) { return this.size() > 100; } }); + private Map eventThreads = new ConcurrentHashMap<>(); public synchronized void resetThreads(List threads) { allThreads.clear(); @@ -46,6 +49,12 @@ public synchronized ThreadReference getThread(long threadId) { } } + for (ThreadReference thread : eventThreads.values()) { + if (threadId == thread.uniqueID()) { + return thread; + } + } + return null; } @@ -59,14 +68,49 @@ public String getThreadName(long threadId) { public void addDeathThread(long threadId) { threadNameMap.remove(threadId); + eventThreads.remove(threadId); deathThreads.put(threadId, true); } - public void removeDeathThread(long threadId) { - deathThreads.remove(threadId); - } - public boolean isDeathThread(long threadId) { return deathThreads.containsKey(threadId); } + + public void addEventThread(ThreadReference thread) { + eventThreads.put(thread.uniqueID(), thread); + } + + public void removeEventThread(long threadId) { + eventThreads.remove(threadId); + } + + public void clearEventThread() { + eventThreads.clear(); + } + + /** + * The visible threads includes: + * 1. The currently running threads returned by the JDI API + * VirtualMachine.allThreads(). + * 2. The threads suspended by events such as Breakpoint, Step, Exception etc. + * + * The part 2 is mainly for virtual threads, since VirtualMachine.allThreads() + * does not include virtual threads by default. For those virtual threads + * that are suspended, we need to show their call stacks in CALL STACK view. + */ + public List visibleThreads(IDebugAdapterContext context) { + List visibleThreads = new ArrayList<>(context.getDebugSession().getAllThreads()); + Set idSet = new HashSet<>(); + visibleThreads.forEach(thread -> idSet.add(thread.uniqueID())); + for (ThreadReference thread : eventThreads.values()) { + if (idSet.contains(thread.uniqueID())) { + continue; + } + + idSet.add(thread.uniqueID()); + visibleThreads.add(thread); + } + + return visibleThreads; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 737d5d858..6805073dc 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2020 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -109,15 +109,15 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // ignore since SetBreakpointsRequestHandler has already handled } else if (event instanceof ExceptionEvent) { ThreadReference thread = ((ExceptionEvent) event).thread(); - ThreadReference bpThread = ((ExceptionEvent) event).thread(); IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); - if (engine.isInEvaluation(bpThread)) { + if (engine.isInEvaluation(thread)) { return; } JdiExceptionReference jdiException = new JdiExceptionReference(((ExceptionEvent) event).exception(), ((ExceptionEvent) event).catchLocation() == null); context.getExceptionManager().setException(thread.uniqueID(), jdiException); + context.getThreadCache().addEventThread(thread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID())); debugEvent.shouldResume = false; } else { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java index 13456029d..5e065edd0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ExceptionInfoRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2019 Microsoft Corporation and others. +* Copyright (c) 2019-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -53,7 +53,11 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { ExceptionInfoArguments exceptionInfoArgs = (ExceptionInfoArguments) arguments; - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), exceptionInfoArgs.threadId); + ThreadReference thread = context.getThreadCache().getThread(exceptionInfoArgs.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), exceptionInfoArgs.threadId); + } + if (thread == null) { throw AdapterUtils.createCompletionException("Thread " + exceptionInfoArgs.threadId + " doesn't exist.", ErrorCode.EXCEPTION_INFO_FAILURE); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 085dfe63f..d02f73bfd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -207,12 +207,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } }); }); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java index be15852e4..6d3e8c0b1 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2019 Microsoft Corporation and others. +* Copyright (c) 2019-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -151,11 +151,13 @@ private void registerWatchpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } }); }); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } debugEvent.shouldResume = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index 3f370b429..59a948d37 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -165,6 +165,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer().sendEvent(new Events.StoppedEvent( "function breakpoint", bpThread.uniqueID())); } @@ -172,6 +173,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { }); } else { + context.getThreadCache().addEventThread(bpThread); context.getProtocolServer() .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 4f58c113f..51e41bafc 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -178,6 +178,7 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingMethodExitRequest.enable(); } + context.getThreadCache().removeEventThread(thread.uniqueID()); DebugUtility.resumeThread(thread); ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context); } catch (IncompatibleThreadStateException ex) { @@ -255,6 +256,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } + context.getThreadCache().addEventThread(thread); context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID())); debugEvent.shouldResume = false; } else if (event instanceof MethodExitEvent) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index cedbf2ae1..7d404691c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -17,10 +17,13 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; -import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -81,12 +84,13 @@ public CompletableFuture handle(Command command, Arguments arguments, private CompletableFuture threads(Requests.ThreadsArguments arguments, Response response, IDebugAdapterContext context) { ArrayList threads = new ArrayList<>(); try { - List allThreads = context.getDebugSession().getAllThreads(); + List allThreads = context.getThreadCache().visibleThreads(context); context.getThreadCache().resetThreads(allThreads); - allThreads = allThreads.stream().filter((thread) -> !context.getThreadCache().isDeathThread(thread.uniqueID())).toList(); + allThreads = allThreads.stream().filter((thread) -> !context.getThreadCache().isDeathThread(thread.uniqueID())).collect(Collectors.toList()); List jdiThreads = resolveThreadInfos(allThreads, context); for (ThreadInfo jdiThread : jdiThreads) { - threads.add(new Types.Thread(jdiThread.thread.uniqueID(), "Thread [" + jdiThread.name + "]")); + String name = StringUtils.isBlank(jdiThread.name) ? String.valueOf(jdiThread.thread.uniqueID()) : jdiThread.name; + threads.add(new Types.Thread(jdiThread.thread.uniqueID(), "Thread [" + name + "]")); } } catch (ObjectCollectedException | CancellationException | CompletionException ex) { // allThreads may throw VMDisconnectedException when VM terminates and thread.name() may throw ObjectCollectedException @@ -124,11 +128,12 @@ private static List resolveThreadInfos(List allThre } private CompletableFuture pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) { - ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + ThreadReference thread = context.getThreadCache().getThread(arguments.threadId); + if (thread == null) { + thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId); + } if (thread != null) { - context.getStepResultManager().removeMethodResult(arguments.threadId); - thread.suspend(); - context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId)); + pauseThread(thread, context); } else { context.getStepResultManager().removeAllMethodResults(); context.getDebugSession().suspend(); @@ -149,6 +154,7 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, * be resumed (through ThreadReference#resume() or VirtualMachine#resume()) the same number of times it has been suspended. */ if (thread != null) { + context.getThreadCache().removeEventThread(arguments.threadId); context.getStepResultManager().removeMethodResult(arguments.threadId); context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; @@ -157,11 +163,7 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); - if (context.asyncJDWP()) { - resumeVMAsync(context.getDebugSession()); - } else { - context.getDebugSession().resume(); - } + resumeVM(context); context.getRecyclableIdPool().removeAllObjects(); } response.body = new Responses.ContinueResponseBody(allThreadsContinued); @@ -169,19 +171,16 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, } private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { + context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); - if (context.asyncJDWP()) { - resumeVMAsync(context.getDebugSession()); - } else { - context.getDebugSession().resume(); - } + resumeVM(context); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); context.getRecyclableIdPool().removeAllObjects(); return CompletableFuture.completedFuture(response); } private CompletableFuture resumeOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { - List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List threads = context.getThreadCache().visibleThreads(context); List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { if (thread.uniqueID() == arguments.threadId) { @@ -205,7 +204,7 @@ private CompletableFuture pauseAll(Requests.ThreadOperationArguments a } private CompletableFuture pauseOthers(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { - List threads = DebugUtility.getAllThreadsSafely(context.getDebugSession()); + List threads = context.getThreadCache().visibleThreads(context); List> futures = new ArrayList<>(); for (ThreadReference thread : threads) { if (thread.uniqueID() == arguments.threadId) { @@ -239,26 +238,42 @@ public static void checkThreadRunningAndRecycleIds(ThreadReference thread, IDebu } } - private void resumeVMAsync(IDebugSession debugSession) { + private void resumeVM(IDebugAdapterContext context) { + List visibleThreads = context.getThreadCache().visibleThreads(context); + context.getThreadCache().clearEventThread(); + List> futures = new ArrayList<>(); - for (ThreadReference tr : DebugUtility.getAllThreadsSafely(debugSession)) { - futures.add(AsyncJdwpUtils.runAsync(() -> { - try { - while (tr.suspendCount() > 1) { - tr.resume(); - } - } catch (ObjectCollectedException ex) { - // Ignore it if the thread is garbage collected. + /** + * To ensure that all threads are fully resumed when the VM is resumed, make sure the suspend count + * of each thread is no larger than 1. + * Notes: Decrementing the thread' suspend count to 1 is on purpose, because it doesn't break the + * the thread's suspend state, and also make sure the next instruction vm.resume() is able to resume + * all threads fully. + */ + Consumer resumeThread = (ThreadReference tr) -> { + try { + while (tr.suspendCount() > 1) { + tr.resume(); } - })); + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + }; + for (ThreadReference tr : visibleThreads) { + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.runAsync(() -> resumeThread.accept(tr))); + } else { + resumeThread.accept(tr); + } } AsyncJdwpUtils.await(futures); - debugSession.getVM().resume(); + context.getDebugSession().getVM().resume(); } private void resumeThread(ThreadReference thread, IDebugAdapterContext context) { try { + context.getThreadCache().removeEventThread(thread.uniqueID()); int suspends = thread.suspendCount(); if (suspends > 0) { long threadId = thread.uniqueID(); @@ -268,19 +283,23 @@ private void resumeThread(ThreadReference thread, IDebugAdapterContext context) checkThreadRunningAndRecycleIds(thread, context); } } catch (ObjectCollectedException ex) { - // ignore it. + // the thread is garbage collected. + context.getThreadCache().addDeathThread(thread.uniqueID()); } } private void pauseThread(ThreadReference thread, IDebugAdapterContext context) { try { - if (!thread.isSuspended()) { + // Ignore it if the thread status is unknown or zombie + if (!thread.isSuspended() && thread.status() > 0) { long threadId = thread.uniqueID(); + context.getStepResultManager().removeMethodResult(threadId); thread.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); } } catch (ObjectCollectedException ex) { - // ignore it if the thread is garbage collected. + // the thread is garbage collected. + context.getThreadCache().addDeathThread(thread.uniqueID()); } } From 195266efa94800c51b14110a72688ab085ed28ae Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 21 Sep 2022 17:23:01 +0800 Subject: [PATCH 054/144] Visualize the inline breakpoint locations (#445) --- .../microsoft/java/debug/core/Breakpoint.java | 110 ++++++++++------ .../java/debug/core/DebugSession.java | 5 + .../debug/core/EvaluatableBreakpoint.java | 8 +- .../java/debug/core/IBreakpoint.java | 6 +- .../java/debug/core/IDebugSession.java | 4 +- .../debug/core/JavaBreakpointLocation.java | 113 +++++++++++++++++ .../java/debug/core/adapter/AdapterUtils.java | 8 +- .../java/debug/core/adapter/DebugAdapter.java | 4 +- .../core/adapter/DebugAdapterContext.java | 6 + .../core/adapter/IDebugAdapterContext.java | 4 +- .../core/adapter/ISourceLookUpProvider.java | 22 +++- .../BreakpointLocationsRequestHander.java | 83 ++++++++++++ .../handler/InitializeRequestHandler.java | 3 +- .../handler/SetBreakpointsRequestHandler.java | 78 ++++++------ .../handler/StackTraceRequestHandler.java | 29 ++++- .../java/debug/core/protocol/Requests.java | 40 +++++- .../java/debug/core/protocol/Responses.java | 18 ++- .../java/debug/core/protocol/Types.java | 53 +++++++- .../java/debug/LambdaExpressionLocator.java | 11 +- .../internal/JdtSourceLookUpProvider.java | 120 +++++++++++++++--- 20 files changed, 605 insertions(+), 120 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index 8b865d44f..dfdbad4bb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -37,13 +37,11 @@ public class Breakpoint implements IBreakpoint { private VirtualMachine vm = null; private IEventHub eventHub = null; - private String className = null; - private int lineNumber = 0; + private JavaBreakpointLocation sourceLocation = null; private int hitCount = 0; private String condition = null; private String logMessage = null; private HashMap propertyMap = new HashMap<>(); - private String methodSignature = null; private boolean async = false; @@ -56,21 +54,37 @@ public class Breakpoint implements IBreakpoint { } Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) { + this(vm, eventHub, className, lineNumber, hitCount, condition, null); + } + + Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) { this.vm = vm; this.eventHub = eventHub; + String contextClass = className; + String methodName = null; + String methodSignature = null; if (className != null && className.contains("#")) { - this.className = className.substring(0, className.indexOf("#")); - this.methodSignature = className.substring(className.indexOf("#") + 1); - } else { - this.className = className; + contextClass = className.substring(0, className.indexOf("#")); + String[] methodInfo = className.substring(className.indexOf("#") + 1).split("#"); + methodName = methodInfo[0]; + methodSignature = methodInfo[1]; } - this.lineNumber = lineNumber; + + this.sourceLocation = new JavaBreakpointLocation(lineNumber, -1); + this.sourceLocation.setClassName(contextClass); + this.sourceLocation.setMethodName(methodName); + this.sourceLocation.setMethodSignature(methodSignature); this.hitCount = hitCount; this.condition = condition; + this.logMessage = logMessage; } - Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition, String logMessage) { - this(vm, eventHub, className, lineNumber, hitCount, condition); + Breakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) { + this.vm = vm; + this.eventHub = eventHub; + this.sourceLocation = sourceLocation; + this.hitCount = hitCount; + this.condition = condition; this.logMessage = logMessage; } @@ -104,14 +118,24 @@ public void close() throws Exception { } // IBreakpoint + @Override + public JavaBreakpointLocation sourceLocation() { + return this.sourceLocation; + } + @Override public String className() { - return className; + return this.sourceLocation.className(); } @Override public int getLineNumber() { - return lineNumber; + return this.sourceLocation.lineNumber(); + } + + @Override + public int getColumnNumber() { + return this.sourceLocation.columnNumber(); } @Override @@ -120,20 +144,20 @@ public String getCondition() { } @Override - public boolean equals(Object obj) { - if (!(obj instanceof Breakpoint)) { - return super.equals(obj); - } - - Breakpoint breakpoint = (Breakpoint) obj; - return Objects.equals(this.className(), breakpoint.className()) - && this.getLineNumber() == breakpoint.getLineNumber() - && Objects.equals(this.methodSignature, breakpoint.methodSignature); + public int hashCode() { + return Objects.hash(sourceLocation); } @Override - public int hashCode() { - return Objects.hash(this.className, this.lineNumber, this.methodSignature); + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Breakpoint)) { + return false; + } + Breakpoint other = (Breakpoint) obj; + return Objects.equals(sourceLocation, other.sourceLocation); } @Override @@ -149,6 +173,7 @@ public void setHitCount(int hitCount) { .filter(request -> request instanceof BreakpointRequest) .subscribe(request -> { request.addCountFilter(hitCount); + request.disable(); request.enable(); }); } @@ -183,13 +208,13 @@ public CompletableFuture install() { // It's possible that different class loaders create new class with the same name. // Here to listen to future class prepare events to handle such case. ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); - classPrepareRequest.addClassFilter(className); + classPrepareRequest.addClassFilter(className()); classPrepareRequest.enable(); requests.add(classPrepareRequest); // Local types also needs to be handled ClassPrepareRequest localClassPrepareRequest = vm.eventRequestManager().createClassPrepareRequest(); - localClassPrepareRequest.addClassFilter(className + "$*"); + localClassPrepareRequest.addClassFilter(className() + "$*"); localClassPrepareRequest.enable(); requests.add(localClassPrepareRequest); @@ -202,7 +227,7 @@ public CompletableFuture install() { .subscribe(debugEvent -> { ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; List newRequests = AsyncJdwpUtils.await( - createBreakpointRequests(event.referenceType(), lineNumber, hitCount, false) + createBreakpointRequests(event.referenceType(), getLineNumber(), hitCount, false) ); requests.addAll(newRequests); if (!newRequests.isEmpty() && !future.isDone()) { @@ -213,8 +238,8 @@ public CompletableFuture install() { subscriptions.add(subscription); Runnable resolveRequestsFromExistingClasses = () -> { - List refTypes = vm.classesByName(className); - createBreakpointRequests(refTypes, lineNumber, hitCount, true) + List refTypes = vm.classesByName(className()); + createBreakpointRequests(refTypes, getLineNumber(), hitCount, true) .whenComplete((newRequests, ex) -> { if (ex != null) { return; @@ -281,14 +306,13 @@ private CompletableFuture> collectLocations(ReferenceType refType }); } - private CompletableFuture> collectLocations(List refTypes, String nameAndSignature) { - String[] segments = nameAndSignature.split("#"); + private CompletableFuture> collectLocations(List refTypes, String methodName, String methodSiguature) { List> futures = new ArrayList<>(); for (ReferenceType refType : refTypes) { if (async()) { - futures.add(AsyncJdwpUtils.supplyAsync(() -> findMethodLocaiton(refType, segments[0], segments[1]))); + futures.add(AsyncJdwpUtils.supplyAsync(() -> findMethodLocaiton(refType, methodName, methodSiguature))); } else { - futures.add(CompletableFuture.completedFuture(findMethodLocaiton(refType, segments[0], segments[1]))); + futures.add(CompletableFuture.completedFuture(findMethodLocaiton(refType, methodName, methodSiguature))); } } @@ -329,10 +353,22 @@ private CompletableFuture> createBreakpointRequests(Refe private CompletableFuture> createBreakpointRequests(List refTypes, int lineNumber, int hitCount, boolean includeNestedTypes) { CompletableFuture> locationsFuture; - if (this.methodSignature != null) { - locationsFuture = collectLocations(refTypes, this.methodSignature); + if (this.sourceLocation.methodName() != null) { + locationsFuture = collectLocations(refTypes, this.sourceLocation.methodName(), this.sourceLocation.methodSignature()); } else { - locationsFuture = collectLocations(refTypes, lineNumber, includeNestedTypes); + locationsFuture = collectLocations(refTypes, lineNumber, includeNestedTypes).thenApply((locations) -> { + if (locations.isEmpty()) { + return locations; + } + + /** + * For a line breakpoint, we default to breaking at the first location + * of the line. If you want to break at other locations on the same line, + * you can add an inline breakpoint based on the locations returned by + * the BreakpointLocation request. + */ + return Arrays.asList(locations.get(0)); + }); } return locationsFuture.thenCompose((locations) -> { @@ -389,11 +425,11 @@ private CompletableFuture> createBreakpointRequests(List } private Object computeRequestType() { - if (this.methodSignature == null) { + if (this.sourceLocation.methodName() == null) { return IBreakpoint.REQUEST_TYPE_LINE; } - if (this.methodSignature.startsWith("lambda$")) { + if (this.sourceLocation.methodName().startsWith("lambda$")) { return IBreakpoint.REQUEST_TYPE_LAMBDA; } else { return IBreakpoint.REQUEST_TYPE_METHOD; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index ed711bf82..1c7990f16 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -114,6 +114,11 @@ public void terminate() { } } + @Override + public IBreakpoint createBreakpoint(JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage) { + return new EvaluatableBreakpoint(vm, this.getEventHub(), sourceLocation, hitCount, condition, logMessage); + } + @Override public IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage) { return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java index 9b3fdb2dd..723e2cadf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/EvaluatableBreakpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -48,6 +48,12 @@ public class EvaluatableBreakpoint extends Breakpoint implements IEvaluatableBre this.eventHub = eventHub; } + EvaluatableBreakpoint(VirtualMachine vm, IEventHub eventHub, JavaBreakpointLocation sourceLocation, int hitCount, + String condition, String logMessage) { + super(vm, eventHub, sourceLocation, hitCount, condition, logMessage); + this.eventHub = eventHub; + } + @Override public boolean containsEvaluatableExpression() { return containsConditionalExpression() || containsLogpointExpression(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java index 04fbf5005..40995e9dd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IBreakpoint.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -23,10 +23,14 @@ public interface IBreakpoint extends IDebugResource { int REQUEST_TYPE_LAMBDA = 2; + JavaBreakpointLocation sourceLocation(); + String className(); int getLineNumber(); + int getColumnNumber(); + int getHitCount(); void setHitCount(int hitCount); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 03780c2d9..4e4078c87 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -30,6 +30,8 @@ public interface IDebugSession { // breakpoints IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage); + IBreakpoint createBreakpoint(JavaBreakpointLocation sourceLocation, int hitCount, String condition, String logMessage); + IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount); void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java new file mode 100644 index 000000000..820e80c9b --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java @@ -0,0 +1,113 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core; + +import java.util.Objects; + +import com.microsoft.java.debug.core.protocol.Types; + +public class JavaBreakpointLocation { + /** + * The source line of the breakpoint or logpoint. + */ + private int lineNumber; + /** + * The source column of the breakpoint. + */ + private int columnNumber = -1; + /** + * The declaring class name that encloses the target position. + */ + private String className; + /** + * The method name and signature when the target position + * points to a method declaration. + */ + private String methodName; + private String methodSignature; + /** + * All possible locations for source breakpoints in a given range. + */ + private Types.BreakpointLocation[] availableBreakpointLocations = new Types.BreakpointLocation[0]; + + public JavaBreakpointLocation(int lineNumber, int columnNumber) { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + @Override + public int hashCode() { + return Objects.hash(lineNumber, columnNumber, className, methodName, methodSignature); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof JavaBreakpointLocation)) { + return false; + } + JavaBreakpointLocation other = (JavaBreakpointLocation) obj; + return lineNumber == other.lineNumber && columnNumber == other.columnNumber + && Objects.equals(className, other.className) && Objects.equals(methodName, other.methodName) + && Objects.equals(methodSignature, other.methodSignature); + } + + public int lineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public int columnNumber() { + return columnNumber; + } + + public void setColumnNumber(int columnNumber) { + this.columnNumber = columnNumber; + } + + public String className() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String methodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String methodSignature() { + return methodSignature; + } + + public void setMethodSignature(String methodSignature) { + this.methodSignature = methodSignature; + } + + public Types.BreakpointLocation[] availableBreakpointLocations() { + return availableBreakpointLocations; + } + + public void setAvailableBreakpointLocations(Types.BreakpointLocation[] availableBreakpointLocations) { + this.availableBreakpointLocations = availableBreakpointLocations; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 5c1272102..9b972334d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -119,13 +119,15 @@ public static int convertLineNumber(int line, boolean sourceLinesStartAt1, boole * the column number from the source platform * @param sourceColumnsStartAt1 * the source platform's column starts at 1 or not + * @param targetColumnStartAt1 + * the target platform's column starts at 1 or not * @return the new column number */ - public static int convertColumnNumber(int column, boolean sourceColumnsStartAt1) { + public static int convertColumnNumber(int column, boolean sourceColumnsStartAt1, boolean targetColumnStartAt1) { if (sourceColumnsStartAt1) { - return column - 1; + return targetColumnStartAt1 ? column : column - 1; } else { - return column; + return targetColumnStartAt1 ? column + 1 : column; } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 0bf5d2683..edc771163 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -21,6 +21,7 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.adapter.handler.AttachRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.BreakpointLocationsRequestHander; import com.microsoft.java.debug.core.adapter.handler.CompletionsHandler; import com.microsoft.java.debug.core.adapter.handler.ConfigurationDoneRequestHandler; import com.microsoft.java.debug.core.adapter.handler.DataBreakpointInfoRequestHandler; @@ -105,7 +106,7 @@ private void initialize() { registerHandler(new InitializeRequestHandler()); registerHandler(new LaunchRequestHandler()); - // DEBUG node only + // DEBUG mode only registerHandlerForDebug(new AttachRequestHandler()); registerHandlerForDebug(new ConfigurationDoneRequestHandler()); registerHandlerForDebug(new DisconnectRequestHandler()); @@ -129,6 +130,7 @@ private void initialize() { registerHandlerForDebug(new RefreshVariablesHandler()); registerHandlerForDebug(new ProcessIdHandler()); registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); + registerHandlerForDebug(new BreakpointLocationsRequestHander()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); registerHandlerForNoDebug(new ProcessIdHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 4185d7597..e317ddf1c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -38,6 +38,8 @@ public class DebugAdapterContext implements IDebugAdapterContext { private IDebugSession debugSession; private boolean debuggerLinesStartAt1 = true; + // The Java model on debugger uses 0-based column number. + private boolean debuggerColumnStartAt1 = false; private boolean debuggerPathsAreUri = true; private boolean clientLinesStartAt1 = true; private boolean clientColumnsStartAt1 = true; @@ -105,6 +107,10 @@ public void setDebuggerLinesStartAt1(boolean debuggerLinesStartAt1) { this.debuggerLinesStartAt1 = debuggerLinesStartAt1; } + public boolean isDebuggerColumnsStartAt1() { + return debuggerColumnStartAt1; + } + @Override public boolean isDebuggerPathsAreUri() { return debuggerPathsAreUri; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index bfcad8905..5d3b75639 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -55,6 +55,8 @@ public interface IDebugAdapterContext { void setClientColumnsStartAt1(boolean clientColumnsStartAt1); + boolean isDebuggerColumnsStartAt1(); + boolean isClientPathsAreUri(); void setClientPathsAreUri(boolean clientPathsAreUri); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java index dd3e4dbde..9976fc16f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,12 +12,32 @@ package com.microsoft.java.debug.core.adapter; import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.JavaBreakpointLocation; +import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; public interface ISourceLookUpProvider extends IProvider { boolean supportsRealtimeBreakpointVerification(); + /** + * Deprecated, please use {@link #getBreakpointLocations(String, SourceBreakpoint[])} instead. + */ + @Deprecated String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) throws DebugException; + /** + * Given a set of source breakpoint locations with line and column numbers, + * verify if they are valid breakpoint locations. If it's a valid location, + * resolve its enclosing class name, method name and signature (for method + * breakpoint) and all possible inline breakpoint locations in that line. + * + * @param sourceUri + * the source file uri + * @param sourceBreakpoints + * the source breakpoints with line and column numbers + * @return Locations of Breakpoints containing context class and method information. + */ + JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException; + /** * Given a fully qualified class name and source file path, search the associated disk source file. * diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java new file mode 100644 index 000000000..57c7ea15e --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/BreakpointLocationsRequestHander.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2022 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Requests; +import com.microsoft.java.debug.core.protocol.Responses; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.BreakpointLocationsArguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; + +/** + * The breakpointLocations request returns all possible locations for source breakpoints in a given range. + * Clients should only call this request if the corresponding capability supportsBreakpointLocationsRequest is true. + */ +public class BreakpointLocationsRequestHander implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Requests.Command.BREAKPOINTLOCATIONS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + BreakpointLocationsArguments bpArgs = (BreakpointLocationsArguments) arguments; + String sourceUri = SetBreakpointsRequestHandler.normalizeSourcePath(bpArgs.source, context); + // When breakpoint source path is null or an invalid file path, send an ErrorResponse back. + if (StringUtils.isBlank(sourceUri)) { + throw AdapterUtils.createCompletionException( + String.format("Failed to get BreakpointLocations. Reason: '%s' is an invalid path.", bpArgs.source.path), + ErrorCode.SET_BREAKPOINT_FAILURE); + } + + int debuggerLine = AdapterUtils.convertLineNumber(bpArgs.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); + IBreakpoint[] breakpoints = context.getBreakpointManager().getBreakpoints(sourceUri); + BreakpointLocation[] locations = new BreakpointLocation[0]; + for (int i = 0; i < breakpoints.length; i++) { + if (breakpoints[i].getLineNumber() == debuggerLine && ArrayUtils.isNotEmpty( + breakpoints[i].sourceLocation().availableBreakpointLocations())) { + locations = Stream.of(breakpoints[i].sourceLocation().availableBreakpointLocations()).map(location -> { + BreakpointLocation newLocaiton = new BreakpointLocation(); + newLocaiton.line = AdapterUtils.convertLineNumber(location.line, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + newLocaiton.column = AdapterUtils.convertColumnNumber(location.column, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + newLocaiton.endLine = AdapterUtils.convertLineNumber(location.endLine, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + newLocaiton.endColumn = AdapterUtils.convertColumnNumber(location.endColumn, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + return newLocaiton; + }).toArray(BreakpointLocation[]::new); + break; + } + } + + response.body = new Responses.BreakpointLocationsResponseBody(locations); + return CompletableFuture.completedFuture(response); + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index ae92f356f..19bbd7d62 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -64,6 +64,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsDataBreakpoints = true; caps.supportsFunctionBreakpoints = true; caps.supportsClipboardContext = true; + caps.supportsBreakpointLocationsRequest = true; response.body = caps; return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index d02f73bfd..7fe6c3fdd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -17,6 +17,7 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Stream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; @@ -26,6 +27,7 @@ import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.IEvaluatableBreakpoint; +import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent.EventType; @@ -93,28 +95,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } SetBreakpointArguments bpArguments = (SetBreakpointArguments) arguments; - String clientPath = bpArguments.source.path; - if (AdapterUtils.isWindows()) { - // VSCode may send drive letters with inconsistent casing which will mess up the key - // in the BreakpointManager. See https://github.com/Microsoft/vscode/issues/6268 - // Normalize the drive letter casing. Note that drive letters - // are not localized so invariant is safe here. - String drivePrefix = FilenameUtils.getPrefix(clientPath); - if (drivePrefix != null && drivePrefix.length() >= 2 - && Character.isLowerCase(drivePrefix.charAt(0)) && drivePrefix.charAt(1) == ':') { - drivePrefix = drivePrefix.substring(0, 2); // d:\ is an illegal regex string, convert it to d: - clientPath = clientPath.replaceFirst(drivePrefix, drivePrefix.toUpperCase()); - } - } - String sourcePath = clientPath; - if (bpArguments.source.sourceReference != 0 && context.getSourceUri(bpArguments.source.sourceReference) != null) { - sourcePath = context.getSourceUri(bpArguments.source.sourceReference); - } else if (StringUtils.isNotBlank(clientPath)) { - // See the bug https://github.com/Microsoft/vscode/issues/30996 - // Source.path in the SetBreakpointArguments could be a file system path or uri. - sourcePath = AdapterUtils.convertPath(clientPath, AdapterUtils.isUri(clientPath), context.isDebuggerPathsAreUri()); - } - + String sourcePath = normalizeSourcePath(bpArguments.source, context); // When breakpoint source path is null or an invalid file path, send an ErrorResponse back. if (StringUtils.isBlank(sourcePath)) { throw AdapterUtils.createCompletionException( @@ -163,6 +144,32 @@ public CompletableFuture handle(Command command, Arguments arguments, } } + public static String normalizeSourcePath(Types.Source source, IDebugAdapterContext context) { + String clientPath = source.path; + if (AdapterUtils.isWindows()) { + // VSCode may send drive letters with inconsistent casing which will mess up the key + // in the BreakpointManager. See https://github.com/Microsoft/vscode/issues/6268 + // Normalize the drive letter casing. Note that drive letters + // are not localized so invariant is safe here. + String drivePrefix = FilenameUtils.getPrefix(clientPath); + if (drivePrefix != null && drivePrefix.length() >= 2 + && Character.isLowerCase(drivePrefix.charAt(0)) && drivePrefix.charAt(1) == ':') { + drivePrefix = drivePrefix.substring(0, 2); // d:\ is an illegal regex string, convert it to d: + clientPath = clientPath.replaceFirst(drivePrefix, drivePrefix.toUpperCase()); + } + } + String sourcePath = clientPath; + if (source.sourceReference != 0 && context.getSourceUri(source.sourceReference) != null) { + sourcePath = context.getSourceUri(source.sourceReference); + } else if (StringUtils.isNotBlank(clientPath)) { + // See the bug https://github.com/Microsoft/vscode/issues/30996 + // Source.path in the SetBreakpointArguments could be a file system path or uri. + sourcePath = AdapterUtils.convertPath(clientPath, AdapterUtils.isUri(clientPath), context.isDebuggerPathsAreUri()); + } + + return sourcePath; + } + private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream().filter( bp -> { @@ -173,11 +180,6 @@ private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext cont ).findFirst().orElse(null); } - private IBreakpoint getAssociatedBreakpoint(IDebugAdapterContext context, BreakpointEvent event) { - return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream() - .filter(bp -> bp.requests().contains(event.request())).findFirst().orElse(null); - } - private void registerBreakpointHandler(IDebugAdapterContext context) { IDebugSession debugSession = context.getDebugSession(); if (debugSession != null) { @@ -300,27 +302,25 @@ private Types.Breakpoint convertDebuggerBreakpointToClient(IBreakpoint breakpoin private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Types.SourceBreakpoint[] sourceBreakpoints, IDebugAdapterContext context) throws DebugException { - int[] lines = Arrays.asList(sourceBreakpoints).stream().map(sourceBreakpoint -> { - return AdapterUtils.convertLineNumber(sourceBreakpoint.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); - }).mapToInt(line -> line).toArray(); - - int[] columns = Arrays.asList(sourceBreakpoints).stream().map(b -> { - return AdapterUtils.convertColumnNumber(b.column, context.isClientColumnsStartAt1()); - }).mapToInt(b -> b).toArray(); + Types.SourceBreakpoint[] debugSourceBreakpoints = Stream.of(sourceBreakpoints).map(sourceBreakpoint -> { + int line = AdapterUtils.convertLineNumber(sourceBreakpoint.line, context.isClientLinesStartAt1(), context.isDebuggerLinesStartAt1()); + int column = AdapterUtils.convertColumnNumber(sourceBreakpoint.column, context.isClientColumnsStartAt1(), context.isDebuggerColumnsStartAt1()); + return new Types.SourceBreakpoint(line, column); + }).toArray(Types.SourceBreakpoint[]::new); ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); - String[] fqns = sourceProvider.getFullyQualifiedName(sourceFile, lines, columns); - IBreakpoint[] breakpoints = new IBreakpoint[lines.length]; - for (int i = 0; i < lines.length; i++) { + JavaBreakpointLocation[] locations = sourceProvider.getBreakpointLocations(sourceFile, debugSourceBreakpoints); + IBreakpoint[] breakpoints = new IBreakpoint[locations.length]; + for (int i = 0; i < locations.length; i++) { int hitCount = 0; try { hitCount = Integer.parseInt(sourceBreakpoints[i].hitCondition); } catch (NumberFormatException e) { hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. } - breakpoints[i] = context.getDebugSession().createBreakpoint(fqns[i], lines[i], hitCount, sourceBreakpoints[i].condition, + breakpoints[i] = context.getDebugSession().createBreakpoint(locations[i], hitCount, sourceBreakpoints[i].condition, sourceBreakpoints[i].logMessage); - if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(fqns[i])) { + if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(locations[i].className())) { breakpoints[i].putProperty("verified", true); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 2120cafab..f22966b03 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -25,6 +26,7 @@ import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugUtility; +import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; @@ -47,6 +49,7 @@ import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; +import com.sun.jdi.request.BreakpointRequest; public class StackTraceRequestHandler implements IDebugRequestHandler { @@ -89,7 +92,7 @@ public CompletableFuture handle(Command command, Arguments arguments, StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i); int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); StackFrameInfo jdiFrame = jdiFrames.get(i); - result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, context)); + result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context)); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException @@ -167,7 +170,7 @@ private static List resolveStackFrameInfos(StackFrame[] frames, return jdiFrames; } - private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFrame, int frameId, IDebugAdapterContext context) + private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFrame, int frameId, boolean isTopFrame, IDebugAdapterContext context) throws URISyntaxException, AbsentInformationException { Types.Source clientSource = convertDebuggerSourceToClient(jdiFrame.typeName, jdiFrame.sourceName, jdiFrame.sourcePath, context); String methodName = formatMethodName(jdiFrame.methodName, jdiFrame.argumentTypeNames, jdiFrame.typeName, true, true); @@ -185,7 +188,27 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra clientSource = null; } } - return new Types.StackFrame(frameId, methodName, clientSource, clientLineNumber, context.isClientColumnsStartAt1() ? 1 : 0, presentationHint); + + int clientColumnNumber = context.isClientColumnsStartAt1() ? 1 : 0; + // If the top-level frame is a lambda method, it might be paused on a lambda breakpoint. + // We can associate its column number with the target lambda breakpoint. + if (isTopFrame && jdiFrame.methodName.startsWith("lambda$")) { + for (IBreakpoint breakpoint : context.getBreakpointManager().getBreakpoints()) { + if (breakpoint.getColumnNumber() > 0 && breakpoint.getLineNumber() == jdiFrame.lineNumber + && Objects.equals(jdiFrame.typeName, breakpoint.className())) { + boolean match = breakpoint.requests().stream().anyMatch(request -> { + return request instanceof BreakpointRequest + && Objects.equals(((BreakpointRequest) request).location(), jdiFrame.location); + }); + if (match) { + clientColumnNumber = AdapterUtils.convertColumnNumber(breakpoint.getColumnNumber(), + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + } + } + } + } + + return new Types.StackFrame(frameId, methodName, clientSource, clientLineNumber, clientColumnNumber, presentationHint); } /** diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 9b444155c..0301bdb38 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -17,6 +17,7 @@ import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.Types.DataBreakpoint; +import com.microsoft.java.debug.core.protocol.Types.Source; /** * The request arguments types defined by VSCode Debug Protocol. @@ -376,6 +377,42 @@ public boolean equals(Object obj) { } } + /** + * Arguments for breakpointLocations request. + */ + public static class BreakpointLocationsArguments extends Arguments { + /** + * The source location of the breakpoints; either `source.path` or + * `source.reference` must be specified. + */ + public Source source; + + /** + * Start line of range to search possible breakpoint locations in. If only the + * line is specified, the request returns all possible locations in that line. + */ + public int line; + + /** + * Start column of range to search possible breakpoint locations in. If no + * start column is given, the first column in the start line is assumed. + */ + public int column; + + /** + * End line of range to search possible breakpoint locations in. If no end + * line is given, then the end line is assumed to be the start line. + */ + public int endLine; + + /** + * End column of range to search possible breakpoint locations in. If no end + * column is given, then it is assumed to be in the last column of the end + * line. + */ + public int endColumn; + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -411,6 +448,7 @@ public static enum Command { INLINEVALUES("inlineValues", InlineValuesArguments.class), REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), PROCESSID("processId", Arguments.class), + BREAKPOINTLOCATIONS("breakpointLocations", BreakpointLocationsArguments.class), UNSUPPORTED("", Arguments.class); private String command; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index 2210c8e93..ea660f812 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,6 +13,7 @@ import java.util.List; +import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; @@ -282,6 +283,21 @@ public DataBreakpointInfoResponseBody(String dataId, String description, DataBre } } + /** + * Response to breakpointLocations request. + * Contains possible locations for source breakpoints. + */ + public static class BreakpointLocationsResponseBody extends ResponseBody { + /** + * Sorted set of possible breakpoint locations. + */ + public BreakpointLocation[] breakpoints; + + public BreakpointLocationsResponseBody(BreakpointLocation[] breakpoints) { + this.breakpoints = breakpoints; + } + } + public static class ContinueResponseBody extends ResponseBody { public boolean allThreadsContinued; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 979c7d632..4781c7a57 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,7 +16,7 @@ import com.google.gson.annotations.SerializedName; /** - * The data types defined by VSCode Debug Protocol. + * The data types defined by Debug Adapter Protocol. */ public class Types { public static class Message { @@ -200,6 +200,9 @@ public Breakpoint(int id, boolean verified, int line, String message) { } } + /** + * Properties of a breakpoint or logpoint passed to the setBreakpoints request. + */ public static class SourceBreakpoint { public int line; public int column; @@ -207,7 +210,9 @@ public static class SourceBreakpoint { public String condition; public String logMessage; - public SourceBreakpoint() { + public SourceBreakpoint(int line, int column) { + this.line = line; + this.column = column; } /** @@ -300,6 +305,46 @@ public DataBreakpoint(String dataId, DataBreakpointAccessType accessType, String } } + /** + * Properties of a breakpoint location returned from the breakpointLocations request. + */ + public static class BreakpointLocation { + /** + * Start line of breakpoint location. + */ + public int line; + + /** + * The start column of breakpoint location. + */ + public int column; + + /** + * The end line of breakpoint location if the location covers a range. + */ + public int endLine; + + /** + * The end column of breakpoint location if the location covers a range. + */ + public int endColumn; + + public BreakpointLocation() { + } + + public BreakpointLocation(int line, int column) { + this.line = line; + this.column = column; + } + + public BreakpointLocation(int line, int column, int endLine, int endColumn) { + this.line = line; + this.column = column; + this.endLine = endLine; + this.endColumn = endColumn; + } + } + public static class CompletionItem { public String label; public String text; @@ -386,5 +431,7 @@ public static class Capabilities { public boolean supportsDataBreakpoints; public boolean supportsClipboardContext; public boolean supportsFunctionBreakpoints; + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_BreakpointLocations + public boolean supportsBreakpointLocationsRequest; } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java index 351be8cb3..b37ff7b1f 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -34,13 +34,9 @@ public LambdaExpressionLocator(CompilationUnit compilationUnit, int line, int co @Override public boolean visit(LambdaExpression node) { - // we only support inline breakpoints which are added before the expression part of the - // lambda. And we don't support lambda blocks since they can be debugged using line - // breakpoints. if (column > -1) { int startPosition = node.getStartPosition(); - int endPosition = node.getBody().getStartPosition(); - int offset = this.compilationUnit.getPosition(line, column); + int breakOffset = this.compilationUnit.getPosition(line, column); // lambda on same line: // list.stream().map(i -> i + 1); // @@ -48,7 +44,10 @@ public boolean visit(LambdaExpression node) { // list.stream().map(user // -> user.isSystem() ? new SystemUser(user) : new EndUser(user)); - if (offset >= startPosition && offset <= endPosition) { + // Since the debugger supports BreakpointLocations Request to hint user + // about possible inline breakpoint locations, we will only support + // inline breakpoints added where a lambda expression begins. + if (breakOffset == startPosition) { this.lambdaMethodBinding = node.resolveMethodBinding(); this.found = true; this.lambdaExpression = node; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 1dc1c0e64..92ce9477a 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2021 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,12 +19,15 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Stream; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -39,7 +42,9 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; @@ -49,10 +54,13 @@ import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; +import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); @@ -112,12 +120,38 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return new String[0]; } + SourceBreakpoint[] sourceBreakpoints = new SourceBreakpoint[lines.length]; + for (int i = 0; i < lines.length; i++) { + sourceBreakpoints[i] = new SourceBreakpoint(lines[i], columns[i]); + } + + JavaBreakpointLocation[] locations = getBreakpointLocations(uri, sourceBreakpoints); + return Stream.of(locations).map(location -> { + if (location.className() != null && location.methodName() != null) { + return location.className() + .concat("#").concat(location.methodName()) + .concat("#").concat(location.methodSignature()); + } + return location.className(); + }).toArray(String[]::new); + } + + @Override + public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException { + if (sourceUri == null) { + throw new IllegalArgumentException("sourceUri is null"); + } + + if (sourceBreakpoints == null || sourceBreakpoints.length == 0) { + return new JavaBreakpointLocation[0]; + } + final ASTParser parser = ASTParser.newParser(this.latestASTLevel); parser.setResolveBindings(true); parser.setBindingsRecovery(true); parser.setStatementsRecovery(true); CompilationUnit astUnit = null; - String filePath = AdapterUtils.toPath(uri); + String filePath = AdapterUtils.toPath(sourceUri); // For file uri, read the file contents directly and pass them to the ast parser. if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { String source = readFile(filePath); @@ -146,27 +180,41 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th } else { // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), // leverage jdt to load the source contents. - ITypeRoot typeRoot = resolveClassFile(uri); + ITypeRoot typeRoot = resolveClassFile(sourceUri); if (typeRoot != null) { parser.setSource(typeRoot); astUnit = (CompilationUnit) parser.createAST(null); } } - String[] fqns = new String[lines.length]; + JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints) + .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) + .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { - for (int i = 0; i < lines.length; i++) { - if (columns[i] > -1) { + Map resolvedLocations = new HashMap<>(); + for (JavaBreakpointLocation sourceLocation : sourceLocations) { + int sourceLine = sourceLocation.lineNumber(); + int sourceColumn = sourceLocation.columnNumber(); + if (sourceColumn > -1) { // if we have a column, try to find the lambda expression at that column - LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, lines[i], - columns[i]); + LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, + sourceLine, sourceColumn); astUnit.accept(lambdaExpressionLocator); if (lambdaExpressionLocator.isFound()) { - fqns[i] = lambdaExpressionLocator.getFullyQualifiedTypeName().concat("#") - .concat(lambdaExpressionLocator.getMethodName()) - .concat("#").concat(lambdaExpressionLocator.getMethodSignature()); - continue; + sourceLocation.setClassName(lambdaExpressionLocator.getFullyQualifiedTypeName()); + sourceLocation.setMethodName(lambdaExpressionLocator.getMethodName()); + sourceLocation.setMethodSignature(lambdaExpressionLocator.getMethodSignature()); } + + if (resolvedLocations.containsKey(sourceLine)) { + sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); + } else { + BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine); + sourceLocation.setAvailableBreakpointLocations(inlineLocations); + resolvedLocations.put(sourceLine, inlineLocations); + } + + continue; } // TODO @@ -181,24 +229,56 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th // mark it as "unverified". // In future, we could consider supporting to update the breakpoint to a valid // location. - BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, - true); + BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, + sourceLine, true, true); astUnit.accept(locator); // When the final valid line location is same as the original line, that // represents it's a valid breakpoint. // Add location type check to avoid breakpoint on method/field which will never // be hit in current implementation. - if (lines[i] == locator.getLineLocation() + if (sourceLine == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { - fqns[i] = locator.getFullyQualifiedTypeName(); + sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + if (resolvedLocations.containsKey(sourceLine)) { + sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); + } else { + BreakpointLocation[] inlineLocations = getInlineBreakpointLocations(astUnit, sourceLine); + sourceLocation.setAvailableBreakpointLocations(inlineLocations); + resolvedLocations.put(sourceLine, inlineLocations); + } } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - fqns[i] = locator.getFullyQualifiedTypeName().concat("#") - .concat(locator.getMethodName()) - .concat("#").concat(locator.getMethodSignature()); + sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setMethodName(locator.getMethodName()); + sourceLocation.setMethodSignature(locator.getMethodSignature()); } } } - return fqns; + + return sourceLocations; + } + + private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { + List locations = new ArrayList<>(); + // The starting position of each line is the default breakpoint location for that line. + locations.add(new BreakpointLocation(sourceLine, 0)); + astUnit.accept(new ASTVisitor() { + @Override + public boolean visit(LambdaExpression node) { + int lambdaStart = node.getStartPosition(); + int startLine = astUnit.getLineNumber(lambdaStart); + if (startLine == sourceLine) { + int startColumn = astUnit.getColumnNumber(lambdaStart); + int lambdaEnd = lambdaStart + node.getLength(); + int endLine = astUnit.getLineNumber(lambdaEnd); + int endColumn = astUnit.getColumnNumber(lambdaEnd); + BreakpointLocation location = new BreakpointLocation(startLine, startColumn, endLine, endColumn); + locations.add(location); + } + return super.visit(node); + } + }); + + return locations.toArray(BreakpointLocation[]::new); } @Override From dea29f55459ac693cb9a29717c62d120b82cb8ad Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 28 Sep 2022 17:36:31 +0800 Subject: [PATCH 055/144] Enable async jdwp based on network latency for auto mode (#447) --- .../core/adapter/DebugAdapterContext.java | 27 +++++++++++++++- .../core/adapter/IDebugAdapterContext.java | 6 ++++ .../adapter/handler/AttachRequestHandler.java | 6 ++-- .../handler/EvaluateRequestHandler.java | 30 +++++++++++++++--- .../handler/InlineValuesRequestHandler.java | 3 +- .../handler/VariablesRequestHandler.java | 31 ++++++++++++++++--- 6 files changed, 90 insertions(+), 13 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index e317ddf1c..345b2fcdf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -61,6 +61,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private long processId = -1; private boolean localDebugging = true; + private long jdwpLatency = 0; private IdCollection sourceReferences = new IdCollection<>(); private RecyclableObjectPool recyclableIdPool = new RecyclableObjectPool<>(); @@ -372,7 +373,21 @@ public ThreadCache getThreadCache() { @Override public boolean asyncJDWP() { - return DebugSettings.getCurrent().asyncJDWP == AsyncMode.ON; + /** + * If we take 1 second as the acceptable latency for DAP requests, + * With a single-threaded strategy for handling JDWP requests, + * a latency of about 15ms per JDWP request can ensure the responsiveness + * for most DAPs. It allows sending 66 JDWP requests within 1 seconds, + * which can cover most DAP operations such as breakpoint, threads, + * call stack, step and continue. + */ + return asyncJDWP(15); + } + + @Override + public boolean asyncJDWP(long usableLatency) { + return DebugSettings.getCurrent().asyncJDWP == AsyncMode.ON + || (DebugSettings.getCurrent().asyncJDWP == AsyncMode.AUTO && this.jdwpLatency > usableLatency); } public boolean isLocalDebugging() { @@ -382,4 +397,14 @@ public boolean isLocalDebugging() { public void setLocalDebugging(boolean local) { this.localDebugging = local; } + + @Override + public long getJDWPLatency() { + return this.jdwpLatency; + } + + @Override + public void setJDWPLatency(long baseLatency) { + this.jdwpLatency = baseLatency; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 5d3b75639..9df539e1d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -145,7 +145,13 @@ public interface IDebugAdapterContext { boolean asyncJDWP(); + boolean asyncJDWP(long usableLatency/**ms*/); + boolean isLocalDebugging(); void setLocalDebugging(boolean local); + + long getJDWPLatency(); + + void setJDWPLatency(long baseLatency); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java index cee6e64a3..5ab848c8a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java @@ -110,8 +110,10 @@ public CompletableFuture handle(Command command, Arguments arguments, long sent = System.currentTimeMillis(); request.enable(); long received = System.currentTimeMillis(); - logger.info("Network latency for JDWP command: " + (received - sent) + "ms"); - traceInfo.put("networkLatency", (received - sent)); + long latency = received - sent; + context.setJDWPLatency(latency); + logger.info("Network latency for JDWP command: " + latency + "ms"); + traceInfo.put("networkLatency", latency); } IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index 1a0e41003..d135ee5b2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2021 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -65,7 +65,9 @@ public CompletableFuture handle(Command command, Arguments arguments, String expression = evalArguments.expression; // Async mode is supposed to be performant, then disable the advanced features like hover evaluation. - if (!context.isLocalDebugging() && context.asyncJDWP() && "hover".equals(evalArguments.context)) { + if (context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY) + && context.getJDWPLatency() > VariablesRequestHandler.USABLE_JDWP_LATENCY + && "hover".equals(evalArguments.context)) { return CompletableFuture.completedFuture(response); } @@ -98,7 +100,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Value sizeValue = null; if (value instanceof ArrayReference) { indexedVariables = ((ArrayReference) value).length(); - } else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && engine != null) { + } else if (value instanceof ObjectReference && supportsLogicStructureView(context, evalArguments.context) && engine != null) { try { JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value); if (structure != null && structure.getSizeExpression() != null) { @@ -135,7 +137,7 @@ public CompletableFuture handle(Command command, Arguments arguments, // If failed to resolve the variable value, skip the details info as well. } else if (sizeValue != null) { detailsString = "size=" + variableFormatter.valueToString(sizeValue, options); - } else if (DebugSettings.getCurrent().showToString) { + } else if (supportsToStringView(context, evalArguments.context)) { try { detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine); } catch (OutOfMemoryError e) { @@ -182,4 +184,24 @@ public CompletableFuture handle(Command command, Arguments arguments, } }); } + + private boolean supportsLogicStructureView(IDebugAdapterContext context, String evalContext) { + if (!"watch".equals(evalContext)) { + return true; + } + + return (!context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY) + || context.getJDWPLatency() <= VariablesRequestHandler.USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showLogicalStructure; + } + + private boolean supportsToStringView(IDebugAdapterContext context, String evalContext) { + if (!"watch".equals(evalContext)) { + return true; + } + + return (!context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY) + || context.getJDWPLatency() <= VariablesRequestHandler.USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showToString; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java index b30959cd5..21f77ee5a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InlineValuesRequestHandler.java @@ -82,7 +82,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } // Async mode is supposed to be performant, then disable the advanced features like inline values. - if (!context.isLocalDebugging() && context.asyncJDWP()) { + if (context.getJDWPLatency() > VariablesRequestHandler.USABLE_JDWP_LATENCY + && context.asyncJDWP(VariablesRequestHandler.USABLE_JDWP_LATENCY)) { response.body = new Responses.InlineValuesResponse(null); return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index c9d7cb666..11a6391de 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -68,6 +68,21 @@ public class VariablesRequestHandler implements IDebugRequestHandler { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + /** + * When the debugger enables logical structures and + * toString settings, for each Object variable in the + * variable list, the debugger needs to check its + * superclass and interface to find out if it inherits + * from Collection or overrides the toString method. + * This will cause the debugger to send a lot of JDWP + * requests for them. For a test case with 4 object + * variables, the debug adapter may need to send more + * than 100 JDWP requests to handle these variable + * requests. To achieve a DAP latency of 1s with a + * single-threaded JDWP request processing strategy, + * a single JDWP latency is about 10ms. + */ + static final long USABLE_JDWP_LATENCY = 10/**ms*/; @Override public List getTargetCommands() { @@ -130,7 +145,7 @@ public CompletableFuture handle(Command command, Arguments arguments, childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null)); } - if (context.asyncJDWP()) { + if (useAsyncJDWP(context)) { childrenList.addAll(getVariablesOfFrameAsync(frame, showStaticVariables)); } else { childrenList.addAll(VariableUtils.listLocalVariables(frame)); @@ -198,7 +213,7 @@ public CompletableFuture handle(Command command, Arguments arguments, if (varArgs.count > 0) { childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count); } else { - childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables, context.asyncJDWP()); + childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables, useAsyncJDWP(context)); } } } catch (AbsentInformationException e) { @@ -215,7 +230,7 @@ public CompletableFuture handle(Command command, Arguments arguments, .filter(var -> duplicateNames.contains(var.name)) .collect(Collectors.toList()); // Since JDI caches the fetched properties locally, in async mode we can warm up the JDI cache in advance. - if (context.asyncJDWP()) { + if (useAsyncJDWP(context)) { try { AsyncJdwpUtils.await(warmUpJDICache(childrenList, duplicateVars)); } catch (CompletionException | CancellationException e) { @@ -377,11 +392,17 @@ public CompletableFuture handle(Command command, Arguments arguments, } private boolean supportsLogicStructureView(IDebugAdapterContext context) { - return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showLogicalStructure; + return (!useAsyncJDWP(context) || context.getJDWPLatency() <= USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showLogicalStructure; } private boolean supportsToStringView(IDebugAdapterContext context) { - return (!context.asyncJDWP() || context.isLocalDebugging()) && DebugSettings.getCurrent().showToString; + return (!useAsyncJDWP(context) || context.getJDWPLatency() <= USABLE_JDWP_LATENCY) + && DebugSettings.getCurrent().showToString; + } + + private boolean useAsyncJDWP(IDebugAdapterContext context) { + return context.asyncJDWP(USABLE_JDWP_LATENCY); } private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter, From 2a48adff464e53b9fa92f4737b52f2dba9eba3fd Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Sat, 8 Oct 2022 15:29:55 +0800 Subject: [PATCH 056/144] Fix NPE from logHandler when stopping debug plugin (#449) --- .../java/debug/plugin/internal/JavaDebuggerServerPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java index 975338371..d3307a5b2 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebuggerServerPlugin.java @@ -34,7 +34,6 @@ public void start(BundleContext context) throws Exception { @Override public void stop(BundleContext context) throws Exception { - logger.info("Stopping " + PLUGIN_ID); LogUtils.cleanupHandlers(); } From f5ab6904f55853e4baf5c459eb38125b42338da7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 12 Oct 2022 12:42:12 +0800 Subject: [PATCH 057/144] Bump version to 0.41.0 (#450) --- .project | 4 ++-- com.microsoft.java.debug.core/.project | 4 ++-- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/.project | 4 ++-- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/.project | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.project b/.project index d34865de3..f00ccfc41 100644 --- a/.project +++ b/.project @@ -16,12 +16,12 @@ - 1600224298170 + 1665543654766 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.core/.project b/com.microsoft.java.debug.core/.project index a7480133e..353c44be5 100644 --- a/com.microsoft.java.debug.core/.project +++ b/com.microsoft.java.debug.core/.project @@ -28,12 +28,12 @@ - 1599036548523 + 1665543654702 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 83fed3cc9..8bb9b9f47 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.40.0 + 0.41.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index b7e330e32..349cb7844 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/.project b/com.microsoft.java.debug.plugin/.project index 72ba17ff7..529a656dd 100644 --- a/com.microsoft.java.debug.plugin/.project +++ b/com.microsoft.java.debug.plugin/.project @@ -39,12 +39,12 @@ - 1599036548577 + 1665543654719 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 9667f9e51..21e32bbde 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.40.0 +Bundle-Version: 0.41.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.40.0.jar + lib/com.microsoft.java.debug.core-0.41.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 56bb813e6..949512717 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.40.0 + 0.41.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.40.0 + 0.41.0 diff --git a/com.microsoft.java.debug.repository/.project b/com.microsoft.java.debug.repository/.project index 887e4a7a8..255908647 100644 --- a/com.microsoft.java.debug.repository/.project +++ b/com.microsoft.java.debug.repository/.project @@ -16,12 +16,12 @@ - 1600224298119 + 1665543654735 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 7d8519073..27c76516b 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 43cd80e43..b8524fa90 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.40.0 + 0.41.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 2b0497f26..4d31432d9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.40.0 + 0.41.0 pom Java Debug Server for Visual Studio Code From c471efc6671adfb12538a94b13f659d590c06b4d Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 9 Aug 2022 20:50:57 +0200 Subject: [PATCH 058/144] Add support for StepInTarget request The change implement the StepInTarget request and also update the StepRequestHandler to support StepInTarget as well. --- .../java/debug/core/adapter/DebugAdapter.java | 3 + .../core/adapter/ISourceLookUpProvider.java | 52 +++++ .../handler/InitializeRequestHandler.java | 1 + .../handler/StepInTargetsRequestHandler.java | 81 ++++++++ .../adapter/handler/StepRequestHandler.java | 195 ++++++++++++------ .../java/debug/core/protocol/Requests.java | 6 + .../java/debug/core/protocol/Responses.java | 9 + .../java/debug/core/protocol/Types.java | 16 ++ .../microsoft/java/debug/BindingUtils.java | 74 +++++++ .../java/debug/BreakpointLocationLocator.java | 20 +- .../java/debug/LambdaExpressionLocator.java | 5 +- .../internal/JdtSourceLookUpProvider.java | 147 +++++++++---- .../internal/MethodInvocationLocator.java | 66 ++++++ 13 files changed, 551 insertions(+), 124 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index edc771163..b853e0469 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -44,6 +44,7 @@ import com.microsoft.java.debug.core.adapter.handler.SetVariableRequestHandler; import com.microsoft.java.debug.core.adapter.handler.SourceRequestHandler; import com.microsoft.java.debug.core.adapter.handler.StackTraceRequestHandler; +import com.microsoft.java.debug.core.adapter.handler.StepInTargetsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.StepRequestHandler; import com.microsoft.java.debug.core.adapter.handler.ThreadsRequestHandler; import com.microsoft.java.debug.core.adapter.handler.VariablesRequestHandler; @@ -131,6 +132,8 @@ private void initialize() { registerHandlerForDebug(new ProcessIdHandler()); registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); registerHandlerForDebug(new BreakpointLocationsRequestHander()); + registerHandlerForDebug(new StepInTargetsRequestHandler()); + // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); registerHandlerForNoDebug(new ProcessIdHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java index 9976fc16f..5e8d94451 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java @@ -11,10 +11,15 @@ package com.microsoft.java.debug.core.adapter; +import java.util.List; +import java.util.Objects; + import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; +import com.sun.jdi.StackFrame; + public interface ISourceLookUpProvider extends IProvider { boolean supportsRealtimeBreakpointVerification(); @@ -60,4 +65,51 @@ public interface ISourceLookUpProvider extends IProvider { default String getJavaRuntimeVersion(String projectName) { return null; } + + /** + * Return method invocation found in the statement as the given line number of + * the source file. + * + * @param stackframe The stack frame where the invocation must be searched. + * + * @return List of found method invocation or empty if not method invocations + * can be found. + */ + List findMethodInvocations(StackFrame stackframe); + + public static class MethodInvocation { + public String expression; + public String methodName; + public String methodSignature; + public String declaringTypeName; + public int lineStart; + public int lineEnd; + public int columnStart; + public int columnEnd; + + @Override + public int hashCode() { + return Objects.hash(columnEnd, columnStart, declaringTypeName, expression, lineEnd, lineStart, methodName, + methodSignature); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + MethodInvocation other = (MethodInvocation) obj; + return columnEnd == other.columnEnd && columnStart == other.columnStart + && Objects.equals(declaringTypeName, other.declaringTypeName) + && Objects.equals(expression, other.expression) && lineEnd == other.lineEnd + && lineStart == other.lineStart && Objects.equals(methodName, other.methodName) + && Objects.equals(methodSignature, other.methodSignature); + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index 19bbd7d62..bf60b6456 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -65,6 +65,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsFunctionBreakpoints = true; caps.supportsClipboardContext = true; caps.supportsBreakpointLocationsRequest = true; + caps.supportsStepInTargetsRequest = true; response.body = caps; return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java new file mode 100644 index 000000000..f61d2a8b9 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java @@ -0,0 +1,81 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider.MethodInvocation; +import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.StepInTargetsArguments; +import com.microsoft.java.debug.core.protocol.Responses.StepInTargetsResponse; +import com.microsoft.java.debug.core.protocol.Types.StepInTarget; +import com.sun.jdi.StackFrame; + +public class StepInTargetsRequestHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.STEPIN_TARGETS); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + final StepInTargetsArguments stepInTargetsArguments = (StepInTargetsArguments) arguments; + + final int frameId = stepInTargetsArguments.frameId; + return CompletableFuture.supplyAsync(() -> { + response.body = new StepInTargetsResponse( + findFrame(frameId, context).map(f -> findTargets(f.thread().uniqueID(), f, context)) + .orElse(Collections.emptyList()).toArray(StepInTarget[]::new)); + return response; + }); + } + + private Optional findFrame(int frameId, IDebugAdapterContext context) { + Object object = context.getRecyclableIdPool().getObjectById(frameId); + if (object instanceof StackFrameReference) { + return Optional.of(context.getStackFrameManager().getStackFrame((StackFrameReference) object)); + } + return Optional.empty(); + } + + private List findTargets(long threadId, StackFrame stackframe, IDebugAdapterContext context) { + ISourceLookUpProvider sourceLookUpProvider = context.getProvider(ISourceLookUpProvider.class); + List invocations = sourceLookUpProvider.findMethodInvocations(stackframe); + if (invocations.isEmpty()) { + return Collections.emptyList(); + } + + List targets = new ArrayList<>(invocations.size()); + for (MethodInvocation methodInvocation : invocations) { + int id = context.getRecyclableIdPool().addObject(threadId, methodInvocation); + StepInTarget target = new StepInTarget(id, methodInvocation.expression); + target.column = methodInvocation.columnStart; + target.endColumn = methodInvocation.columnEnd; + target.line = methodInvocation.lineStart; + target.endLine = methodInvocation.lineEnd; + targets.add(target); + } + return targets; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 51e41bafc..f9baa0e5d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -29,19 +31,24 @@ import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider.MethodInvocation; import com.microsoft.java.debug.core.protocol.Events; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.StepArguments; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; +import com.microsoft.java.debug.core.protocol.Requests.StepInArguments; +import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; +import com.sun.jdi.VirtualMachine; import com.sun.jdi.VoidValue; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; @@ -67,10 +74,13 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { if (context.getDebugSession() == null) { - return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Debug Session doesn't exist."); + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, + "Debug Session doesn't exist."); } - long threadId = ((StepArguments) arguments).threadId; + StepArguments stepArguments = (StepArguments) arguments; + long threadId = stepArguments.threadId; + int targetId = (stepArguments instanceof StepInArguments) ? ((StepInArguments) stepArguments).targetId : 0; ThreadReference thread = context.getThreadCache().getThread(threadId); if (thread == null) { thread = DebugUtility.getThread(context.getDebugSession(), threadId); @@ -83,28 +93,34 @@ public CompletableFuture handle(Command command, Arguments arguments, ThreadState threadState = new ThreadState(); threadState.threadId = threadId; threadState.pendingStepType = command; + threadState.stackDepth = thread.frameCount(); + threadState.stepLocation = resolveLocation(thread, targetId, context); + threadState.targetStepIn = targetId > 0; threadState.eventSubscription = context.getDebugSession().getEventHub().events() - .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) - || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) - || debugEvent.event instanceof BreakpointEvent - || debugEvent.event instanceof ExceptionEvent) - .subscribe(debugEvent -> { - handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); - }); + .filter(debugEvent -> (debugEvent.event instanceof StepEvent + && debugEvent.event.request().equals(threadState.pendingStepRequest)) + || (debugEvent.event instanceof MethodExitEvent + && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) + || debugEvent.event instanceof BreakpointEvent + || debugEvent.event instanceof ExceptionEvent) + .subscribe(debugEvent -> { + handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); + }); if (command == Command.STEPIN) { threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else { threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); + threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager() + .createMethodExitRequest(); threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); if (context.asyncJDWP()) { @@ -114,7 +130,8 @@ public CompletableFuture handle(Command command, Arguments arguments, // JDWP Command: TR_FRAMES threadState.topFrame = getTopFrame(targetThread); threadState.stepLocation = threadState.topFrame.location(); - threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + threadState.pendingMethodExitRequest + .addClassFilter(threadState.stepLocation.declaringType()); if (targetThread.virtualMachine().canUseInstanceFilters()) { try { // JDWP Command: SF_THIS_OBJECT @@ -131,9 +148,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } })); futures.add(AsyncJdwpUtils.runAsync( - // JDWP Command: OR_IS_COLLECTED - () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread) - )); + // JDWP Command: OR_IS_COLLECTED + () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread))); futures.add(AsyncJdwpUtils.runAsync(() -> { try { // JDWP Command: TR_FRAME_COUNT @@ -143,9 +159,8 @@ public CompletableFuture handle(Command command, Arguments arguments, } })); futures.add( - // JDWP Command: ER_SET - AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable()) - ); + // JDWP Command: ER_SET + AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable())); try { AsyncJdwpUtils.await(futures); @@ -184,39 +199,80 @@ public CompletableFuture handle(Command command, Arguments arguments, } catch (IncompatibleThreadStateException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); + final String failureMessage = String.format( + "Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (IndexOutOfBoundsException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); + final String failureMessage = String.format( + "Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); + } catch (AbsentInformationException ex) { + // Roll back the Exception info if stepping fails. + context.getExceptionManager().setException(threadId, exception); + final String failureMessage = String.format( + "Failed to step because the thread '%s' doesn't contain required line information in stack frame", + thread.name()); + throw AdapterUtils.createCompletionException( + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (Exception ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because of the error '%s'", ex.getMessage()); + final String failureMessage = String.format("Failed to step because of the error '%s'", + ex.getMessage()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex.getCause() != null ? ex.getCause() : ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex.getCause() != null ? ex.getCause() : ex); } } return CompletableFuture.completedFuture(response); } + private Location resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) + throws IncompatibleThreadStateException, AbsentInformationException { + if (targetId > 0) { + Object value = context.getRecyclableIdPool().getObjectById(targetId); + if (value instanceof MethodInvocation) { + MethodInvocation invocation = (MethodInvocation) value; + VirtualMachine vm = thread.virtualMachine(); + List refTypes = vm.classesByName(invocation.declaringTypeName); + for (ReferenceType referenceType : refTypes) { + Optional location = referenceType.allLineLocations().stream() + .filter(l -> matchesLocation(l, invocation)) + .findFirst(); + if (location.isPresent()) { + return location.get(); + } + } + } + } + return getTopFrame(thread).location(); + } + + private boolean matchesLocation(Location l, MethodInvocation invocation) { + Method method = l.method(); + return method != null && Objects.equals(method.name(), invocation.methodName) + && Objects.equals(method.signature(), invocation.methodSignature); + } + private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); - // When a breakpoint occurs, abort any pending step requests from the same thread. + // When a breakpoint occurs, abort any pending step requests from the same + // thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { @@ -230,19 +286,20 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); threadState.deleteStepRequest(eventRequestManager); - if (isStepFiltersConfigured(context.getStepFilters())) { + if (isStepFiltersConfigured(context.getStepFilters()) || threadState.targetStepIn) { try { if (threadState.pendingStepType == Command.STEPIN) { int currentStackDepth = thread.frameCount(); Location currentStepLocation = getTopFrame(thread).location(); - - // If the ending step location is filtered, or same as the original location where the step into operation is originated, + // If the ending step location is filtered, or same as the original location + // where the step into operation is originated, // do another step of the same kind. if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) - || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, currentStackDepth, currentStepLocation)) { + || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, + currentStackDepth, currentStepLocation, threadState.targetStepIn)) { threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; @@ -262,7 +319,8 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (event instanceof MethodExitEvent) { MethodExitEvent methodExitEvent = (MethodExitEvent) event; long threadId = methodExitEvent.thread().uniqueID(); - if (threadId == threadState.threadId && methodExitEvent.method().equals(threadState.stepLocation.method())) { + if (threadId == threadState.threadId + && methodExitEvent.method().equals(threadState.stepLocation.method())) { Value returnValue = methodExitEvent.returnValue(); if (returnValue instanceof VoidValue) { context.getStepResultManager().removeMethodResult(threadId); @@ -280,22 +338,26 @@ private boolean isStepFiltersConfigured(StepFilters filters) { return false; } return ArrayUtils.isNotEmpty(filters.allowClasses) || ArrayUtils.isNotEmpty(filters.skipClasses) - || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors - || filters.skipStaticInitializers || filters.skipSynthetics; + || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors + || filters.skipStaticInitializers || filters.skipSynthetics; } /** - * Return true if the StepEvent's location is a Method that the user has indicated to filter. + * Return true if the StepEvent's location is a Method that the user has + * indicated to filter. * * @throws IncompatibleThreadStateException - * if the thread is not suspended in the target VM. + * if the thread is not suspended in + * the target VM. */ - private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, IDebugAdapterContext context) + private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, + IDebugAdapterContext context) throws IncompatibleThreadStateException { if (originalLocation == null || currentLocation == null) { return false; } - return !shouldFilterMethod(originalLocation.method(), context) && shouldFilterMethod(currentLocation.method(), context); + return !shouldFilterMethod(originalLocation.method(), context) + && shouldFilterMethod(currentLocation.method(), context); } private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) { @@ -305,40 +367,50 @@ private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) } /** - * Check if the current top stack is same as the original top stack. + * Check if the current top stack is same as the original top stack and if we + * are not in target step in we should not request an extra step in. But if we + * are processing a target step in, we only check if the original and current + * location are same. If they are not same we request a extra step in. * * @throws IncompatibleThreadStateException - * if the thread is not suspended in the target VM. + * if the thread is not suspended in + * the target VM. */ - private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, Location currentLocation) + private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, + Location currentLocation, boolean targetStepIn) throws IncompatibleThreadStateException { - if (originalStackDepth != currentStackDepth) { - return false; - } - if (originalLocation == null) { - return false; + if (!targetStepIn) { + if (originalStackDepth != currentStackDepth) { + return false; + } + if (originalLocation == null) { + return false; + } } + Method originalMethod = originalLocation.method(); Method currentMethod = currentLocation.method(); if (!originalMethod.equals(currentMethod)) { - return false; + return targetStepIn; } if (originalLocation.lineNumber() != currentLocation.lineNumber()) { - return false; + return targetStepIn; } - return true; + return !targetStepIn; } /** * Return the top stack frame of the target thread. * * @param thread - * the target thread. + * the target thread. * @return the top frame. * @throws IncompatibleThreadStateException - * if the thread is not suspended in the target VM. + * if the thread is not suspended in + * the target VM. * @throws IndexOutOfBoundsException - * if the thread doesn't contain any stack frame. + * if the thread doesn't contain any + * stack frame. */ private StackFrame getTopFrame(ThreadReference thread) throws IncompatibleThreadStateException { return thread.frame(0); @@ -353,6 +425,7 @@ class ThreadState { StackFrame topFrame = null; Location stepLocation = null; Disposable eventSubscription = null; + boolean targetStepIn = false; public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 0301bdb38..697eef93d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -271,6 +271,10 @@ public static class StepOutArguments extends StepArguments { } + public static class StepInTargetsArguments extends Arguments { + public int frameId; + } + public static class PauseArguments extends Arguments { public long threadId; } @@ -423,6 +427,8 @@ public static enum Command { CONTINUE("continue", ContinueArguments.class), STEPIN("stepIn", StepInArguments.class), STEPOUT("stepOut", StepOutArguments.class), + STEPIN_TARGETS("stepInTargets", + StepInTargetsArguments.class), PAUSE("pause", PauseArguments.class), STACKTRACE("stackTrace", StackTraceArguments.class), RESTARTFRAME("restartFrame", RestartFrameArguments.class), diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java index ea660f812..af701552e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java @@ -17,6 +17,7 @@ import com.microsoft.java.debug.core.protocol.Types.DataBreakpointAccessType; import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode; import com.microsoft.java.debug.core.protocol.Types.ExceptionDetails; +import com.microsoft.java.debug.core.protocol.Types.StepInTarget; import com.microsoft.java.debug.core.protocol.Types.Variable; /** @@ -364,4 +365,12 @@ public InlineValuesResponse(Variable[] variables) { this.variables = variables; } } + + public static class StepInTargetsResponse extends ResponseBody { + public StepInTarget[] targets; + + public StepInTargetsResponse(StepInTarget[] targets) { + this.targets = targets; + } + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 4781c7a57..33308af6d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -433,5 +433,21 @@ public static class Capabilities { public boolean supportsFunctionBreakpoints; // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_BreakpointLocations public boolean supportsBreakpointLocationsRequest; + public boolean supportsStepInTargetsRequest; } + + public static class StepInTarget { + public int id; + public String label; + public int line; + public int column; + public int endLine; + public int endColumn; + + public StepInTarget(int id, String label) { + this.id = id; + this.label = label; + } + } + } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java new file mode 100644 index 000000000..e30b0dd30 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2017-2020 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug; + +import org.eclipse.jdt.core.dom.IMethodBinding; + +/** + * Utility methods around working with JDT Bindings. + */ +public final class BindingUtils { + private BindingUtils() { + + } + + /** + * Return the method name from the binding using either the + * {@link IMethodBinding#getKey()} or {@link IMethodBinding#getName()}. The key + * can be used to find the name of a generated lambda method if the minding + * represents a lambda method. + * + * @param binding the binding to extract the name from. + * @param fromKey use binging key to resolve the method name. + * @return the name of the method. + */ + public static String getMethodName(IMethodBinding binding, boolean fromKey) { + if (fromKey) { + String key = binding.getKey(); + return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + } else { + return binding.getName(); + } + } + + /** + * Returns the method signature of the method represented by the binding. Since + * this implementation use the {@link IMethodBinding#getKey()} to extract the + * signature from, the method name must be passed in. + * + * @param binding the binding which the signature must be resolved for. + * @param name the name of the method. + * @return the signature or null if the signature could not be resolved from the + * key. + */ + public static String toSignature(IMethodBinding binding, String name) { + // use key for now until JDT core provides a public API for this. + // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" + // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" + if (!binding.getName().equals(name)) { + throw new IllegalArgumentException("The method name and binding method name doesn't match."); + } + + String signatureString = binding.getKey(); + if (signatureString != null) { + int index = signatureString.indexOf(name); + if (index > -1) { + int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); + if (exceptionIndex > -1) { + return signatureString.substring(index + name.length(), exceptionIndex); + } + return signatureString.substring(index + name.length()); + } + } + return null; + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index 00cffb766..68f593aff 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -46,7 +46,7 @@ public String getMethodSignature() { if (this.methodBinding == null) { return null; } - return toSignature(this.methodBinding, getMethodName()); + return BindingUtils.toSignature(this.methodBinding, getMethodName()); } /** @@ -70,22 +70,4 @@ public String getFullyQualifiedTypeName() { } return super.getFullyQualifiedTypeName(); } - - static String toSignature(IMethodBinding binding, String name) { - // use key for now until JDT core provides a public API for this. - // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" - // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" - String signatureString = binding.getKey(); - if (signatureString != null) { - int index = signatureString.indexOf(name); - if (index > -1) { - int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); - if (exceptionIndex > -1) { - return signatureString.substring(index + name.length(), exceptionIndex); - } - return signatureString.substring(index + name.length()); - } - } - return null; - } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java index b37ff7b1f..afffd9741 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -71,7 +71,7 @@ public String getMethodSignature() { if (!this.found) { return null; } - return BreakpointLocationLocator.toSignature(this.lambdaMethodBinding, getMethodName()); + return BindingUtils.toSignature(this.lambdaMethodBinding, getMethodName()); } /** @@ -81,8 +81,7 @@ public String getMethodName() { if (!this.found) { return null; } - String key = this.lambdaMethodBinding.getKey(); - return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + return BindingUtils.getMethodName(lambdaMethodBinding, true); } /** diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 92ce9477a..eba9a42e6 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -15,11 +15,13 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +30,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; +import java.util.stream.Collectors; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -37,6 +40,7 @@ import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; @@ -45,11 +49,14 @@ import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import com.microsoft.java.debug.BindingUtils; import com.microsoft.java.debug.BreakpointLocationLocator; import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; @@ -62,6 +69,8 @@ import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; +import com.sun.jdi.StackFrame; + public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; @@ -146,47 +155,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB return new JavaBreakpointLocation[0]; } - final ASTParser parser = ASTParser.newParser(this.latestASTLevel); - parser.setResolveBindings(true); - parser.setBindingsRecovery(true); - parser.setStatementsRecovery(true); - CompilationUnit astUnit = null; - String filePath = AdapterUtils.toPath(sourceUri); - // For file uri, read the file contents directly and pass them to the ast parser. - if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { - String source = readFile(filePath); - parser.setSource(source.toCharArray()); - /** - * See the java doc for { @link ASTParser#setResolveBindings(boolean) }. - * Binding information is obtained from the Java model. This means that the compilation unit must be located relative to the Java model. - * This happens automatically when the source code comes from either setSource(ICompilationUnit) or setSource(IClassFile). - * When source is supplied by setSource(char[]), the location must be established explicitly - * by setting an environment using setProject(IJavaProject) or setEnvironment(String [], String [], String [], boolean) - * and a unit name setUnitName(String). - */ - parser.setEnvironment(new String[0], new String[0], null, true); - parser.setUnitName(Paths.get(filePath).getFileName().toString()); - /** - * See the java doc for { @link ASTParser#setSource(char[]) }, - * the user need specify the compiler options explicitly. - */ - Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); - parser.setCompilerOptions(javaOptions); - astUnit = (CompilationUnit) parser.createAST(null); - } else { - // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), - // leverage jdt to load the source contents. - ITypeRoot typeRoot = resolveClassFile(sourceUri); - if (typeRoot != null) { - parser.setSource(typeRoot); - astUnit = (CompilationUnit) parser.createAST(null); - } - } - + CompilationUnit astUnit = asCompilationUnit(sourceUri); JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints) .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) .toArray(JavaBreakpointLocation[]::new); @@ -281,6 +250,55 @@ public boolean visit(LambdaExpression node) { return locations.toArray(BreakpointLocation[]::new); } + private CompilationUnit asCompilationUnit(String uri) { + final ASTParser parser = ASTParser.newParser(this.latestASTLevel); + parser.setResolveBindings(true); + parser.setBindingsRecovery(true); + parser.setStatementsRecovery(true); + CompilationUnit astUnit = null; + String filePath = AdapterUtils.toPath(uri); + // For file uri, read the file contents directly and pass them to the ast + // parser. + if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { + String source = readFile(filePath); + parser.setSource(source.toCharArray()); + /** + * See the java doc for { @link ASTParser#setResolveBindings(boolean) }. + * Binding information is obtained from the Java model. This means that the + * compilation unit must be located relative to the Java model. + * This happens automatically when the source code comes from either + * setSource(ICompilationUnit) or setSource(IClassFile). + * When source is supplied by setSource(char[]), the location must be + * established explicitly + * by setting an environment using setProject(IJavaProject) or + * setEnvironment(String [], String [], String [], boolean) + * and a unit name setUnitName(String). + */ + parser.setEnvironment(new String[0], new String[0], null, true); + parser.setUnitName(Paths.get(filePath).getFileName().toString()); + /** + * See the java doc for { @link ASTParser#setSource(char[]) }, + * the user need specify the compiler options explicitly. + */ + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + parser.setCompilerOptions(javaOptions); + astUnit = (CompilationUnit) parser.createAST(null); + } else { + // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), + // leverage jdt to load the source contents. + ITypeRoot typeRoot = resolveClassFile(uri); + if (typeRoot != null) { + parser.setSource(typeRoot); + astUnit = (CompilationUnit) parser.createAST(null); + } + } + return astUnit; + } + @Override public String getSourceFileURI(String fullyQualifiedName, String sourcePath) { if (sourcePath == null) { @@ -431,4 +449,51 @@ private static String resolveSystemLibraryVersion(IJavaProject project, IVMInsta return null; } + + @Override + public List findMethodInvocations(StackFrame frame) { + if (frame == null) { + throw new IllegalArgumentException("frame is null"); + } + + IJavaProject project = JdtUtils.findProject(frame, getSourceContainers()); + if (project == null) { + logger.log(Level.WARNING, + String.format("Failed to resolve project for the frame: %s", frame)); + return Collections.emptyList(); + } + + String uri; + try { + IType type = project.findType(JdtUtils.getDeclaringTypeName(frame)); + uri = type.getResource().getLocationURI().toURL().toString(); + } catch (JavaModelException | DebugException | MalformedURLException e) { + logger.log(Level.SEVERE, + String.format("Failed to resolve type for the frame: %s", frame)); + return Collections.emptyList(); + } + + CompilationUnit astUnit = asCompilationUnit(uri); + if (astUnit == null) { + return Collections.emptyList(); + } + + MethodInvocationLocator locator = new MethodInvocationLocator(frame.location().lineNumber(), astUnit); + astUnit.accept(locator); + + return locator.getTargets().entrySet().stream().map(entry -> { + MethodInvocation invocation = new MethodInvocation(); + Expression expression = entry.getKey(); + invocation.expression = expression.toString(); + IMethodBinding binding = entry.getValue(); + invocation.methodName = binding.getName(); + invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + invocation.methodSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); + invocation.lineStart = astUnit.getLineNumber(expression.getStartPosition()); + invocation.lineEnd = astUnit.getLineNumber(expression.getStartPosition() + expression.getLength()); + invocation.columnStart = astUnit.getColumnNumber(expression.getStartPosition()); + invocation.columnEnd = astUnit.getColumnNumber(expression.getStartPosition() + expression.getLength()); + return invocation; + }).collect(Collectors.toList()); + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java new file mode 100644 index 000000000..ab6efdaa2 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -0,0 +1,66 @@ +/******************************************************************************* +* Copyright (c) 2022 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gayan Perera (gayanper@gmail.com) - initial API and implementation +*******************************************************************************/ +package com.microsoft.java.debug.plugin.internal; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; + +public class MethodInvocationLocator extends ASTVisitor { + private int line; + private CompilationUnit unit; + private Map targets; + + private boolean collectMethodInvocations = false; + + public MethodInvocationLocator(int line, CompilationUnit unit) { + super(false); + this.line = line; + this.unit = unit; + this.targets = new HashMap<>(); + } + + @Override + public boolean visit(ExpressionStatement node) { + int start = unit.getLineNumber(node.getStartPosition()); + int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + + if (line >= start && line <= end) { + collectMethodInvocations = true; + } + return collectMethodInvocations; + } + + @Override + public boolean visit(MethodInvocation node) { + int lineNumber = unit.getLineNumber(node.getStartPosition()); + if (lineNumber == this.line) { + targets.put(node, node.resolveMethodBinding()); + return true; + } + return false; + } + + @Override + public void endVisit(ExpressionStatement node) { + collectMethodInvocations = false; + } + + public Map getTargets() { + return targets; + } +} From 69230b339d68dfa2b3a1c251c29f3bd577fd0cf8 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Fri, 19 Aug 2022 19:12:01 +0200 Subject: [PATCH 059/144] Fix and improve targets and stepin Improve to only show valid targets within an expression, also added support for stepin into unloaded classes by delaying the stepin. --- .../adapter/handler/StepRequestHandler.java | 256 ++++++++++-------- .../microsoft/java/debug/BindingUtils.java | 9 +- .../internal/JdtSourceLookUpProvider.java | 16 +- .../internal/MethodInvocationLocator.java | 29 +- 4 files changed, 193 insertions(+), 117 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index f9baa0e5d..660d4bbdd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -74,8 +74,7 @@ public List getTargetCommands() { public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { if (context.getDebugSession() == null) { - return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, - "Debug Session doesn't exist."); + return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Debug Session doesn't exist."); } StepArguments stepArguments = (StepArguments) arguments; @@ -93,63 +92,59 @@ public CompletableFuture handle(Command command, Arguments arguments, ThreadState threadState = new ThreadState(); threadState.threadId = threadId; threadState.pendingStepType = command; - threadState.stackDepth = thread.frameCount(); - threadState.stepLocation = resolveLocation(thread, targetId, context); - threadState.targetStepIn = targetId > 0; threadState.eventSubscription = context.getDebugSession().getEventHub().events() - .filter(debugEvent -> (debugEvent.event instanceof StepEvent - && debugEvent.event.request().equals(threadState.pendingStepRequest)) - || (debugEvent.event instanceof MethodExitEvent - && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) - || debugEvent.event instanceof BreakpointEvent - || debugEvent.event instanceof ExceptionEvent) - .subscribe(debugEvent -> { - handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); - }); + .filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest)) + || (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest)) + || debugEvent.event instanceof BreakpointEvent + || debugEvent.event instanceof ExceptionEvent) + .subscribe(debugEvent -> { + handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState); + }); if (command == Command.STEPIN) { + String[] allowedClasses = threadState.pendingTargetStepIn != null + ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} + : context.getStepFilters().allowClasses; threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + allowedClasses, + context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); } else { threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null); } - threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager() - .createMethodExitRequest(); + threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + LocationResponse locationResponse = resolveLocation(thread, targetId, context); if (context.asyncJDWP()) { List> futures = new ArrayList<>(); futures.add(AsyncJdwpUtils.runAsync(() -> { - try { - // JDWP Command: TR_FRAMES - threadState.topFrame = getTopFrame(targetThread); - threadState.stepLocation = threadState.topFrame.location(); - threadState.pendingMethodExitRequest - .addClassFilter(threadState.stepLocation.declaringType()); - if (targetThread.virtualMachine().canUseInstanceFilters()) { - try { - // JDWP Command: SF_THIS_OBJECT - ObjectReference thisObject = threadState.topFrame.thisObject(); - if (thisObject != null) { - threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); - } - } catch (Exception e) { - // ignore + // JDWP Command: TR_FRAMES + threadState.stepLocation = locationResponse.location; + threadState.pendingTargetStepIn = locationResponse.methodInvocation; + threadState.targetStepIn = targetId > 0; + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + // JDWP Command: SF_THIS_OBJECT + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); } + } catch (Exception e) { + // ignore } - } catch (IncompatibleThreadStateException e) { - throw new CompletionException(e); } })); futures.add(AsyncJdwpUtils.runAsync( - // JDWP Command: OR_IS_COLLECTED - () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread))); + // JDWP Command: OR_IS_COLLECTED + () -> threadState.pendingMethodExitRequest.addThreadFilter(targetThread) + )); futures.add(AsyncJdwpUtils.runAsync(() -> { try { // JDWP Command: TR_FRAME_COUNT @@ -159,8 +154,9 @@ public CompletableFuture handle(Command command, Arguments arguments, } })); futures.add( - // JDWP Command: ER_SET - AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable())); + // JDWP Command: ER_SET + AsyncJdwpUtils.runAsync(() -> threadState.pendingStepRequest.enable()) + ); try { AsyncJdwpUtils.await(futures); @@ -175,7 +171,9 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingMethodExitRequest.enable(); } else { threadState.stackDepth = targetThread.frameCount(); - threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = locationResponse.location; + threadState.pendingTargetStepIn = locationResponse.methodInvocation; + threadState.targetStepIn = targetId > 0; threadState.stepLocation = threadState.topFrame.location(); threadState.pendingMethodExitRequest.addThreadFilter(thread); threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); @@ -199,81 +197,97 @@ public CompletableFuture handle(Command command, Arguments arguments, } catch (IncompatibleThreadStateException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format( - "Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); + final String failureMessage = String.format("Failed to step because the thread '%s' is not suspended in the target VM.", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (IndexOutOfBoundsException ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format( - "Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); - throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); - } catch (AbsentInformationException ex) { - // Roll back the Exception info if stepping fails. - context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format( - "Failed to step because the thread '%s' doesn't contain required line information in stack frame", - thread.name()); + final String failureMessage = String.format("Failed to step because the thread '%s' doesn't contain any stack frame", thread.name()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex); } catch (Exception ex) { // Roll back the Exception info if stepping fails. context.getExceptionManager().setException(threadId, exception); - final String failureMessage = String.format("Failed to step because of the error '%s'", - ex.getMessage()); + final String failureMessage = String.format("Failed to step because of the error '%s'", ex.getMessage()); throw AdapterUtils.createCompletionException( - failureMessage, - ErrorCode.STEP_FAILURE, - ex.getCause() != null ? ex.getCause() : ex); + failureMessage, + ErrorCode.STEP_FAILURE, + ex.getCause() != null ? ex.getCause() : ex); } } return CompletableFuture.completedFuture(response); } - private Location resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) + private LocationResponse resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) throws IncompatibleThreadStateException, AbsentInformationException { if (targetId > 0) { Object value = context.getRecyclableIdPool().getObjectById(targetId); if (value instanceof MethodInvocation) { - MethodInvocation invocation = (MethodInvocation) value; - VirtualMachine vm = thread.virtualMachine(); - List refTypes = vm.classesByName(invocation.declaringTypeName); - for (ReferenceType referenceType : refTypes) { - Optional location = referenceType.allLineLocations().stream() - .filter(l -> matchesLocation(l, invocation)) - .findFirst(); - if (location.isPresent()) { - return location.get(); - } + return resolveLocation((MethodInvocation) value, thread); + } + } + return LocationResponse.absolute(getTopFrame(thread).location()); + } + + private LocationResponse resolveLocation(MethodInvocation invocation, ThreadReference thread) + throws AbsentInformationException, IncompatibleThreadStateException { + VirtualMachine vm = thread.virtualMachine(); + List refTypes = vm.classesByName(invocation.declaringTypeName); + + // if class is not yet loaded try to make the debugger step in until class is + // loaded. + if (refTypes.isEmpty()) { + return LocationResponse.waitFor(invocation, getTopFrame(thread).location()); + } else { + for (ReferenceType referenceType : refTypes) { + Optional location = referenceType.allLineLocations().stream() + .filter(l -> matchesLocation(l, invocation)) + .findFirst(); + if (location.isPresent()) { + return LocationResponse.absolute(location.get()); } } + return LocationResponse.absolute(getTopFrame(thread).location()); } - return getTopFrame(thread).location(); } private boolean matchesLocation(Location l, MethodInvocation invocation) { Method method = l.method(); - return method != null && Objects.equals(method.name(), invocation.methodName) + return method != null && Objects.equals(fixedName(method), invocation.methodName) && Objects.equals(method.signature(), invocation.methodSignature); } + /* + * Fix the name so for constructors we return empty to match the captured AST + * invocations. + */ + private String fixedName(Method method) { + String name = method.name(); + if ("".equals(name)) { + return ""; + } + return name; + } + private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; EventRequestManager eventRequestManager = debugSession.getVM().eventRequestManager(); - // When a breakpoint occurs, abort any pending step requests from the same - // thread. + // When a breakpoint occurs, abort any pending step requests from the same thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { + // if we have a pending target step in then ignore and continue. + if (threadState.pendingTargetStepIn != null) { + debugEvent.shouldResume = true; + return; + } + long threadId = ((LocatableEvent) event).thread().uniqueID(); if (threadId == threadState.threadId && threadState.pendingStepRequest != null) { threadState.deleteStepRequest(eventRequestManager); @@ -291,21 +305,35 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.pendingStepType == Command.STEPIN) { int currentStackDepth = thread.frameCount(); Location currentStepLocation = getTopFrame(thread).location(); - // If the ending step location is filtered, or same as the original location - // where the step into operation is originated, + + // if we are in targetStepIn where the location was pending, then try to resolve + // the location and use it. + if (threadState.pendingTargetStepIn != null) { + LocationResponse newLocation = resolveLocation(threadState.pendingTargetStepIn, thread); + if (!newLocation.isLocationPending()) { + threadState.stepLocation = newLocation.location; + threadState.pendingTargetStepIn = null; + } + } + + // If the ending step location is filtered, or same as the original location where the step into operation is originated, // do another step of the same kind. if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, - currentStackDepth, currentStepLocation, threadState.targetStepIn)) { + currentStackDepth, currentStepLocation, threadState.targetStepIn) + || threadState.pendingTargetStepIn != null) { + String[] allowedClasses = threadState.pendingTargetStepIn != null + ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} + : context.getStepFilters().allowClasses; threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - context.getStepFilters().allowClasses, - context.getStepFilters().skipClasses); + allowedClasses, + context.getStepFilters().skipClasses); threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; } } - } catch (IncompatibleThreadStateException | IndexOutOfBoundsException ex) { + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | AbsentInformationException ex) { // ignore. } } @@ -319,8 +347,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (event instanceof MethodExitEvent) { MethodExitEvent methodExitEvent = (MethodExitEvent) event; long threadId = methodExitEvent.thread().uniqueID(); - if (threadId == threadState.threadId - && methodExitEvent.method().equals(threadState.stepLocation.method())) { + if (threadId == threadState.threadId && methodExitEvent.method().equals(threadState.stepLocation.method())) { Value returnValue = methodExitEvent.returnValue(); if (returnValue instanceof VoidValue) { context.getStepResultManager().removeMethodResult(threadId); @@ -338,26 +365,22 @@ private boolean isStepFiltersConfigured(StepFilters filters) { return false; } return ArrayUtils.isNotEmpty(filters.allowClasses) || ArrayUtils.isNotEmpty(filters.skipClasses) - || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors - || filters.skipStaticInitializers || filters.skipSynthetics; + || ArrayUtils.isNotEmpty(filters.classNameFilters) || filters.skipConstructors + || filters.skipStaticInitializers || filters.skipSynthetics; } /** - * Return true if the StepEvent's location is a Method that the user has - * indicated to filter. + * Return true if the StepEvent's location is a Method that the user has indicated to filter. * * @throws IncompatibleThreadStateException - * if the thread is not suspended in - * the target VM. + * if the thread is not suspended in the target VM. */ - private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, - IDebugAdapterContext context) + private boolean shouldFilterLocation(Location originalLocation, Location currentLocation, IDebugAdapterContext context) throws IncompatibleThreadStateException { if (originalLocation == null || currentLocation == null) { return false; } - return !shouldFilterMethod(originalLocation.method(), context) - && shouldFilterMethod(currentLocation.method(), context); + return !shouldFilterMethod(originalLocation.method(), context) && shouldFilterMethod(currentLocation.method(), context); } private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) { @@ -387,7 +410,6 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL return false; } } - Method originalMethod = originalLocation.method(); Method currentMethod = currentLocation.method(); if (!originalMethod.equals(currentMethod)) { @@ -403,14 +425,12 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL * Return the top stack frame of the target thread. * * @param thread - * the target thread. + * the target thread. * @return the top frame. * @throws IncompatibleThreadStateException - * if the thread is not suspended in - * the target VM. + * if the thread is not suspended in the target VM. * @throws IndexOutOfBoundsException - * if the thread doesn't contain any - * stack frame. + * if the thread doesn't contain any stack frame. */ private StackFrame getTopFrame(ThreadReference thread) throws IncompatibleThreadStateException { return thread.frame(0); @@ -426,6 +446,7 @@ class ThreadState { Location stepLocation = null; Disposable eventSubscription = null; boolean targetStepIn = false; + MethodInvocation pendingTargetStepIn; public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); @@ -437,4 +458,27 @@ public void deleteStepRequest(EventRequestManager manager) { this.pendingStepRequest = null; } } + + static class LocationResponse { + Location location; + MethodInvocation methodInvocation; + + private LocationResponse(Location location, MethodInvocation methodInvocation) { + this.location = location; + this.methodInvocation = methodInvocation; + } + + public static LocationResponse waitFor(MethodInvocation invocation, Location location) { + return new LocationResponse(location, invocation); + } + + public static LocationResponse absolute(Location location) { + return new LocationResponse(location, null); + } + + boolean isLocationPending() { + return methodInvocation != null; + } + + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java index e30b0dd30..a6535715a 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -34,7 +34,14 @@ private BindingUtils() { public static String getMethodName(IMethodBinding binding, boolean fromKey) { if (fromKey) { String key = binding.getKey(); - return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + int dotAt = key.indexOf('.'); + int end = key.indexOf('<', dotAt); + if (end == -1) { + end = key.indexOf('('); + } else { + end = Math.min(end, key.indexOf('(')); + } + return key.substring(dotAt + 1, end); } else { return binding.getName(); } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index eba9a42e6..eb3eb46f2 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; @@ -51,6 +52,7 @@ import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; @@ -487,13 +489,23 @@ public List findMethodInvocations(StackFrame frame) { invocation.expression = expression.toString(); IMethodBinding binding = entry.getValue(); invocation.methodName = binding.getName(); - invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + if (binding.getDeclaringClass().isAnonymous()) { + ITypeBinding superclass = binding.getDeclaringClass().getSuperclass(); + if (superclass != null + && !superclass.isEqualTo(astUnit.getAST().resolveWellKnownType("java.lang.Object"))) { + invocation.declaringTypeName = superclass.getQualifiedName(); + } else { + return null; + } + } else { + invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + } invocation.methodSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); invocation.lineStart = astUnit.getLineNumber(expression.getStartPosition()); invocation.lineEnd = astUnit.getLineNumber(expression.getStartPosition() + expression.getLength()); invocation.columnStart = astUnit.getColumnNumber(expression.getStartPosition()); invocation.columnEnd = astUnit.getColumnNumber(expression.getStartPosition() + expression.getLength()); return invocation; - }).collect(Collectors.toList()); + }).filter(Objects::nonNull).collect(Collectors.toList()); } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java index ab6efdaa2..f0364d98b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -14,6 +14,7 @@ import java.util.Map; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; @@ -25,7 +26,7 @@ public class MethodInvocationLocator extends ASTVisitor { private CompilationUnit unit; private Map targets; - private boolean collectMethodInvocations = false; + private int expressionCount = 0; public MethodInvocationLocator(int line, CompilationUnit unit) { super(false); @@ -40,24 +41,36 @@ public boolean visit(ExpressionStatement node) { int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); if (line >= start && line <= end) { - collectMethodInvocations = true; + expressionCount++; } - return collectMethodInvocations; + return expressionCount > 0; } @Override public boolean visit(MethodInvocation node) { - int lineNumber = unit.getLineNumber(node.getStartPosition()); - if (lineNumber == this.line) { + if (expressionCount > 0) { targets.put(node, node.resolveMethodBinding()); - return true; } - return false; + return expressionCount > 0; + } + + @Override + public boolean visit(ClassInstanceCreation node) { + if (expressionCount > 0) { + targets.put(node, node.resolveConstructorBinding()); + expressionCount--; + } + return expressionCount > 0; } @Override public void endVisit(ExpressionStatement node) { - collectMethodInvocations = false; + expressionCount--; + } + + @Override + public void endVisit(ClassInstanceCreation node) { + expressionCount++; } public Map getTargets() { From a6ca87a0572d55ac9c289ba5a5a0f16318883a44 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 25 Oct 2022 17:17:32 +0800 Subject: [PATCH 060/144] Enable stepInTarget feature on more use cases --- .../core/adapter/ISourceLookUpProvider.java | 27 +- .../handler/StackTraceRequestHandler.java | 8 +- .../handler/StepInTargetsRequestHandler.java | 75 ++++- .../adapter/handler/StepRequestHandler.java | 276 +++++++++--------- .../variables/StackFrameReference.java | 12 +- .../microsoft/java/debug/BindingUtils.java | 7 +- .../internal/JdtSourceLookUpProvider.java | 83 +++--- .../internal/MethodInvocationLocator.java | 199 +++++++++++-- 8 files changed, 459 insertions(+), 228 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java index 5e8d94451..ead10e33c 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java @@ -18,8 +18,6 @@ import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; -import com.sun.jdi.StackFrame; - public interface ISourceLookUpProvider extends IProvider { boolean supportsRealtimeBreakpointVerification(); @@ -70,17 +68,19 @@ default String getJavaRuntimeVersion(String projectName) { * Return method invocation found in the statement as the given line number of * the source file. * - * @param stackframe The stack frame where the invocation must be searched. + * @param uri The source file where the invocation must be searched. + * @param line The line number where the invocation must be searched. * * @return List of found method invocation or empty if not method invocations * can be found. */ - List findMethodInvocations(StackFrame stackframe); + List findMethodInvocations(String uri, int line); public static class MethodInvocation { public String expression; public String methodName; public String methodSignature; + public String methodGenericSignature; public String declaringTypeName; public int lineStart; public int lineEnd; @@ -89,8 +89,8 @@ public static class MethodInvocation { @Override public int hashCode() { - return Objects.hash(columnEnd, columnStart, declaringTypeName, expression, lineEnd, lineStart, methodName, - methodSignature); + return Objects.hash(expression, methodName, methodSignature, methodGenericSignature, declaringTypeName, + lineStart, lineEnd, columnStart, columnEnd); } @Override @@ -98,18 +98,15 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (!(obj instanceof MethodInvocation)) { return false; } MethodInvocation other = (MethodInvocation) obj; - return columnEnd == other.columnEnd && columnStart == other.columnStart - && Objects.equals(declaringTypeName, other.declaringTypeName) - && Objects.equals(expression, other.expression) && lineEnd == other.lineEnd - && lineStart == other.lineStart && Objects.equals(methodName, other.methodName) - && Objects.equals(methodSignature, other.methodSignature); + return Objects.equals(expression, other.expression) && Objects.equals(methodName, other.methodName) + && Objects.equals(methodSignature, other.methodSignature) + && Objects.equals(methodGenericSignature, other.methodGenericSignature) + && Objects.equals(declaringTypeName, other.declaringTypeName) && lineStart == other.lineStart + && lineEnd == other.lineEnd && columnStart == other.columnStart && columnEnd == other.columnEnd; } } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index f22966b03..fbfce49ef 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -89,10 +89,12 @@ public CompletableFuture handle(Command command, Arguments arguments, StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count); List jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP()); for (int i = 0; i < count; i++) { - StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i); - int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe); + StackFrameReference frameReference = new StackFrameReference(thread, stacktraceArgs.startFrame + i); + int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, frameReference); StackFrameInfo jdiFrame = jdiFrames.get(i); - result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context)); + Types.StackFrame lspFrame = convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context); + result.add(lspFrame); + frameReference.setSource(lspFrame.source); } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java index f61d2a8b9..5b3b735fe 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepInTargetsRequestHandler.java @@ -10,13 +10,19 @@ *******************************************************************************/ package com.microsoft.java.debug.core.adapter.handler; +import java.io.File; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; @@ -27,10 +33,14 @@ import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.StepInTargetsArguments; import com.microsoft.java.debug.core.protocol.Responses.StepInTargetsResponse; +import com.microsoft.java.debug.core.protocol.Types.Source; import com.microsoft.java.debug.core.protocol.Types.StepInTarget; +import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; public class StepInTargetsRequestHandler implements IDebugRequestHandler { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); @Override public List getTargetCommands() { @@ -45,37 +55,84 @@ public CompletableFuture handle(Command command, Arguments arguments, final int frameId = stepInTargetsArguments.frameId; return CompletableFuture.supplyAsync(() -> { response.body = new StepInTargetsResponse( - findFrame(frameId, context).map(f -> findTargets(f.thread().uniqueID(), f, context)) + findFrame(frameId, context).map(f -> findTargets(f, context)) .orElse(Collections.emptyList()).toArray(StepInTarget[]::new)); return response; }); } - private Optional findFrame(int frameId, IDebugAdapterContext context) { + private Optional findFrame(int frameId, IDebugAdapterContext context) { Object object = context.getRecyclableIdPool().getObjectById(frameId); if (object instanceof StackFrameReference) { - return Optional.of(context.getStackFrameManager().getStackFrame((StackFrameReference) object)); + return Optional.of((StackFrameReference) object); } return Optional.empty(); } - private List findTargets(long threadId, StackFrame stackframe, IDebugAdapterContext context) { + private List findTargets(StackFrameReference frameReference, IDebugAdapterContext context) { + StackFrame stackframe = context.getStackFrameManager().getStackFrame(frameReference); + if (stackframe == null) { + return Collections.emptyList(); + } + + Source source = frameReference.getSource() == null ? findSource(stackframe, context) : frameReference.getSource(); + if (source == null) { + return Collections.emptyList(); + } + + String sourceUri = AdapterUtils.convertPath(source.path, AdapterUtils.isUri(source.path), true); + if (sourceUri == null) { + return Collections.emptyList(); + } + ISourceLookUpProvider sourceLookUpProvider = context.getProvider(ISourceLookUpProvider.class); - List invocations = sourceLookUpProvider.findMethodInvocations(stackframe); + List invocations = sourceLookUpProvider.findMethodInvocations(sourceUri, stackframe.location().lineNumber()); if (invocations.isEmpty()) { return Collections.emptyList(); } + long threadId = stackframe.thread().uniqueID(); List targets = new ArrayList<>(invocations.size()); for (MethodInvocation methodInvocation : invocations) { int id = context.getRecyclableIdPool().addObject(threadId, methodInvocation); StepInTarget target = new StepInTarget(id, methodInvocation.expression); - target.column = methodInvocation.columnStart; - target.endColumn = methodInvocation.columnEnd; - target.line = methodInvocation.lineStart; - target.endLine = methodInvocation.lineEnd; + target.column = AdapterUtils.convertColumnNumber(methodInvocation.columnStart, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + target.endColumn = AdapterUtils.convertColumnNumber(methodInvocation.columnEnd, + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + target.line = AdapterUtils.convertLineNumber(methodInvocation.lineStart, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + target.endLine = AdapterUtils.convertLineNumber(methodInvocation.lineEnd, + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); targets.add(target); } + + // TODO remove the executed method calls. return targets; } + + private Source findSource(StackFrame frame, IDebugAdapterContext context) { + ReferenceType declaringType = frame.location().declaringType(); + String typeName = declaringType.name(); + String sourceName = null; + String sourcePath = null; + try { + // When the .class file doesn't contain source information in meta data, + // invoking ReferenceType#sourceName() would throw AbsentInformationException. + sourceName = declaringType.sourceName(); + sourcePath = declaringType.sourcePaths(null).get(0); + } catch (AbsentInformationException e) { + String enclosingType = AdapterUtils.parseEnclosingType(typeName); + sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java"; + sourcePath = enclosingType.replace('.', File.separatorChar) + ".java"; + } + + try { + return StackTraceRequestHandler.convertDebuggerSourceToClient(typeName, sourceName, sourcePath, context); + } catch (URISyntaxException e) { + logger.log(Level.SEVERE, "Failed to resolve the source info of the stack frame.", e); + } + + return null; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 660d4bbdd..bd324a7fa 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -15,7 +15,6 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -39,8 +38,9 @@ import com.microsoft.java.debug.core.protocol.Requests.StepArguments; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; import com.microsoft.java.debug.core.protocol.Requests.StepInArguments; -import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.ClassType; import com.sun.jdi.IncompatibleThreadStateException; +import com.sun.jdi.InterfaceType; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; @@ -48,7 +48,6 @@ import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; -import com.sun.jdi.VirtualMachine; import com.sun.jdi.VoidValue; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; @@ -102,11 +101,8 @@ public CompletableFuture handle(Command command, Arguments arguments, }); if (command == Command.STEPIN) { - String[] allowedClasses = threadState.pendingTargetStepIn != null - ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} - : context.getStepFilters().allowClasses; threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, - allowedClasses, + context.getStepFilters().allowClasses, context.getStepFilters().skipClasses); } else if (command == Command.STEPOUT) { threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, @@ -119,26 +115,29 @@ public CompletableFuture handle(Command command, Arguments arguments, threadState.pendingMethodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest(); threadState.pendingMethodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - LocationResponse locationResponse = resolveLocation(thread, targetId, context); + threadState.targetStepIn = targetId > 0 + ? (MethodInvocation) context.getRecyclableIdPool().getObjectById(targetId) : null; if (context.asyncJDWP()) { List> futures = new ArrayList<>(); futures.add(AsyncJdwpUtils.runAsync(() -> { // JDWP Command: TR_FRAMES - threadState.stepLocation = locationResponse.location; - threadState.pendingTargetStepIn = locationResponse.methodInvocation; - threadState.targetStepIn = targetId > 0; - threadState.stepLocation = threadState.topFrame.location(); - threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); - if (targetThread.virtualMachine().canUseInstanceFilters()) { - try { - // JDWP Command: SF_THIS_OBJECT - ObjectReference thisObject = threadState.topFrame.thisObject(); - if (thisObject != null) { - threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + try { + threadState.topFrame = getTopFrame(targetThread); + threadState.stepLocation = threadState.topFrame.location(); + threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); + if (targetThread.virtualMachine().canUseInstanceFilters()) { + try { + // JDWP Command: SF_THIS_OBJECT + ObjectReference thisObject = threadState.topFrame.thisObject(); + if (thisObject != null) { + threadState.pendingMethodExitRequest.addInstanceFilter(thisObject); + } + } catch (Exception e) { + // ignore } - } catch (Exception e) { - // ignore } + } catch (IncompatibleThreadStateException e1) { + throw new CompletionException(e1); } })); futures.add(AsyncJdwpUtils.runAsync( @@ -170,10 +169,8 @@ public CompletableFuture handle(Command command, Arguments arguments, // JDWP Command: ER_SET threadState.pendingMethodExitRequest.enable(); } else { + threadState.topFrame = getTopFrame(targetThread); threadState.stackDepth = targetThread.frameCount(); - threadState.stepLocation = locationResponse.location; - threadState.pendingTargetStepIn = locationResponse.methodInvocation; - threadState.targetStepIn = targetId > 0; threadState.stepLocation = threadState.topFrame.location(); threadState.pendingMethodExitRequest.addThreadFilter(thread); threadState.pendingMethodExitRequest.addClassFilter(threadState.stepLocation.declaringType()); @@ -224,57 +221,6 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } - private LocationResponse resolveLocation(ThreadReference thread, int targetId, IDebugAdapterContext context) - throws IncompatibleThreadStateException, AbsentInformationException { - if (targetId > 0) { - Object value = context.getRecyclableIdPool().getObjectById(targetId); - if (value instanceof MethodInvocation) { - return resolveLocation((MethodInvocation) value, thread); - } - } - return LocationResponse.absolute(getTopFrame(thread).location()); - } - - private LocationResponse resolveLocation(MethodInvocation invocation, ThreadReference thread) - throws AbsentInformationException, IncompatibleThreadStateException { - VirtualMachine vm = thread.virtualMachine(); - List refTypes = vm.classesByName(invocation.declaringTypeName); - - // if class is not yet loaded try to make the debugger step in until class is - // loaded. - if (refTypes.isEmpty()) { - return LocationResponse.waitFor(invocation, getTopFrame(thread).location()); - } else { - for (ReferenceType referenceType : refTypes) { - Optional location = referenceType.allLineLocations().stream() - .filter(l -> matchesLocation(l, invocation)) - .findFirst(); - if (location.isPresent()) { - return LocationResponse.absolute(location.get()); - } - } - return LocationResponse.absolute(getTopFrame(thread).location()); - } - } - - private boolean matchesLocation(Location l, MethodInvocation invocation) { - Method method = l.method(); - return method != null && Objects.equals(fixedName(method), invocation.methodName) - && Objects.equals(method.signature(), invocation.methodSignature); - } - - /* - * Fix the name so for constructors we return empty to match the captured AST - * invocations. - */ - private String fixedName(Method method) { - String name = method.name(); - if ("".equals(name)) { - return ""; - } - return name; - } - private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, IDebugAdapterContext context, ThreadState threadState) { Event event = debugEvent.event; @@ -283,7 +229,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, // When a breakpoint occurs, abort any pending step requests from the same thread. if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) { // if we have a pending target step in then ignore and continue. - if (threadState.pendingTargetStepIn != null) { + if (threadState.targetStepIn != null) { debugEvent.shouldResume = true; return; } @@ -299,41 +245,62 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } } else if (event instanceof StepEvent) { ThreadReference thread = ((StepEvent) event).thread(); + long threadId = thread.uniqueID(); threadState.deleteStepRequest(eventRequestManager); - if (isStepFiltersConfigured(context.getStepFilters()) || threadState.targetStepIn) { + if (isStepFiltersConfigured(context.getStepFilters()) || threadState.targetStepIn != null) { try { - if (threadState.pendingStepType == Command.STEPIN) { + if (threadState.pendingStepType == Command.STEPIN || threadState.targetStepIn != null) { int currentStackDepth = thread.frameCount(); - Location currentStepLocation = getTopFrame(thread).location(); - - // if we are in targetStepIn where the location was pending, then try to resolve - // the location and use it. - if (threadState.pendingTargetStepIn != null) { - LocationResponse newLocation = resolveLocation(threadState.pendingTargetStepIn, thread); - if (!newLocation.isLocationPending()) { - threadState.stepLocation = newLocation.location; - threadState.pendingTargetStepIn = null; + StackFrame topFrame = getTopFrame(thread); + Location currentStepLocation = topFrame.location(); + if (threadState.targetStepIn != null) { + if (isStoppedAtSelectedMethod(topFrame, threadState.targetStepIn)) { + // hit: send StoppedEvent + } else { + if (currentStackDepth > threadState.stackDepth) { + context.getStepResultManager().removeMethodResult(threadId); + threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); + threadState.pendingStepRequest.enable(); + debugEvent.shouldResume = true; + return; + } else if (currentStackDepth == threadState.stackDepth) { + // If the ending step location is same as the original location where the step into operation is originated, + // do another step of the same kind. + if (isSameLocation(currentStepLocation, threadState.stepLocation)) { + context.getStepResultManager().removeMethodResult(threadId); + threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, + context.getStepFilters().allowClasses, + context.getStepFilters().skipClasses); + threadState.pendingStepRequest.enable(); + debugEvent.shouldResume = true; + return; + } + } } - } - - // If the ending step location is filtered, or same as the original location where the step into operation is originated, - // do another step of the same kind. - if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) + } else if (shouldFilterLocation(threadState.stepLocation, currentStepLocation, context) || shouldDoExtraStepInto(threadState.stackDepth, threadState.stepLocation, - currentStackDepth, currentStepLocation, threadState.targetStepIn) - || threadState.pendingTargetStepIn != null) { - String[] allowedClasses = threadState.pendingTargetStepIn != null - ? new String[]{threadState.pendingTargetStepIn.declaringTypeName} - : context.getStepFilters().allowClasses; - threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, + currentStackDepth, currentStepLocation)) { + // If the ending step location is filtered, or same as the original location where the step into operation is originated, + // do another step of the same kind. + context.getStepResultManager().removeMethodResult(threadId); + String[] allowedClasses = context.getStepFilters().allowClasses; + if (currentStackDepth > threadState.stackDepth) { + threadState.pendingStepRequest = DebugUtility.createStepOutRequest(thread, allowedClasses, - context.getStepFilters().skipClasses); + context.getStepFilters().skipClasses); + } else { + threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, + allowedClasses, + context.getStepFilters().skipClasses); + } threadState.pendingStepRequest.enable(); debugEvent.shouldResume = true; return; } } - } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | AbsentInformationException ex) { + } catch (IncompatibleThreadStateException | IndexOutOfBoundsException ex) { // ignore. } } @@ -360,6 +327,56 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } } + private boolean isStoppedAtSelectedMethod(StackFrame frame, MethodInvocation selectedMethod) { + Method method = frame.location().method(); + if (method != null + && Objects.equals(method.name(), selectedMethod.methodName) + && (Objects.equals(method.signature(), selectedMethod.methodSignature) + || Objects.equals(method.genericSignature(), selectedMethod.methodGenericSignature))) { + ObjectReference thisObject = frame.thisObject(); + ReferenceType currentType = (thisObject == null) ? method.declaringType() : thisObject.referenceType(); + if ("java.lang.Object".equals(selectedMethod.declaringTypeName)) { + return true; + } + + return isSubType(currentType, selectedMethod.declaringTypeName); + } + + return false; + } + + private boolean isSubType(ReferenceType currentType, String baseType) { + if (baseType.equals(currentType.name())) { + return true; + } + + if (currentType instanceof ClassType) { + ClassType classType = (ClassType) currentType; + ClassType superClassType = classType.superclass(); + if (superClassType != null && isSubType(superClassType, baseType)) { + return true; + } + + List interfaces = classType.allInterfaces(); + for (InterfaceType iface : interfaces) { + if (isSubType(iface, baseType)) { + return true; + } + } + } + + if (currentType instanceof InterfaceType) { + List superInterfaces = ((InterfaceType) currentType).superinterfaces(); + for (InterfaceType superInterface : superInterfaces) { + if (isSubType(superInterface, baseType)) { + return true; + } + } + } + + return false; + } + private boolean isStepFiltersConfigured(StepFilters filters) { if (filters == null) { return false; @@ -400,25 +417,36 @@ private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) * the target VM. */ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalLocation, int currentStackDepth, - Location currentLocation, boolean targetStepIn) + Location currentLocation) throws IncompatibleThreadStateException { - if (!targetStepIn) { - if (originalStackDepth != currentStackDepth) { - return false; - } - if (originalLocation == null) { - return false; - } + if (originalStackDepth != currentStackDepth) { + return false; + } + if (originalLocation == null) { + return false; } + Method originalMethod = originalLocation.method(); Method currentMethod = currentLocation.method(); if (!originalMethod.equals(currentMethod)) { - return targetStepIn; + return false; } if (originalLocation.lineNumber() != currentLocation.lineNumber()) { - return targetStepIn; + return false; } - return !targetStepIn; + + return true; + } + + private boolean isSameLocation(Location original, Location current) { + if (original == null || current == null) { + return false; + } + + Method originalMethod = original.method(); + Method currentMethod = current.method(); + return originalMethod.equals(currentMethod) + && original.lineNumber() == current.lineNumber(); } /** @@ -445,8 +473,7 @@ class ThreadState { StackFrame topFrame = null; Location stepLocation = null; Disposable eventSubscription = null; - boolean targetStepIn = false; - MethodInvocation pendingTargetStepIn; + MethodInvocation targetStepIn = null; public void deleteMethodExitRequest(EventRequestManager manager) { DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest); @@ -458,27 +485,4 @@ public void deleteStepRequest(EventRequestManager manager) { this.pendingStepRequest = null; } } - - static class LocationResponse { - Location location; - MethodInvocation methodInvocation; - - private LocationResponse(Location location, MethodInvocation methodInvocation) { - this.location = location; - this.methodInvocation = methodInvocation; - } - - public static LocationResponse waitFor(MethodInvocation invocation, Location location) { - return new LocationResponse(location, invocation); - } - - public static LocationResponse absolute(Location location) { - return new LocationResponse(location, null); - } - - boolean isLocationPending() { - return methodInvocation != null; - } - - } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java index 4dc895007..46726cde8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/StackFrameReference.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,12 +11,14 @@ package com.microsoft.java.debug.core.adapter.variables; +import com.microsoft.java.debug.core.protocol.Types.Source; import com.sun.jdi.ThreadReference; public class StackFrameReference { private final int depth; private final int hash; private final ThreadReference thread; + private Source source; /** * Create a wrapper of JDI stackframe to keep the immutable properties of a stackframe, IStackFrameManager will use @@ -48,6 +50,14 @@ public ThreadReference getThread() { return thread; } + public Source getSource() { + return source; + } + + public void setSource(Source source) { + this.source = source; + } + @Override public int hashCode() { return hash; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java index a6535715a..b696be049 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017-2020 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -61,12 +61,9 @@ public static String toSignature(IMethodBinding binding, String name) { // use key for now until JDT core provides a public API for this. // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" - if (!binding.getName().equals(name)) { - throw new IllegalArgumentException("The method name and binding method name doesn't match."); - } - String signatureString = binding.getKey(); if (signatureString != null) { + name = "." + name; int index = signatureString.indexOf(name); if (index > -1) { int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index eb3eb46f2..c4d4fe318 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -15,7 +15,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; @@ -35,28 +34,31 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.URIUtil; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; -import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.LambdaExpression; -import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import org.eclipse.jdt.ls.core.internal.JDTUtils; import com.microsoft.java.debug.BindingUtils; import com.microsoft.java.debug.BreakpointLocationLocator; @@ -71,8 +73,6 @@ import com.microsoft.java.debug.core.protocol.Types.BreakpointLocation; import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint; -import com.sun.jdi.StackFrame; - public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; @@ -452,60 +452,71 @@ private static String resolveSystemLibraryVersion(IJavaProject project, IVMInsta return null; } - @Override - public List findMethodInvocations(StackFrame frame) { - if (frame == null) { - throw new IllegalArgumentException("frame is null"); - } - - IJavaProject project = JdtUtils.findProject(frame, getSourceContainers()); - if (project == null) { - logger.log(Level.WARNING, - String.format("Failed to resolve project for the frame: %s", frame)); + public List findMethodInvocations(String uri, int line) { + if (uri == null) { return Collections.emptyList(); } - String uri; - try { - IType type = project.findType(JdtUtils.getDeclaringTypeName(frame)); - uri = type.getResource().getLocationURI().toURL().toString(); - } catch (JavaModelException | DebugException | MalformedURLException e) { - logger.log(Level.SEVERE, - String.format("Failed to resolve type for the frame: %s", frame)); - return Collections.emptyList(); + boolean useCache = false; + CompilationUnit cachedUnit = CoreASTProvider.getInstance().getCachedAST(); + if (cachedUnit != null) { + ITypeRoot cachedElement = cachedUnit.getTypeRoot(); + if (cachedElement != null && isSameURI(JDTUtils.toUri(cachedElement), uri)) { + useCache = true; + } } - CompilationUnit astUnit = asCompilationUnit(uri); + final CompilationUnit astUnit = useCache ? cachedUnit : asCompilationUnit(uri); if (astUnit == null) { return Collections.emptyList(); } - MethodInvocationLocator locator = new MethodInvocationLocator(frame.location().lineNumber(), astUnit); + MethodInvocationLocator locator = new MethodInvocationLocator(line, astUnit); astUnit.accept(locator); return locator.getTargets().entrySet().stream().map(entry -> { MethodInvocation invocation = new MethodInvocation(); - Expression expression = entry.getKey(); - invocation.expression = expression.toString(); - IMethodBinding binding = entry.getValue(); - invocation.methodName = binding.getName(); + ASTNode astNode = entry.getKey(); + invocation.expression = astNode.toString(); + IMethodBinding binding = entry.getValue().getMethodDeclaration(); + invocation.methodName = binding.isConstructor() ? "" : binding.getName(); if (binding.getDeclaringClass().isAnonymous()) { ITypeBinding superclass = binding.getDeclaringClass().getSuperclass(); if (superclass != null && !superclass.isEqualTo(astUnit.getAST().resolveWellKnownType("java.lang.Object"))) { - invocation.declaringTypeName = superclass.getQualifiedName(); + invocation.declaringTypeName = superclass.getBinaryName(); } else { return null; } } else { - invocation.declaringTypeName = binding.getDeclaringClass().getQualifiedName(); + // Keep consistent with JDI since JDI uses binary class name + invocation.declaringTypeName = binding.getDeclaringClass().getBinaryName(); } - invocation.methodSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); - invocation.lineStart = astUnit.getLineNumber(expression.getStartPosition()); - invocation.lineEnd = astUnit.getLineNumber(expression.getStartPosition() + expression.getLength()); - invocation.columnStart = astUnit.getColumnNumber(expression.getStartPosition()); - invocation.columnEnd = astUnit.getColumnNumber(expression.getStartPosition() + expression.getLength()); + invocation.methodGenericSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); + invocation.methodSignature = Signature.getTypeErasure(invocation.methodGenericSignature); + int startOffset = astNode.getStartPosition(); + if (astNode instanceof org.eclipse.jdt.core.dom.MethodInvocation) { + // The range covered by the stepIn target should start with the method name. + startOffset = ((org.eclipse.jdt.core.dom.MethodInvocation) astNode).getName().getStartPosition(); + } + invocation.lineStart = astUnit.getLineNumber(startOffset); + invocation.columnStart = astUnit.getColumnNumber(startOffset); + int endOffset = astNode.getStartPosition() + astNode.getLength(); + invocation.lineEnd = astUnit.getLineNumber(endOffset); + invocation.columnEnd = astUnit.getColumnNumber(endOffset); return invocation; }).filter(Objects::nonNull).collect(Collectors.toList()); } + + private boolean isSameURI(String uri1, String uri2) { + if (Objects.equals(uri1, uri2)) { + return true; + } + + try { + return URIUtil.sameURI(new URI(uri1), new URI(uri2)); + } catch (URISyntaxException e) { + return false; + } + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java index f0364d98b..a25fbc3f6 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -13,20 +13,44 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; +import org.eclipse.jdt.core.dom.AssertStatement; +import org.eclipse.jdt.core.dom.BreakStatement; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ConstructorInvocation; +import org.eclipse.jdt.core.dom.ContinueStatement; +import org.eclipse.jdt.core.dom.DoStatement; +import org.eclipse.jdt.core.dom.EmptyStatement; +import org.eclipse.jdt.core.dom.EnhancedForStatement; +import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.IfStatement; +import org.eclipse.jdt.core.dom.LabeledStatement; +import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.ReturnStatement; +import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.SwitchStatement; +import org.eclipse.jdt.core.dom.SynchronizedStatement; +import org.eclipse.jdt.core.dom.ThrowStatement; +import org.eclipse.jdt.core.dom.TryStatement; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.TypeDeclarationStatement; +import org.eclipse.jdt.core.dom.VariableDeclarationStatement; +import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.dom.YieldStatement; public class MethodInvocationLocator extends ASTVisitor { private int line; private CompilationUnit unit; - private Map targets; - - private int expressionCount = 0; + private Map targets; public MethodInvocationLocator(int line, CompilationUnit unit) { super(false); @@ -35,45 +59,174 @@ public MethodInvocationLocator(int line, CompilationUnit unit) { this.targets = new HashMap<>(); } + @Override + public boolean visit(FieldDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(MethodDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(TypeDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(EnumDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(VariableDeclarationStatement node) { + return shouldVisitNode(node); + } + @Override public boolean visit(ExpressionStatement node) { - int start = unit.getLineNumber(node.getStartPosition()); - int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + return shouldVisitNode(node); + } + + @Override + public boolean visit(AssertStatement node) { + return shouldVisitNode(node); - if (line >= start && line <= end) { - expressionCount++; - } - return expressionCount > 0; } @Override - public boolean visit(MethodInvocation node) { - if (expressionCount > 0) { - targets.put(node, node.resolveMethodBinding()); + public boolean visit(BreakStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ContinueStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(DoStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(EmptyStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(EnhancedForStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ForStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(IfStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(LabeledStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ReturnStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(SwitchStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(SynchronizedStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ThrowStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(TryStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(TypeDeclarationStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(WhileStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(YieldStatement node) { + return shouldVisitNode(node); + } + + @Override + public boolean visit(ConstructorInvocation node) { + if (shouldVisitNode(node)) { + targets.put(node, node.resolveConstructorBinding()); + return true; } - return expressionCount > 0; + return false; } @Override - public boolean visit(ClassInstanceCreation node) { - if (expressionCount > 0) { + public boolean visit(SuperConstructorInvocation node) { + if (shouldVisitNode(node)) { targets.put(node, node.resolveConstructorBinding()); - expressionCount--; + return true; } - return expressionCount > 0; + return false; } @Override - public void endVisit(ExpressionStatement node) { - expressionCount--; + public boolean visit(MethodInvocation node) { + targets.put(node, node.resolveMethodBinding()); + return true; } @Override - public void endVisit(ClassInstanceCreation node) { - expressionCount++; + public boolean visit(ClassInstanceCreation node) { + targets.put(node, node.resolveConstructorBinding()); + return true; + } + + private boolean shouldVisitNode(ASTNode node) { + int start = unit.getLineNumber(node.getStartPosition()); + int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + + if (line >= start && line <= end) { + return true; + } + + return false; } - public Map getTargets() { + public Map getTargets() { return targets; } } From ec39583b4b5ae79c0c220e4300ede4be5b87c928 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 31 Oct 2022 14:56:22 +0800 Subject: [PATCH 061/144] Conditional Breakpoint got error code in reply:504 (#453) --- .../debug/core/adapter/IStackFrameManager.java | 16 +++++++++++++++- .../debug/core/adapter/StackFrameManager.java | 16 +++++++++++++++- .../adapter/handler/ThreadsRequestHandler.java | 4 ++++ .../internal/eval/JdtEvaluationProvider.java | 2 +- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java index abce2ff3e..8d6c74486 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Microsoft Corporation and others. + * Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -32,6 +32,15 @@ public interface IStackFrameManager { */ StackFrame[] reloadStackFrames(ThreadReference thread); + /** + * Refresh all stackframes from jdi thread. + * + * @param thread the jdi thread + * @param force Whether to load the whole frames if the thread's stackframes haven't been cached. + * @return all the stackframes in the specified thread + */ + StackFrame[] reloadStackFrames(ThreadReference thread, boolean force); + /** * Refersh the stackframes starting from the specified depth and length. * @@ -48,4 +57,9 @@ public interface IStackFrameManager { * @param thread the jdi thread */ void clearStackFrames(ThreadReference thread); + + /** + * Clear the whole stackframes cache. + */ + void clearStackFrames(); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java index 2a9d1e47e..518cc7761 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java @@ -32,10 +32,19 @@ public synchronized StackFrame getStackFrame(StackFrameReference ref) { @Override public synchronized StackFrame[] reloadStackFrames(ThreadReference thread) { + return reloadStackFrames(thread, true); + } + + @Override + public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, boolean force) { return threadStackFrameMap.compute(thread.uniqueID(), (key, old) -> { try { if (old == null || old.length == 0) { - return thread.frames().toArray(new StackFrame[0]); + if (force) { + return thread.frames().toArray(new StackFrame[0]); + } else { + return new StackFrame[0]; + } } else { return thread.frames(0, old.length).toArray(new StackFrame[0]); } @@ -71,4 +80,9 @@ public synchronized StackFrame[] reloadStackFrames(ThreadReference thread, int s public synchronized void clearStackFrames(ThreadReference thread) { threadStackFrameMap.remove(thread.uniqueID()); } + + @Override + public synchronized void clearStackFrames() { + threadStackFrameMap.clear(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index 7d404691c..fceac73a5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -159,11 +159,13 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; DebugUtility.resumeThread(thread); + context.getStackFrameManager().clearStackFrames(thread); checkThreadRunningAndRecycleIds(thread, context); } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); resumeVM(context); + context.getStackFrameManager().clearStackFrames(); context.getRecyclableIdPool().removeAllObjects(); } response.body = new Responses.ContinueResponseBody(allThreadsContinued); @@ -175,6 +177,7 @@ private CompletableFuture resumeAll(Requests.ThreadOperationArguments context.getExceptionManager().removeAllExceptions(); resumeVM(context); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); + context.getStackFrameManager().clearStackFrames(); context.getRecyclableIdPool().removeAllObjects(); return CompletableFuture.completedFuture(response); } @@ -280,6 +283,7 @@ private void resumeThread(ThreadReference thread, IDebugAdapterContext context) context.getExceptionManager().removeException(threadId); DebugUtility.resumeThread(thread, suspends); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(threadId)); + context.getStackFrameManager().clearStackFrames(thread); checkThreadRunningAndRecycleIds(thread, context); } } catch (ObjectCollectedException ex) { diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index d4fac9054..a48cf7ead 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -317,7 +317,7 @@ private JDIThread getMockJDIThread(ThreadReference thread) { @Override protected synchronized void invokeComplete(int restoreTimeout) { super.invokeComplete(restoreTimeout); - context.getStackFrameManager().reloadStackFrames(thread); + context.getStackFrameManager().reloadStackFrames(thread, false); } }); } From fbf15977219a45c2e0f3c3317a5dd422f627a253 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 31 Oct 2022 17:26:56 +0800 Subject: [PATCH 062/144] Bump version to 0.42.0 (#454) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 8bb9b9f47..d9fb24c2e 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.41.0 + 0.42.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 21e32bbde..0858d90eb 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.41.0 +Bundle-Version: 0.42.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.41.0.jar + lib/com.microsoft.java.debug.core-0.42.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 949512717..c1b4144fa 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.41.0 + 0.42.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.41.0 + 0.42.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 27c76516b..ff39c1d7f 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index b8524fa90..c5a1f46a0 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.41.0 + 0.42.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 4d31432d9..2adb8badf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.41.0 + 0.42.0 pom Java Debug Server for Visual Studio Code From 4b773bd9b1891dbadd93a0e995656b575c80213a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 8 Nov 2022 18:29:58 +0800 Subject: [PATCH 063/144] Fix version info in .classpath (#456) --- com.microsoft.java.debug.plugin/.classpath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 349cb7844..43f07f603 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + From de77ff37cb5e3a9301e4dc83ae47af211bd59be5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 15 Nov 2022 10:29:11 +0800 Subject: [PATCH 064/144] "shortenCommandLine": "argfile|auto" should include "vmArgs" (#457) --- .../debug/core/adapter/handler/LaunchRequestHandler.java | 4 ++-- .../java/debug/core/adapter/handler/LaunchUtils.java | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index e5b580f50..e99706bda 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -134,8 +134,8 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { - Path tempfile = LaunchUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); - launchArguments.vmArgs += " \"@" + tempfile.toAbsolutePath().toString() + "\""; + Path tempfile = LaunchUtils.generateArgfile(launchArguments.vmArgs, launchArguments.classPaths, launchArguments.modulePaths); + launchArguments.vmArgs = " \"@" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; launchArguments.modulePaths = new String[0]; context.setArgsfile(tempfile); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index ca7d69a99..3be6fcbac 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -40,6 +40,7 @@ import com.microsoft.java.debug.core.adapter.AdapterUtils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; public class LaunchUtils { @@ -81,10 +82,14 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws * @return the file path of the generated argfile * @throws IOException Some errors occur during generating the argfile */ - public static synchronized Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { + public static synchronized Path generateArgfile(String vmArgs, String[] classPaths, String[] modulePaths) throws IOException { String argfile = ""; + if (StringUtils.isNotBlank(vmArgs)) { + argfile += vmArgs; + } + if (ArrayUtils.isNotEmpty(classPaths)) { - argfile = "-cp \"" + String.join(File.pathSeparator, classPaths) + "\""; + argfile += " -cp \"" + String.join(File.pathSeparator, classPaths) + "\""; } if (ArrayUtils.isNotEmpty(modulePaths)) { From 791cd97260f204e2cb851e79287881f6d634c1b4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 16 Nov 2022 17:03:13 +0800 Subject: [PATCH 065/144] Use the system encoding to generate the *.argfile (#458) --- .../adapter/handler/LaunchRequestHandler.java | 52 ++++++++++++++++--- .../core/adapter/handler/LaunchUtils.java | 40 ++++++++++++-- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index e99706bda..b1daf1b01 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018-2021 Microsoft Corporation and others. +* Copyright (c) 2018-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,6 +16,7 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -134,11 +135,50 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { - Path tempfile = LaunchUtils.generateArgfile(launchArguments.vmArgs, launchArguments.classPaths, launchArguments.modulePaths); - launchArguments.vmArgs = " \"@" + tempfile.toAbsolutePath().toString() + "\""; - launchArguments.classPaths = new String[0]; - launchArguments.modulePaths = new String[0]; - context.setArgsfile(tempfile); + /** + * See the JDK spec https://docs.oracle.com/en/java/javase/18/docs/specs/man/java.html#java-command-line-argument-files. + * The argument file must contain only ASCII characters or characters in system default encoding that's ASCII friendly. + */ + Charset systemCharset = LaunchUtils.getSystemCharset(); + CharsetEncoder encoder = systemCharset.newEncoder(); + String vmArgsForShorten = null; + String[] classPathsForShorten = null; + String[] modulePathsForShorten = null; + if (StringUtils.isNotBlank(launchArguments.vmArgs)) { + if (!encoder.canEncode(launchArguments.vmArgs)) { + logger.warning(String.format("Cannot generate the 'vmArgs' argument into the argfile because it contains characters " + + "that cannot be encoded in the system charset '%s'.", systemCharset.displayName())); + } else { + vmArgsForShorten = launchArguments.vmArgs; + } + } + + if (ArrayUtils.isNotEmpty(launchArguments.classPaths)) { + if (!encoder.canEncode(String.join(File.pathSeparator, launchArguments.classPaths))) { + logger.warning(String.format("Cannot generate the '-cp' argument into the argfile because it contains characters " + + "that cannot be encoded in the system charset '%s'.", systemCharset.displayName())); + } else { + classPathsForShorten = launchArguments.classPaths; + } + } + + if (ArrayUtils.isNotEmpty(launchArguments.modulePaths)) { + if (!encoder.canEncode(String.join(File.pathSeparator, launchArguments.modulePaths))) { + logger.warning(String.format("Cannot generate the '--module-path' argument into the argfile because it contains characters " + + "that cannot be encoded in the system charset '%s'.", systemCharset.displayName())); + } else { + modulePathsForShorten = launchArguments.modulePaths; + } + } + + if (vmArgsForShorten != null || classPathsForShorten != null || modulePathsForShorten != null) { + Path tempfile = LaunchUtils.generateArgfile(vmArgsForShorten, classPathsForShorten, modulePathsForShorten, systemCharset); + launchArguments.vmArgs = (vmArgsForShorten == null ? launchArguments.vmArgs : "") + + " \"@" + tempfile.toAbsolutePath().toString() + "\""; + launchArguments.classPaths = (classPathsForShorten == null ? launchArguments.classPaths : new String[0]); + launchArguments.modulePaths = (modulePathsForShorten == null ? launchArguments.modulePaths : new String[0]); + context.setArgsfile(tempfile); + } } catch (IOException e) { logger.log(Level.SEVERE, String.format("Failed to create a temp argfile: %s", e.toString()), e); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 3be6fcbac..27fdb1813 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.math.BigInteger; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -36,16 +37,45 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -import com.microsoft.java.debug.core.Configuration; -import com.microsoft.java.debug.core.adapter.AdapterUtils; - import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; +import com.microsoft.java.debug.core.Configuration; +import com.microsoft.java.debug.core.adapter.AdapterUtils; + public class LaunchUtils { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static Set tempFilesInUse = new HashSet<>(); + private static final Charset SYSTEM_CHARSET; + + static { + Charset result = null; + try { + // JEP 400: Java 17+ populates this system property. + String encoding = System.getProperty("native.encoding"); //$NON-NLS-1$ + if (encoding != null && !encoding.isBlank()) { + result = Charset.forName(encoding); + } else { + // JVM internal property, works on older JVM's too + encoding = System.getProperty("sun.jnu.encoding"); //$NON-NLS-1$ + if (encoding != null && !encoding.isBlank()) { + result = Charset.forName(encoding); + } + } + } catch (Exception e) { + logger.log(Level.SEVERE, "Error occurs during resolving system encoding", e); + } + if (result == null) { + // This is always UTF-8 on Java >= 18. + result = Charset.defaultCharset(); + } + SYSTEM_CHARSET = result; + } + + public static Charset getSystemCharset() { + return SYSTEM_CHARSET; + } /** * Generate the classpath parameters to a temporary classpath.jar. @@ -82,7 +112,7 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws * @return the file path of the generated argfile * @throws IOException Some errors occur during generating the argfile */ - public static synchronized Path generateArgfile(String vmArgs, String[] classPaths, String[] modulePaths) throws IOException { + public static synchronized Path generateArgfile(String vmArgs, String[] classPaths, String[] modulePaths, Charset encoding) throws IOException { String argfile = ""; if (StringUtils.isNotBlank(vmArgs)) { argfile += vmArgs; @@ -100,7 +130,7 @@ public static synchronized Path generateArgfile(String vmArgs, String[] classPat String baseName = "cp_" + getMd5(argfile); cleanupTempFiles(baseName, ".argfile"); Path tempfile = createTempFile(baseName, ".argfile"); - Files.write(tempfile, argfile.getBytes()); + Files.writeString(tempfile, argfile, encoding); lockTempLaunchFile(tempfile); return tempfile; From 4384d854bc60a9fffd95b90131f0ca1e98f79cfe Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 21 Nov 2022 10:08:17 +0800 Subject: [PATCH 066/144] Only pop-up build errors notification when the errors are on the running project's classpath (#459) --- .../java/debug/plugin/internal/Compile.java | 145 ++++++++++++++++++ .../JavaDebugDelegateCommandHandler.java | 10 +- 2 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java new file mode 100644 index 000000000..415456ce5 --- /dev/null +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2022 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package com.microsoft.java.debug.plugin.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; +import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; + +import com.microsoft.java.debug.core.Configuration; + +public class Compile { + private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); + + public static BuildWorkspaceStatus compile(CompileParams params, IProgressMonitor monitor) { + try { + if (monitor.isCanceled()) { + return BuildWorkspaceStatus.CANCELLED; + } + + long compileAt = System.currentTimeMillis(); + if (params != null && params.isFullBuild()) { + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor); + } else { + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor); + } + logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); + + IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); + IResource currentResource = mainProject; + if (isUnmanagedFolder(mainProject) && StringUtils.isNotBlank(params.getMainClass())) { + IType mainType = ProjectUtils.getJavaProject(mainProject).findType(params.getMainClass()); + if (mainType != null && mainType.getResource() != null) { + currentResource = mainType.getResource(); + } + } + + List problemMarkers = new ArrayList<>(); + if (currentResource != null) { + List markers = ResourceUtils.getErrorMarkers(currentResource); + if (markers != null) { + problemMarkers.addAll(markers); + } + + // Check if the referenced projects contain compilation errors. + if (currentResource instanceof IProject && ProjectUtils.isJavaProject((IProject) currentResource)) { + IJavaProject currentJavaProject = ProjectUtils.getJavaProject((IProject) currentResource); + IJavaProject[] javaProjects = ProjectUtils.getJavaProjects(); + for (IJavaProject otherJavaProject : javaProjects) { + IProject other = otherJavaProject.getProject(); + if (!other.equals(getDefaultProject()) && !other.equals((IProject) currentResource) + && currentJavaProject.isOnClasspath(otherJavaProject)) { + markers = ResourceUtils.getErrorMarkers(other); + if (markers != null) { + problemMarkers.addAll(markers); + } + } + } + } + } else { + IJavaProject[] javaProjects = ProjectUtils.getJavaProjects(); + for (IJavaProject javaProject : javaProjects) { + IProject project = javaProject.getProject(); + if (!project.equals(getDefaultProject())) { + List markers = ResourceUtils.getErrorMarkers(project); + if (markers != null) { + problemMarkers.addAll(markers); + } + } + } + } + + if (problemMarkers.isEmpty()) { + return BuildWorkspaceStatus.SUCCEED; + } + + return BuildWorkspaceStatus.WITH_ERROR; + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Failed to build workspace.", e); + return BuildWorkspaceStatus.FAILED; + } catch (OperationCanceledException e) { + return BuildWorkspaceStatus.CANCELLED; + } + } + + private static boolean isUnmanagedFolder(IProject project) { + return project != null && ProjectUtils.isUnmanagedFolder(project) + && ProjectUtils.isJavaProject(project); + } + + private static IProject getDefaultProject() { + return getWorkspaceRoot().getProject(ProjectsManager.DEFAULT_PROJECT_NAME); + } + + private static IWorkspaceRoot getWorkspaceRoot() { + return ResourcesPlugin.getWorkspace().getRoot(); + } + + class CompileParams { + String projectName; + String mainClass; + boolean isFullBuild = false; + + public String getMainClass() { + return mainClass; + } + + public boolean isFullBuild() { + return isFullBuild; + } + + public String getProjectName() { + return projectName; + } + } +} diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java index 1875d514e..f4e7b0ba3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -64,8 +64,12 @@ public Object executeCommand(String commandId, List arguments, IProgress ResolveMainClassHandler resolveMainClassHandler = new ResolveMainClassHandler(); return resolveMainClassHandler.resolveMainClass(arguments); case BUILD_WORKSPACE: - // TODO - break; + Compile.CompileParams params = null; + if (arguments != null && !arguments.isEmpty()) { + params = JsonUtils.fromJson((String) arguments.get(0), Compile.CompileParams.class); + } + + return Compile.compile(params, progress); case FETCH_USER_DATA: return UsageDataStore.getInstance().fetchAll(); case UPDATE_DEBUG_SETTINGS: From 42f9ac7d4843b75b1fdde64762d8c5ce4a4c51b2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Nov 2022 16:14:19 +0800 Subject: [PATCH 067/144] Bump version to 0.43.0 (#461) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index d9fb24c2e..6d4f847a0 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.42.0 + 0.43.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 43f07f603..505cc54c2 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 0858d90eb..949581277 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.42.0 +Bundle-Version: 0.43.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.42.0.jar + lib/com.microsoft.java.debug.core-0.43.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index c1b4144fa..98bf36ba0 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.42.0 + 0.43.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.42.0 + 0.43.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index ff39c1d7f..f2141039b 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index c5a1f46a0..3670a20f1 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.42.0 + 0.43.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index 2adb8badf..e75506938 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.42.0 + 0.43.0 pom Java Debug Server for Visual Studio Code From e0f2bd29a87eececabb2726f08d4d6d5479eb15b Mon Sep 17 00:00:00 2001 From: mozhuanzuojing <63572041+mozhuanzuojing@users.noreply.github.com> Date: Thu, 12 Jan 2023 22:20:19 +0800 Subject: [PATCH 068/144] prioritize lookup the project source code during debugging (#463) * prioritize lookup the project source code during debugging * fix null check --- .../java/debug/plugin/internal/InlineValueHandler.java | 2 +- .../com/microsoft/java/debug/plugin/internal/JdtUtils.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java index 5c2cf8834..15aeeb2b4 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/InlineValueHandler.java @@ -166,7 +166,7 @@ private static IMethod findMethodInLocalTypes(IMethod enclosingMethod, int stopp */ private static Position getPosition(IBuffer buffer, int offset) { int[] result = JsonRpcHelpers.toLine(buffer, offset); - if (result == null && result.length < 1) { + if (result == null || result.length < 1) { return new Position(-1, -1); } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java index 9a918112b..a4c06c6d4 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java @@ -213,10 +213,10 @@ public static ISourceContainer[] getSourceContainers(String projectName) { projects.stream().distinct().map(project -> JdtUtils.getJavaProject(project)) .filter(javaProject -> javaProject != null && javaProject.exists()) .forEach(javaProject -> { - // Add source containers associated with the project's runtime classpath entries. - containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); // Add source containers associated with the project's source folders. containers.add(new JavaProjectSourceContainer(javaProject)); + // Add source containers associated with the project's runtime classpath entries. + containers.addAll(Arrays.asList(getSourceContainers(javaProject, calculated))); }); return containers.toArray(new ISourceContainer[0]); From 6e294e1e7c00014eb1b0ec1e67aa643efd97e19f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Feb 2023 10:33:52 +0800 Subject: [PATCH 069/144] Reject invalid DAP request (#466) --- .../core/adapter/DebugAdapterContext.java | 11 +++++++ .../java/debug/core/adapter/ErrorCode.java | 5 ++-- .../core/adapter/IDebugAdapterContext.java | 4 +++ .../handler/InitializeRequestHandler.java | 1 + .../adapter/handler/LaunchRequestHandler.java | 6 ++++ .../core/protocol/AbstractProtocolServer.java | 30 +++++++++++++++---- 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index 345b2fcdf..f8ac01c3e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -56,6 +56,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private StepFilters stepFilters; private Path classpathJar = null; private Path argsfile = null; + private boolean isInitialized = false; private long shellProcessId = -1; private long processId = -1; @@ -407,4 +408,14 @@ public long getJDWPLatency() { public void setJDWPLatency(long baseLatency) { this.jdwpLatency = baseLatency; } + + @Override + public boolean isInitialized() { + return isInitialized; + } + + @Override + public void setInitialized(boolean isInitialized) { + this.isInitialized = isInitialized; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java index 1cdc4f9ce..6cfe523cf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ErrorCode.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -35,7 +35,8 @@ public enum ErrorCode { EXCEPTION_INFO_FAILURE(1018), EVALUATION_COMPILE_ERROR(2001), EVALUATE_NOT_SUSPENDED_THREAD(2002), - HCR_FAILURE(3001); + HCR_FAILURE(3001), + INVALID_DAP_HEADER(3002); private int id; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 9df539e1d..9a38e8598 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -154,4 +154,8 @@ public interface IDebugAdapterContext { long getJDWPLatency(); void setJDWPLatency(long baseLatency); + + boolean isInitialized(); + + void setInitialized(boolean isInitialized); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java index bf60b6456..6b9245166 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/InitializeRequestHandler.java @@ -67,6 +67,7 @@ public CompletableFuture handle(Requests.Command command, Req caps.supportsBreakpointLocationsRequest = true; caps.supportsStepInTargetsRequest = true; response.body = caps; + context.setInitialized(true); return CompletableFuture.completedFuture(response); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index b1daf1b01..e5662f936 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -77,6 +77,12 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + if (!context.isInitialized()) { + final String errorMessage = "'launch' request is rejected since the debug session has not been initialized yet."; + logger.log(Level.SEVERE, errorMessage); + return CompletableFuture.completedFuture( + AdapterUtils.setErrorResponse(response, ErrorCode.LAUNCH_FAILURE, errorMessage)); + } LaunchArguments launchArguments = (LaunchArguments) arguments; Map traceInfo = new HashMap<>(); traceInfo.put("asyncJDWP", context.asyncJDWP()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java index 9bec3c228..61d57bd85 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/AbstractProtocolServer.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017 Microsoft Corporation and others. +* Copyright (c) 2017-2022 Microsoft Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -33,6 +33,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.protocol.Events.DebugEvent; import io.reactivex.disposables.Disposable; @@ -54,6 +56,7 @@ public abstract class AbstractProtocolServer implements IProtocolServer { private ByteBuffer rawData; private int contentLength = -1; private AtomicInteger sequenceNumber = new AtomicInteger(1); + private boolean isValidDAPRequest = true; private PublishSubject responseSubject = PublishSubject.create(); private PublishSubject requestSubject = PublishSubject.create(); @@ -217,7 +220,14 @@ private void processData() { if (message.type.equals("request")) { Messages.Request request = JsonUtils.fromJson(messageData, Messages.Request.class); - requestSubject.onNext(request); + if (this.isValidDAPRequest) { + requestSubject.onNext(request); + } else { + Messages.Response response = new Messages.Response(request.seq, request.command); + sendResponse(AdapterUtils.setErrorResponse(response, + ErrorCode.INVALID_DAP_HEADER, + String.format("'%s' request is rejected due to not being a valid DAP message.", request.command))); + } } else if (message.type.equals("response")) { Messages.Response response = JsonUtils.fromJson(messageData, Messages.Response.class); responseSubject.onNext(response); @@ -235,10 +245,20 @@ private void processData() { if (idx != -1) { Matcher matcher = CONTENT_LENGTH_MATCHER.matcher(rawMessage); if (matcher.find()) { - this.contentLength = Integer.parseInt(matcher.group(1)); - int headerByteLength = rawMessage.substring(0, idx + TWO_CRLF.length()) - .getBytes(PROTOCOL_ENCODING).length; + final String contentLengthText = matcher.group(1); + this.contentLength = Integer.parseInt(contentLengthText); + final String headerMessage = rawMessage.substring(0, idx + TWO_CRLF.length()); + final int headerByteLength = headerMessage.getBytes(PROTOCOL_ENCODING).length; this.rawData.removeFirst(headerByteLength); // Remove the header from the raw message. + + int expectedHeaderLength = 16 /*"Content-Length: ".length()*/ + contentLengthText.length(); + int actualHeaderLength = idx; + if (expectedHeaderLength != actualHeaderLength) { + this.isValidDAPRequest = false; + logger.log(Level.SEVERE, String.format("Illegal DAP request is detected: %s", headerMessage)); + } else { + this.isValidDAPRequest = true; + } continue; } } From f329b41a61518463a40f176d980737abf6fef5d8 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Feb 2023 14:59:55 +0800 Subject: [PATCH 070/144] Bump version to 0.44.0 (#467) --- .github/CODEOWNERS | 1 + com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..3f8625455 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @testforstephen @jdneo @Eskibear @CsCherrYY \ No newline at end of file diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 6d4f847a0..f253b45da 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.43.0 + 0.44.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 505cc54c2..3f86555d4 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 949581277..3bc8a69fc 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.43.0 +Bundle-Version: 0.44.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.43.0.jar + lib/com.microsoft.java.debug.core-0.44.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 98bf36ba0..75466e4df 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.43.0 + 0.44.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.43.0 + 0.44.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index f2141039b..cd199eabf 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 3670a20f1..48d8b7f68 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.43.0 + 0.44.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/pom.xml b/pom.xml index e75506938..6af31be29 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.43.0 + 0.44.0 pom Java Debug Server for Visual Studio Code From ed35d9a4584076950584b701344987ecdf51d619 Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Mon, 6 Mar 2023 21:41:33 -0500 Subject: [PATCH 071/144] Add LSP4J 0.20.0 to the target platform. (#473) - JDT-LS now uses LSP4J 0.20.0, which is not available in Orbit or the main release repository Signed-off-by: Roland Grunberg --- pom.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6af31be29..f1c64e509 100644 --- a/pom.xml +++ b/pom.xml @@ -154,9 +154,9 @@ - 202112 + 202212 p2 - https://download.eclipse.org/releases/2021-12/202112081000/ + https://download.eclipse.org/releases/2022-12/202212071000/ oss.sonatype.org @@ -180,5 +180,10 @@ p2 https://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ + + lsp4j + p2 + https://download.eclipse.org/lsp4j/updates/releases/0.20.0/ + From 31cc03389a7890aa8ff5be6c897ca2575b910329 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 7 Mar 2023 16:09:04 +0800 Subject: [PATCH 072/144] Make pom.xml to reuse the tp file (#476) --- .../com.microsoft.java.debug.tp.target | 9 ++-- com.microsoft.java.debug.target/pom.xml | 12 +++++ javaConfig.json | 2 +- pom.xml | 44 ++++++++----------- 4 files changed, 38 insertions(+), 29 deletions(-) rename java.debug.target => com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target (86%) create mode 100644 com.microsoft.java.debug.target/pom.xml diff --git a/java.debug.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target similarity index 86% rename from java.debug.target rename to com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index dcb0b8c5e..187803840 100644 --- a/java.debug.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,12 +16,11 @@ - + - - + @@ -31,5 +30,9 @@ + + + + \ No newline at end of file diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml new file mode 100644 index 000000000..537f0ddcd --- /dev/null +++ b/com.microsoft.java.debug.target/pom.xml @@ -0,0 +1,12 @@ + + 4.0.0 + + com.microsoft.java + java-debug-parent + 0.44.0 + + com.microsoft.java.debug.tp + ${base.name} :: Target Platform + eclipse-target-definition + diff --git a/javaConfig.json b/javaConfig.json index 91ccf4714..8ab611931 100644 --- a/javaConfig.json +++ b/javaConfig.json @@ -3,5 +3,5 @@ "com.microsoft.java.debug.core", "com.microsoft.java.debug.plugin" ], - "targetPlatform": "java.debug.target" + "targetPlatform": "com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target" } diff --git a/pom.xml b/pom.xml index f1c64e509..fcfa85628 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ com.microsoft.java.debug.core com.microsoft.java.debug.plugin com.microsoft.java.debug.repository + com.microsoft.java.debug.target @@ -135,9 +136,27 @@ org.eclipse.tycho target-platform-configuration ${tycho-version} + + p2 + + + com.microsoft.java + com.microsoft.java.debug.tp + ${project.version} + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + @@ -153,11 +172,6 @@ - - 202212 - p2 - https://download.eclipse.org/releases/2022-12/202212071000/ - oss.sonatype.org https://oss.sonatype.org/content/repositories/snapshots/ @@ -165,25 +179,5 @@ true - - JDT.LS - p2 - https://download.eclipse.org/jdtls/snapshots/repository/latest/ - - - JBOLL.TOOLS - p2 - https://download.jboss.org/jbosstools/updates/m2e-extensions/m2e-apt/1.5.3-2019-11-08_11-04-22-H22/ - - - orbit - p2 - https://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository/ - - - lsp4j - p2 - https://download.eclipse.org/lsp4j/updates/releases/0.20.0/ - From 9df6f5dbe303e6927d5abe4b559d32541d9880a7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Sun, 23 Apr 2023 17:11:38 +0800 Subject: [PATCH 073/144] Support specifying the exception types you want to break on (#481) * support specifying the exception types you want to break on --- .github/CODEOWNERS | 2 +- .../java/debug/core/DebugSession.java | 98 ++++++++++++++++--- .../java/debug/core/DebugSettings.java | 4 +- .../java/debug/core/IDebugSession.java | 2 + ...SetExceptionBreakpointsRequestHandler.java | 7 +- .../java/debug/core/protocol/Requests.java | 9 ++ 6 files changed, 104 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f8625455..b11684f52 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @testforstephen @jdneo @Eskibear @CsCherrYY \ No newline at end of file +* @testforstephen @jdneo \ No newline at end of file diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 1c7990f16..9dfea1a98 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -16,16 +16,26 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; + import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ReferenceType; import com.sun.jdi.ThreadReference; +import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.ClassPrepareEvent; +import com.sun.jdi.request.ClassPrepareRequest; import com.sun.jdi.request.EventRequest; import com.sun.jdi.request.EventRequestManager; import com.sun.jdi.request.ExceptionRequest; +import io.reactivex.disposables.Disposable; + public class DebugSession implements IDebugSession { private VirtualMachine vm; private EventHub eventHub = new EventHub(); + private List eventRequests = new ArrayList<>(); + private List subscriptions = new ArrayList<>(); public DebugSession(VirtualMachine virtualMachine) { vm = virtualMachine; @@ -136,9 +146,27 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught @Override public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters) { + setExceptionBreakpoints(notifyCaught, notifyUncaught, null, classFilters, classExclusionFilters); + } + + @Override + public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, + String[] classFilters, String[] classExclusionFilters) { EventRequestManager manager = vm.eventRequestManager(); - ArrayList legacy = new ArrayList<>(manager.exceptionRequests()); - manager.deleteEventRequests(legacy); + + try { + ArrayList legacy = new ArrayList<>(manager.exceptionRequests()); + manager.deleteEventRequests(legacy); + manager.deleteEventRequests(eventRequests); + } catch (VMDisconnectedException ex) { + // ignore since removing breakpoints is meaningless when JVM is terminated. + } + subscriptions.forEach(subscription -> { + subscription.dispose(); + }); + subscriptions.clear(); + eventRequests.clear(); + // When no exception breakpoints are requested, no need to create an empty exception request. if (notifyCaught || notifyUncaught) { // from: https://www.javatips.net/api/REPLmode-master/src/jm/mode/replmode/REPLRunner.java @@ -153,20 +181,48 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught // a thread to be available, and queries it by calling allThreads(). // See org.eclipse.debug.jdi.tests.AbstractJDITest for the example. - // get only the uncaught exceptions - ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); - request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); - if (classFilters != null) { - for (String classFilter : classFilters) { - request.addClassFilter(classFilter); + if (exceptionTypes == null || exceptionTypes.length == 0) { + ExceptionRequest request = manager.createExceptionRequest(null, notifyCaught, notifyUncaught); + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (classFilters != null) { + for (String classFilter : classFilters) { + request.addClassFilter(classFilter); + } } + if (classExclusionFilters != null) { + for (String exclusionFilter : classExclusionFilters) { + request.addClassExclusionFilter(exclusionFilter); + } + } + request.enable(); + return; } - if (classExclusionFilters != null) { - for (String exclusionFilter : classExclusionFilters) { - request.addClassExclusionFilter(exclusionFilter); + + for (String exceptionType : exceptionTypes) { + if (StringUtils.isBlank(exceptionType)) { + continue; + } + + // register exception breakpoint in the future loaded classes. + ClassPrepareRequest classPrepareRequest = manager.createClassPrepareRequest(); + classPrepareRequest.addClassFilter(exceptionType); + classPrepareRequest.enable(); + eventRequests.add(classPrepareRequest); + + Disposable subscription = eventHub.events() + .filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent + && eventRequests.contains(debugEvent.event.request())) + .subscribe(debugEvent -> { + ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event; + createExceptionBreakpoint(event.referenceType(), notifyCaught, notifyUncaught, classFilters, classExclusionFilters); + }); + subscriptions.add(subscription); + + // register exception breakpoint in the loaded classes. + for (ReferenceType refType : vm.classesByName(exceptionType)) { + createExceptionBreakpoint(refType, notifyCaught, notifyUncaught, classFilters, classExclusionFilters); } } - request.enable(); } } @@ -195,4 +251,22 @@ public IMethodBreakpoint createFunctionBreakpoint(String className, String funct int hitCount) { return new MethodBreakpoint(vm, this.getEventHub(), className, functionName, condition, hitCount); } + + private void createExceptionBreakpoint(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught, + String[] classFilters, String[] classExclusionFilters) { + EventRequestManager manager = vm.eventRequestManager(); + ExceptionRequest request = manager.createExceptionRequest(refType, notifyCaught, notifyUncaught); + request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + if (classFilters != null) { + for (String classFilter : classFilters) { + request.addClassFilter(classFilter); + } + } + if (classExclusionFilters != null) { + for (String exclusionFilter : classExclusionFilters) { + request.addClassExclusionFilter(exclusionFilter); + } + } + request.enable(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index f59f10043..eaafdd064 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -19,7 +19,7 @@ import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.SerializedName; import com.microsoft.java.debug.core.protocol.JsonUtils; -import com.microsoft.java.debug.core.protocol.Requests.ClassFilters; +import com.microsoft.java.debug.core.protocol.Requests.ExceptionFilters; import com.microsoft.java.debug.core.protocol.Requests.StepFilters; public final class DebugSettings { @@ -39,7 +39,7 @@ public final class DebugSettings { public String javaHome; public HotCodeReplace hotCodeReplace = HotCodeReplace.MANUAL; public StepFilters stepFilters = new StepFilters(); - public ClassFilters exceptionFilters = new ClassFilters(); + public ExceptionFilters exceptionFilters = new ExceptionFilters(); public boolean exceptionFiltersUpdated = false; public int limitOfVariablesPerJdwpRequest = 100; public int jdwpRequestTimeout = 3000; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index 4e4078c87..f490fb0c0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -38,6 +38,8 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] classFilters, String[] classExclusionFilters); + void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters); + IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount); Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java index 3a4e642c8..62a30595a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java @@ -26,8 +26,8 @@ import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; -import com.microsoft.java.debug.core.protocol.Requests.ClassFilters; import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.ExceptionFilters; import com.microsoft.java.debug.core.protocol.Requests.SetExceptionBreakpointsArguments; import com.microsoft.java.debug.core.protocol.Types; import com.sun.jdi.event.VMDeathEvent; @@ -77,10 +77,11 @@ public synchronized CompletableFuture handle(Command command, Argument } private void setExceptionBreakpoints(IDebugSession debugSession, boolean notifyCaught, boolean notifyUncaught) { - ClassFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters; + ExceptionFilters exceptionFilters = DebugSettings.getCurrent().exceptionFilters; + String[] exceptionTypes = (exceptionFilters == null ? null : exceptionFilters.exceptionTypes); String[] classFilters = (exceptionFilters == null ? null : exceptionFilters.allowClasses); String[] classExclusionFilters = (exceptionFilters == null ? null : exceptionFilters.skipClasses); - debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, classFilters, classExclusionFilters); + debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); } @Override diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 697eef93d..09de7bfd9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -67,6 +67,15 @@ public static class ClassFilters { public String[] skipClasses = new String[0]; } + public static class ExceptionFilters extends ClassFilters { + /** + * Specifies that exceptions which are instances of refType will be reported. + * Note: this will include instances of sub-types. If null, all instances + * will be reported. + */ + public String[] exceptionTypes = new String[0]; + } + public static class StepFilters extends ClassFilters { /** * Deprecated - please use {@link ClassFilters#skipClasses } instead. From 87fae71227a58888d56b54c68fc47eaf24087102 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Apr 2023 13:05:08 +0800 Subject: [PATCH 074/144] Support set exception breakpoint asynchronously & bump veriosn to 0.45.0 (#482) * Support set exception breakpoint asynchronously & bump veriosn to 0.45.0 --- com.microsoft.java.debug.core/pom.xml | 2 +- .../com/microsoft/java/debug/core/DebugSession.java | 12 ++++++++++++ .../com/microsoft/java/debug/core/IDebugSession.java | 3 +++ .../SetExceptionBreakpointsRequestHandler.java | 4 +++- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 28 insertions(+), 11 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index f253b45da..a347b1883 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index 9dfea1a98..fbad52fe2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -226,6 +226,18 @@ public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught } } + @Override + public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, + String[] classFilters, String[] classExclusionFilters, boolean async) { + if (async) { + AsyncJdwpUtils.runAsync(() -> { + setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); + }); + } else { + setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); + } + } + @Override public Process process() { return vm.process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java index f490fb0c0..6cc3f3a46 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java @@ -40,6 +40,9 @@ public interface IDebugSession { void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters); + void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught, String[] exceptionTypes, String[] classFilters, String[] classExclusionFilters, + boolean async); + IMethodBreakpoint createFunctionBreakpoint(String className, String functionName, String condition, int hitCount); Process process(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java index 62a30595a..b51c5fe27 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetExceptionBreakpointsRequestHandler.java @@ -38,6 +38,7 @@ public class SetExceptionBreakpointsRequestHandler implements IDebugRequestHandl private boolean isInitialized = false; private boolean notifyCaught = false; private boolean notifyUncaught = false; + private boolean asyncJDWP = false; @Override public List getTargetCommands() { @@ -53,6 +54,7 @@ public synchronized CompletableFuture handle(Command command, Argument if (!isInitialized) { isInitialized = true; debugSession = context.getDebugSession(); + asyncJDWP = context.asyncJDWP(); DebugSettings.addDebugSettingChangeListener(this); debugSession.getEventHub().events().subscribe(debugEvent -> { if (debugEvent.event instanceof VMDeathEvent @@ -81,7 +83,7 @@ private void setExceptionBreakpoints(IDebugSession debugSession, boolean notifyC String[] exceptionTypes = (exceptionFilters == null ? null : exceptionFilters.exceptionTypes); String[] classFilters = (exceptionFilters == null ? null : exceptionFilters.allowClasses); String[] classExclusionFilters = (exceptionFilters == null ? null : exceptionFilters.skipClasses); - debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters); + debugSession.setExceptionBreakpoints(notifyCaught, notifyUncaught, exceptionTypes, classFilters, classExclusionFilters, this.asyncJDWP); } @Override diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 3f86555d4..0fc9cfb6c 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 3bc8a69fc..f04641553 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.44.0 +Bundle-Version: 0.45.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.44.0.jar + lib/com.microsoft.java.debug.core-0.45.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 75466e4df..b8d0b7582 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.44.0 + 0.45.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index cd199eabf..38801a366 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 48d8b7f68..096a5ecc9 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 537f0ddcd..c333a4d02 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.44.0 + 0.45.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index fcfa85628..4eda39462 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.44.0 + 0.45.0 pom Java Debug Server for Visual Studio Code From 31f79a5f3ab7efb1d66904fcdfdceb547aae4b83 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 12 May 2023 11:28:27 +0800 Subject: [PATCH 075/144] Add a task to sign maven artifacts with gpg (#485) * Add a task to sign maven artifacts with gpg --- scripts/publishMaven.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/scripts/publishMaven.js b/scripts/publishMaven.js index 1f960766c..deb75fb8b 100644 --- a/scripts/publishMaven.js +++ b/scripts/publishMaven.js @@ -1,7 +1,8 @@ /** * Usage: - * node publishMaven.js -task [upload|promote] + * node publishMaven.js -task [gpg][upload|promote] * + * gpg: Sign artifacts with GPG. * upload: Upload artifacts to a nexus staging repo. * promote: Promote a repo to get it picked up by Maven Central. */ @@ -33,7 +34,9 @@ main(configs, artifactFolder); function main() { const argv = process.argv; const task = argv[argv.indexOf("-task") + 1]; - if (task === "upload") { + if (task === "gpg") { + gpgSign(configs, artifactFolder); + } else if (task === "upload") { uploadToStaging(configs, artifactFolder); } else if (task === "promote") { promoteToCentral(configs); @@ -43,6 +46,27 @@ function main() { } } +/** + * Task gpg: Sign artifacts with GPG. + * + * Required binaries: + * - gpg + * + * Required Environment Variables: + * - artifactFolder: folder containing *.jar/*.pom files. + * - GPGPASS: passphrase of GPG key. + */ +function gpgSign(configs, artifactFolder) { + const props = ["artifactFolder", "gpgpass" ]; + for (const prop of props) { + if (!configs[prop]) { + console.error(`${prop} is not set.`); + process.exit(1); + } + } + addChecksumsAndGpgSignature(configs, artifactFolder); +} + /** * Task upload: Upload artifacts to a nexus staging repo. * @@ -141,7 +165,7 @@ function addChecksumsAndGpgSignature(configs, artifactFolder) { fs.readdirSync(modulePath) .filter(name => name.endsWith(".md5") || name.endsWith(".sha1") || name.endsWith(".asc")) .forEach(name => fs.unlinkSync(path.join(modulePath, name))); - + const files = fs.readdirSync(modulePath); for (let file of files) { // calc md5. @@ -153,7 +177,7 @@ function addChecksumsAndGpgSignature(configs, artifactFolder) { const sha1 = childProcess.execSync(`sha1sum "${path.join(modulePath, file)}"`); const sha1Match = /([a-z0-9]{40})/.exec(sha1.toString()); fs.writeFileSync(path.join(modulePath, file + ".sha1"), sha1Match[0]); - + // gpg sign. childProcess.execSync(`gpg --batch --pinentry-mode loopback --passphrase "${configs.gpgpass}" -ab "${path.join(modulePath, file)}"`) } From a01104960e77323db7a80f1cc7b39f8787855c2a Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Wed, 24 May 2023 07:30:28 +0200 Subject: [PATCH 076/144] Improve lambda method discovery Fixes: #477 (#478) * Improve lambda method discovery Fixes: #477 --- .../microsoft/java/debug/BindingUtils.java | 31 +++++-------------- .../java/debug/BreakpointLocationLocator.java | 2 +- .../java/debug/LambdaExpressionLocator.java | 2 +- .../internal/JdtSourceLookUpProvider.java | 18 ++++++++--- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java index b696be049..085bf0d4c 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BindingUtils.java @@ -12,10 +12,12 @@ package com.microsoft.java.debug; import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.internal.debug.core.breakpoints.LambdaLocationLocatorHelper; /** * Utility methods around working with JDT Bindings. */ +@SuppressWarnings("restriction") public final class BindingUtils { private BindingUtils() { @@ -48,31 +50,14 @@ public static String getMethodName(IMethodBinding binding, boolean fromKey) { } /** - * Returns the method signature of the method represented by the binding. Since - * this implementation use the {@link IMethodBinding#getKey()} to extract the - * signature from, the method name must be passed in. + * Returns the method signature of the method represented by the binding + * including the synthetic outer locals. * * @param binding the binding which the signature must be resolved for. - * @param name the name of the method. - * @return the signature or null if the signature could not be resolved from the - * key. + * @return the signature or null if the signature could not be resolved. */ - public static String toSignature(IMethodBinding binding, String name) { - // use key for now until JDT core provides a public API for this. - // "Ljava/util/Arrays;.asList([TT;)Ljava/util/List;" - // "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;" - String signatureString = binding.getKey(); - if (signatureString != null) { - name = "." + name; - int index = signatureString.indexOf(name); - if (index > -1) { - int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")")); - if (exceptionIndex > -1) { - return signatureString.substring(index + name.length(), exceptionIndex); - } - return signatureString.substring(index + name.length()); - } - } - return null; + public static String toSignature(IMethodBinding binding) { + return LambdaLocationLocatorHelper.toMethodSignature(binding); } + } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index 68f593aff..b14afde83 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -46,7 +46,7 @@ public String getMethodSignature() { if (this.methodBinding == null) { return null; } - return BindingUtils.toSignature(this.methodBinding, getMethodName()); + return BindingUtils.toSignature(this.methodBinding); } /** diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java index afffd9741..a5456809a 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/LambdaExpressionLocator.java @@ -71,7 +71,7 @@ public String getMethodSignature() { if (!this.found) { return null; } - return BindingUtils.toSignature(this.lambdaMethodBinding, getMethodName()); + return BindingUtils.toSignature(this.lambdaMethodBinding); } /** diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index c4d4fe318..6d42572e4 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -29,10 +29,12 @@ import java.util.jar.Manifest; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Stream; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.URIUtil; import org.eclipse.debug.core.sourcelookup.ISourceContainer; @@ -50,10 +52,10 @@ import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.LambdaExpression; -import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; @@ -276,7 +278,13 @@ private CompilationUnit asCompilationUnit(String uri) { * setEnvironment(String [], String [], String [], boolean) * and a unit name setUnitName(String). */ - parser.setEnvironment(new String[0], new String[0], null, true); + IFile resource = (IFile) JDTUtils.findResource(JDTUtils.toURI(uri), + ResourcesPlugin.getWorkspace().getRoot()::findFilesForLocationURI); + if (resource != null && JdtUtils.isJavaProject(resource.getProject())) { + parser.setProject(JavaCore.create(resource.getProject())); + } else { + parser.setEnvironment(new String[0], new String[0], null, true); + } parser.setUnitName(Paths.get(filePath).getFileName().toString()); /** * See the java doc for { @link ASTParser#setSource(char[]) }, @@ -492,7 +500,7 @@ public List findMethodInvocations(String uri, int line) { // Keep consistent with JDI since JDI uses binary class name invocation.declaringTypeName = binding.getDeclaringClass().getBinaryName(); } - invocation.methodGenericSignature = BindingUtils.toSignature(binding, BindingUtils.getMethodName(binding, true)); + invocation.methodGenericSignature = BindingUtils.toSignature(binding); invocation.methodSignature = Signature.getTypeErasure(invocation.methodGenericSignature); int startOffset = astNode.getStartPosition(); if (astNode instanceof org.eclipse.jdt.core.dom.MethodInvocation) { From d37cb100fb3b47c70479306c4d855060eafaabbd Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Fri, 26 May 2023 09:45:14 +0200 Subject: [PATCH 077/144] Improve ASTParser options when project is resolved. Fix #488 (#490) * Improve ASTParser options when project is resolved. Fix #488 Co-authored-by: Jinbo Wang --- .../internal/JdtSourceLookUpProvider.java | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 6d42572e4..bd41f99ca 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -100,7 +100,8 @@ public void initialize(IDebugAdapterContext context, Map props) throw new IllegalArgumentException("argument is null"); } options.putAll(props); - // During initialization, trigger a background job to load the source containers to improve the perf. + // During initialization, trigger a background job to load the source containers + // to improve the perf. new Thread(() -> { getSourceContainers(); }).start(); @@ -142,15 +143,16 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th return Stream.of(locations).map(location -> { if (location.className() != null && location.methodName() != null) { return location.className() - .concat("#").concat(location.methodName()) - .concat("#").concat(location.methodSignature()); + .concat("#").concat(location.methodName()) + .concat("#").concat(location.methodSignature()); } return location.className(); }).toArray(String[]::new); } @Override - public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException { + public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) + throws DebugException { if (sourceUri == null) { throw new IllegalArgumentException("sourceUri is null"); } @@ -161,8 +163,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB CompilationUnit astUnit = asCompilationUnit(sourceUri); JavaBreakpointLocation[] sourceLocations = Stream.of(sourceBreakpoints) - .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) - .toArray(JavaBreakpointLocation[]::new); + .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) + .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { Map resolvedLocations = new HashMap<>(); for (JavaBreakpointLocation sourceLocation : sourceLocations) { @@ -171,7 +173,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB if (sourceColumn > -1) { // if we have a column, try to find the lambda expression at that column LambdaExpressionLocator lambdaExpressionLocator = new LambdaExpressionLocator(astUnit, - sourceLine, sourceColumn); + sourceLine, sourceColumn); astUnit.accept(lambdaExpressionLocator); if (lambdaExpressionLocator.isFound()) { sourceLocation.setClassName(lambdaExpressionLocator.getFullyQualifiedTypeName()); @@ -232,7 +234,8 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { List locations = new ArrayList<>(); - // The starting position of each line is the default breakpoint location for that line. + // The starting position of each line is the default breakpoint location for + // that line. locations.add(new BreakpointLocation(sourceLine, 0)); astUnit.accept(new ASTVisitor() { @Override @@ -284,18 +287,18 @@ private CompilationUnit asCompilationUnit(String uri) { parser.setProject(JavaCore.create(resource.getProject())); } else { parser.setEnvironment(new String[0], new String[0], null, true); + /** + * See the java doc for { @link ASTParser#setSource(char[]) }, + * the user need specify the compiler options explicitly. + */ + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + parser.setCompilerOptions(javaOptions); } parser.setUnitName(Paths.get(filePath).getFileName().toString()); - /** - * See the java doc for { @link ASTParser#setSource(char[]) }, - * the user need specify the compiler options explicitly. - */ - Map javaOptions = JavaCore.getOptions(); - javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); - javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); - parser.setCompilerOptions(javaOptions); astUnit = (CompilationUnit) parser.createAST(null); } else { // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), @@ -336,7 +339,8 @@ public String getJavaRuntimeVersion(String projectName) { return resolveSystemLibraryVersion(project, vmInstall); } catch (CoreException e) { - logger.log(Level.SEVERE, "Failed to get Java runtime version for project '" + projectName + "': " + e.getMessage(), e); + logger.log(Level.SEVERE, + "Failed to get Java runtime version for project '" + projectName + "': " + e.getMessage(), e); } } @@ -345,6 +349,7 @@ public String getJavaRuntimeVersion(String projectName) { /** * Get the project associated source containers. + * * @return the initialized source container list */ public synchronized ISourceContainer[] getSourceContainers() { @@ -373,7 +378,8 @@ private String getContents(IClassFile cf) { source = buffer.getContents(); } } catch (JavaModelException e) { - logger.log(Level.SEVERE, String.format("Failed to parse the source contents of the class file: %s", e.toString()), e); + logger.log(Level.SEVERE, + String.format("Failed to parse the source contents of the class file: %s", e.toString()), e); } if (source == null) { source = ""; @@ -388,7 +394,7 @@ private static String getFileURI(IClassFile classFile) { try { return new URI(JDT_SCHEME, "contents", PATH_SEPARATOR + jarName + PATH_SEPARATOR + packageName + PATH_SEPARATOR + classFile.getElementName(), classFile.getHandleIdentifier(), null) - .toASCIIString(); + .toASCIIString(); } catch (URISyntaxException e) { return null; } @@ -425,8 +431,7 @@ private static IClassFile resolveClassFile(String uriString) { private static String readFile(String filePath) { StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferReader = - new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) { + try (BufferedReader bufferReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) { final int BUFFER_SIZE = 4096; char[] buffer = new char[BUFFER_SIZE]; while (true) { @@ -442,7 +447,8 @@ private static String readFile(String filePath) { return builder.toString(); } - private static String resolveSystemLibraryVersion(IJavaProject project, IVMInstall vmInstall) throws JavaModelException { + private static String resolveSystemLibraryVersion(IJavaProject project, IVMInstall vmInstall) + throws JavaModelException { LibraryLocation[] libraries = JavaRuntime.getLibraryLocations(vmInstall); if (libraries != null && libraries.length > 0) { IPackageFragmentRoot root = project.findPackageFragmentRoot(libraries[0].getSystemLibraryPath()); From 3e9adddd8c21a13b66114fb2b8f3bc711aa11ec0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 30 May 2023 13:29:16 +0800 Subject: [PATCH 078/144] Bump version to 0.46.0 (#492) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index a347b1883..9faa391fb 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 0fc9cfb6c..d3415ec62 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index f04641553..e754ee286 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.45.0 +Bundle-Version: 0.46.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.45.0.jar + lib/com.microsoft.java.debug.core-0.46.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index b8d0b7582..c69609528 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.45.0 + 0.46.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 38801a366..4efab9fba 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 096a5ecc9..9ff9e2352 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index c333a4d02..61bb2fca2 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.45.0 + 0.46.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 4eda39462..269077f6e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.45.0 + 0.46.0 pom Java Debug Server for Visual Studio Code From 25dd38f7202f0841ec8dd0686112e8809ee79c19 Mon Sep 17 00:00:00 2001 From: "microsoft-github-policy-service[bot]" <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 13:06:22 +0800 Subject: [PATCH 079/144] Microsoft mandatory file (#493) Co-authored-by: microsoft-github-policy-service[bot] <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> --- SECURITY.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e138ec5d6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + From 6f5a9b386035e0663c343abc898b2c9e9c31b69d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 26 Jun 2023 15:27:49 +0800 Subject: [PATCH 080/144] Debug support on the decompiled source code (#495) * Debug support on the decompiled source code --- .../java/debug/core/DebugSettings.java | 8 ++ .../debug/core/JavaBreakpointLocation.java | 14 ++- .../java/debug/core/adapter/AdapterUtils.java | 54 ++++++++++ .../core/adapter/ISourceLookUpProvider.java | 20 ++++ .../handler/SetBreakpointsRequestHandler.java | 18 +++- .../handler/StackTraceRequestHandler.java | 10 ++ .../internal/JdtSourceLookUpProvider.java | 100 +++++++++++++++++- .../com.microsoft.java.debug.tp.target | 2 +- 8 files changed, 219 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java index eaafdd064..0a3e05ec8 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java @@ -44,6 +44,7 @@ public final class DebugSettings { public int limitOfVariablesPerJdwpRequest = 100; public int jdwpRequestTimeout = 3000; public AsyncMode asyncJDWP = AsyncMode.OFF; + public Switch debugSupportOnDecompiledSource = Switch.OFF; public static DebugSettings getCurrent() { return current; @@ -97,6 +98,13 @@ public static enum AsyncMode { OFF } + public static enum Switch { + @SerializedName("on") + ON, + @SerializedName("off") + OFF + } + public static interface IDebugSettingChangeListener { public void update(DebugSettings oldSettings, DebugSettings newSettings); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java index 820e80c9b..3dbc1a24d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/JavaBreakpointLocation.java @@ -17,7 +17,11 @@ public class JavaBreakpointLocation { /** - * The source line of the breakpoint or logpoint. + * The line number in the source file. + */ + private int lineNumberInSourceFile = Integer.MIN_VALUE; + /** + * The line number in the class file. */ private int lineNumber; /** @@ -110,4 +114,12 @@ public Types.BreakpointLocation[] availableBreakpointLocations() { public void setAvailableBreakpointLocations(Types.BreakpointLocation[] availableBreakpointLocations) { this.availableBreakpointLocations = availableBreakpointLocations; } + + public int lineNumberInSourceFile() { + return lineNumberInSourceFile == Integer.MIN_VALUE ? lineNumber : lineNumberInSourceFile; + } + + public void setLineNumberInSourceFile(int lineNumberInSourceFile) { + this.lineNumberInSourceFile = lineNumberInSourceFile; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 9b972334d..c30aa8742 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -310,4 +310,58 @@ public static String decodeURIComponent(String uri) { return uri; } } + + /** + * Find the mapped lines based on the given line number. + * + * The line mappings format is as follows: + * - [i]: key + * - [i+1]: value + */ + public static int[] binarySearchMappedLines(int[] lineMappings, int targetLine) { + if (lineMappings == null || lineMappings.length == 0 || lineMappings.length % 2 != 0) { + return null; + } + + final int MAX = lineMappings.length / 2 - 1; + int low = 0; + int high = MAX; + int found = -1; + while (low <= high) { + int mid = low + (high - low) / 2; + int actualMid = mid * 2; + if (lineMappings[actualMid] == targetLine) { + found = mid; + break; + } + + if (lineMappings[actualMid] < targetLine) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + if (found == -1) { + return null; + } + + // Find the duplicates in the sorted array + int left = found; + while ((left - 1) >= 0 && lineMappings[(left - 1) * 2] == targetLine) { + left--; + } + + int right = found; + while ((right + 1) <= MAX && lineMappings[(right + 1) * 2] == targetLine) { + right++; + } + + int[] values = new int[right - left + 1]; + for (int i = 0; i < values.length; i++) { + values[i] = lineMappings[(left + i) * 2 + 1]; + } + + return values; + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java index ead10e33c..5ffed6e0f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java @@ -76,6 +76,26 @@ default String getJavaRuntimeVersion(String projectName) { */ List findMethodInvocations(String uri, int line); + /** + * Return the line mappings from the original line to the decompiled line. + * + * @param uri The uri + * @return the line mappings from the original line to the decompiled line. + */ + default int[] getOriginalLineMappings(String uri) { + return null; + } + + /** + * Return the line mappings from the decompiled line to the original line. + * + * @param uri The uri + * @return the line mappings from the decompiled line to the original line. + */ + default int[] getDecompiledLineMappings(String uri) { + return null; + } + public static class MethodInvocation { public String expression; public String methodName; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index 7fe6c3fdd..a8774ad37 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -24,6 +24,8 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.IBreakpoint; import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.IEvaluatableBreakpoint; @@ -296,7 +298,8 @@ public static boolean handleEvaluationResult(IDebugAdapterContext context, Threa private Types.Breakpoint convertDebuggerBreakpointToClient(IBreakpoint breakpoint, IDebugAdapterContext context) { int id = (int) breakpoint.getProperty("id"); boolean verified = breakpoint.getProperty("verified") != null && (boolean) breakpoint.getProperty("verified"); - int lineNumber = AdapterUtils.convertLineNumber(breakpoint.getLineNumber(), context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + int lineNumber = AdapterUtils.convertLineNumber(breakpoint.sourceLocation().lineNumberInSourceFile(), + context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); return new Types.Breakpoint(id, verified, lineNumber, ""); } @@ -318,6 +321,19 @@ private IBreakpoint[] convertClientBreakpointsToDebugger(String sourceFile, Type } catch (NumberFormatException e) { hitCount = 0; // If hitCount is an illegal number, ignore hitCount condition. } + + if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON) { + // Align the decompiled line with the original line. + int[] lineMappings = sourceProvider.getDecompiledLineMappings(sourceFile); + if (locations[i] != null && lineMappings != null) { + int lineNumberInSourceFile = locations[i].lineNumber(); + int[] originalLines = AdapterUtils.binarySearchMappedLines(lineMappings, lineNumberInSourceFile); + if (originalLines != null && originalLines.length > 0) { + locations[i].setLineNumberInSourceFile(lineNumberInSourceFile); + locations[i].setLineNumber(originalLines[0]); + } + } + } breakpoints[i] = context.getDebugSession().createBreakpoint(locations[i], hitCount, sourceBreakpoints[i].condition, sourceBreakpoints[i].logMessage); if (sourceProvider.supportsRealtimeBreakpointVerification() && StringUtils.isNotBlank(locations[i].className())) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index fbfce49ef..601a29f56 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -25,8 +25,10 @@ import org.apache.commons.lang3.StringUtils; import com.microsoft.java.debug.core.AsyncJdwpUtils; +import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IBreakpoint; +import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; @@ -189,6 +191,14 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra // display "Unknown Source" in the Call Stack View. clientSource = null; } + } else if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON + && clientSource != null && clientSource.path != null) { + // Align the original line with the decompiled line. + int[] lineMappings = context.getProvider(ISourceLookUpProvider.class).getOriginalLineMappings(clientSource.path); + int[] renderLines = AdapterUtils.binarySearchMappedLines(lineMappings, clientLineNumber); + if (renderLines != null && renderLines.length > 0) { + clientLineNumber = renderLines[0]; + } } int clientColumnNumber = context.isClientColumnsStartAt1() ? 1 : 0; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index bd41f99ca..f0c8795cc 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -36,6 +36,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.URIUtil; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.jdt.core.IBuffer; @@ -60,14 +61,19 @@ import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; +import org.eclipse.jdt.ls.core.internal.DecompilerResult; import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.managers.ContentProviderManager; import com.microsoft.java.debug.BindingUtils; import com.microsoft.java.debug.BreakpointLocationLocator; import com.microsoft.java.debug.LambdaExpressionLocator; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; +import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.JavaBreakpointLocation; +import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -303,10 +309,42 @@ private CompilationUnit asCompilationUnit(String uri) { } else { // For non-file uri (e.g. jdt://contents/rt.jar/java.io/PrintStream.class), // leverage jdt to load the source contents. - ITypeRoot typeRoot = resolveClassFile(uri); - if (typeRoot != null) { - parser.setSource(typeRoot); - astUnit = (CompilationUnit) parser.createAST(null); + IClassFile typeRoot = resolveClassFile(uri); + try { + if (typeRoot != null && typeRoot.getSourceRange() != null) { + parser.setSource(typeRoot); + astUnit = (CompilationUnit) parser.createAST(null); + } else if (typeRoot != null && DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON) { + ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager(); + try { + String contents = contentProvider.getSource(typeRoot, new NullProgressMonitor()); + if (contents != null && !contents.isBlank()) { + IJavaProject javaProject = typeRoot.getJavaProject(); + if (javaProject != null) { + parser.setProject(javaProject); + } else { + parser.setEnvironment(new String[0], new String[0], null, true); + /** + * See the java doc for { @link ASTParser#setSource(char[]) }, + * the user need specify the compiler options explicitly. + */ + Map javaOptions = JavaCore.getOptions(); + javaOptions.put(JavaCore.COMPILER_SOURCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_COMPLIANCE, this.latestJavaVersion); + javaOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + parser.setCompilerOptions(javaOptions); + } + parser.setUnitName(typeRoot.getElementName()); + parser.setSource(contents.toCharArray()); + astUnit = (CompilationUnit) parser.createAST(null); + } + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + } catch (JavaModelException e) { + // ignore } } return astUnit; @@ -533,4 +571,58 @@ private boolean isSameURI(String uri1, String uri2) { return false; } } + + public int[] getOriginalLineMappings(String uri) { + IClassFile classFile = resolveClassFile(uri); + try { + if (classFile == null) { + return null; + } + + IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) classFile.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); + if (packageRoot != null && packageRoot.getSourceAttachmentPath() != null) { + return null; + } + + if (classFile.getSourceRange() == null) { + ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager(); + try { + DecompilerResult result = contentProvider.getSourceResult(classFile, new NullProgressMonitor()); + if (result != null) { + return result.getOriginalLineMappings(); + } + } catch (NoSuchMethodError e) { + // ignore it if old language server version is installed. + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + } catch (JavaModelException e) { + // ignore + } + return null; + } + + public int[] getDecompiledLineMappings(String uri) { + IClassFile classFile = resolveClassFile(uri); + try { + if (classFile != null && classFile.getSourceRange() == null) { + ContentProviderManager contentProvider = JavaLanguageServerPlugin.getContentProviderManager(); + try { + DecompilerResult result = contentProvider.getSourceResult(classFile, new NullProgressMonitor()); + if (result != null) { + return result.getDecompiledLineMappings(); + } + } catch (NoSuchMethodError e) { + // ignore it if old Java language server version is installed. + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + } catch (JavaModelException e) { + // ignore + } + + return null; + } } diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 187803840..fbc981533 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -31,7 +31,7 @@ - + From b6564781f1b212036d5fceed4e9b757d40cc0ce4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 30 Jun 2023 09:57:33 +0800 Subject: [PATCH 081/144] Track the perf of stackTrace dap request (#496) --- .../handler/StackTraceRequestHandler.java | 15 ++++++++++++ .../java/debug/core/protocol/Events.java | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 601a29f56..6b44d52f5 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils; +import com.google.gson.JsonObject; import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugSettings; import com.microsoft.java.debug.core.DebugUtility; @@ -41,6 +42,7 @@ import com.microsoft.java.debug.core.protocol.Requests.StackTraceArguments; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; +import com.microsoft.java.debug.core.protocol.Events.TelemetryEvent; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.LocalVariable; @@ -54,6 +56,7 @@ import com.sun.jdi.request.BreakpointRequest; public class StackTraceRequestHandler implements IDebugRequestHandler { + private ThreadLocal isDecompilerInvoked = new ThreadLocal<>(); @Override public List getTargetCommands() { @@ -62,6 +65,8 @@ public List getTargetCommands() { @Override public CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context) { + final long startAt = System.currentTimeMillis(); + isDecompilerInvoked.set(false); StackTraceArguments stacktraceArgs = (StackTraceArguments) arguments; List result = new ArrayList<>(); if (stacktraceArgs.startFrame < 0 || stacktraceArgs.levels < 0) { @@ -108,6 +113,15 @@ public CompletableFuture handle(Command command, Arguments arguments, } } response.body = new Responses.StackTraceResponseBody(result, totalFrames); + long duration = System.currentTimeMillis() - startAt; + JsonObject properties = new JsonObject(); + properties.addProperty("command", "stackTrace"); + properties.addProperty("duration", duration); + properties.addProperty("decompileSupport", DebugSettings.getCurrent().debugSupportOnDecompiledSource.toString()); + if (isDecompilerInvoked.get() != null) { + properties.addProperty("isDecompilerInvoked", Boolean.toString(isDecompilerInvoked.get())); + } + context.getProtocolServer().sendEvent(new TelemetryEvent("dap", properties)); return CompletableFuture.completedFuture(response); } @@ -198,6 +212,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra int[] renderLines = AdapterUtils.binarySearchMappedLines(lineMappings, clientLineNumber); if (renderLines != null && renderLines.length > 0) { clientLineNumber = renderLines[0]; + isDecompilerInvoked.set(true); } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index 681ec543d..1973444b6 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -246,6 +246,30 @@ public UserNotificationEvent(NotificationType notifyType, String message) { } } + public static class TelemetryEvent extends DebugEvent { + /** + * The telemetry event name. + */ + public String name; + + /** + * The properties is an object as below. + * { + * [key: string]: string | number; + * } + */ + public Object properties; + + /** + * Constructor. + */ + public TelemetryEvent(String name, Object data) { + super("telemetry"); + this.name = name; + this.properties = data; + } + } + public static enum InvalidatedAreas { @SerializedName("all") ALL, From 7bdf2effd7be8f54745e04ea8334e337ed86b75b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 30 Jun 2023 10:44:35 +0800 Subject: [PATCH 082/144] Bump version to 0.47.0 (#497) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 9faa391fb..3b1d57547 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index d3415ec62..5c303928d 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index e754ee286..0965d8ed5 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.46.0 +Bundle-Version: 0.47.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.46.0.jar + lib/com.microsoft.java.debug.core-0.47.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index c69609528..c41c9de3b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.46.0 + 0.47.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 4efab9fba..20eac94c8 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 9ff9e2352..665b94a6a 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 61bb2fca2..6e22d46ab 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.46.0 + 0.47.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 269077f6e..c296ce2c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.46.0 + 0.47.0 pom Java Debug Server for Visual Studio Code From f9c02cffebf3f7b62ca5f980fa842033f3d53049 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 24 Jul 2023 13:57:20 +0800 Subject: [PATCH 083/144] Custom request 'refreshFrames' to refresh the thread's frames (#501) * Custom request 'refreshFrames' to refresh the thread's frames --- .../java/debug/core/adapter/DebugAdapter.java | 2 + .../java/debug/core/adapter/ThreadCache.java | 46 ++++++ .../ConfigurationDoneRequestHandler.java | 3 +- .../adapter/handler/RefreshFramesHandler.java | 136 ++++++++++++++++++ .../adapter/handler/RestartFrameHandler.java | 1 + .../handler/SetBreakpointsRequestHandler.java | 4 +- .../SetDataBreakpointsRequestHandler.java | 4 +- .../SetFunctionBreakpointsRequestHandler.java | 4 +- .../handler/StackTraceRequestHandler.java | 19 ++- .../adapter/handler/StepRequestHandler.java | 2 +- .../handler/ThreadsRequestHandler.java | 7 + .../java/debug/core/protocol/Events.java | 5 + .../java/debug/core/protocol/Requests.java | 10 ++ 13 files changed, 233 insertions(+), 10 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index b853e0469..10c0df902 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -34,6 +34,7 @@ import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler; import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler; import com.microsoft.java.debug.core.adapter.handler.ProcessIdHandler; +import com.microsoft.java.debug.core.adapter.handler.RefreshFramesHandler; import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler; import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler; import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler; @@ -133,6 +134,7 @@ private void initialize() { registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler()); registerHandlerForDebug(new BreakpointLocationsRequestHander()); registerHandlerForDebug(new StepInTargetsRequestHandler()); + registerHandlerForDebug(new RefreshFramesHandler()); // NO_DEBUG mode only registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler()); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java index ce1c17282..57d39154e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -32,6 +33,8 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) { } }); private Map eventThreads = new ConcurrentHashMap<>(); + private Map> decompiledClassesByThread = new HashMap<>(); + private Map threadStoppedReasons = new HashMap<>(); public synchronized void resetThreads(List threads) { allThreads.clear(); @@ -80,6 +83,13 @@ public void addEventThread(ThreadReference thread) { eventThreads.put(thread.uniqueID(), thread); } + public void addEventThread(ThreadReference thread, String reason) { + eventThreads.put(thread.uniqueID(), thread); + if (reason != null) { + threadStoppedReasons.put(thread.uniqueID(), reason); + } + } + public void removeEventThread(long threadId) { eventThreads.remove(threadId); } @@ -113,4 +123,40 @@ public List visibleThreads(IDebugAdapterContext context) { return visibleThreads; } + + public Set getDecompiledClassesByThread(long threadId) { + return decompiledClassesByThread.get(threadId); + } + + public void setDecompiledClassesByThread(long threadId, Set decompiledClasses) { + if (decompiledClasses == null || decompiledClasses.isEmpty()) { + decompiledClassesByThread.remove(threadId); + return; + } + + decompiledClassesByThread.put(threadId, decompiledClasses); + } + + public String getThreadStoppedReason(long threadId) { + return threadStoppedReasons.get(threadId); + } + + public void setThreadStoppedReason(long threadId, String reason) { + if (reason == null) { + threadStoppedReasons.remove(threadId); + return; + } + + threadStoppedReasons.put(threadId, reason); + } + + public void clearThreadStoppedState(long threadId) { + threadStoppedReasons.remove(threadId); + decompiledClassesByThread.remove(threadId); + } + + public void clearAllThreadStoppedState() { + threadStoppedReasons.clear(); + decompiledClassesByThread.clear(); + } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java index 6805073dc..1c543bce4 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ConfigurationDoneRequestHandler.java @@ -76,6 +76,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (context.isVmStopOnEntry()) { DebugUtility.stopOnEntry(debugSession, context.getMainClass()).thenAccept(threadId -> { context.getProtocolServer().sendEvent(new Events.StoppedEvent("entry", threadId)); + context.getThreadCache().setThreadStoppedReason(threadId, "entry"); }); } } else if (event instanceof VMDeathEvent) { @@ -117,7 +118,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, JdiExceptionReference jdiException = new JdiExceptionReference(((ExceptionEvent) event).exception(), ((ExceptionEvent) event).catchLocation() == null); context.getExceptionManager().setException(thread.uniqueID(), jdiException); - context.getThreadCache().addEventThread(thread); + context.getThreadCache().addEventThread(thread, "exception"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID())); debugEvent.shouldResume = false; } else { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java new file mode 100644 index 000000000..b91daaf80 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RefreshFramesHandler.java @@ -0,0 +1,136 @@ +/******************************************************************************* +* Copyright (c) 2023 Microsoft Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter.handler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.java.debug.core.AsyncJdwpUtils; +import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; +import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; +import com.microsoft.java.debug.core.protocol.Events.StoppedEvent; +import com.microsoft.java.debug.core.protocol.Messages.Response; +import com.microsoft.java.debug.core.protocol.Requests.Arguments; +import com.microsoft.java.debug.core.protocol.Requests.Command; +import com.microsoft.java.debug.core.protocol.Requests.RefreshFramesArguments; +import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ThreadReference; + +public class RefreshFramesHandler implements IDebugRequestHandler { + + @Override + public List getTargetCommands() { + return Arrays.asList(Command.REFRESHFRAMES); + } + + @Override + public CompletableFuture handle(Command command, Arguments arguments, Response response, + IDebugAdapterContext context) { + RefreshFramesArguments refreshArgs = (RefreshFramesArguments) arguments; + String[] affectedRootPaths = refreshArgs == null ? null : refreshArgs.affectedRootPaths; + List pausedThreads = getPausedThreads(context); + for (long threadId : pausedThreads) { + if (affectedRootPaths == null || affectedRootPaths.length == 0) { + refreshFrames(threadId, context); + continue; + } + + Set decompiledClasses = context.getThreadCache().getDecompiledClassesByThread(threadId); + if (decompiledClasses == null || decompiledClasses.isEmpty()) { + continue; + } + + if (anyInAffectedRootPaths(decompiledClasses, affectedRootPaths)) { + refreshFrames(threadId, context); + } + } + + return CompletableFuture.completedFuture(response); + } + + List getPausedThreads(IDebugAdapterContext context) { + List results = new ArrayList<>(); + List> futures = new ArrayList<>(); + List threads = context.getThreadCache().visibleThreads(context); + for (ThreadReference thread : threads) { + if (context.asyncJDWP()) { + futures.add(AsyncJdwpUtils.supplyAsync(() -> { + try { + if (thread.isSuspended()) { + return thread.uniqueID(); + } + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + + return -1L; + })); + } else { + try { + if (thread.isSuspended()) { + results.add(thread.uniqueID()); + } + } catch (ObjectCollectedException ex) { + // Ignore it if the thread is garbage collected. + } + } + } + + List awaitedResutls = AsyncJdwpUtils.await(futures); + for (Long threadId : awaitedResutls) { + if (threadId > 0) { + results.add(threadId); + } + } + + return results; + } + + /** + * See https://github.com/microsoft/vscode/issues/188606, + * VS Code doesn't provide a simple way to refetch the stack frames. + * We're going to resend a thread stopped event to trick the client + * into refetching the thread stack frames. + */ + void refreshFrames(long threadId, IDebugAdapterContext context) { + StoppedEvent stoppedEvent = new StoppedEvent(context.getThreadCache().getThreadStoppedReason(threadId), threadId); + stoppedEvent.preserveFocusHint = true; + context.getProtocolServer().sendEvent(stoppedEvent); + } + + boolean anyInAffectedRootPaths(Collection classes, String[] affectedRootPaths) { + if (affectedRootPaths == null || affectedRootPaths.length == 0) { + return true; + } + + for (String classUri : classes) { + // decompiled class uri is like 'jdt://contents/rt.jar/java.io/PrintStream.class?=1.helloworld/%5C/usr%5C/lib%5C/jvm%5C/ + // java-8-oracle%5C/jre%5C/lib%5C/rt.jar%3Cjava.io(PrintStream.class'. + if (classUri.startsWith("jdt://contents/")) { + String jarName = classUri.substring("jdt://contents/".length()); + int sep = jarName.indexOf("/"); + jarName = sep >= 0 ? jarName.substring(0, sep) : jarName; + for (String affectedRootPath : affectedRootPaths) { + if (affectedRootPath.endsWith("/" + jarName) || affectedRootPath.endsWith("\\" + jarName)) { + return true; + } + } + } + } + + return false; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java index 2023209f4..164909656 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java @@ -122,6 +122,7 @@ private void stepInto(IDebugAdapterContext context, ThreadReference thread) { // Have to send two events to keep the UI sync with the step in operations: context.getProtocolServer().sendEvent(new Events.ContinuedEvent(thread.uniqueID())); context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID())); + context.getThreadCache().setThreadStoppedReason(thread.uniqueID(), "restartframe"); }); request.enable(); thread.resume(); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java index a8774ad37..09dafd1b0 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java @@ -211,14 +211,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, breakpointName); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } }); }); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, breakpointName); context.getProtocolServer().sendEvent(new Events.StoppedEvent( breakpointName, bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java index 6d3e8c0b1..373b1c31b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetDataBreakpointsRequestHandler.java @@ -151,13 +151,13 @@ private void registerWatchpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "data breakpoint"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } }); }); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "data breakpoint"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID())); } debugEvent.shouldResume = false; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java index 59a948d37..96a0e395b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetFunctionBreakpointsRequestHandler.java @@ -165,7 +165,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { if (resume) { debugEvent.eventSet.resume(); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "function breakpoint"); context.getProtocolServer().sendEvent(new Events.StoppedEvent( "function breakpoint", bpThread.uniqueID())); } @@ -173,7 +173,7 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) { }); } else { - context.getThreadCache().addEventThread(bpThread); + context.getThreadCache().addEventThread(bpThread, "function breakpoint"); context.getProtocolServer() .sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID())); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 6b44d52f5..0ba3b2004 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -15,8 +15,10 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -73,16 +75,23 @@ public CompletableFuture handle(Command command, Arguments arguments, response.body = new Responses.StackTraceResponseBody(result, 0); return CompletableFuture.completedFuture(response); } - ThreadReference thread = context.getThreadCache().getThread(stacktraceArgs.threadId); + long threadId = stacktraceArgs.threadId; + ThreadReference thread = context.getThreadCache().getThread(threadId); if (thread == null) { - thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId); + thread = DebugUtility.getThread(context.getDebugSession(), threadId); } int totalFrames = 0; if (thread != null) { + Set decompiledClasses = new LinkedHashSet<>(); try { // Thread state has changed and then invalidate the stack frame cache. if (stacktraceArgs.startFrame == 0) { context.getStackFrameManager().clearStackFrames(thread); + } else { + Set existing = context.getThreadCache().getDecompiledClassesByThread(threadId); + if (existing != null) { + decompiledClasses.addAll(existing); + } } totalFrames = thread.frameCount(); @@ -102,6 +111,10 @@ public CompletableFuture handle(Command command, Arguments arguments, Types.StackFrame lspFrame = convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context); result.add(lspFrame); frameReference.setSource(lspFrame.source); + int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); + if (jdiLineNumber != lspFrame.line) { + decompiledClasses.add(lspFrame.source.path); + } } } catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException | AbsentInformationException | ObjectCollectedException @@ -110,6 +123,8 @@ public CompletableFuture handle(Command command, Arguments arguments, // 1. the vscode has wrong parameter/wrong uri // 2. the thread actually terminates // TODO: should record a error log here. + } finally { + context.getThreadCache().setDecompiledClassesByThread(threadId, decompiledClasses); } } response.body = new Responses.StackTraceResponseBody(result, totalFrames); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index bd324a7fa..71f28355a 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -308,7 +308,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, if (threadState.eventSubscription != null) { threadState.eventSubscription.dispose(); } - context.getThreadCache().addEventThread(thread); + context.getThreadCache().addEventThread(thread, "step"); context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID())); debugEvent.shouldResume = false; } else if (event instanceof MethodExitEvent) { diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java index fceac73a5..6573f11da 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java @@ -138,6 +138,7 @@ private CompletableFuture pause(Requests.PauseArguments arguments, Res context.getStepResultManager().removeAllMethodResults(); context.getDebugSession().suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); + context.getThreadCache().setThreadStoppedReason(arguments.threadId, "pause"); } return CompletableFuture.completedFuture(response); } @@ -158,12 +159,14 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, context.getStepResultManager().removeMethodResult(arguments.threadId); context.getExceptionManager().removeException(arguments.threadId); allThreadsContinued = false; + context.getThreadCache().clearThreadStoppedState(arguments.threadId); DebugUtility.resumeThread(thread); context.getStackFrameManager().clearStackFrames(thread); checkThreadRunningAndRecycleIds(thread, context); } else { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); + context.getThreadCache().clearAllThreadStoppedState(); resumeVM(context); context.getStackFrameManager().clearStackFrames(); context.getRecyclableIdPool().removeAllObjects(); @@ -175,6 +178,7 @@ private CompletableFuture resume(Requests.ContinueArguments arguments, private CompletableFuture resumeAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { context.getStepResultManager().removeAllMethodResults(); context.getExceptionManager().removeAllExceptions(); + context.getThreadCache().clearAllThreadStoppedState(); resumeVM(context); context.getProtocolServer().sendEvent(new Events.ContinuedEvent(arguments.threadId, true)); context.getStackFrameManager().clearStackFrames(); @@ -190,6 +194,7 @@ private CompletableFuture resumeOthers(Requests.ThreadOperationArgumen continue; } + context.getThreadCache().clearThreadStoppedState(thread.uniqueID()); if (context.asyncJDWP()) { futures.add(AsyncJdwpUtils.runAsync(() -> resumeThread(thread, context))); } else { @@ -203,6 +208,7 @@ private CompletableFuture resumeOthers(Requests.ThreadOperationArgumen private CompletableFuture pauseAll(Requests.ThreadOperationArguments arguments, Response response, IDebugAdapterContext context) { context.getDebugSession().suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true)); + context.getThreadCache().setThreadStoppedReason(arguments.threadId, "pause"); return CompletableFuture.completedFuture(response); } @@ -300,6 +306,7 @@ private void pauseThread(ThreadReference thread, IDebugAdapterContext context) { context.getStepResultManager().removeMethodResult(threadId); thread.suspend(); context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", threadId)); + context.getThreadCache().setThreadStoppedReason(threadId, "pause"); } } catch (ObjectCollectedException ex) { // the thread is garbage collected. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java index 1973444b6..e692e9b8d 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java @@ -38,6 +38,11 @@ public static class StoppedEvent extends DebugEvent { public String description; public String text; public boolean allThreadsStopped; + /** + * A value of true hints to the client that this event should not change the + * focus. + */ + public Boolean preserveFocusHint; /** * Constructor. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java index 09de7bfd9..1129d230e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java @@ -426,6 +426,15 @@ public static class BreakpointLocationsArguments extends Arguments { public int endColumn; } + public static class RefreshFramesArguments extends Arguments { + /** + * If provided, refresh the stack frames of the paused threads that previously + * requested decompiled sources for classes in the affected root paths. + * Otherwise, refresh all paused threads. + */ + public String[] affectedRootPaths; + } + public static enum Command { INITIALIZE("initialize", InitializeArguments.class), LAUNCH("launch", LaunchArguments.class), @@ -464,6 +473,7 @@ public static enum Command { REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class), PROCESSID("processId", Arguments.class), BREAKPOINTLOCATIONS("breakpointLocations", BreakpointLocationsArguments.class), + REFRESHFRAMES("refreshFrames", RefreshFramesArguments.class), UNSUPPORTED("", Arguments.class); private String command; From 1d76af515d6926db737f89438c056c2d0d2150df Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 31 Jul 2023 12:13:50 +0800 Subject: [PATCH 084/144] Bump version to 0.48.0 (#502) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 3b1d57547..8ebf3fb5f 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 5c303928d..2f7e62dc3 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 0965d8ed5..520d7d0da 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.47.0 +Bundle-Version: 0.48.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.47.0.jar + lib/com.microsoft.java.debug.core-0.48.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index c41c9de3b..e50a7f992 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.47.0 + 0.48.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 20eac94c8..8cf6b4f60 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 665b94a6a..27132434c 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 6e22d46ab..c7fcc1716 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.47.0 + 0.48.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index c296ce2c1..55e421c19 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.47.0 + 0.48.0 pom Java Debug Server for Visual Studio Code From 5174aa3744281ad05531c1c1984631e2ff1c68de Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 4 Aug 2023 16:12:49 +0800 Subject: [PATCH 085/144] Only build main project for build server project (#503) * Only build main project for build server project * Add a dedicated error code for build server --------- Signed-off-by: Sheng Chen --- .../java/debug/plugin/internal/Compile.java | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java index 415456ce5..d38227785 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -18,9 +18,11 @@ import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; @@ -40,7 +42,42 @@ public class Compile { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); - public static BuildWorkspaceStatus compile(CompileParams params, IProgressMonitor monitor) { + private static final int GRADLE_BS_COMPILATION_ERROR = 100; + + public static Object compile(CompileParams params, IProgressMonitor monitor) { + IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); + if (mainProject == null) { + try { + // Q: is infer project by main class name necessary? perf impact? + List javaProjects = ResolveClasspathsHandler.getJavaProjectFromType(params.getMainClass()); + if (javaProjects.size() == 1) { + mainProject = javaProjects.get(0).getProject(); + } + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Failed to resolve project from main class name.", e); + } + } + + if (isBspProject(mainProject)) { + // Just need to trigger a build for the target project, the Gradle build server will + // handle the build dependencies for us. + try { + ResourcesPlugin.getWorkspace().build( + new IBuildConfiguration[]{mainProject.getActiveBuildConfig()}, + IncrementalProjectBuilder.INCREMENTAL_BUILD, + false /*buildReference*/, + monitor + ); + } catch (CoreException e) { + if (e.getStatus().getCode() == IResourceStatus.BUILD_FAILED) { + return GRADLE_BS_COMPILATION_ERROR; + } else { + return BuildWorkspaceStatus.FAILED; + } + } + return BuildWorkspaceStatus.SUCCEED; + } + try { if (monitor.isCanceled()) { return BuildWorkspaceStatus.CANCELLED; @@ -55,7 +92,6 @@ public static BuildWorkspaceStatus compile(CompileParams params, IProgressMonito } logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); - IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); IResource currentResource = mainProject; if (isUnmanagedFolder(mainProject) && StringUtils.isNotBlank(params.getMainClass())) { IType mainType = ProjectUtils.getJavaProject(mainProject).findType(params.getMainClass()); @@ -117,6 +153,11 @@ private static boolean isUnmanagedFolder(IProject project) { && ProjectUtils.isJavaProject(project); } + private static boolean isBspProject(IProject project) { + return project != null && ProjectUtils.isJavaProject(project) + && ProjectUtils.hasNature(project, "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature"); + } + private static IProject getDefaultProject() { return getWorkspaceRoot().getProject(ProjectsManager.DEFAULT_PROJECT_NAME); } From 0e03327569c18a83d0c56f63bbb80ba404bb20b3 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 15 Aug 2023 12:46:31 +0800 Subject: [PATCH 086/144] Code completion: Use fully qualified name as needed in DEBUG CONSOLE (#504) --- .../internal/CompletionProposalRequestor.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index be7932992..41b4da929 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -45,6 +45,7 @@ import org.eclipse.jdt.ls.core.internal.handlers.CompletionResponses; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.CompletionItemLabelDetails; import com.google.common.collect.ImmutableSet; import com.microsoft.java.debug.core.Configuration; @@ -159,6 +160,8 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, String.valueOf(index)); $.setData(data); this.descriptionProvider.updateDescription(proposal, $); + // Use fully qualified name as needed. + $.setInsertText(String.valueOf(proposal.getCompletion())); adjustCompleteItem($); $.setSortText(SortTextHelper.computeSortText(proposal)); return $; @@ -166,6 +169,17 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { private void adjustCompleteItem(CompletionItem item) { if (item.getKind() == CompletionItemKind.Function) { + // Merge the label details into the label property + // because the completion provider in DEBUG CONSOLE + // doesn't support the label details. + CompletionItemLabelDetails labelDetails = item.getLabelDetails(); + if (labelDetails != null && StringUtils.isNotBlank(labelDetails.getDetail())) { + item.setLabel(item.getLabel() + labelDetails.getDetail()); + } + if (labelDetails != null && StringUtils.isNotBlank(labelDetails.getDescription())) { + item.setLabel(item.getLabel() + " : " + labelDetails.getDescription()); + } + String text = item.getInsertText(); if (StringUtils.isNotBlank(text) && !text.endsWith(")")) { item.setInsertText(text + "()"); From f8b6db6eea40a6faab6be9c8ec8a8844584533ac Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 17 Aug 2023 14:35:42 +0800 Subject: [PATCH 087/144] Display the package name in the label property when suggesting types in DEBUG CONSOLE (#505) --- .../internal/CompletionProposalRequestor.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index 41b4da929..1df752f24 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -28,6 +28,7 @@ import org.eclipse.jdt.core.CompletionContext; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.core.CompletionRequestor; +import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; @@ -154,7 +155,7 @@ public List getCompletionItems() { */ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { final CompletionItem $ = new CompletionItem(); - $.setKind(mapKind(proposal.getKind())); + $.setKind(mapKind(proposal.getKind(), proposal.getFlags())); Map data = new HashMap<>(); data.put(CompletionResolveHandler.DATA_FIELD_REQUEST_ID, String.valueOf(response.getId())); data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, String.valueOf(index)); @@ -168,7 +169,15 @@ public CompletionItem toCompletionItem(CompletionProposal proposal, int index) { } private void adjustCompleteItem(CompletionItem item) { - if (item.getKind() == CompletionItemKind.Function) { + CompletionItemKind itemKind = item.getKind(); + if (itemKind == CompletionItemKind.Class || itemKind == CompletionItemKind.Interface + || itemKind == CompletionItemKind.Enum) { + // Display the package name in the label property. + CompletionItemLabelDetails labelDetails = item.getLabelDetails(); + if (labelDetails != null && StringUtils.isNotBlank(labelDetails.getDescription())) { + item.setLabel(item.getLabel() + " - " + labelDetails.getDescription()); + } + } else if (itemKind == CompletionItemKind.Function) { // Merge the label details into the label property // because the completion provider in DEBUG CONSOLE // doesn't support the label details. @@ -195,7 +204,7 @@ public void acceptContext(CompletionContext context) { this.descriptionProvider = new CompletionProposalDescriptionProvider(context); } - private CompletionItemKind mapKind(final int kind) { + private CompletionItemKind mapKind(final int kind, final int flags) { // When a new CompletionItemKind is added, don't forget to update // SUPPORTED_KINDS switch (kind) { @@ -204,6 +213,11 @@ private CompletionItemKind mapKind(final int kind) { return CompletionItemKind.Constructor; case CompletionProposal.ANONYMOUS_CLASS_DECLARATION: case CompletionProposal.TYPE_REF: + if (Flags.isInterface(flags)) { + return CompletionItemKind.Interface; + } else if (Flags.isEnum(flags)) { + return CompletionItemKind.Enum; + } return CompletionItemKind.Class; case CompletionProposal.FIELD_IMPORT: case CompletionProposal.METHOD_IMPORT: @@ -213,6 +227,9 @@ private CompletionItemKind mapKind(final int kind) { return CompletionItemKind.Module; case CompletionProposal.FIELD_REF: case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER: + if (Flags.isStatic(flags) && Flags.isFinal(flags)) { + return CompletionItemKind.Constant; + } return CompletionItemKind.Field; case CompletionProposal.KEYWORD: return CompletionItemKind.Keyword; From c0e8f92f54defe9af77c6d091bb96a57ff114ac1 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 28 Aug 2023 11:33:14 +0800 Subject: [PATCH 088/144] bump version to 0.49.0 (#506) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 8ebf3fb5f..5412d44a4 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 2f7e62dc3..c2b013548 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 520d7d0da..cdc8e2f81 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.48.0 +Bundle-Version: 0.49.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.48.0.jar + lib/com.microsoft.java.debug.core-0.49.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index e50a7f992..7fd392bbe 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.48.0 + 0.49.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 8cf6b4f60..dc3efd066 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 27132434c..0adda64ab 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index c7fcc1716..cfae4adb8 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.48.0 + 0.49.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 55e421c19..c71560b64 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.48.0 + 0.49.0 pom Java Debug Server for Visual Studio Code From 8905c4a7367991a492ba280d8ba616f73fdea557 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Tue, 19 Sep 2023 00:51:18 -0700 Subject: [PATCH 089/144] Special handling build only when it's build server project and not buildship project (#511) Signed-off-by: Sheng Chen --- .../java/com/microsoft/java/debug/plugin/internal/Compile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java index d38227785..9cb6fbbfa 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -58,7 +58,7 @@ public static Object compile(CompileParams params, IProgressMonitor monitor) { } } - if (isBspProject(mainProject)) { + if (isBspProject(mainProject) && !ProjectUtils.isGradleProject(mainProject)) { // Just need to trigger a build for the target project, the Gradle build server will // handle the build dependencies for us. try { From 4f42baa62997c29507f930e009f28d4453f59596 Mon Sep 17 00:00:00 2001 From: Vladimir Makaev Date: Tue, 26 Sep 2023 09:50:31 +0100 Subject: [PATCH 090/144] Add extensibility points to DebugAdapter and ProtocolServer (#509) Co-authored-by: Vladimir Makayev Co-authored-by: Jinbo Wang --- .../java/debug/core/adapter/DebugAdapter.java | 9 +++++---- .../core/adapter/IDebugAdapterFactory.java | 19 +++++++++++++++++++ .../debug/core/adapter/ProtocolServer.java | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java index 10c0df902..8f595151f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java @@ -102,7 +102,7 @@ public CompletableFuture dispatchRequest(Messages.Request req } } - private void initialize() { + protected void initialize() { // Register request handlers. // When there are multiple handlers registered for the same request, follow the rule "first register, first execute". registerHandler(new InitializeRequestHandler()); @@ -141,15 +141,15 @@ private void initialize() { registerHandlerForNoDebug(new ProcessIdHandler()); } - private void registerHandlerForDebug(IDebugRequestHandler handler) { + protected void registerHandlerForDebug(IDebugRequestHandler handler) { registerHandler(requestHandlersForDebug, handler); } - private void registerHandlerForNoDebug(IDebugRequestHandler handler) { + protected void registerHandlerForNoDebug(IDebugRequestHandler handler) { registerHandler(requestHandlersForNoDebug, handler); } - private void registerHandler(IDebugRequestHandler handler) { + protected void registerHandler(IDebugRequestHandler handler) { registerHandler(requestHandlersForDebug, handler); registerHandler(requestHandlersForNoDebug, handler); } @@ -165,4 +165,5 @@ private void registerHandler(Map> requestHan handlerList.add(handler); } } + } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java new file mode 100644 index 000000000..d392d3b48 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterFactory.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2023 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation +*******************************************************************************/ + +package com.microsoft.java.debug.core.adapter; + +import com.microsoft.java.debug.core.protocol.IProtocolServer; + +@FunctionalInterface +public interface IDebugAdapterFactory { + public IDebugAdapter create(IProtocolServer server); +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java index 0526293b9..9e503e0af 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java @@ -52,6 +52,20 @@ public ProtocolServer(InputStream input, OutputStream output, IProviderContext c debugAdapter = new DebugAdapter(this, context); } + /** + * Constructs a protocol server instance based on the given input stream and output stream. + * @param input + * the input stream + * @param output + * the output stream + * @param debugAdapterFactory + * factory to create debug adapter that implements DAP communication + */ + public ProtocolServer(InputStream input, OutputStream output, IDebugAdapterFactory debugAdapterFactory) { + super(input, output); + debugAdapter = debugAdapterFactory.create(this); + } + /** * A while-loop to parse input data and send output data constantly. */ From 488a56ef9fe4c8766b47af6443986936283e1b7f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 1 Nov 2023 14:02:42 +0800 Subject: [PATCH 091/144] Bump version to 0.50.0 (#516) --- .mvn/wrapper/maven-wrapper.properties | 2 +- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- .../com.microsoft.java.debug.tp.target | 4 ++-- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 56bb0164e..9e0264d04 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip \ No newline at end of file +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip \ No newline at end of file diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 5412d44a4..635993d56 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index c2b013548..ca5f41e6f 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index cdc8e2f81..84a0ab36f 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.49.0 +Bundle-Version: 0.50.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.49.0.jar + lib/com.microsoft.java.debug.core-0.50.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 7fd392bbe..b895ffd28 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.49.0 + 0.50.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index dc3efd066..639d50082 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 0adda64ab..4f05eb331 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index fbc981533..1c10398c4 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,11 +16,11 @@ - + - + diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index cfae4adb8..80395f6dd 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.49.0 + 0.50.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index c71560b64..d562f82b5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,12 +6,12 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.49.0 + 0.50.0 pom Java Debug Server for Visual Studio Code UTF-8 - 2.7.3 + 4.0.3 ${basedir} From b0c70e3ddbf8cca9dc697f02f84914d7792b5982 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 21 Nov 2023 18:01:28 +0800 Subject: [PATCH 092/144] Update target platform dependencies to fix build errors (#518) --- .../com.microsoft.java.debug.tp.target | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 1c10398c4..ef0a5284e 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,11 +16,8 @@ - - - - + From 39046b7ab8b1acc47894745f517fcd5fe010cf64 Mon Sep 17 00:00:00 2001 From: John Olson <69521422+olsonjohn@users.noreply.github.com> Date: Sun, 10 Dec 2023 20:21:59 -0600 Subject: [PATCH 093/144] updated dependency location (#528) --- .../com.microsoft.java.debug.tp.target | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index ef0a5284e..2e7920ed7 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -17,7 +17,7 @@ - + @@ -32,4 +32,4 @@ - \ No newline at end of file + From 27f72fbdf79097794d8a546a1206e620ecdde863 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 12 Dec 2023 03:24:24 +0100 Subject: [PATCH 094/144] Fix generic return type lambda breakpoint issue. Fix #1359 (#499) * Fix generic return type lambda breakpoint issue. Fix #1359 The fix tries to compare the runtime lambda method signature with none generic version of the method signature found from AST traversal as a additional comparison to what is there. --- .../microsoft/java/debug/core/Breakpoint.java | 25 ++++++++++++++++++- .../java/debug/core/BreakpointTest.java | 17 +++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java index dfdbad4bb..82859b268 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/Breakpoint.java @@ -325,7 +325,8 @@ private Location findMethodLocaiton(ReferenceType refType, String methodName, St for (Method method : methods) { if (!method.isAbstract() && !method.isNative() && methodName.equals(method.name()) - && (methodSiguature.equals(method.genericSignature()) || methodSiguature.equals(method.signature()))) { + && (methodSiguature.equals(method.genericSignature()) || methodSiguature.equals(method.signature()) + || toNoneGeneric(methodSiguature).equals(method.signature()))) { location = method.location(); break; } @@ -334,6 +335,28 @@ private Location findMethodLocaiton(ReferenceType refType, String methodName, St return location; } + static String toNoneGeneric(String genericSig) { + StringBuilder builder = new StringBuilder(); + boolean append = true; + int depth = 0; + char[] chars = genericSig.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (c == '<') { + depth++; + append = (depth == 0); + } + if (append) { + builder.append(c); + } + if (c == '>') { + depth--; + append = (depth == 0); + } + } + return builder.toString(); + } + private List findLocaitonsOfLine(Method method, int lineNumber) { try { return method.locationsOfLine(lineNumber); diff --git a/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java new file mode 100644 index 000000000..5cc6fa3d7 --- /dev/null +++ b/com.microsoft.java.debug.core/src/test/java/com/microsoft/java/debug/core/BreakpointTest.java @@ -0,0 +1,17 @@ +package com.microsoft.java.debug.core; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class BreakpointTest { + @Test + public void testToNoneGeneric() { + assertEquals("Ljava.util.List;", Breakpoint.toNoneGeneric("Ljava.util.List;")); + assertEquals("(Ljava/util/Map;)Ljava/util/Map;", Breakpoint.toNoneGeneric( + "(Ljava/util/Map;>;)Ljava/util/Map;>;")); + assertEquals("(Ljava/util/Map;)Ljava/util/Map;", + Breakpoint.toNoneGeneric( + "(Ljava/util/Map;Ljava/util/List;>;)Ljava/util/Map;Ljava/util/List;>;")); + } +} From 9bdd9977860e5912a39b8efbaf62b9ee5577ec98 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Wed, 13 Dec 2023 02:54:30 +0100 Subject: [PATCH 095/144] Improve inline breakpoint discovery when expression is multiline. Fix #521 (#522) * Improve inline breakpoint discovery when expression is multiline. Fix #521 --- .../microsoft/java/debug/BreakpointLocationLocator.java | 4 ++-- .../debug/plugin/internal/JdtSourceLookUpProvider.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java index b14afde83..97581242e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/BreakpointLocationLocator.java @@ -22,8 +22,8 @@ public class BreakpointLocationLocator public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved, - boolean bestMatch) { - super(compilationUnit, lineNumber, bindingsResolved, bestMatch); + boolean bestMatch, int offset, int end) { + super(compilationUnit, lineNumber, bindingsResolved, bestMatch, offset, end); } @Override diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index f0c8795cc..1ebf4c7c3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -72,8 +72,8 @@ import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.DebugSettings; -import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.DebugSettings.Switch; +import com.microsoft.java.debug.core.JavaBreakpointLocation; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -210,8 +210,11 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB // mark it as "unverified". // In future, we could consider supporting to update the breakpoint to a valid // location. + + // passing the offset to the constructor, it can recognize the multiline lambda + // expression well BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, - sourceLine, true, true); + sourceLine, true, true, astUnit.getPosition(sourceLine, 0), 0); astUnit.accept(locator); // When the final valid line location is same as the original line, that // represents it's a valid breakpoint. From f8da9e2e2f55a2cefaf658ae24154c784eaa113e Mon Sep 17 00:00:00 2001 From: Vladimir Makaev Date: Mon, 8 Jan 2024 03:22:46 +0000 Subject: [PATCH 096/144] Allow ISourceLookUpProvider specify whether file is Local or Remote (#515) --- .../core/adapter/DebugAdapterContext.java | 4 +-- .../core/adapter/IDebugAdapterContext.java | 2 +- .../core/adapter/ISourceLookUpProvider.java | 28 ++++++++++----- .../java/debug/core/adapter/Source.java | 30 ++++++++++++++++ .../java/debug/core/adapter/SourceType.java | 16 +++++++++ .../handler/StackTraceRequestHandler.java | 35 +++++++++++++------ 6 files changed, 93 insertions(+), 22 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java index f8ac01c3e..bbf215c67 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java @@ -32,7 +32,7 @@ public class DebugAdapterContext implements IDebugAdapterContext { private static final int MAX_CACHE_ITEMS = 10000; private final StepFilters defaultFilters = new StepFilters(); - private Map sourceMappingCache = Collections.synchronizedMap(new LRUCache<>(MAX_CACHE_ITEMS)); + private Map sourceMappingCache = Collections.synchronizedMap(new LRUCache<>(MAX_CACHE_ITEMS)); private IProviderContext providerContext; private IProtocolServer server; @@ -212,7 +212,7 @@ public void setVariableFormatter(IVariableFormatter variableFormatter) { } @Override - public Map getSourceLookupCache() { + public Map getSourceLookupCache() { return sourceMappingCache; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java index 9a38e8598..e65270eef 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java @@ -85,7 +85,7 @@ public interface IDebugAdapterContext { void setVariableFormatter(IVariableFormatter variableFormatter); - Map getSourceLookupCache(); + Map getSourceLookupCache(); void setDebuggeeEncoding(Charset encoding); diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java index 5ffed6e0f..f33742852 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java @@ -8,7 +8,6 @@ * Contributors: * Microsoft Corporation - initial API and implementation *******************************************************************************/ - package com.microsoft.java.debug.core.adapter; import java.util.List; @@ -42,18 +41,31 @@ public interface ISourceLookUpProvider extends IProvider { JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceBreakpoint[] sourceBreakpoints) throws DebugException; /** - * Given a fully qualified class name and source file path, search the associated disk source file. - * - * @param fullyQualifiedName - * the fully qualified class name (e.g. com.microsoft.java.debug.core.adapter.ISourceLookUpProvider). - * @param sourcePath - * the qualified source file path (e.g. com\microsoft\java\debug\core\adapter\ISourceLookupProvider.java). - * @return the associated source file uri. + * Deprecated, please use {@link #getSource(String, String)} instead. */ + @Deprecated String getSourceFileURI(String fullyQualifiedName, String sourcePath); String getSourceContents(String uri); + /** + * Retrieves a {@link Source} object representing the source code associated with the given fully qualified class name and source file path. + * The implementation of this interface can determine a source is "local" or "remote". + * In case of "remote" a follow up "source" request will be issued by the client + * + * @param fullyQualifiedName + * the fully qualified class name, + * e.g., "com.microsoft.java.debug.core.adapter.ISourceLookUpProvider". + * @param sourcePath + * the qualified source file path, + * e.g., "com/microsoft/java/debug/core/adapter/ISourceLookupProvider.java". + * @return A {@link Source} object encapsulating the source file URI obtained from + * {@link #getSourceFileURI(String, String)} and the source type as {@link SourceType#LOCAL}. + */ + default Source getSource(String fullyQualifiedName, String sourcePath) { + return new Source(getSourceFileURI(fullyQualifiedName, sourcePath), SourceType.LOCAL); + } + /** * Returns the Java runtime that the specified project's build path used. * @param projectName diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java new file mode 100644 index 000000000..d00b4cb4c --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Source.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package com.microsoft.java.debug.core.adapter; + +public class Source { + public final String uri; + public final SourceType type; + + public Source(String uri, SourceType type) { + this.uri = uri; + this.type = type; + } + + public String getUri() { + return this.uri; + } + + public SourceType getType() { + return this.type; + } +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java new file mode 100644 index 000000000..724bf3bda --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/SourceType.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package com.microsoft.java.debug.core.adapter; + +public enum SourceType { + REMOTE, + LOCAL +} diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 0ba3b2004..49f304771 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -36,6 +36,8 @@ import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; +import com.microsoft.java.debug.core.adapter.Source; +import com.microsoft.java.debug.core.adapter.SourceType; import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; import com.microsoft.java.debug.core.protocol.Messages.Response; @@ -141,7 +143,7 @@ public CompletableFuture handle(Command command, Arguments arguments, } private static List resolveStackFrameInfos(StackFrame[] frames, boolean async) - throws AbsentInformationException, IncompatibleThreadStateException { + throws AbsentInformationException, IncompatibleThreadStateException { List jdiFrames = new ArrayList<>(); List> futures = new ArrayList<>(); for (StackFrame frame : frames) { @@ -221,7 +223,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra clientSource = null; } } else if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON - && clientSource != null && clientSource.path != null) { + && clientSource != null && clientSource.path != null) { // Align the original line with the decompiled line. int[] lineMappings = context.getProvider(ISourceLookUpProvider.class).getOriginalLineMappings(clientSource.path); int[] renderLines = AdapterUtils.binarySearchMappedLines(lineMappings, clientLineNumber); @@ -244,7 +246,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra }); if (match) { clientColumnNumber = AdapterUtils.convertColumnNumber(breakpoint.getColumnNumber(), - context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); + context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1()); } } } @@ -256,33 +258,44 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra /** * Find the source mapping for the specified source file name. */ - public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedName, String sourceName, String relativeSourcePath, + public static Types.Source convertDebuggerSourceToClient(String fullyQualifiedName, String sourceName, + String relativeSourcePath, IDebugAdapterContext context) throws URISyntaxException { + // use a lru cache for better performance - String uri = context.getSourceLookupCache().computeIfAbsent(fullyQualifiedName, key -> { - String fromProvider = context.getProvider(ISourceLookUpProvider.class).getSourceFileURI(key, relativeSourcePath); - // avoid return null which will cause the compute function executed again - return StringUtils.isBlank(fromProvider) ? "" : fromProvider; + Source source = context.getSourceLookupCache().computeIfAbsent(fullyQualifiedName, key -> { + Source result = context.getProvider(ISourceLookUpProvider.class).getSource(key, relativeSourcePath); + if (result == null) { + return new Source("", SourceType.LOCAL); + } + return result; }); + Integer sourceReference = 0; + String uri = source.getUri(); + + if (source.getType().equals(SourceType.REMOTE)) { + sourceReference = context.createSourceReference(source.getUri()); + } + if (!StringUtils.isBlank(uri)) { // The Source.path could be a file system path or uri string. if (uri.startsWith("file:")) { String clientPath = AdapterUtils.convertPath(uri, context.isDebuggerPathsAreUri(), context.isClientPathsAreUri()); - return new Types.Source(sourceName, clientPath, 0); + return new Types.Source(sourceName, clientPath, sourceReference); } else { // If the debugger returns uri in the Source.path for the StackTrace response, VSCode client will try to find a TextDocumentContentProvider // to render the contents. // Language Support for Java by Red Hat extension has already registered a jdt TextDocumentContentProvider to parse the jdt-based uri. // The jdt uri looks like 'jdt://contents/rt.jar/java.io/PrintStream.class?=1.helloworld/%5C/usr%5C/lib%5C/jvm%5C/java-8-oracle%5C/jre%5C/ // lib%5C/rt.jar%3Cjava.io(PrintStream.class'. - return new Types.Source(sourceName, uri, 0); + return new Types.Source(sourceName, uri, sourceReference); } } else { // If the source lookup engine cannot find the source file, then lookup it in the source directories specified by user. String absoluteSourcepath = AdapterUtils.sourceLookup(context.getSourcePaths(), relativeSourcePath); if (absoluteSourcepath != null) { - return new Types.Source(sourceName, absoluteSourcepath, 0); + return new Types.Source(sourceName, absoluteSourcepath, sourceReference); } else { return null; } From b578b50078cef9ce7b37710966bd2c2496c1c00d Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Thu, 18 Jan 2024 15:39:43 +0800 Subject: [PATCH 097/144] Migrate to 1es pipelines (#532) --- .azure-pipelines/signjars-nightly.yml | 146 ++++++++++++++++ .azure-pipelines/signjars-rc.yml | 236 ++++++++++++++++++++++++++ 2 files changed, 382 insertions(+) create mode 100644 .azure-pipelines/signjars-nightly.yml create mode 100644 .azure-pipelines/signjars-rc.yml diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml new file mode 100644 index 000000000..bdaeea871 --- /dev/null +++ b/.azure-pipelines/signjars-nightly.yml @@ -0,0 +1,146 @@ +name: $(Date:yyyyMMdd).$(Rev:r) +variables: + - name: Codeql.Enabled + value: true +schedules: + - cron: 0 5 * * 1,2,3,4,5 + branches: + include: + - refs/heads/main +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release +trigger: none +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + os: linux + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Ubuntu-2004 + sdl: + sourceAnalysisPool: + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Windows_2022 + os: windows + customBuildTags: + - MigrationTooling-mseng-VSJava-13474-Tool + stages: + - stage: Build + jobs: + - job: Job_1 + displayName: Sign-Jars-Nightly + templateContext: + outputs: + - output: pipelineArtifact + artifactName: plugin + targetPath: $(Build.ArtifactStagingDirectory) + displayName: "Publish Artifact: plugin" + steps: + - checkout: self + fetchTags: true + - task: JavaToolInstaller@0 + displayName: Use Java 17 + inputs: + versionSpec: "17" + jdkArchitectureOption: x64 + jdkSourceOption: PreInstalled + - task: CmdLine@2 + displayName: Parse the release version from pom.xml + inputs: + script: |- + #!/bin/bash + + sudo apt-get install xmlstarlet + xmlstarlet --version + RELEASE_VERSION=$(xmlstarlet sel -t -v "/_:project/_:version" pom.xml) + echo $RELEASE_VERSION + echo "##vso[task.setvariable variable=RELEASE_VERSION]$RELEASE_VERSION" + - task: CmdLine@2 + displayName: Build core.jar + inputs: + script: | + ./mvnw clean install -f com.microsoft.java.debug.core/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign core.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.core*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: install signed core.jar + inputs: + script: cp jars/com.microsoft.java.debug.core*.jar .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/ + - task: CmdLine@2 + displayName: Build plugin.jar + inputs: + script: |- + ./mvnw clean install -f com.microsoft.java.debug.target/pom.xml -Dmaven.repo.local=./.repository + ./mvnw clean install -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign plugin.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.plugin*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CopyFiles@2 + displayName: "Copy plugin.jar to: $(Build.ArtifactStagingDirectory)" + inputs: + Contents: |+ + jars/com.microsoft.java.debug.plugin*.jar + + TargetFolder: $(Build.ArtifactStagingDirectory) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml new file mode 100644 index 000000000..4e51eff7f --- /dev/null +++ b/.azure-pipelines/signjars-rc.yml @@ -0,0 +1,236 @@ +name: $(Date:yyyyMMdd).$(Rev:r) +variables: + - name: Codeql.Enabled + value: true +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release +trigger: none +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + os: linux + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Ubuntu-2004 + sdl: + sourceAnalysisPool: + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Windows_2022 + os: windows + customBuildTags: + - MigrationTooling-mseng-VSJava-9151-Tool + stages: + - stage: Build + jobs: + - job: Job_1 + displayName: Sign-Jars-RC + templateContext: + outputs: + - output: pipelineArtifact + artifactName: m2 + targetPath: $(Build.ArtifactStagingDirectory)/m2 + displayName: "Publish Artifact: m2" + - output: pipelineArtifact + artifactName: p2 + targetPath: $(Build.ArtifactStagingDirectory)/p2 + displayName: "Publish Artifact: p2" + steps: + - checkout: self + fetchTags: true + - task: JavaToolInstaller@0 + displayName: Use Java 17 + inputs: + versionSpec: "17" + jdkArchitectureOption: x64 + jdkSourceOption: PreInstalled + - task: CmdLine@2 + displayName: Parse the release version from pom.xml + inputs: + script: |- + #!/bin/bash + + sudo apt-get install xmlstarlet + xmlstarlet --version + RELEASE_VERSION=$(xmlstarlet sel -t -v "/_:project/_:version" pom.xml) + echo $RELEASE_VERSION + echo "##vso[task.setvariable variable=RELEASE_VERSION]$RELEASE_VERSION" + - task: CmdLine@2 + displayName: Build core.jar + inputs: + script: | + ./mvnw -N clean install -Dmaven.repo.local=./.repository + + ./mvnw clean install -f com.microsoft.java.debug.core/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign core.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.core*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: install signed core.jar + inputs: + script: cp jars/com.microsoft.java.debug.core*.jar .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/ + - task: CmdLine@2 + displayName: Build plugin.jar + inputs: + script: |- + ./mvnw clean install -f com.microsoft.java.debug.target/pom.xml -Dmaven.repo.local=./.repository + ./mvnw clean install -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p jars + mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign plugin.jar + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: jars + Pattern: com.microsoft.java.debug.plugin*.jar + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: install signed plugin.jar + inputs: + script: cp jars/com.microsoft.java.debug.plugin*.jar .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/ + - task: CmdLine@2 + displayName: Build p2 artifacts + inputs: + script: |- + # 3. Build the p2 artifacts. + ./mvnw clean package -f com.microsoft.java.debug.repository/pom.xml -Dmaven.repo.local=./.repository + + mkdir -p p2/target + cp -r com.microsoft.java.debug.repository/target/repository p2/target/ + cp com.microsoft.java.debug.repository/pushToBintray.sh p2/ + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign p2 + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: p2 + Pattern: "*.jar" + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CmdLine@2 + displayName: build m2 artifacts + inputs: + script: | + ./mvnw source:jar -f com.microsoft.java.debug.core/pom.xml -Dmaven.repo.local=./.repository + ./mvnw javadoc:jar -f com.microsoft.java.debug.core/pom.xml -Ddoclint=none -Dmaven.repo.local=./.repository + + ./mvnw source:jar -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository + ./mvnw javadoc:jar -f com.microsoft.java.debug.plugin/pom.xml -Ddoclint=none -Dmaven.repo.local=./.repository + + mkdir -p m2/java-debug-parent + cp pom.xml m2/java-debug-parent/java-debug-parent-$RELEASE_VERSION.pom + + mkdir -p m2/com.microsoft.java.debug.core + cp com.microsoft.java.debug.core/target/com.microsoft.java.debug.core*.jar m2/com.microsoft.java.debug.core + cp com.microsoft.java.debug.core/pom.xml m2/com.microsoft.java.debug.core/com.microsoft.java.debug.core-$RELEASE_VERSION.pom + + mkdir -p m2/com.microsoft.java.debug.plugin + cp com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin*.jar m2/com.microsoft.java.debug.plugin + cp com.microsoft.java.debug.plugin/pom.xml m2/com.microsoft.java.debug.plugin/com.microsoft.java.debug.plugin-$RELEASE_VERSION.pom + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + displayName: Sign m2 + inputs: + ConnectedServiceName: vscjavaci_codesign + FolderPath: m2 + Pattern: "*.jar" + signConfigType: inlineSignParams + inlineOperation: |- + [ + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaSign", + "Parameters" : { + "SigAlg" : "SHA256withRSA", + "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-447347-Java", + "OperationCode" : "JavaVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: CopyFiles@2 + displayName: "Copy p2/m2 to: $(Build.ArtifactStagingDirectory)" + inputs: + Contents: |+ + p2/** + m2/** + + TargetFolder: $(Build.ArtifactStagingDirectory) From 431de232d233783f2711a880d0c498a27baccb13 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Tue, 30 Jan 2024 06:13:54 +0100 Subject: [PATCH 098/144] Fix the step-into target on multi line expression. Fix #519 (#520) * Fix the step-into target on multi line expression. Fix #519 --------- Co-authored-by: Jinbo Wang --- .../core/adapter/handler/StepRequestHandler.java | 11 ++++++++--- .../plugin/internal/MethodInvocationLocator.java | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java index 71f28355a..e8f782668 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java @@ -268,7 +268,8 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession, } else if (currentStackDepth == threadState.stackDepth) { // If the ending step location is same as the original location where the step into operation is originated, // do another step of the same kind. - if (isSameLocation(currentStepLocation, threadState.stepLocation)) { + if (isSameLocation(currentStepLocation, threadState.stepLocation, + threadState.targetStepIn)) { context.getStepResultManager().removeMethodResult(threadId); threadState.pendingStepRequest = DebugUtility.createStepIntoRequest(thread, context.getStepFilters().allowClasses, @@ -438,15 +439,19 @@ private boolean shouldDoExtraStepInto(int originalStackDepth, Location originalL return true; } - private boolean isSameLocation(Location original, Location current) { + private boolean isSameLocation(Location current, Location original, MethodInvocation targetStepIn) { if (original == null || current == null) { return false; } Method originalMethod = original.method(); Method currentMethod = current.method(); + // if the lines doesn't match, check if the current line is still behind the + // target if a target exist. This handles where the target is part of a + // expression which is wrapped. return originalMethod.equals(currentMethod) - && original.lineNumber() == current.lineNumber(); + && (original.lineNumber() == current.lineNumber() + || (targetStepIn != null && targetStepIn.lineEnd >= current.lineNumber())); } /** diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java index a25fbc3f6..654cbb00b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/MethodInvocationLocator.java @@ -217,7 +217,7 @@ public boolean visit(ClassInstanceCreation node) { private boolean shouldVisitNode(ASTNode node) { int start = unit.getLineNumber(node.getStartPosition()); - int end = unit.getLineNumber(node.getStartPosition() + node.getLength()); + int end = unit.getLineNumber(node.getStartPosition() + node.getLength() - 1); if (line >= start && line <= end) { return true; From fcfa80d2ba6b6e60da34d9451c4fc297fc821f0d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 30 Jan 2024 13:59:01 +0800 Subject: [PATCH 099/144] Bump version to 0.51.0 (#533) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 635993d56..cd41ea9bc 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index ca5f41e6f..4d55efda0 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 84a0ab36f..077d85c95 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.50.0 +Bundle-Version: 0.51.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.50.0.jar + lib/com.microsoft.java.debug.core-0.51.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index b895ffd28..68e29fa33 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.50.0 + 0.51.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 639d50082..332d14f9e 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 4f05eb331..314b894cb 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 80395f6dd..42dbf2724 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.50.0 + 0.51.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index d562f82b5..46aece86b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.50.0 + 0.51.0 pom Java Debug Server for Visual Studio Code From b5bc37d18e885c686866b40fbf7e8f9ed82a7d47 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 6 Feb 2024 09:20:06 +0800 Subject: [PATCH 100/144] Fix build errors (#535) * Fix build errors --- .../internal/CompletionProposalRequestor.java | 20 ++++++++++++++++++- .../com.microsoft.java.debug.tp.target | 5 ++++- pom.xml | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java index 1df752f24..431c283f5 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionProposalRequestor.java @@ -72,6 +72,8 @@ public final class CompletionProposalRequestor extends CompletionRequestor { CompletionItemKind.Text); // @formatter:on + private static boolean isFilterFailed = false; + /** * Constructor. * @param typeRoot ITypeRoot @@ -321,7 +323,7 @@ private boolean isFiltered(CompletionProposal proposal) { case CompletionProposal.JAVADOC_TYPE_REF: case CompletionProposal.TYPE_REF: { char[] declaringType = getDeclaringType(proposal); - return declaringType != null && org.eclipse.jdt.ls.core.internal.contentassist.TypeFilter.isFiltered(declaringType); + return declaringType != null && isFiltered(declaringType); } default: // do nothing } @@ -332,6 +334,22 @@ private boolean isFiltered(CompletionProposal proposal) { return false; } + // Temp workaround for the completion error https://github.com/microsoft/java-debug/issues/534 + private static boolean isFiltered(char[] fullTypeName) { + if (isFilterFailed) { + return false; + } + + try { + return JavaLanguageServerPlugin.getInstance().getTypeFilter().filter(new String(fullTypeName)); + } catch (NoSuchMethodError ex) { + isFilterFailed = true; + JavaLanguageServerPlugin.logException("isFiltered for the completion failed.", ex); + } + + return false; + } + /** * copied from * org.eclipse.jdt.ui.text.java.CompletionProposalCollector.getDeclaringType(CompletionProposal) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 2e7920ed7..828da98a2 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -15,7 +15,10 @@ - + + + + diff --git a/pom.xml b/pom.xml index 46aece86b..8911c9d58 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.3 + 4.0.5 ${basedir} From f212c3a2f7aa2490c2df5dfa8dfda9fd0dd70d09 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 19 Feb 2024 11:27:03 +0800 Subject: [PATCH 101/144] Bump version to 0.51.1 (#536) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index cd41ea9bc..a7c2ca8ce 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 4d55efda0..b4fb64d7d 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 077d85c95..73af47a6d 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.51.0 +Bundle-Version: 0.51.1 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.51.0.jar + lib/com.microsoft.java.debug.core-0.51.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 68e29fa33..4657ab08b 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.51.0 + 0.51.1 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 332d14f9e..53e8bf963 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 314b894cb..68ff9fc42 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 42dbf2724..6977f1831 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.0 + 0.51.1 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 8911c9d58..b039fe1c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.51.0 + 0.51.1 pom Java Debug Server for Visual Studio Code From 6e064b0ca2e8d894ab66b6b341dae00f4060317a Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 19 Feb 2024 13:39:33 +0800 Subject: [PATCH 102/144] Update signjars-rc.yml for Azure Pipelines --- .azure-pipelines/signjars-rc.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 4e51eff7f..a8f1d3268 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -74,7 +74,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -114,7 +114,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams @@ -155,7 +155,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign p2 inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: p2 Pattern: "*.jar" signConfigType: inlineSignParams @@ -202,7 +202,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign m2 inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_codesign_token FolderPath: m2 Pattern: "*.jar" signConfigType: inlineSignParams From 5f0d1bb9109f2ebfcc1f8faf12eac77f59b5a380 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 19 Feb 2024 14:59:26 +0800 Subject: [PATCH 103/144] Update signjars-rc.yml for Azure Pipelines --- .azure-pipelines/signjars-rc.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index a8f1d3268..ebb333f70 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -74,7 +74,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -114,7 +114,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams @@ -155,7 +155,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign p2 inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: p2 Pattern: "*.jar" signConfigType: inlineSignParams @@ -202,7 +202,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign m2 inputs: - ConnectedServiceName: vscjavaci_codesign_token + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: m2 Pattern: "*.jar" signConfigType: inlineSignParams From 6c2377eae78c308bf87fbc2a6438a93734b06f5f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 23 Feb 2024 14:18:08 +0800 Subject: [PATCH 104/144] Update signjars-nightly.yml for Azure Pipelines --- .azure-pipelines/signjars-nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index bdaeea871..cae5ac6d5 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -73,7 +73,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -113,7 +113,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams From 98db4b16262488c5351a922f5781f7b95e1290d4 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 14 Mar 2024 13:58:54 +0800 Subject: [PATCH 105/144] Update com.microsoft.java.debug.tp.target to 4.31 release (#538) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 828da98a2..55695f6a9 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -16,7 +16,7 @@ - + From 736dcacf41e4da79b7419e7b7067f9c886252530 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 18 Mar 2024 18:29:13 +0800 Subject: [PATCH 106/144] Update lsp4j to 0.22.0 (#541) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 55695f6a9..200e532e3 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -31,7 +31,7 @@ - + From 56c014531273afd1c3a3b30712c2fb4d257e61f0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 21 Mar 2024 20:34:44 +0800 Subject: [PATCH 107/144] Support debugging Java 21 instance main method and unnamed class (#542) * Support debugging Java 21 instance main method and unnamed class --- .github/workflows/build.yml | 4 +- .../internal/JdtSourceLookUpProvider.java | 32 ++++- .../internal/ResolveMainClassHandler.java | 124 ++++++++++-------- .../internal/ResolveMainMethodHandler.java | 51 ++++++- .../com.microsoft.java.debug.tp.target | 9 +- pom.xml | 2 +- 6 files changed, 158 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41c85cb62..1ace44224 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: ${{ runner.os }}-maven- - name: Verify - run: ./mvnw clean verify + run: ./mvnw clean verify -U - name: Checkstyle run: ./mvnw checkstyle:check @@ -85,7 +85,7 @@ jobs: ${{ runner.os }}-maven- - name: Verify - run: ./mvnw clean verify + run: ./mvnw clean verify -U - name: Checkstyle run: ./mvnw checkstyle:check diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 1ebf4c7c3..c96b98342 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; @@ -56,6 +57,7 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.UnnamedClass; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; @@ -172,6 +174,11 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { + List types = astUnit.types(); + String unnamedClass = null; + if (types.size() == 1 && types.get(0) instanceof UnnamedClass) { + unnamedClass = inferPrimaryTypeName(sourceUri, astUnit); + } Map resolvedLocations = new HashMap<>(); for (JavaBreakpointLocation sourceLocation : sourceLocations) { int sourceLine = sourceLocation.lineNumber(); @@ -222,7 +229,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB // be hit in current implementation. if (sourceLine == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { - sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setClassName(StringUtils.isBlank(unnamedClass) ? locator.getFullyQualifiedTypeName() : unnamedClass); if (resolvedLocations.containsKey(sourceLine)) { sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); } else { @@ -231,7 +238,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB resolvedLocations.put(sourceLine, inlineLocations); } } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setClassName(StringUtils.isBlank(unnamedClass) ? locator.getFullyQualifiedTypeName() : unnamedClass); sourceLocation.setMethodName(locator.getMethodName()); sourceLocation.setMethodSignature(locator.getMethodSignature()); } @@ -241,6 +248,27 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB return sourceLocations; } + private String inferPrimaryTypeName(String uri, CompilationUnit astUnit) { + String fileName = ""; + String filePath = AdapterUtils.toPath(uri); + if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { + fileName = Paths.get(filePath).getFileName().toString(); + } else if (astUnit.getTypeRoot() != null) { + fileName = astUnit.getTypeRoot().getElementName(); + } + + if (StringUtils.isNotBlank(fileName)) { + String[] extensions = JavaCore.getJavaLikeExtensions(); + for (String extension : extensions) { + if (fileName.endsWith("." + extension)) { + return fileName.substring(0, fileName.length() - 1 - extension.length()); + } + } + } + + return fileName; + } + private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { List locations = new ArrayList<>(); // The starting position of each line is the default breakpoint location for diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index f70d0c044..38ad1532b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -43,6 +43,7 @@ import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; +import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -101,8 +102,7 @@ private List resolveMainClassUnderPaths(List parentPaths) // Limit to search main method from source code only. IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { @Override @@ -110,40 +110,36 @@ public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IMethod) { IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null) { - String moduleName = JdtUtils.getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (isMainMethod(method)) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; } - String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); - if (parentPaths.isEmpty() - || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) - || isContainedInInvisibleProject(project, parentPaths)) { - String filePath = null; - - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + } + String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); + if (parentPaths.isEmpty() + || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) + || isContainedInInvisibleProject(project, parentPaths)) { + String filePath = null; + + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } - res.add(new ResolutionItem(mainClass, projectName, filePath)); } + res.add(new ResolutionItem(mainClass, projectName, filePath)); } } } - } catch (JavaModelException e) { - // ignore } } } @@ -166,8 +162,7 @@ private List resolveMainClassUnderProject(final String projectNa IJavaProject javaProject = ProjectUtils.getJavaProject(projectName); IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(javaProject == null ? new IJavaProject[0] : new IJavaProject[] {javaProject}, IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { @Override @@ -175,35 +170,31 @@ public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IMethod) { IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null) { - String moduleName = JdtUtils.getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (isMainMethod(method)) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; } + } - String filePath = null; - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } - res.add(new ResolutionItem(mainClass, projectName, filePath)); } + res.add(new ResolutionItem(mainClass, projectName, filePath)); } } - } catch (JavaModelException e) { - // ignore } } } @@ -221,6 +212,29 @@ public void acceptSearchMatch(SearchMatch match) { return resolutions; } + private SearchPattern createMainMethodSearchPattern() { + SearchPattern pattern1 = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern2 = SearchPattern.createPattern("main() void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + return SearchPattern.createOrPattern(pattern1, pattern2); + } + + private boolean isMainMethod(IMethod method) { + try { + if (method instanceof SourceMethod + && ((SourceMethod) method).isMainMethodCandidate()) { + return true; + } + + return method.isMainMethod(); + } catch (JavaModelException e) { + // do nothing + } + + return false; + } + private boolean isContainedInInvisibleProject(IProject project, Collection rootPaths) { if (project == null) { return false; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index a3c61e814..fb8dfc33e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -30,7 +31,11 @@ import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -104,16 +109,58 @@ private static List searchMainMethods(ICompilationUnit compilationUnit) * Returns the main method defined in the type. */ public static IMethod getMainMethod(IType type) throws JavaModelException { + boolean allowInstanceMethod = isInstanceMainMethodSupported(type); + List methods = new ArrayList<>(); for (IMethod method : type.getMethods()) { - // Have at most one main method in the member methods of the type. + if (method instanceof SourceMethod + && ((SourceMethod) method).isMainMethodCandidate()) { + methods.add(method); + } + if (method.isMainMethod()) { - return method; + methods.add(method); + } + + if (!allowInstanceMethod && !methods.isEmpty()) { + return methods.get(0); } } + if (!methods.isEmpty()) { + methods.sort((method1, method2) -> { + return getMainMethodPriority(method1) - getMainMethodPriority(method2); + }); + + return methods.get(0); + } + return null; } + private static boolean isInstanceMainMethodSupported(IType type) { + Map options = type.getJavaProject().getOptions(true); + return CompilerOptions.versionToJdkLevel(options.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)) >= ClassFileConstants.JDK21; + } + + private static int getMainMethodPriority(IMethod method) { + int flags = 0; + try { + flags = method.getFlags(); + } catch (JavaModelException e) { + // do nothing + } + String[] params = method.getParameterTypes(); + if (Flags.isStatic(flags) && params.length == 1) { + return 1; + } else if (Flags.isStatic(flags)) { + return 2; + } else if (params.length == 1) { + return 3; + } + + return 4; + } + private static List getPotentialMainClassTypes(ICompilationUnit compilationUnit) throws JavaModelException { List result = new ArrayList<>(); IType[] topLevelTypes = compilationUnit.getTypes(); diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 200e532e3..a52901d01 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -14,13 +14,18 @@ - + + + + + + - + diff --git a/pom.xml b/pom.xml index b039fe1c1..2917f4092 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.5 + 4.0.6 ${basedir} From 1a06b1f6d27be3e765750433792da5ed02b9b0cd Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 25 Mar 2024 12:50:51 +0800 Subject: [PATCH 108/144] bump version to 0.52.0 (#543) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index a7c2ca8ce..3b42e40cb 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index b4fb64d7d..6de8860a5 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 73af47a6d..b45c99f7a 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.51.1 +Bundle-Version: 0.52.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.51.1.jar + lib/com.microsoft.java.debug.core-0.52.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 4657ab08b..6d58947b0 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.51.1 + 0.52.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 53e8bf963..fc6ff6a62 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 68ff9fc42..3d5537f13 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 6977f1831..e4ee29b92 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.51.1 + 0.52.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 2917f4092..f2e4d0eef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.51.1 + 0.52.0 pom Java Debug Server for Visual Studio Code From ae16f7ce3205047fd687aab9d877eeb78c62ace2 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 26 Mar 2024 10:45:23 +0800 Subject: [PATCH 109/144] Check the class name of ASTNode to see if it's unnamed class (#544) --- .../plugin/internal/JdtSourceLookUpProvider.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index c96b98342..5652b922e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -20,11 +20,14 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.logging.Level; @@ -57,7 +60,6 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.LambdaExpression; -import org.eclipse.jdt.core.dom.UnnamedClass; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; @@ -87,6 +89,9 @@ public class JdtSourceLookUpProvider implements ISourceLookUpProvider { private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); private static final String JDT_SCHEME = "jdt"; private static final String PATH_SEPARATOR = "/"; + private static final Set IMPLICITLY_DECLARED_CLASSES = new HashSet<>( + Arrays.asList("org.eclipse.jdt.core.dom.UnnamedClass", + "org.eclipse.jdt.core.dom.ImplicitTypeDeclaration")); private ISourceContainer[] sourceContainers = null; private HashMap options = new HashMap(); @@ -176,7 +181,10 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB if (astUnit != null) { List types = astUnit.types(); String unnamedClass = null; - if (types.size() == 1 && types.get(0) instanceof UnnamedClass) { + // See https://github.com/eclipse-jdt/eclipse.jdt.core/pull/2220 + // Given that the JDT plans to rename UnamedClass to ImplicitTypeDeclaration, we will check + // the class name of the ASTNode to prevent the potential breaking in the future. + if (types.size() == 1 && IMPLICITLY_DECLARED_CLASSES.contains(types.get(0).getClass().getName())) { unnamedClass = inferPrimaryTypeName(sourceUri, astUnit); } Map resolvedLocations = new HashMap<>(); From 37d4e66c5717179b1487ff72fcbc8622b31a3648 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 26 Mar 2024 12:47:07 +0800 Subject: [PATCH 110/144] Update signjars-nightly.yml for Azure Pipelines --- .azure-pipelines/signjars-nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index cae5ac6d5..0e53a4219 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -105,6 +105,7 @@ extends: displayName: Build plugin.jar inputs: script: |- + ./mvnw clean install -N -f pom.xml -Dmaven.repo.local=./.repository ./mvnw clean install -f com.microsoft.java.debug.target/pom.xml -Dmaven.repo.local=./.repository ./mvnw clean install -f com.microsoft.java.debug.plugin/pom.xml -Dmaven.repo.local=./.repository From 83403a458e0d01fba5a3871fea81af742460bdf0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 10 Apr 2024 15:59:06 +0800 Subject: [PATCH 111/144] fix Java 22 main method searching order (#548) --- .../internal/ResolveMainMethodHandler.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index fb8dfc33e..be1092bb3 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -142,23 +142,15 @@ private static boolean isInstanceMainMethodSupported(IType type) { return CompilerOptions.versionToJdkLevel(options.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)) >= ClassFileConstants.JDK21; } + /** + * See Java 22 JEP 463 https://openjdk.org/jeps/463. + * It searches the main method in the launched class by following a specific order: + * - If the launched class contains a main method with a String[] parameter then choose that method. + * - Otherwise, if the class contains a main method with no parameters then choose that method. + */ private static int getMainMethodPriority(IMethod method) { - int flags = 0; - try { - flags = method.getFlags(); - } catch (JavaModelException e) { - // do nothing - } String[] params = method.getParameterTypes(); - if (Flags.isStatic(flags) && params.length == 1) { - return 1; - } else if (Flags.isStatic(flags)) { - return 2; - } else if (params.length == 1) { - return 3; - } - - return 4; + return params.length == 1 ? 1 : 2; } private static List getPotentialMainClassTypes(ICompilationUnit compilationUnit) throws JavaModelException { From 36572435d0518cb2566ab9705c3d2b8e2a797402 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 14 Jun 2024 16:04:40 +0800 Subject: [PATCH 112/144] Support HCR for gradle build server projects (#555) * Support HCR for gradle build server projects * Fix checkstyle violations * Fix NPE when auto build is disabled Signed-off-by: Sheng Chen * Address comments * Fix checkstyle violations * Use isNotBlank() --------- Signed-off-by: Sheng Chen --- .../java/debug/plugin/internal/Compile.java | 78 ++++++++++--------- .../internal/JavaHotCodeReplaceProvider.java | 45 +++++++++++ .../java/debug/plugin/internal/JdtUtils.java | 34 ++++++++ 3 files changed, 120 insertions(+), 37 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java index 9cb6fbbfa..245281816 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/Compile.java @@ -14,6 +14,7 @@ package com.microsoft.java.debug.plugin.internal; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; @@ -28,14 +29,16 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import org.eclipse.jdt.ls.core.internal.handlers.BuildWorkspaceHandler; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.extended.ProjectBuildParams; import com.microsoft.java.debug.core.Configuration; @@ -45,20 +48,12 @@ public class Compile { private static final int GRADLE_BS_COMPILATION_ERROR = 100; public static Object compile(CompileParams params, IProgressMonitor monitor) { - IProject mainProject = params == null ? null : ProjectUtils.getProject(params.getProjectName()); - if (mainProject == null) { - try { - // Q: is infer project by main class name necessary? perf impact? - List javaProjects = ResolveClasspathsHandler.getJavaProjectFromType(params.getMainClass()); - if (javaProjects.size() == 1) { - mainProject = javaProjects.get(0).getProject(); - } - } catch (CoreException e) { - JavaLanguageServerPlugin.logException("Failed to resolve project from main class name.", e); - } + if (params == null) { + throw new IllegalArgumentException("The compile parameters should not be null."); } - if (isBspProject(mainProject) && !ProjectUtils.isGradleProject(mainProject)) { + IProject mainProject = JdtUtils.getMainProject(params.getProjectName(), params.getMainClass()); + if (JdtUtils.isBspProject(mainProject) && !ProjectUtils.isGradleProject(mainProject)) { // Just need to trigger a build for the target project, the Gradle build server will // handle the build dependencies for us. try { @@ -78,20 +73,37 @@ public static Object compile(CompileParams params, IProgressMonitor monitor) { return BuildWorkspaceStatus.SUCCEED; } - try { - if (monitor.isCanceled()) { - return BuildWorkspaceStatus.CANCELLED; - } + if (monitor.isCanceled()) { + return BuildWorkspaceStatus.CANCELLED; + } - long compileAt = System.currentTimeMillis(); - if (params != null && params.isFullBuild()) { - ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); - ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor); - } else { - ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor); + ProjectBuildParams buildParams = new ProjectBuildParams(); + List identifiers = new LinkedList<>(); + buildParams.setFullBuild(params.isFullBuild); + for (IJavaProject javaProject : ProjectUtils.getJavaProjects()) { + if (ProjectsManager.getDefaultProject().equals(javaProject.getProject())) { + continue; } - logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); + // we only build project which is not a BSP project, in case that the compile request is triggered by + // HCR with auto-build disabled, the build for BSP projects will be triggered by JavaHotCodeReplaceProvider. + if (!JdtUtils.isBspProject(javaProject.getProject())) { + identifiers.add(new TextDocumentIdentifier(javaProject.getProject().getLocationURI().toString())); + } + } + if (identifiers.size() == 0) { + return BuildWorkspaceStatus.SUCCEED; + } + buildParams.setIdentifiers(identifiers); + long compileAt = System.currentTimeMillis(); + BuildWorkspaceHandler buildWorkspaceHandler = new BuildWorkspaceHandler(JavaLanguageServerPlugin.getProjectsManager()); + BuildWorkspaceStatus status = buildWorkspaceHandler.buildProjects(buildParams, monitor); + logger.info("Time cost for ECJ: " + (System.currentTimeMillis() - compileAt) + "ms"); + if (status == BuildWorkspaceStatus.FAILED || status == BuildWorkspaceStatus.CANCELLED) { + return status; + } + + try { IResource currentResource = mainProject; if (isUnmanagedFolder(mainProject) && StringUtils.isNotBlank(params.getMainClass())) { IType mainType = ProjectUtils.getJavaProject(mainProject).findType(params.getMainClass()); @@ -135,17 +147,14 @@ public static Object compile(CompileParams params, IProgressMonitor monitor) { } } - if (problemMarkers.isEmpty()) { - return BuildWorkspaceStatus.SUCCEED; + if (!problemMarkers.isEmpty()) { + return BuildWorkspaceStatus.WITH_ERROR; } - - return BuildWorkspaceStatus.WITH_ERROR; } catch (CoreException e) { - JavaLanguageServerPlugin.logException("Failed to build workspace.", e); - return BuildWorkspaceStatus.FAILED; - } catch (OperationCanceledException e) { - return BuildWorkspaceStatus.CANCELLED; + JavaLanguageServerPlugin.log(e); } + + return BuildWorkspaceStatus.SUCCEED; } private static boolean isUnmanagedFolder(IProject project) { @@ -153,11 +162,6 @@ private static boolean isUnmanagedFolder(IProject project) { && ProjectUtils.isJavaProject(project); } - private static boolean isBspProject(IProject project) { - return project != null && ProjectUtils.isJavaProject(project) - && ProjectUtils.hasNature(project, "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature"); - } - private static IProject getDefaultProject() { return getWorkspaceRoot().getProject(ProjectsManager.DEFAULT_PROJECT_NAME); } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java index 2bf905c89..3a2b2f3df 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaHotCodeReplaceProvider.java @@ -34,16 +34,20 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; @@ -55,6 +59,7 @@ import org.eclipse.jdt.core.util.ISourceAttribute; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.jdt.ls.core.internal.JobHelpers; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; import com.microsoft.java.debug.core.Configuration; import com.microsoft.java.debug.core.DebugException; @@ -63,6 +68,7 @@ import com.microsoft.java.debug.core.IDebugSession; import com.microsoft.java.debug.core.StackFrameUtility; import com.microsoft.java.debug.core.adapter.AdapterUtils; +import com.microsoft.java.debug.core.adapter.Constants; import com.microsoft.java.debug.core.adapter.ErrorCode; import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; @@ -104,6 +110,8 @@ public class JavaHotCodeReplaceProvider implements IHotCodeReplaceProvider, IRes private List deltaClassNames = new ArrayList<>(); + private String mainProjectName = ""; + /** * Visitor for resource deltas. */ @@ -269,6 +277,7 @@ public void initialize(IDebugAdapterContext context, Map options } this.context = context; currentDebugSession = context.getDebugSession(); + this.mainProjectName = ((String) options.get(Constants.PROJECT_NAME)); } @Override @@ -319,6 +328,7 @@ public void onClassRedefined(Consumer> consumer) { @Override public CompletableFuture> redefineClasses() { + triggerBuildForBspProject(); JobHelpers.waitForBuildJobs(10 * 1000); return CompletableFuture.supplyAsync(() -> { List classNames = new ArrayList<>(); @@ -737,4 +747,39 @@ private List getStackFrames(ThreadReference thread, boolean refresh) } }); } + + /** + * Trigger build separately if the main project is a BSP project. + * This is because auto build for BSP project will not update the class files to disk. + */ + private void triggerBuildForBspProject() { + // check if the workspace contains BSP project first. This is for performance consideration. + // Due to that getJavaProjectFromType() is a heavy operation. + if (!containsBspProjects()) { + return; + } + + IProject mainProject = JdtUtils.getMainProject(this.mainProjectName, context.getMainClass()); + if (mainProject != null && JdtUtils.isBspProject(mainProject)) { + try { + ResourcesPlugin.getWorkspace().build( + new IBuildConfiguration[]{mainProject.getActiveBuildConfig()}, + IncrementalProjectBuilder.INCREMENTAL_BUILD, + false /*buildReference*/, + new NullProgressMonitor() + ); + } catch (CoreException e) { + // ignore compilation errors + } + } + } + + private boolean containsBspProjects() { + for (IJavaProject javaProject : ProjectUtils.getJavaProjects()) { + if (JdtUtils.isBspProject(javaProject.getProject())) { + return true; + } + } + return false; + } } diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java index a4c06c6d4..66562ae74 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtUtils.java @@ -40,6 +40,8 @@ import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; import com.microsoft.java.debug.core.DebugException; import com.microsoft.java.debug.core.StackFrameUtility; @@ -415,4 +417,36 @@ public static boolean isSameFile(IResource resource1, IResource resource2) { return Objects.equals(resource1.getLocation(), resource2.getLocation()); } + + /** + * Check if the project is managed by Gradle Build Server. + */ + public static boolean isBspProject(IProject project) { + return project != null && ProjectUtils.isJavaProject(project) + && ProjectUtils.hasNature(project, "com.microsoft.gradle.bs.importer.GradleBuildServerProjectNature"); + } + + /** + * Get main project according to the main project name or main class name, + * or return null if the main project cannot be resolved. + */ + public static IProject getMainProject(String mainProjectName, String mainClassName) { + IProject mainProject = null; + if (StringUtils.isNotBlank(mainProjectName)) { + mainProject = ProjectUtils.getProject(mainProjectName); + } + + if (mainProject == null && StringUtils.isNotBlank(mainClassName)) { + try { + List javaProjects = ResolveClasspathsHandler.getJavaProjectFromType(mainClassName); + if (javaProjects.size() == 1) { + mainProject = javaProjects.get(0).getProject(); + } + } catch (CoreException e) { + JavaLanguageServerPlugin.logException("Failed to resolve project from main class name.", e); + } + } + + return mainProject; + } } From 6511f97c76d4dd562f94c3ac503a982407d211a3 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 17 Jun 2024 16:50:27 +0800 Subject: [PATCH 113/144] Update tp to fix the build failure (#556) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index a52901d01..6edb44dec 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -15,7 +15,7 @@ - + From e06a1b686625b28a66e0bd6a22f4668fb4f02b4d Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 27 Jun 2024 10:31:27 +0800 Subject: [PATCH 114/144] bump version to 0.53.0 (#557) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 3b42e40cb..bb1449401 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index 6de8860a5..dc81da8e6 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index b45c99f7a..192908dcf 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.52.0 +Bundle-Version: 0.53.0 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.52.0.jar + lib/com.microsoft.java.debug.core-0.53.0.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 6d58947b0..93323db5c 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.52.0 + 0.53.0 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index fc6ff6a62..b09605569 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 3d5537f13..edb1c9891 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index e4ee29b92..2dc407f6c 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.52.0 + 0.53.0 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index f2e4d0eef..c277e8a98 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.52.0 + 0.53.0 pom Java Debug Server for Visual Studio Code From 24c61a7b06b2e3dec661b97688f9b6d033de5fab Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 19 Jul 2024 13:34:22 +0800 Subject: [PATCH 115/144] Remove the deprecated debug parameters from the launching connector (#562) --- .../java/debug/plugin/internal/AdvancedLaunchingConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index 3551ab2e6..087f0fae7 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -195,7 +195,7 @@ private static String[] constructLaunchCommand(Map l StringBuilder execString = new StringBuilder(); execString.append("\"" + javaHome + slash + "bin" + slash + javaExec + "\""); - execString.append(" -Xdebug -Xnoagent -Djava.compiler=NONE"); + execString.append(" -Djava.compiler=NONE"); execString.append(" -Xrunjdwp:transport=dt_socket,address=" + address + ",server=n,suspend=" + (suspend ? "y" : "n")); if (javaOptions != null) { execString.append(" " + javaOptions); From 1b669f8c4c92c179acf89498cd14bcb8397bd3b5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 19 Jul 2024 14:36:08 +0800 Subject: [PATCH 116/144] Adopt the new code sign job in the pipeline (#563) --- .azure-pipelines/signjars-nightly.yml | 18 +++++++++++--- .azure-pipelines/signjars-rc.yml | 36 +++++++++++++++++++++------ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 0e53a4219..2c0373aab 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -70,10 +70,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -111,10 +116,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index ebb333f70..851b73775 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -71,10 +71,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign core.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.core*.jar signConfigType: inlineSignParams @@ -111,10 +116,15 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign plugin.jar inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: jars Pattern: com.microsoft.java.debug.plugin*.jar signConfigType: inlineSignParams @@ -152,10 +162,15 @@ extends: mkdir -p p2/target cp -r com.microsoft.java.debug.repository/target/repository p2/target/ cp com.microsoft.java.debug.repository/pushToBintray.sh p2/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign p2 inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: p2 Pattern: "*.jar" signConfigType: inlineSignParams @@ -199,10 +214,15 @@ extends: mkdir -p m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin*.jar m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/pom.xml m2/com.microsoft.java.debug.plugin/com.microsoft.java.debug.plugin-$RELEASE_VERSION.pom - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign m2 inputs: - ConnectedServiceName: vscjavaci_esrp_codesign + ConnectedServiceName: $(ConnectedServiceName) + AppRegistrationClientId: $(AppRegistrationClientId) + AppRegistrationTenantId: $(AppRegistrationTenantId) + AuthAKVName: $(AuthAKVName) + AuthCertName: $(AuthCertName) + AuthSignCertName: $(AuthSignCertName) FolderPath: m2 Pattern: "*.jar" signConfigType: inlineSignParams From 26daf612fb03f8dd7befaa980feb327644ee43a7 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 22 Jul 2024 10:07:30 +0800 Subject: [PATCH 117/144] Remove the obselete parameter '-Djava.compiler=NONE' from launching command (#564) --- .../java/debug/plugin/internal/AdvancedLaunchingConnector.java | 1 - 1 file changed, 1 deletion(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java index 087f0fae7..24c65785d 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/AdvancedLaunchingConnector.java @@ -195,7 +195,6 @@ private static String[] constructLaunchCommand(Map l StringBuilder execString = new StringBuilder(); execString.append("\"" + javaHome + slash + "bin" + slash + javaExec + "\""); - execString.append(" -Djava.compiler=NONE"); execString.append(" -Xrunjdwp:transport=dt_socket,address=" + address + ",server=n,suspend=" + (suspend ? "y" : "n")); if (javaOptions != null) { execString.append(" " + javaOptions); From 3691f3b5f2cae05b06b43333dc435c172e27f1a8 Mon Sep 17 00:00:00 2001 From: bsolar17 Date: Thu, 8 Aug 2024 03:49:58 +0200 Subject: [PATCH 118/144] Update lsp4j dependency to version 0.23.1 (#565) New version of jdtls core requires lsp4j 0.23 or greater. --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 6edb44dec..41215e0ba 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -36,7 +36,7 @@ - + From e54f5c20c9cac846eab9f4af0f4fef671727b919 Mon Sep 17 00:00:00 2001 From: Vincentius Adrian Date: Wed, 18 Sep 2024 14:36:52 +0700 Subject: [PATCH 119/144] Update eclipse to use v4.33 (#571) --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 41215e0ba..5c386e467 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -15,7 +15,7 @@ - + From d4afc7e55107b333c3a1126fbf42c18c0dfd69cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:32:24 +0800 Subject: [PATCH 120/144] Bump commons-io:commons-io in /com.microsoft.java.debug.core (#573) Bumps commons-io:commons-io from 2.11.0 to 2.14.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index bb1449401..d8dae21e7 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -62,7 +62,7 @@ commons-io commons-io - 2.11.0 + 2.14.0 From 5c7562ade22977b15f76f46d13c098e82cc58ced Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 31 Oct 2024 14:38:24 +0800 Subject: [PATCH 121/144] bump version to 0.53.1 (#574) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index d8dae21e7..64827ab13 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index dc81da8e6..dbf84ced8 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 192908dcf..ea6b7127e 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.53.0 +Bundle-Version: 0.53.1 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.11.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.53.0.jar + lib/com.microsoft.java.debug.core-0.53.1.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index 93323db5c..dde9c7ec6 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.53.0 + 0.53.1 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index b09605569..5d3e664df 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index edb1c9891..40519fce2 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 2dc407f6c..737decee1 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.0 + 0.53.1 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index c277e8a98..53aec5c9c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.53.0 + 0.53.1 pom Java Debug Server for Visual Studio Code From 4cb23d2493aae8f17618764591fb37d71a178d0f Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 27 Feb 2025 18:49:15 -0800 Subject: [PATCH 122/144] Update CI to run against jdk 21 (#581) --- .azure-pipelines/signjars-nightly.yml | 4 ++-- .azure-pipelines/signjars-rc.yml | 4 ++-- .github/workflows/build.yml | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 2c0373aab..8f405a725 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -46,9 +46,9 @@ extends: - checkout: self fetchTags: true - task: JavaToolInstaller@0 - displayName: Use Java 17 + displayName: Use Java 21 inputs: - versionSpec: "17" + versionSpec: "21" jdkArchitectureOption: x64 jdkSourceOption: PreInstalled - task: CmdLine@2 diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 851b73775..c0c11e6cc 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -45,9 +45,9 @@ extends: - checkout: self fetchTags: true - task: JavaToolInstaller@0 - displayName: Use Java 17 + displayName: Use Java 21 inputs: - versionSpec: "17" + versionSpec: "21" jdkArchitectureOption: x64 jdkSourceOption: PreInstalled - task: CmdLine@2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1ace44224..c147c4838 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: '17' + java-version: '21' - name: Cache local Maven repository uses: actions/cache@v2 @@ -45,10 +45,10 @@ jobs: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: '17' + java-version: '21' - name: Cache local Maven repository uses: actions/cache@v2 @@ -71,10 +71,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: '17' + java-version: '21' - name: Cache local Maven repository uses: actions/cache@v2 From dff3538d4f741e7e05a304614e3d5d9e7e0bfb0b Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 28 Feb 2025 07:13:49 +0000 Subject: [PATCH 123/144] Attempt to terminate the process normally (#579) Fixes https://github.com/microsoft/vscode-java-debug/issues/1274 --- .../main/java/com/microsoft/java/debug/core/DebugSession.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java index fbad52fe2..38a234fa9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java @@ -119,7 +119,9 @@ public void detach() { @Override public void terminate() { - if (vm.process() == null || vm.process().isAlive()) { + if (vm.process() != null && vm.process().isAlive()) { + vm.process().destroy(); + } else if (vm.process() == null || vm.process().isAlive()) { vm.exit(0); } } From 31b22551b27712d265694c7afbe21d0eabccd1d1 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 21 Mar 2025 16:31:31 +0800 Subject: [PATCH 124/144] Onboard pipelines to MicroBuild (#583) * Onboard pipelines to MicroBuild * onboard code sign to microbuild * remove p2 copy logic * remove p2 buildartifact dir * use MicroBuild to publish to maven * fix m2 sign path * upgrade to actions/cache@v4 --- .azure-pipelines/publish-to-maven.yml | 100 ++++++++++++++ .azure-pipelines/signjars-nightly.yml | 97 +++++-------- .azure-pipelines/signjars-rc.yml | 187 +++++++------------------- .github/workflows/build.yml | 6 +- 4 files changed, 183 insertions(+), 207 deletions(-) create mode 100644 .azure-pipelines/publish-to-maven.yml diff --git a/.azure-pipelines/publish-to-maven.yml b/.azure-pipelines/publish-to-maven.yml new file mode 100644 index 000000000..58dc58cf2 --- /dev/null +++ b/.azure-pipelines/publish-to-maven.yml @@ -0,0 +1,100 @@ +name: $(Date:yyyyMMdd).$(Rev:r) +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release +trigger: none +extends: + template: azure-pipelines/1ES.Official.Publish.yml@MicroBuildTemplate + parameters: + pool: + os: linux + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Ubuntu-2004 + sdl: + sourceAnalysisPool: + name: 1ES_JavaTooling_Pool + image: 1ES_JavaTooling_Windows_2022 + os: windows + stages: + - stage: PublishToMaven + jobs: + - job: PublishToMaven + steps: + - task: DownloadBuildArtifacts@1 + displayName: 'Download Jar Artifacts' + inputs: + buildType: specific + project: 'a4d27ce2-a42d-4b71-8eef-78cee9a9728e' + pipeline: 16486 + downloadType: specific + extractTars: false + itemPattern: 'm2/**' + - script: | + echo "import public key" + echo $GPG_PUBLIC_B64 | base64 -d | gpg --import + + echo "import secret key" + echo $GPG_SECRET_B64 | base64 -d | gpg --batch --passphrase $GPGPASS --import + displayName: 'import GPG keys' + env: + GPG_PUBLIC_B64: $(GPG_PUBLIC_B64) + GPG_SECRET_B64: $(GPG_SECRET_B64) + GPGPASS: $(GPGPASS) + - task: NodeTool@0 + displayName: 'Use Node 20.x' + inputs: + versionSpec: 20.x + - script: | + cd $(System.ArtifactsDirectory)/m2 + pluginJarFile=$(basename -- java-debug-parent/*.pom) + + # remove .* from end + noExt=${pluginJarFile%.*} + + # remove *- from start + export releaseVersion=${noExt##*-} + echo $releaseVersion + + export artifactFolder=$(pwd .) + wget https://raw.githubusercontent.com/microsoft/java-debug/master/scripts/publishMaven.js + + export GPG_TTY=$(tty) + node publishMaven.js -task gpg + displayName: 'sign artifacts' + env: + GPG_PUBLIC_B64: $(GPG_PUBLIC_B64) + GPG_SECRET_B64: $(GPG_SECRET_B64) + GPGPASS: $(GPGPASS) + NEXUS_OSSRHPASS: $(NEXUS_OSSRHPASS) + NEXUS_OSSRHUSER: $(NEXUS_OSSRHUSER) + NEXUS_STAGINGPROFILEID: $(NEXUS_STAGINGPROFILEID) + - template: MicroBuild.Publish.yml@MicroBuildTemplate + parameters: + intent: 'PackageDistribution' + contentType: 'Maven' + contentSource: 'Folder' + folderLocation: '$(System.ArtifactsDirectory)/m2/java-debug-parent' + waitForReleaseCompletion: true + owners: 'jinbwan@microsoft.com' + approvers: 'roml@microsoft.com' + - template: MicroBuild.Publish.yml@MicroBuildTemplate + parameters: + intent: 'PackageDistribution' + contentType: 'Maven' + contentSource: 'Folder' + folderLocation: '$(System.ArtifactsDirectory)/m2/com.microsoft.java.debug.core' + waitForReleaseCompletion: true + owners: 'jinbwan@microsoft.com' + approvers: 'roml@microsoft.com' + - template: MicroBuild.Publish.yml@MicroBuildTemplate + parameters: + intent: 'PackageDistribution' + contentType: 'Maven' + contentSource: 'Folder' + folderLocation: '$(System.ArtifactsDirectory)/m2/com.microsoft.java.debug.plugin' + waitForReleaseCompletion: true + owners: 'jinbwan@microsoft.com' + approvers: 'roml@microsoft.com' \ No newline at end of file diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 8f405a725..19ab29baf 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -45,6 +45,23 @@ extends: steps: - checkout: self fetchTags: true + - task: UsePythonVersion@0 + displayName: 'Use Python 3.11.x' + inputs: + versionSpec: 3.11.x + - task: UseDotNet@2 + displayName: 'Use .NET Core 3.1.x' + inputs: + packageType: 'sdk' + version: '3.1.x' + - task: MicroBuildSigningPlugin@4 + displayName: 'Install Signing Plugin' + inputs: + signType: real + azureSubscription: 'MicroBuild Signing Task (MSEng)' + feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: @@ -70,38 +87,16 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign core.jar + - task: CmdLine@2 + displayName: Sign core jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.core*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.core*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -116,38 +111,16 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign plugin.jar + - task: CmdLine@2 + displayName: Sign plugin jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.plugin*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.plugin*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CopyFiles@2 displayName: "Copy plugin.jar to: $(Build.ArtifactStagingDirectory)" inputs: diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index c0c11e6cc..201ff20f8 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -37,13 +37,26 @@ extends: artifactName: m2 targetPath: $(Build.ArtifactStagingDirectory)/m2 displayName: "Publish Artifact: m2" - - output: pipelineArtifact - artifactName: p2 - targetPath: $(Build.ArtifactStagingDirectory)/p2 - displayName: "Publish Artifact: p2" steps: - checkout: self fetchTags: true + - task: UsePythonVersion@0 + displayName: 'Use Python 3.11.x' + inputs: + versionSpec: 3.11.x + - task: UseDotNet@2 + displayName: 'Use .NET Core 3.1.x' + inputs: + packageType: 'sdk' + version: '3.1.x' + - task: MicroBuildSigningPlugin@4 + displayName: 'Install Signing Plugin' + inputs: + signType: real + azureSubscription: 'MicroBuild Signing Task (MSEng)' + feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: @@ -71,38 +84,16 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.core/$RELEASE_VERSION/com.microsoft.java.debug.core*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign core.jar + - task: CmdLine@2 + displayName: Sign core jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.core*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.core*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -116,84 +107,20 @@ extends: mkdir -p jars mv .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/com.microsoft.java.debug.plugin*.jar jars/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign plugin.jar + - task: CmdLine@2 + displayName: Sign plugin jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: jars - Pattern: com.microsoft.java.debug.plugin*.jar - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "com.microsoft.java.debug.plugin*.jar") + for file in $files; do + fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 + done + workingDirectory: 'jars' - task: CmdLine@2 displayName: install signed plugin.jar inputs: script: cp jars/com.microsoft.java.debug.plugin*.jar .repository/com/microsoft/java/com.microsoft.java.debug.plugin/$RELEASE_VERSION/ - - task: CmdLine@2 - displayName: Build p2 artifacts - inputs: - script: |- - # 3. Build the p2 artifacts. - ./mvnw clean package -f com.microsoft.java.debug.repository/pom.xml -Dmaven.repo.local=./.repository - - mkdir -p p2/target - cp -r com.microsoft.java.debug.repository/target/repository p2/target/ - cp com.microsoft.java.debug.repository/pushToBintray.sh p2/ - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign p2 - inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: p2 - Pattern: "*.jar" - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] - task: CmdLine@2 displayName: build m2 artifacts inputs: @@ -214,43 +141,19 @@ extends: mkdir -p m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin*.jar m2/com.microsoft.java.debug.plugin cp com.microsoft.java.debug.plugin/pom.xml m2/com.microsoft.java.debug.plugin/com.microsoft.java.debug.plugin-$RELEASE_VERSION.pom - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 - displayName: Sign m2 + - task: CmdLine@2 + displayName: Sign m2 jars inputs: - ConnectedServiceName: $(ConnectedServiceName) - AppRegistrationClientId: $(AppRegistrationClientId) - AppRegistrationTenantId: $(AppRegistrationTenantId) - AuthAKVName: $(AuthAKVName) - AuthCertName: $(AuthCertName) - AuthSignCertName: $(AuthSignCertName) - FolderPath: m2 - Pattern: "*.jar" - signConfigType: inlineSignParams - inlineOperation: |- - [ - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaSign", - "Parameters" : { - "SigAlg" : "SHA256withRSA", - "Timestamp" : "-tsa http://sha256timestamp.ws.digicert.com/sha256/timestamp" - }, - "ToolName" : "sign", - "ToolVersion" : "1.0" - }, - { - "KeyCode" : "CP-447347-Java", - "OperationCode" : "JavaVerify", - "Parameters" : {}, - "ToolName" : "sign", - "ToolVersion" : "1.0" - } - ] + script: | + files=$(find . -type f -name "*.jar") + for file in $files; do + # fileName=$(basename "$file") + dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$file" /certs:100010171 + done + workingDirectory: 'm2' - task: CopyFiles@2 - displayName: "Copy p2/m2 to: $(Build.ArtifactStagingDirectory)" + displayName: "Copy m2 to: $(Build.ArtifactStagingDirectory)" inputs: Contents: |+ - p2/** m2/** - TargetFolder: $(Build.ArtifactStagingDirectory) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c147c4838..71d726f63 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: java-version: '21' - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -51,7 +51,7 @@ jobs: java-version: '21' - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: $HOME/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -77,7 +77,7 @@ jobs: java-version: '21' - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From b98d4936c52ea933b00faacfac67fd4fcb86e790 Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Thu, 17 Apr 2025 03:47:29 -0400 Subject: [PATCH 125/144] Update target platform to more closely follow JDT-LS. (#585) - Update Apache Commons IO from 2.11.0 to 2.19.0 Signed-off-by: Roland Grunberg --- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 2 +- com.microsoft.java.debug.plugin/pom.xml | 2 +- .../com.microsoft.java.debug.tp.target | 42 ++++--------------- 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index dbf84ced8..a83e822fe 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -7,7 +7,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index ea6b7127e..073c31c8e 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -21,7 +21,7 @@ Require-Bundle: org.eclipse.core.runtime, org.apache.commons.lang3, org.eclipse.lsp4j, com.google.guava -Bundle-ClassPath: lib/commons-io-2.11.0.jar, +Bundle-ClassPath: lib/commons-io-2.19.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index dde9c7ec6..d2d7b9aa4 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -51,7 +51,7 @@ commons-io commons-io - 2.11.0 + 2.19.0 com.microsoft.java diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 5c386e467..8a85443a4 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -1,43 +1,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - + + + + + + From b77b590ad89345557c87c975a4b41c5f648e65f1 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Wed, 23 Apr 2025 13:39:54 +0800 Subject: [PATCH 126/144] Override MicroBuild Output Folder (#586) * Override MicroBuild Output Folder * Use env to override MicroBuildOutputFolder --- .azure-pipelines/signjars-nightly.yml | 1 + .azure-pipelines/signjars-rc.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 19ab29baf..b2ab77ab7 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -62,6 +62,7 @@ extends: feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 201ff20f8..8e639bafb 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -57,6 +57,7 @@ extends: feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' - task: JavaToolInstaller@0 displayName: Use Java 21 inputs: From 54058b9eb466576a7a77c9303963ca88967ec58b Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Sun, 27 Apr 2025 10:50:06 +0800 Subject: [PATCH 127/144] Bump version to 0.53.2 (#587) --- com.microsoft.java.debug.core/pom.xml | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF | 4 ++-- com.microsoft.java.debug.plugin/pom.xml | 4 ++-- com.microsoft.java.debug.repository/category.xml | 2 +- com.microsoft.java.debug.repository/pom.xml | 2 +- com.microsoft.java.debug.target/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index 64827ab13..d63b93c01 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.core jar diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index a83e822fe..de9b5e366 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -11,6 +11,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 073c31c8e..442e9bb17 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true -Bundle-Version: 0.53.1 +Bundle-Version: 0.53.2 Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin @@ -25,4 +25,4 @@ Bundle-ClassPath: lib/commons-io-2.19.0.jar, ., lib/rxjava-2.2.21.jar, lib/reactive-streams-1.0.4.jar, - lib/com.microsoft.java.debug.core-0.53.1.jar + lib/com.microsoft.java.debug.core-0.53.2.jar diff --git a/com.microsoft.java.debug.plugin/pom.xml b/com.microsoft.java.debug.plugin/pom.xml index d2d7b9aa4..2e2c0e6a8 100644 --- a/com.microsoft.java.debug.plugin/pom.xml +++ b/com.microsoft.java.debug.plugin/pom.xml @@ -5,7 +5,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.plugin eclipse-plugin @@ -56,7 +56,7 @@ com.microsoft.java com.microsoft.java.debug.core - 0.53.1 + 0.53.2 diff --git a/com.microsoft.java.debug.repository/category.xml b/com.microsoft.java.debug.repository/category.xml index 5d3e664df..f30a2f4c4 100644 --- a/com.microsoft.java.debug.repository/category.xml +++ b/com.microsoft.java.debug.repository/category.xml @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.repository/pom.xml b/com.microsoft.java.debug.repository/pom.xml index 40519fce2..573aff2ac 100644 --- a/com.microsoft.java.debug.repository/pom.xml +++ b/com.microsoft.java.debug.repository/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.repository eclipse-repository diff --git a/com.microsoft.java.debug.target/pom.xml b/com.microsoft.java.debug.target/pom.xml index 737decee1..8e49b638b 100644 --- a/com.microsoft.java.debug.target/pom.xml +++ b/com.microsoft.java.debug.target/pom.xml @@ -4,7 +4,7 @@ com.microsoft.java java-debug-parent - 0.53.1 + 0.53.2 com.microsoft.java.debug.tp ${base.name} :: Target Platform diff --git a/pom.xml b/pom.xml index 53aec5c9c..9f59485d9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ${base.name} :: Parent The Java Debug Server is an implementation of Visual Studio Code (VSCode) Debug Protocol. It can be used in Visual Studio Code to debug Java programs. https://github.com/Microsoft/java-debug - 0.53.1 + 0.53.2 pom Java Debug Server for Visual Studio Code From bccb4808571f2fcdce497e5585320ff62491c9f5 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Thu, 26 Jun 2025 09:54:18 +0800 Subject: [PATCH 128/144] switch sha256 to calculate temp file name (#592) --- .../debug/core/adapter/handler/LaunchUtils.java | 15 +++++++++------ .../com.microsoft.java.debug.tp.target | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 27fdb1813..7370328b2 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -95,7 +95,7 @@ public static synchronized Path generateClasspathJar(String[] classPaths) throws // In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar String classpathValue = String.join(" ", classpathUrls); attributes.put(Attributes.Name.CLASS_PATH, classpathValue); - String baseName = "cp_" + getMd5(classpathValue); + String baseName = "cp_" + getSha256(classpathValue); cleanupTempFiles(baseName, ".jar"); Path tempfile = createTempFile(baseName, ".jar"); JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest); @@ -127,7 +127,7 @@ public static synchronized Path generateArgfile(String vmArgs, String[] classPat } argfile = argfile.replace("\\", "\\\\"); - String baseName = "cp_" + getMd5(argfile); + String baseName = "cp_" + getSha256(argfile); cleanupTempFiles(baseName, ".argfile"); Path tempfile = createTempFile(baseName, ".argfile"); Files.writeString(tempfile, argfile, encoding); @@ -364,12 +364,15 @@ private static Path createTempFile(String baseName, String suffix) throws IOExce } } - private static String getMd5(String input) { + private static String getSha256(String input) { try { - MessageDigest md = MessageDigest.getInstance("MD5"); + MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] messageDigest = md.digest(input.getBytes()); - BigInteger md5 = new BigInteger(1, messageDigest); - return md5.toString(Character.MAX_RADIX); + // Use only first 16 bytes to keep filename shorter + byte[] truncated = new byte[16]; + System.arraycopy(messageDigest, 0, truncated, 0, 16); + BigInteger hash = new BigInteger(1, truncated); + return hash.toString(Character.MAX_RADIX); } catch (NoSuchAlgorithmException e) { return Integer.toString(input.hashCode(), Character.MAX_RADIX); } diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 8a85443a4..1d322d84c 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -2,7 +2,7 @@ - + From a1f68f4265458e9386bf16d107c2383b68690e43 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 27 Jun 2025 15:01:25 +0800 Subject: [PATCH 129/144] Update codesign task (#593) --- .azure-pipelines/signjars-nightly.yml | 10 ++++++---- .azure-pipelines/signjars-rc.yml | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index b2ab77ab7..2045f54ce 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -45,20 +45,22 @@ extends: steps: - checkout: self fetchTags: true - - task: UsePythonVersion@0 - displayName: 'Use Python 3.11.x' - inputs: - versionSpec: 3.11.x - task: UseDotNet@2 displayName: 'Use .NET Core 3.1.x' inputs: packageType: 'sdk' version: '3.1.x' + - task: UseDotNet@2 + displayName: 'Use .NET Core 8.0.x' + inputs: + packageType: 'sdk' + version: '8.0.x' - task: MicroBuildSigningPlugin@4 displayName: 'Install Signing Plugin' inputs: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' + useEsrpCli: true feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 8e639bafb..cd3377cfd 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -40,20 +40,22 @@ extends: steps: - checkout: self fetchTags: true - - task: UsePythonVersion@0 - displayName: 'Use Python 3.11.x' - inputs: - versionSpec: 3.11.x - task: UseDotNet@2 displayName: 'Use .NET Core 3.1.x' inputs: packageType: 'sdk' version: '3.1.x' + - task: UseDotNet@2 + displayName: 'Use .NET Core 8.0.x' + inputs: + packageType: 'sdk' + version: '8.0.x' - task: MicroBuildSigningPlugin@4 displayName: 'Install Signing Plugin' inputs: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' + useEsrpCli: true feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 36a6b3f3606bd500fd7b257c55ae2572893d319e Mon Sep 17 00:00:00 2001 From: chagong Date: Wed, 13 Aug 2025 14:35:29 +0800 Subject: [PATCH 130/144] Update CODEOWNERS (#599) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b11684f52..9d7579633 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @testforstephen @jdneo \ No newline at end of file +* @testforstephen @jdneo @chagong @wenytang-ms From c7642bc738b2dbfeefa0ea85e338d9eefd0ebc25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:09:45 +0800 Subject: [PATCH 131/144] Bump org.apache.commons:commons-lang3 in /com.microsoft.java.debug.core (#594) Bumps org.apache.commons:commons-lang3 from 3.6 to 3.18.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.18.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: chagong --- com.microsoft.java.debug.core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/pom.xml b/com.microsoft.java.debug.core/pom.xml index d63b93c01..8bfdb7292 100644 --- a/com.microsoft.java.debug.core/pom.xml +++ b/com.microsoft.java.debug.core/pom.xml @@ -42,7 +42,7 @@ org.apache.commons commons-lang3 - 3.6 + 3.18.0 com.google.code.gson From a209e39b9e07e5b2eacb74a2a2250da99c6751e3 Mon Sep 17 00:00:00 2001 From: Snjeza Date: Wed, 13 Aug 2025 09:18:31 +0200 Subject: [PATCH 132/144] Improve ResolveMainClassHandler.resolveMainClassUnderPaths (#596) Co-authored-by: wenyt <75360946+wenytang-ms@users.noreply.github.com> --- .../plugin/internal/ResolveMainClassHandler.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index 38ad1532b..fc8189445 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -35,6 +35,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; @@ -100,8 +101,18 @@ private List resolveMainClassCore(List arguments) { private List resolveMainClassUnderPaths(List parentPaths) { // Limit to search main method from source code only. - IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), - IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); + IJavaProject[] projects; + if (parentPaths == null || parentPaths.isEmpty()) { + projects = ProjectUtils.getJavaProjects(); + } else { + projects = Stream.of(ProjectUtils.getAllProjects()) + .filter(p -> ProjectUtils.isJavaProject(p) && p.getLocation() != null && ResourceUtils.isContainedIn(p.getLocation(), parentPaths)) + .map(p -> JavaCore.create(p)) + .filter(p -> p.exists()) + .toArray(IJavaProject[]::new); + } + IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(projects, + IJavaSearchScope.SOURCES); SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { From 65dcd3d77c548bd842a74e472c8b25cd8489905a Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:24:44 +0800 Subject: [PATCH 133/144] ci: update the pipeline to do publish (#602) --- .azure-pipelines/publish-to-maven.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.azure-pipelines/publish-to-maven.yml b/.azure-pipelines/publish-to-maven.yml index 58dc58cf2..a1ef0a204 100644 --- a/.azure-pipelines/publish-to-maven.yml +++ b/.azure-pipelines/publish-to-maven.yml @@ -22,6 +22,10 @@ extends: - stage: PublishToMaven jobs: - job: PublishToMaven + displayName: Maven Release job + templateContext: + type: releaseJob + isProduction: true steps: - task: DownloadBuildArtifacts@1 displayName: 'Download Jar Artifacts' From 3bec8be6525ff8ced06d57157c847870adc2bc49 Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Mon, 25 Aug 2025 11:27:00 +0800 Subject: [PATCH 134/144] ci: support PME to do code sign (#603) --- .azure-pipelines/signjars-nightly.yml | 1 + .azure-pipelines/signjars-rc.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 2045f54ce..70fe81be2 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -61,6 +61,7 @@ extends: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' useEsrpCli: true + ConnectedPMEServiceName: 0e38ce24-f885-4c86-b997-5887b97a1899 feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index cd3377cfd..819d39de7 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -56,6 +56,7 @@ extends: signType: real azureSubscription: 'MicroBuild Signing Task (MSEng)' useEsrpCli: true + ConnectedPMEServiceName: 0e38ce24-f885-4c86-b997-5887b97a1899 feedSource: 'https://mseng.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 77d10aa0e8030da7570bdde8400f2d0fb3aa9e7d Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:16:24 +0800 Subject: [PATCH 135/144] ci: sign jar with access token (#604) --- .azure-pipelines/signjars-nightly.yml | 4 ++++ .azure-pipelines/signjars-rc.yml | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/.azure-pipelines/signjars-nightly.yml b/.azure-pipelines/signjars-nightly.yml index 70fe81be2..8b1e8d12a 100644 --- a/.azure-pipelines/signjars-nightly.yml +++ b/.azure-pipelines/signjars-nightly.yml @@ -101,6 +101,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -125,6 +127,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CopyFiles@2 displayName: "Copy plugin.jar to: $(Build.ArtifactStagingDirectory)" inputs: diff --git a/.azure-pipelines/signjars-rc.yml b/.azure-pipelines/signjars-rc.yml index 819d39de7..e87444603 100644 --- a/.azure-pipelines/signjars-rc.yml +++ b/.azure-pipelines/signjars-rc.yml @@ -98,6 +98,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CmdLine@2 displayName: install signed core.jar inputs: @@ -121,6 +123,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$fileName" /certs:100010171 done workingDirectory: 'jars' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CmdLine@2 displayName: install signed plugin.jar inputs: @@ -155,6 +159,8 @@ extends: dotnet "$MBSIGN_APPFOLDER/DDSignFiles.dll" -- /file:"$file" /certs:100010171 done workingDirectory: 'm2' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: CopyFiles@2 displayName: "Copy m2 to: $(Build.ArtifactStagingDirectory)" inputs: From 614b10df99acc34ced35fc50b9b70a8191719e2c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:30:26 +0800 Subject: [PATCH 136/144] Add AI triage workflow and LLM documentation for automated issue management (#606) * Initial plan * Add triage agent workflow and LLM documentation from vscode-gradle Co-authored-by: chagong <831821+chagong@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: chagong <831821+chagong@users.noreply.github.com> --- .github/llms.md | 38 +++++++++ .github/workflows/triage-agent.yml | 122 +++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 .github/llms.md create mode 100644 .github/workflows/triage-agent.yml diff --git a/.github/llms.md b/.github/llms.md new file mode 100644 index 000000000..55d69b238 --- /dev/null +++ b/.github/llms.md @@ -0,0 +1,38 @@ +# Extension Pack for Java +Extension Pack for Java is a collection of popular extensions that can help write, test and debug Java applications in Visual Studio Code. By installing Extension Pack for Java, the following extensions are installed: + +- [📦 Language Support for Java™ by Red Hat ](https://marketplace.visualstudio.com/items?itemName=redhat.java) + - Code Navigation + - Auto Completion + - Refactoring + - Code Snippets +- [📦 Debugger for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-debug) + - Debugging +- [📦 Test Runner for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-test) + - Run & Debug JUnit/TestNG Test Cases +- [📦 Maven for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-maven) + - Project Scaffolding + - Custom Goals +- [📦 Gradle for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-gradle) + - View Gradle tasks and project dependencies + - Gradle file authoring + - Import Gradle projects via [Gradle Build Server](https://github.com/microsoft/build-server-for-gradle) +- [📦 Project Manager for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-dependency) + - Manage Java projects, referenced libraries, resource files, packages, classes, and class members +- [📦 Visual Studio IntelliCode](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode) + - AI-assisted development + - Completion list ranked by AI + +## Label +When labeling an issue, follow the rules below per label category: +### General Rules +- Analyze if the issue is related with the scope of using extensions for Java development. If not, STOP labelling IMMEDIATELY. +- Assign label per category. +- If a category is not applicable or you're unsure, you may skip it. +- Do not assign multiple labels within the same category, unless explicitly allowed as an exception. + +### Issue Type Labels +- [bug]: Primary label for real bug issues +- [enhancement]: Primary label for enhancement issues +- [documentation]: Primary label for documentation issues +- [question]: Primary label for question issues \ No newline at end of file diff --git a/.github/workflows/triage-agent.yml b/.github/workflows/triage-agent.yml new file mode 100644 index 000000000..185cf8f32 --- /dev/null +++ b/.github/workflows/triage-agent.yml @@ -0,0 +1,122 @@ +name: AI Triage - Label and Comment on New Issues +on: + issues: + types: [opened] + workflow_dispatch: + inputs: + issue_number: + description: 'Issue number to triage (manual run). e.g. 123' + required: true + +permissions: + issues: write + contents: read + +jobs: + label_and_comment: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Get issue data + id: get_issue + uses: actions/github-script@v6 + with: + script: | + const eventName = context.eventName; + let issue; + if (eventName === 'workflow_dispatch') { + const inputs = context.payload.inputs || {}; + const issueNumber = inputs.issue_number || inputs.issueNumber; + if (!issueNumber) core.setFailed('Input issue_number is required for manual run.'); + const { data } = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(issueNumber, 10), + }); + issue = data; + } else if (context.payload.issue) { + issue = context.payload.issue; + } else { + core.setFailed('No issue information found in the event payload.'); + } + core.setOutput('id', String(issue.number)); + core.setOutput('user', String((issue.user && issue.user.login) || '')); + core.setOutput('title', String(issue.title || '')); + core.setOutput('body', String(issue.body || '')); + const labelNames = (issue.labels || []).map(label => label.name); + core.setOutput('labels', JSON.stringify(labelNames)); + + - name: Call Azure Function + id: call_azure_function + env: + PAYLOAD: >- + { + "authToken": "${{ secrets.GITHUB_TOKEN }}", + "repoId": "microsoft/java-debug", + "issueData": { + "id": ${{ steps.get_issue.outputs.id }}, + "user": ${{ toJson(steps.get_issue.outputs.user) }}, + "title": ${{ toJson(steps.get_issue.outputs.title) }}, + "body": ${{ toJson(steps.get_issue.outputs.body) }}, + "labels": ${{ steps.get_issue.outputs.labels }} + }, + "mode": "DirectUpdate" + } + + run: | + # Make the HTTP request with improved error handling and timeouts + echo "Making request to triage agent..." + + # Add timeout handling and better error detection + set +e # Don't exit on curl failure + response=$(timeout ${{ vars.TRIAGE_AGENT_TIMEOUT }} curl \ + --max-time 0 \ + --connect-timeout 30 \ + --fail-with-body \ + --silent \ + --show-error \ + --write-out "HTTPSTATUS:%{http_code}" \ + --header "Content-Type: application/json" \ + --request POST \ + --data "$PAYLOAD" \ + ${{ secrets.TRIAGE_FUNCTION_LINK }} 2>&1) + + curl_exit_code=$? + set -e # Re-enable exit on error + + echo "Curl exit code: $curl_exit_code" + + # Check if curl command timed out or failed + if [ $curl_exit_code -eq 124 ]; then + echo "❌ Request timed out after 650 seconds" + exit 1 + elif [ $curl_exit_code -ne 0 ]; then + echo "❌ Curl command failed with exit code: $curl_exit_code" + echo "Response: $response" + exit 1 + fi + + # Extract HTTP status code and response body + http_code=$(echo "$response" | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2) + response_body=$(echo "$response" | sed 's/HTTPSTATUS:[0-9]*$//') + + echo "HTTP Status Code: $http_code" + + # Validate HTTP status code + if [ -z "$http_code" ]; then + echo "❌ Failed to extract HTTP status code from response" + echo "Raw response: $response" + exit 1 + fi + + # Check if the request was successful + if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then + echo "✅ Azure Function call succeeded" + else + echo "❌ Azure Function call failed with status code: $http_code" + echo "Response: $response_body" + exit 1 + fi \ No newline at end of file From ab5d534f5af932fc2fcb3f3c54be9777151b5c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Sat, 11 Oct 2025 04:33:46 +0200 Subject: [PATCH 137/144] Update target platform to 4.38 (#607) The 4.37 builds disappeared, breaking the build. --- .../com.microsoft.java.debug.tp.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 1d322d84c..baf2807df 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -2,7 +2,7 @@ - + From 4c80493fa08dd31f4adc22722b8423ed148837ff Mon Sep 17 00:00:00 2001 From: Karl-Erik Enkelmann <110300169+playdohface@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:24:35 +0100 Subject: [PATCH 138/144] Handle unavailable sources in compliance with DAP spec (#609) --- .../debug/core/adapter/handler/StackTraceRequestHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 49f304771..3fa0a9a9b 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -220,8 +220,10 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra } else { // For other unavailable method, such as lambda expression's built-in methods run/accept/apply, // display "Unknown Source" in the Call Stack View. - clientSource = null; + clientSource = new Types.Source("Unknown Source", "unknown", 0); } + // DAP specifies lineNumber to be set to 0 when unavailable + clientLineNumber = 0; } else if (DebugSettings.getCurrent().debugSupportOnDecompiledSource == Switch.ON && clientSource != null && clientSource.path != null) { // Align the original line with the decompiled line. From 6ff846580e33dd5f935d86da80674a4de15f1527 Mon Sep 17 00:00:00 2001 From: mozhuanzuojing <63572041+mozhuanzuojing@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:49:23 +0800 Subject: [PATCH 139/144] Update tycho-version 5.0.0 (#598) * Update tycho-version 4.0.13 upgrade tycho * bump JavaSE-11 to JavaSE-21 of eclipse config * Update maven-wrapper.properties bump maven wrapper 3.0.11 * Update pom.xml bump tycho 5.0.0 * mvn wrapper:wrapper -Dmaven=3.9.11 --------- Co-authored-by: Changyong Gong --- .mvn/wrapper/maven-wrapper.jar | Bin 47774 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 3 +- com.microsoft.java.debug.core/.classpath | 2 +- com.microsoft.java.debug.plugin/.classpath | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../com.microsoft.java.debug.tp.target | 5 +- mvnw | 394 ++++++++++-------- mvnw.cmd | 332 ++++++++------- pom.xml | 2 +- 9 files changed, 430 insertions(+), 312 deletions(-) delete mode 100644 .mvn/wrapper/maven-wrapper.jar diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 41c70a7e0b7da7ecbbcf53b62aacdf8bc81e10b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47774 zcmbTd1CVCTvMxN+wrv~Jwr!hl+qP}nwrxz?wmEIPr*-E$XCM6Mzx#``?;BAOE7rRz ztFkJq^2w?v<)wf@P`*JxKz#f5jqp$TuOH-}M;Q@i0a^)JQF`ES@>1Y`ee(_IA79A- z(~2nny`qeOtc0kDk}{o)XmYFoRR0eIk!Sx+LUeJnX6V!DjtT+@v{s}9N;vDpAJM3` zwpt4LHJh+s@YlES>i}5qB-1>+?g^0!r0H?Grdj_;&Y1kFA1!f_vhcfy*-jArd~*1L z1*#TPYblec;CZ(8Bz(JW5w1fT_5-Zr`wV?LGWr}`pg+_LU!^Xq$Tilr^VAgBGU6I> z{bTe^Jy=W&oJ6~eRIjX=s<+Ax6HM0TJGBSakpI#Y%C^+2?2jJQ-@pCQ{GSaG{D0Tf z8sK7V^Dhk)=KsICxhO!G`flJw*Bv^UcAQuAq zk4{t2NzqV>(Cokeo0Wxs2lnIq(=^AQ^3TT}B~RT2Nc zRsY{6`>+1~qwRke@V}Xr@Bd?6GX39r@*jc(ZEc+#o&Lu=2k|{Jt&zVJ7yFKB3E^*-9eCQMT!%!b8P*r?d8|az5Ah>j~wj#Cx7T_CTF6 zy_tu|40^lja%x*SPu-(r@Wwjn1X4_1Uyw~2lVB@oE9wj86U~`ahwA(k=%t5E{4uD| zO%?#lAgT{tZj$k3q#6i*AoP+72!ioq8iOpiD#dZLr~Ftli!h=7&cj@_6_(>E%1412 z2aNW(6a%hEh(UA*tvy-A9*~-Ogi!$7_GC?GqZmxaW?MR?)G;N4I4qEr#E1b9XO1aF z97humU;81u*A#%Rx#-ep=GdLLFOmgVO|M-?>a{7d)7r1lQbICO7J)hrs~Q+;GluG+ z(S&dwX^kQRNqCSzGGvqspgU6`*Py&LwzzZH0-m5l%`zH87H!B!ka!JQ1a((O}0A}W3~n68&p=__?ouqwUu53z^E1g&i` zs`W{sBKt8`7ns0ctfj1I5WFWH`ty@Zc?P8)<<(49ber*5D2+_Qf@5HDoVZ5Bp^Z|+ zE;7Hi+KH3BU~s2oBTt&8hCr%m!=Xq&CZa{CJk%b%w%iTZc9E35Ph^hBUi*aK&PM4d z6LQ_FM_4UWRg6F%*Bz82L`bVzLE+mLKwv<-;Y#V)s&VGG$lCq_6DezESceULoPJ9u zRhuM{{_0W&OGq5o>9%OR7r2fXlh!65CHm?Q9+j)DBycsKkTr4L=VfyM`>aQ7tumJD z>!-`ABd}o+y2HuL94Q^Df=Yc5c!Ma6n7ArqhMPf6a=CVU!M=65Z}7B}BKJ4i)d?dtvMTOseZ+Tl3=BFf!3sy` zGeQu#j8U;#ONxXhnnbDv%n*3UB6yi`$4Hi&Q1zDRrG_D=8qkA_tPU!Wi06D)59G#W zxs@+KdI>H^w3+)B3$ozdF0<1fpxlEi4f8!~0qI^j$FS~#dzh6L$FHca2$v5HWWQW+ zic@F7U+)!Sw;g{|G0&G7FM@%oB6>(?TSF|conUp=2liNae{be0Cq*=5t6mrK=8Jxk z#Bd365ob5mH$#0R%iYyq{^Bao*s+71S+sL9f&E~Csoif^_`=GG{cXvP{V1kevG;C9 zz{smS@QbC$R;-4vZ|_3>aG1)3TZtJVowSTgL+_Ao?J63qP(Tz+Z<$T}fX$ZAg<~vF zjFLHSHsB=x#=|}=XIuB|nNYNLPr*a<_m2?6Hh<*NLVQw>b7>1}m^R_sWlA?;V_Q5& zI(Kkr3Zzql@gHT!#6^@e`@F@wDAUM_=}1ZBwPZA!D!22hxt@_nEn|Fyy3@WzwsGrC zBwQHvIt@fzSZ*Hc*Vu2Mb7py_Mga~DIPRq(&pv|sCo5vS%L~80UUPeMb^)w+62#vv zU#w8|KI(mJz(-MkNEaoF&+fq~exZnJdoZYdJ9YLjPemzg?!2j%irwQvKYjE4{ueHo zUwt1I1pW3+AL_sRrvJG`EB)OTEo^K8aJF(1wsp0!vIQ6!JCG>3J31L#%m1hQ{PX1I zF5v8BW^ChRZU}I){l^wwoup%nB!CjK1KcFlx~iyL=hOT&p`)zY&DfWQfQ5|Q6s?j_ zqNaoam|_$%2_^cHw6!V>=lh9oob_GVLSjAAroqwVD9e3@$8_<$#O3c-(nxQBO6LX~8hE28jr7?U4T>Q%u3y{FD_?H(g= z#ImDHMu8&EC4yXdawSe&QIK5CQX=%oDO%ds4eVD^OIbI;iU`X*y-%)s3xVTsDG=1h z!o~KgT53$zDpj!eP=z5NY*3e&LncnN1ZO~+pPD{eSK%H_5<2_hb!UNZ9-{zYC6|(j%Uhq*YWmStgr74qTj$vW)vDE zPYYA-Jo@8)KzzjsIxMyG*c>`KEV*-k+CruAb&&TM)rQBIoly`;Q}zn|S$TuaR4JLs z47P}zsBr&QYsn9lk-)h4DzAZ7O{vy)lWq-GHNQ)5koAC) zpLYW?ZR7C zkkHtaC970NDQS@CMM8c7YA16OpfzaO=-CMciIc@jOgah)%Y~rC!6`I^D|k9vWFv#= zxH5*}bTzdWr`7{HK56b9)D6s6Q59V}Oxi^U`bR!;-BTbmgT6kHoqjO0Rlpoyc9 zD7uhwRG+p@cOa0!mU!5B;ZZv`*fjWz0d|C8@(K}k5f_ZSN#oS$+2c#_H7G=3NAg#B z!f9JTJvmEOzxSfv6tSUuh~cR*WP>ghM~IYZ{bP7MJ?X%sbfv)Ys;HcX^mv;3R9?OG zz;Rq6!qY|hrW?V}-w*7k4;0v@OY?TlHq&-d^TJD7RUSrJ`D#W813UtHeHy4rZqjz8 z1o=Geh+e4flVNKkYqge*0>C}X$G1gMj^{%7D|v<~?g`pxu9T6T*|{&ikNIdDORain48RY9 z;nAg7uOKjVOJB5>K*Ks_lEXf2Mt~q`N>fAVAXoxvIgev!SZ3|3J13KVN{qT>KW6$b zo2;HTs8k=m2f5gV=Az?r8mNh^G$6rKzo2d?spIU1Xf)u0_L|qF1ASpA+a+6`CyZy@ z&}Ou3eUB8Y2;hqgc{h{&Eaxrf8Uh9NJt}egDT8bGOb*{B^r`2nU$&36op^io`(U`X zG341k8**w*R)icCZ#4jl>AS**N&V8^z>6CSGrWrmh4dRF+q|u8bFEDE7O+*Tk8{|% zjl5gd>zw*_4W~$9PrQp2rzKdBfI$WzJ3GGNssrqKwC2kjXphbYs`?$CIH)@=-wwyP zw>d7>v!3>*nvl*09cN@{E8zOa@%rqvX_g+Rvm1W@bPnm~v#yw!+>Z0b-paORkH6f_ z+V{B`9;aogMsMbYnIYg8lH#}XiTYUL7veqHJF$KMw6A-zc^mfq0GP|rb%%eaEm2F< zRX|hFHa3R(J^H;ho(b0T{J5zXpQg9YK&nJ^gq+ceiL#PCOuthEc>MK-e(}I%=(=iB6B4d+ zEzwf`RSa(}dW}tyQLKB+8kRMT7J z$z5k|e>O_r$h%fb-`G3S@ld4G%Bl`Z(UZ{Okm&Y4sGtuy~mH2LpdTqLFs zu8$ufr!CpnfqN%# zTHa-WAcmPE0%LMt{E);^l`z04qXAJ#r%ZSvE*d&?Hrkj@glI{i>WKjyGau0hQP8)| z>n(z*UWfiYHlis_4DziIw=Yu%Shi8TvN4h@|M~0Lk*0YL9of!Z_^%U(S(RF^7dh`=o#ud*+GN<2cb1xC-{rDZq9I+#pBbh;f6NO^A;C78*Ie{e8R&!-|u0P|LnV)Yrc3NxA!R3G7S;TL;<^uHJBt?`bjdF-zvQE_q&Y9FD%~y$JZAra4%T^O0Rw+l@p_13DV zFAdeM3T<7*?p%&TPwyDYSE4X#TG6W zY+|JNL7T5_*AF;5DG&R!qhGhFgE8fQS!l+j8un#0q|V*nf@)5-x4*M z?77y*cEbXOY6v;2JG?m|Sf<7sFtalpJJT$em{!ytJ?umC%$%7K9Fy8M2Js$lW-YcL z2FiR|beKx!L!Ey;rnf1{ojYGF7G&txQZW#;<$F||nkq@0UNhG3u=vbEX~n1nblT7I z>sOc$@5ith7$9I>xdgQ9#Q)kBF@e+2^p7W_H)CK*#6sCu;|YB%%Qq+Qs-kox=Z-=I z-jiOILp3HUC}Az&M2$%zf=Y?7 zSb}Qa3`FG@C^FwQo(Vg`b&)c?ERu)^(TLz&+1>NL$k~CbRqx#kDRS;0JyR@_WSryP zBE?Mm?9WmVwd3hJE<}4lV$~Kmw_D6Uf4FF5-ybuUpUEn>$)9=FLm!A6f9@a<$q^!6 zfaB{g{DoA#ntLD3lIT_CAF-iC0({QR^k*SsUssM`8a4Nh8p(!lIZD>zaIqn0Ti9k2 zu7K4bSW0Khd40zQCtiq{5{6Y5%`=CzESmHf?7>VyBM(v*JQlH}*)`WYt5(G+@K)A2 zrKF!L{Pb9Ep>&I15AqFDv`*H$(hx2>v-;*;A{KC3d=Eb~BJ=pt^j1J%uj>&Q-|tk; z!x4p!WM!OXK}wSC*Q8FM4pD71Nx8enA+dIRlSzG30WG}K*>l%MSPGF)uUYYCDT@## zc7f9n9VZOFoSL(I0y=u^&5_uVCY&5!O}Lq*k~@NMq+9ty@qUS6@m^Ciex;ZjuNGz}l6bp<8AU?*e%yw>5JxB=Eb7=CR%4~Dk$6oE=2B24h3wO&kIsg@Ga>uN z^zJGE_w6nTfeXcl#1kU|?-;8dsyhu` z2vFn6sS(g`6VAfG#jXm__lTuuvRcV+_1NxTqaGYFUABM|xsV8ZmhxX&zZa(L_$uDu zrgiW4MB}Hupm#|vTV)#B>>nLlYl3U;?cCsleP*L&)SK<|vCu}(@sF+nl;*xlTsvt) zUz5gce3?_XeGFs^cYc1!S4S1Ml7@s-rJ-(Gn+q`C^mb_&FRv5L33p7Eq^P8RvE3SK z8%*9Ux`SMd++}oif#U6o5jBO}^t85ls3J9wzz-D)i@;>#C`eZAw_+$6ScFFA(5UiK zil(c1Ik~8GQT3}aK-Tw1P8~f5RVl5pmJ8lXr(Zv*C^MF|lAtahCpwh>IIQUMDY8~; ziA!scZ#8?{5P%1TvtKYMu0_#KP257uk5ea-M3`J6Xcv`l+>#9nw5bpOf)VpD4%fF< zTZI~=O`+)3okg)i?h1;26%zVn;M&9g6|4qe6~IC-4yz=+(l4#{u0M@w-5}l^m1ZnY z#+9<9qX@$@TFb&rPT!6)8{607m0ESE)kQ~doxwBn_=x7}dq;D~06v-hAtMW^>XVvo zdR9V{q_bQkqr2x0%hjWq$%=oK!ctO&x?LgpD2Ui-u54FB$b(^)ROJRvH?YLu~$ z0!B5dSz?9c;tk5PwKfCqUg@zAC#sZT#3aJy(#bP!AkzCw_*iL%Xq{S%c}{7!c(WyA zo@S%zH4>z{&_gKwv7@h3rXKg&Np6Rty5u4bzO5v>6u}hD$>1kE+(Pe#)9Ho=WROl* zTr;8^OhqT`DM{-l$7&)=kyMHtXOUFjO;wCb<~*%uTRa=bC1@C{QpduwxWS-g>b2r& zG8gP=W4>shq;0jcKz|KD`(Tgx@Co> zOM-YAur-rIPm)}rg^O>dWvQ##5M3TFPTe?6&DY%}gNrcttIn>g4AjDmi-P5c@ z4ZDuyjUIotw6JD~#Y}|4x-;Wv5W8pMC62g%6LGxym7`+aF`xj50a7)BnmPW)}BczM%eWw zjgX&Y3$z8jDUj~nw%0D%xq3POwXQvR;K;l4TcYsoXh~XMK<2Wtt)k>gg@*gwM)$(z z{-!-+1}sS*wzZ}h;%Dx9NTogOZj5qN8!@W#&)a78{+)C7`9M_zn}UCuCSiPkelkq;x8^ zf0{%_RXNY*fBZY?>7H4@04hYUofR`;y+1`OG=R z1%1^yZ|nl{%e$Hdl@t{HE;MMxG)Pa;;xR@|u5==KY5cK5uDziMu2+yO&@z(+>ufOg zX;J3MqNu1ca*837jt{g)9Vl=c>azhg9zPS_5G!|#Dt9oho4?v(>TF2uKC2gY>j7?z zuOD1Fe<@8LUAbT?^DdmZK&w-s*i)g}e%k_UfnmA)CrWx?#LYnG5X!N7p$J? zZ=X~%j~iL{d#)6-MOBOIncCE|*EC)}1F)aA9k2E=#8XApiEfp?K)t1ip7)Sd2Q$~t zY-qjp^<{R2rZ_9gXlicD&dcPtBjoa$YQEonHT`-H@XBwLQL;N8WOb&uSK8m76e>H@ zsNNKUfy)NA9)9|0a-6Ie56g5^uN|(R6|cnB|BGu92oZsAFc)Uex$vsS=l>ORX%8Jd$kJ zp6Jua=>W>@H>Y&cp~mq;C8pJgQmkTw zZBR}Rr9!)1H)U=h8Y2bUh~t1g2CO^*Rl66T+NfArX#-q=tdjk}_$jqW?0_N#tNhot z<5gKqAy9W|$(&d2x%ck6LpdKk?D64n(dMSE7F{dJujPQ5ywQ7yy@`dq4~Brp125lz zb-vRa6F`L6Z)V3IaxI;x#r%Bz#iZFs(ulrnOC5y09juhJxWQV(_Bzqn0B5OXhQTm@ zY#yi*kzjg!`Xo|k+pLZUovYPUB?HV@(2@%OQu9I6x^FWMR~%J?_v%B>&g`dD&Ag;^ zmisa`SdGl{`dr1)n@30>Hi;YN`^GTu>b(G8(GWknDKFN*Cf1zYR zV&=rjEB2!xEG5_piVQmjyJTYvm6olSmLHlAPn(uM9oGTn9s_!Rn!OTn-ja%*n2YXX zitcEv{LoSq8^bz6sAHyXAwW*aahU}kq_izmGXCAZJ zV?j$?xksrPr_v^s8yC|FuKYoPPh4t1lN>vtq}&Fg>z={;Xjht!hdRncC6|+Y{8G7N zvCQP16Z#JFSBzc2wGRdNhqgk6_jfTi{olpdGJo-0)c&1L@)yhCAJ+!z;BLsrm_A=_ zSu}9iMlh>1!KbtYkTO3A8CoERQy6py<3jN6)^NmK_niz#%tbexiap7#7fNI@uM&!q zbvA>XNUb|uGq1YAaZhD`f4Vl1Of;PH!k=7yzJ1zu?YwzEfB3wYJHi96649yH#q(zy z#|AgGp>b4%8mvof!DKfyyJ@!y0bZScdO}!+l;^4I&oNvUp+#66shjP@8+r^PsxNQX zcRJU3?%K6EmlYo0==+N8?D4$y;{y3#RXOzd4hQf2aJjy+ zvnSNq;7Z5@z#Or<_M~5QIOWP?+a|Me<>a z6oaQ!4@3=$7Ii9HYmLZr9QAW}JzalsO8d@`B>-w!TJ5|?$(~^uih`W<$D8QAvd0=l zRRhwwiq%h1|9RZgvP!6Srb8jxd(;7>{U-aD z>f2KOq|KmSG=V==QAsI`KRaNRt|f+JPM-W+NGhc8G!SVyi`GKCT+lx#`dKj-4LC1w zSdf5Vx9&?A&DC)ZRP@a+^9A`KUbtk%9@-4!qM>R~$|fa^w_og%n{3E+Seq(VN$+*X zyUvd!(#Kp-aaX6b&&U%rnddl6G+VYyNFOwi>}KU~p2|)Jbv@#qd}>XU22dGbqHYa>1!It{c^7Hn1>s`ZBaq}AOuf5q95W0+$e zU@O{xTWQd*BB|`5Dy&WI;fms4F`(teIjsDC$?k~iqyS$6fd6feTcdU}Nl8(9t!%G; zrxZR>Tt<1j_=nOFgiEL;W@9u_nkUXF%ADkVFkDoL0jkkLRkH@URXI{MYO@j1yl$x6 z(4)WY_z=>}j*7)zvx_02HJTSeVKC{+ecB{iSY&R!{ngAAjEjXCBT);=+V z{7e$3%pYZGO6r8Q7_;C(a;n}Ek;pD$Bue|n&x=~5dc-*+EqhaK<2+G7^zFWRwZKot z`m$<2yCGbxu%mic)c?1}uk}X-T1@BlY9*7!`Ayb0D~&AjDJ7pxKP{v19jdx={$`X4 zEzlW&Ue=Ok?GcW(49NpVp!4Y`H)`vIrq;?n}3s6f9!%=e3xfE^xg%) z=-oD5$1KBnWi7R?begIsQYGE)g@8-7w{R`y;T6s*5yZVcLTxvh24hPGg2@WS;NfE0 z2#GmVnh<{TNWH3EL|Nl!!8`xy6Qa};d!LCsZ?>y;*s+46f`O<44XyU>J8M>ktdOGR zXVS0Z&K(RU7IO22i*0u&Kiqr3mh5uB7KoSnZSnH%|)VHV;94$JT+JAHa!3utpY?Fn9&){y6o?pjNM z!uevJ`zFcAv%8+KPnG5I32N|?Wbw7y?RPKvyg}<3Al%@@AEhR0TjDXFXqdl0sPKLU z8tDxND&>9ng77NIZB4pM_JywDmc;i5u1KZxZ)}PlCAuOx+LPeZ`A>b(|AZprI6q3s zgom&1>AL06a`DL;a9>-*LsVybb0u6*{&->ME#F0UGFOegJJ=LjS=Cndf{INLdJmd; zf2gM9E;#h^nb}*Q;yqwB;{Y*hvEfuy?s=_>m>H8Ornwib@evq8|5eeyghwk1(lbf_ z;5;?L?AZ;k_e+@K(YfHRPds8Y?Rz?Rh*2Q^A!Os3O zlnGYUzQRufJD{=7>;KCK^ggS)qgzu81sU!Hy>Tu&tl zGQ(M%6l;FCfeQNKQsxI94B`6y9J7f+DcCpSx5Hwbw012-PMF#u(46nEQ_}*bv^#60 z36cgc4?ajxlExCx$P-#_8Okaelq*aVtMk)^Y%A!1h0CUp-}5H(64}10mF5QzVTE$N zGR-|zft*|$iKUEND3E{0nq1ZeRyLW~b$Mdwu0mJI2vi4Hpa!wD#5|69N+Wk~e298^ zgo*)+68&*AP3+y`Ee%b;Hcg^kx@q1p`7(oYhB!T#%yS%;^x$I{gT)E4zyJ(heCbYJ zwU`s})y5Rg?FylK^uA zo-p!pUOTp`O>o`a1*W(fY7pQomcHG(lS<4w1)RIq1GgXhtl}na{G0y%n zl#9i)4+Jfn*EU^2y*|1E=oWR5sU4Mh8L*hfgrjiZk``kR8ZKHCT^UYn6nMUK1E=0= zhEUgKE7IBs1925$Ar^(sZ)gE?0iz}VW5S-W+IIf$~Khc0_H6Zx8jJzgFv>HTeMh1~t52jTggS0_uk7~F+;MN~y z5*EK1K)n_A)}?+!)s?XVD!$L+Mr&C86Vhqlxv4H_WOwDhCX7?8KG$_4&L<3i)sLNS+sj?(c$}#XzjG0gd+3usA(Pma`Ms;z6Y2Tp_miEFA=LBTS0@}Z z^K$7;56U!e0DSfoNNbX)U*)L@b$C6ridiA?WzD^SobkFl{+1*lYnr~FQF@nnaW~cU z-suxwAyL{EcQ1F4Biwwq@W9Ot2;UHGKJt-6N+W1ON66Ex3%PkUb0C=$9Z{m^<3s|x zgGQzaH0qe)T8|Yk)@Ba}69|ZQ zMPWrkj>q|#k-7or2}~GjM|KaK@|`Al$7w4xSb8vcFU0=+m0N}z^F32ExIQ!*)B#Grly7;IE?=74;RG5sa*yN@3bWqncQCqt5_4ehfRL&%7Tj4Lp% z=A1m`a?#+>-f+~UCgnm~cbe^r8hY+!{7T8a&h|vdZ~HmBoKKLkoh_SCbK(~+H++{` zUExk~5`uo-AN^1Z^;uPLcT)A=N7Ri+>VuQ(fytZ)+jkCapjU2sB9ov8Oc;@E4j-e6 zVJ-_ag0yim22E6>@?tDp5*0Pi{Uu>q+B7x0%l9hxS}drl&KRUXA7Ce0+fG!)M~*^jAth8!#Yc_e6#63zyu&(~1(8AIub-;2*Z* zv>=zaW@w0!h*8luS;Rso7vqfw=@ZBbXN5JP`&z&ol@hq=;QcNU4f<7bSwZ7diu7u- z&Z(HOi{_nd+fA~Vd4I~Zv87f{wT-krBa^|SkH(@IbO%^YmfBVFrZFfFKmx)?twNG~ z6rFIY;N@;3>pU?0P6sG1SA4bSwvzf#se>VGI%*111(S<3z8CI4=dkUz`N79=F}DAe z@JLt2R)eLr$I-G$0H)Vae>Dn?Fdf$QgFE1hP^Oqy8f3*09y{prHHM^0mkSyDLMHsH z3`x)s!8E@f1_H>i;_^nNZAdS7&Xt|Qu}Xvsc{AoypL9K&ASy>r{%yz^-1QQUX~DQ> zkTFI<1pc|L!KT@wEtch{iReUqx&oUf{Eb}&sv}7Z^qg%FAAM?P3wsmvN2LF$V1FU@ zSaap!K3GHG$kQ8$9EU26H|`Ao@n2?wV>=_!`6mOx3Ha~Kgyw&3CjTC)WvjmWB0nQ~ zTS}$SpaDUpt?N@wk0;OulQn?=htt<7PPlmc+haGRpt%cdtGK9vwVG$*kyy{a$x3SyjX0b~cJBq9Uq(j+s1zt-fQ=fL297eQ|ad~vMg#SuQof+2eCd3gW@pGj< zanDPmg4_K8jel8jXmV!b;6Wtf3l&qr9&!NXqRfQ7RD>s1kV)Hlwd8N*6B|L%As$&x zuPOC=mR5}oH#P-%&eL^sL_%AH%_Hn*3yokMHAD$EyPJN=^(_LwqJKyI&;C;h)a#)_ zD7uIRu@N5;i-Pl&d_NkwBU5M6hkEI+G+w_P1CdgOP@>B1 z0b9uvZcZvjY*aY0aM7!34wV(5J5`TQARu9&f5R`rYkkloX78UXilDS7I@Du;>hI;w zQLr~3Fa*%}X@Hh~?UX?8F&=?zgraC~KH-=r33(UTeHyb;sR-PCFP6DGO1Cdo30TwI z0W&&1oz;r>M`#cS&#Rs2S)VJ=gEA55P-~Mkm4mgM7DQM*K|0Wgd3@L{N5DY=&f5UaX)iR5XxXD9U+6e6<9Je%b=5|d zYST!Uqr7fR1kjOR({~K(dXQ!tqvH38Nu)7fG*sSC>_)mwJwnIDmj6Ndeaex+RY#0L ze4HygEXY5}Ilgm`kmwVx*&ZsER2~;8;Cg8enGOP^mZ6dQ@7)BN?uhV3lp6H#yk#Fg z6CPA6_QZd4_tHO(Ad4)ppm9hsc?ekG4x*#t;M7IDS`9XBjsj6eVVLBfh~mr$5e22* z8biLJg4T-~Hhvme^tITGm)5H(zF~<;f9TK>^{H`BxOxzWLvj8&<49oD%WpQ12^1%c zd=te2If31W(<3h;+flmpe)oHvvV?=qTCyEGv|tZWH25M$d2p zy$0l@^Pv{t7Dz>X=ax8%r7Vf*3#IRTKdS4Z?X57BIqy$&WxrvN**U=o10LuWDPUBV z9S&ljiiS84jxvQ^;=GXL0gtrU_3REMqbb5wIAOL&XtsbMDbCL7GtSJxFJ4F7yyiF) zH2{zeG{Ga!oP*=jbVJ|OK@RAGw{F|%!=Dk*27iqLXZXC{+bZm$>iTVH9He(I{d~ta zNPV&yO1wIftjQpV{d7+Sx2@uA=bvJLjRao*f^Amox%NwdOO$?OE&@5Hv?aoMDX|J9 z#gE-DP$l<^)Xv|V3ynU%F^c$9-4&Bu)R5J?~)W2?VRZ5w+*fFwgyG1(!jc($a2&k;`{-S zBn*CHc9Eg(64q@%9VzS9UKnyr#j4z)m75!bI#O;Sl_r{L4~v2pV6*ViC@-g`SQu1#_-V@=MkKFhrRy`fz&AOkt#ZWT}?#HiBkT^q$cs(O&_=Sr%q&Lp+deFaaV7(R= zu6(?pcgT7QwR;1u-4Y^~<9Mgoh?=@@Z(OC^9yj~AZbsDhr@*CN0NJT^sZ$7+PX4-d z3Fk@Ze2QyYk-g}A)lXBh?=XDN2De!{(Z&m!?@n>CdPSWg9JM*uMnA8TuViZc}XroI#$xQ8c4!VL_BR5&dZxNfxGV;J7O-aO^LQzaAJ7E^IosUGZ<1TVJI>i9ug)~cn zoUuMx?!lVmeP1Fjh4j6D`ojIXAt|-X4e%9w#sAAOhk8-x^8I0KZT~G}i}mju^Zz9M zivw(o{)@PzqM?PXhT#(n0@mM_UnU^3SbzYbfY=OTzGP4g8yQj{-wCsq21CYlU_>-K zbyaz(V*AiPamU?V&<#<{E!TY=Yw?yt5(i??+Rkn|-I{&v+57ALbELNSJA3psvlTMC zac6kl9>#4EOlkDnJk(5Q$bmQ;j@G0bBhS@3w_C}iHdEDFk`v1dh|3h_bF|%f9nxNX zItS)6$QhXQ!~+#;F)AmeXQ^tiI`D)1etF5AhbHAsH!XMuE$wxC~ZJ z`LsyEj8q1uKhxaTnVPV^oS;W$JCGP>fxE2?m)DZ?n7HX)+T*0M+n`<93IY>y%AT># zHs8bZJn)?2A;SzywXhMj@s9#P?9Z!&BNpPsq85A^vl~NvII~syywsf{Vn4KgGm}M1 z_=BbpAs|mF6G_0UJpwFN48#RJ8>1D6My#$l@ue7b4An*KT;h~~4AsZ6Q_}$mq1@VV z)ldHf`SkQID{4+BrwW?oVI>z)ixVU>${}xJOLr4ZINHJDgY=eS!lo;AOuE!Y6ARUj zM+j&fdr6SxN*NtFm&`^)oef+M_qkY`ELFrTM6;_^sK&c0Y*XilJsf|fB>+Q= ze7jjb=SpMSQhFGpy}-!*33}P{1yEw1tCdQ=6$0>hek4#I6JqgU4XWDX} zUkWp0;bde8==5~-HvWd0p+9bM4vlm%ZgQY-UOh#>9~xjg2+H8-v{cu+$%5~h=w#LKU1Z{X`_-gabEkHdB`*1Sw3tKGRVZkNX zxf?>3DYs`?*Kx=9UXUJq@O@>$yyTWzf(8mLA!MubJuAvhK}*YaG6!B~p@`%%!F>B7 z3%5A5b%v;cKANT4P|9?M*uT8<{pdr-aBQAdK+hc9J`mS|ok_a4YC)TDOmg$usq>tv z%Q>KG`YZZmil-KNKOL(r(;09Me!|VaeVZ=CzYmfi{1I+aBZj%cFzlHX-=efnBA2Zl z=!MW>7hy^Nd=EhwfG1nOkT#6DN&Lu@HB{>5gtw2=Q#uG=M6V=C=#fc*L`$=ed-eE& zc&(V@94zLBkALWGF%0Y_u18el6BPT(7OnLX#A<2Q3!l5^_)>;?$o?Mi>Jf(K6<(Vb zTN}wiz54Md<1F;m%KPo#*sq>I`m`s1(pXmhB%J?q+UMV9z5e4iO32p6#N5=`0q|b| zaB|X)61ED;NH4k|(tL0bNJUulyv5pJ2(4g~W(2GRm9-F(1n~ID4|Mf`<+{wZt)I$Y zr1(yv!!ht5!0&}PGh18y4V&B#gq}XRZ)={@M>}7i?$G+6Qy9Y-cPU%TZmIa$C+n1a z3o@6RM_V;EmYzK{8b>ptou-Z}b(0uGFy(6RQI@ziEzQ68s6R%H(0?t)(fZ5YMwm%Sl~VQakY6LAz8@d(@4ls+egR(A<5z zZmZOd7`zvZBcteN^1eK(wEm`92dIR!m;+v+`Q9tl#4C@=9v#FzV^DM912=xhOWWu+ zJTVYHwKxG~GfOBF?@MS<$1ugl3cy+O_G!LN?_nse4o#%G~@eT>O`2u%F-J|xBN!UYN21bZ33X?hf6nuJ~7*?1m zheMImM&lb6mG2T*GbG|_76)@Ys8tmH<7f9(RfG;ADJ0y?- z#IaORMtw_%;*VHO@=N-J@aIknP`-J9ZCTq!3T3E0(B2L zW{mT~D3WGLMkFx51_?n|j7$D8nu1b91+D=Zs9bL12_jDRZ7n6BDntNFF)jnghy~^@ z!9tUm-vl_W1EmN7>{Saq0FqwqaEoLj#Cn zLvt(mc$PD>AciK1^&;zplq;=yGHWx!Ebx_&*s~n zjw_Bc-S_XyU%H;aKV9D0!bG49N?6io?=(Fm<)kf5AG8gI=kMINByk*byiFgS=2)u< zfS^>fmZ#0at5PzhVWM-F7g_>Rwm%m~?R=l-;y?5Il*B^8Wl2Lr6TAnA4WWpDRbG>< zG26#@pi-XF+5@V8T`16MEee?_*Kr%7ym)k(VVhi)C9Blc3|qLWRn!`=f+YL9v2-*C z)aXy$ejb)Dj?UWYb-z1+o^cT88lZI$YgzKHu{f*}Bx zO|w1(1T4WpFel59P#=RBXsDd!#TzS6;7ANehjsf|h&DP1TbG#~N4g9p4K_v&jTtxD zQ@Y8mPs(5n(P(CfM^{vmNk;<04W6*jd}SUZiug*Tyqld_b}8a4M%dRu>8kqB6fO z5k{#tK_}uup^a#Y4G@GGe93~odC{(*-=WP$B`gDr%Hq?=#-1;Sv7^8ON}4aqOfU_u z@t|7TZm88K9NJb%El&s?e8q=4>l540O~+;eqF&q(Iwejl5C(>_tfkMrO&2_L7ukcl zq1JUW=cv-=AT^_HHZ)h!rqVW#q`9arT|(;duOavOWqP^PD^3L2fc29WurS-HU6?k> z!OluS!W|NoF*;|d{jQW|FAPPJ3>ek}*{kvnYQ%Ae37gm&V&E%R{h|$=g@^tRYws9b z3A=5Lc1InvW81cE+qP{dE4FRhwr#s(tCMt`+`N0A`+cX*sr{b2Pu*IzeyytY=Xu7Q zW6n9|cq}RrD)5ml=Ll3{O4ULh^0?E9R!Cek%XPR+NhZ6m(Hnn#zp{mgW)xJ%An zq3UWeDMTM+ILeP*xE!3@{op2;MxwwO5LYP5-Ozynr8I1s@1MZWRJipqyI3F7a5t@F z9`f>6iB*aF4w@`C6_qL7z3{^&N8l-q3q(O*V$nu<_egB;b%6rsF+xd7?AAv^cv`mS zji+W0Sb$gReP5*8syVkG#bQ@r>J}ZD5$Ci%P96D}YkrRn09TLgF=@a;NKR{klXZIv zJCT{ZHAkNM0?<24fZS^du`(KGFx~us+Z&jyeyN_1#R~e9$nP<9ZQ?~irF-WW!`=<0 z$`XW^tz5f3k-2C!y*#Z#1cIHddJECFRFqt8O!P$3)2^@8TN3!B6$ZOWNb3pqAHO&IATK9mv^b`&0_q$7)oB$8gnSs$9OTG?H;r<}JrNy1G18Y)jFSGtMF z@71BjFucR7^qo5N`UdQmbS3WyEra&Sx)WN;4R>G6wQ5ImX7|au%A4F!%#Go)c5D=5 zH^l)xp)u`#3IE$5rjRn<5b|x3g_x=I+yy@2ai*%9ylISLQT3ycJ=Cr04q{V(Qe{vb9mW^n%H~#z!bgJh3_%jb3}SF4!#; zXvUS5&QQ&bWwTg%>Z$_;B^!Ll1eYzrgYB&eJgd#4@$U%jq3at@m0G-J$mS;kl@Z)} zkp>cWYL#bjB;Nt?#N}<+~PrvF3e;nSZF~Hk)K@Pl)t`KXc zFGamE(tKrx9;vx;hA6mWqe+P)-lLCsOrqn7fjG7*ax@ujsB~~->DEYgXLJJjGdKIP7veCG-WbaG%pJJA-Cc4gWJD_0L? z?}QYGb6;-VrtIReHE6-vv{MlIm9Y)+Qyz-8&bt1MsCvYH1*B?&?Bt)f zbB>tD-+6>v@k7K{&NS(c#mNtf3x6?FwZ1^8_VAyqP@-~=!yq5#>^sBZ$ci(ngHf0M zGp#Cgr`%$*8{QLg`3v}@g2+|f*^Qe{1DipLwcg* zM_}478k5$8+W{}r>y*~VTq?N%gh=;=XW)I1>_7>&Tbc!P%FjOp2ojx324b_$&eE?; z@BC?F`!PLKe(S9CV~TnHmqPQ9!(Lsuk=3x{y#6M=vVH5eGntBSieqD(`+FcI!h1!M zRs0p!Cf-OdFXkpa9SXd(Qg)y-xz0^tYu=uH;JJKXPtlw22i#vBqo%&n@h~BRhsxfA0$QU$_5x|EE9WH~7mc$k0j9*4p~3`y^#!T^Peld0?ESXlK1KtxKitd zaI+y6FHT6&;!@JyguL6hDE@=p)HX+O7AFGVS{vzM*8Pvt#qm& zHCZK!yVXnCSy(fxB-$pc0xQkKs=}SAtVCDw$)F2F-%`(sZIB;gf(Z55@b4L^TtOdz z4NqoLlTWSaK=#780_&C8;VFR7APE8AgAvktG-*)*S^GipdT28$&^qIe8;N%&a`v!O z7=j$0=k4>*a}%Q_OTH(zws09^hx3>KY=%y7L9XmBJKGQiB2kXGE(>yNg&kjJp(T1s z6>7kVbIGzuk2&hOu|9j%J9OZy$=sgIR>GSBI*Mn}_P&es zDn&6}VZbgw8e=N4NBL)jy3?uZ-q!mCpCsvTL_40y5Vt}2%5k!RKfak4kXHmajq-&> zlcf5Q`{V;1mW9q&f4JF#HiX-w5qQ0wjOejfVZ-BWTW*Tj+$o8C0S7hX6pv& zjKUJID~{|Y@Hz5s3j<<^LMRkG%{#9Qk9y_WR5L>mUDeSJO`@dZkw zhfctn(O@a$x@h7tO6BL7p){dOQz8=qag>;j!DD7>9)+oRUuK2C-JfQq!E~9?Im%9w zzD92iON?<|S;t<75nw_RZVN;El{tOyZ!jVLZa=MYq7O5RF#Jy7CbzAG~x znZM@>`{N;Gsw}z;c?Kq^o4~AD*H}K3YjBdG(QSm_Uw7gw$P4Bq-fSg+J`r|e0?ujx z>A#x~xNGed?LzGLx~JJjnj4{WXx1jW-$%@fmk;7X_1<$9xLBN}lo1P|3w;#U;sMSK zF9uMe8N@ald9IJAgqL5M17xn!`3H#xSyBbA&Y`iOJoo?>8VQW6&#{O z<>WkL-Z_i9(xN;h{^KUpe+woLiXDRTDL_DdfpxKKNILk+pWujup))6Nmz3y_QTalmN`(wzIsZoU%A9A%`JyWh9j*JgY-Be>dwKQJ8XLz$YTPD zKITj)p0+1cwPYzc5^9~p#s;Yl&-P~SCX?k*lIr#FVicF+Zw*^%!=mvvAM6kp8-tytY}RH-bJM)bRoPSi2k}}v%&C9msdt`LA-d% zAf>!Nz=3=NTD@D>U4LgA5x7-YEE1V_uPp&SU7f04_KfeTY9JT8y0nHl@5UIwjuP(O zn<7uf=c*CF7ys-N(B#1ZqvaD!BS0%vD#N!H5CuPbhPqshfSW_T$(8n8tz$ULN6v>5 z!o3C8Ev2;HIs$i4j;w-~H>2~KtY5N3fxfgWHl?~yf1Gl_s!7_37)vG7d;7_vkpNIr z&yZp1W^ORFp#~84o}ZN!zM?hR8)V{HiA&9zg_+_;6F19+BA+Gl?40?wY#y$3eSN7OizE}s2J*% zqNo{Pd{#id`yMy%sA9hl@ctDfEspU-s=qq8)6jnxB>zV`@}EK?zk{i>^_TmVu$!T= z-T&?8X2tT$_S5|w`4nDt%EyBgL5$i2iHr~p#HSZRN*D@+zcOYjFgZ`Q0q#jAMTGPD z#+z7&4HAV2_h+{I@#XsZU3Z69XXl&N-mFmcFN(x$X~Hv^RP0%Dq(4&gWCrst-Zqc@ zc)11_c6~O5DPU4WEi*I`h&Yf)?g1}ISqd8^{SCN^aW{TdR}hhwG;Z_6rP=Jd?LGu; zHyl&={N1S0X+bbn$nt*ta`Ra@^Lm2^e$ieca`pl#FMFFBhM|*u>R<^ zY{g%5o)pf2WsI2}ioh^v)Ac9}Az8UOq(lfv8fn9!%IYOVwRgHCnvQ*s=wV$HTBdI> z@WLyUB?J%R7Ocw4%I-2*A{E>tN0KhKWn1Irq7v#O&EXbM$l&K24?*QxyQBw2^Cw$GmNn$C{SGG|qm4=lj*}o3()tV(y=}Tv1`jzSab(UlO zpR(M4I>7(JBa^;#XkQ1{mDLNe{dGwhsfwWTn8-}3wm z?nV?{l3uKEt}djw%k(kRapUgx`48+jXSU`(0=XDtKK1@L(?Wy^xmnb)co5d;Hk32i0mxtXEjQl6~}~sHEWlwDDtF>OiV;a z31%LnC~7cvWu!kI7sg0M+qcrys6QJxV^Httw$UCdYgJ2e^~BeY&mh6w-!XsWspHK? z&O5(QNNu?hcz5pS<+ITnKU!I+gElT%(sbC;rbvK*SN7dgmM@~_fuO9ODak!JmzHLM zVq&Z@;b~&5vEXrHlwrhb2VL}aZ{wak++ec50R44f+-y0CQeOww@wdV-rhntWWdAeF zlr>e6RDXWJqLKo{dzISMD%6Ao2?je<_$qRf5PMNT`14@L%+aJw=EnvlUnSQ=y47{P z%sL=x&NZ~Xf9F{~nBLnqzE2yCW;I!;E>EU8PH*r0^yL4umcMmkZWV@%#f!6v6u>)_0;v0hgLENU+`;K$br)NPbuj*Uwob~2R2$(rOPr$s@xuk7 z{GPse*DA_NV(4VhU2D;fA5WeacDEx2?bfQ2$<8@C%nKC>m%$WA6dt8Eve=B}*hn#b@ zh4QbG0ws4*Q8fH;eA5G{2$!ZArN+5K-!BWa?*md60M4tzWL0X;JS110_29T2RSt zPjS7jpKlLH_!{(mj|gx+vAJDRxwzbH(P+VT{`<J z)&s<=&$P%drEevFk=P_9d=5(T>{bC^QWW8KMM2c4SkNB@QTR8$=7HE4rrRER#VuRV z!bfD{FQ^Uijv}BD%j826$_+m?q7O?kMmi3%?ff)EapVzU4tw@MOv#9Ug3fuJUNNfz zGOv4LIl|nFSupPpJRwg@lC0M6Qv~!HuqW_((>;T^k_R3VF}6RVcxXj&s%am!exS@f z!Q%MD=yiJP{Wv{zBP#flIM@D&K<~XFx|3hC&2pS}(OX^U5flLWimP|svHHy8@*`XTbgM=& zZi&iY`%ecB$q0XT!m*jH=h{nswKH6~1}aU!jSzH=!dH`1?5JVDxHq&v_ zb}yy0jsu*IHc!n}eLC$Ex_Mo*HJ8OVMu=wRq!^XyV{=9Nnw5w(3yLGt?4^~@e}r5X z)mlk}FXzwfFWL6L&XNCPCHS9mq@2EkZ(oqWPD4 z`=gkeifTf+&8&xzQdV8j3=Nq(v2)|#f-DV<9eZLyxL*y!xwf6+_TIK1P$of2z z3<=@I68i*;E+ngI`|cCwj1wsp>oGB_O1J?iet)a1V1)XN!^KmwT|{OfDOZBrWtDo{ z%{P6$>6{_7b6Cyh!zlPjo1SrBo*dprjeO*Dbpin5bh$c|Kvpn?QU!RvPGA6rKmk`{ zZJav&mZ|~qRIA1jmSABDz;etx4n1Cv-G!&CDiw5C*Ay$8i{9@r75pK3%G|C?zyaKH z%kkVm@x)xeA4UY!3&-cAlROp1Mw93k|;Y5sM^=@#$IaUlI5X@H*0WXz?lh?q+6hV1jynBgBVNBk-53Gn!2YH1^ z;yrxd^uFxUd{I9;{&F!t8vsSoe-ihAj(?#nQ90vqoAEI3^}56ji@hTV+V#;fVQUa|*j&-zdz-8zWUuRU^IiUTX2v!IE^m_q|9KBh&G=!g z?Qj0wVxvcz39J~*l2ww7sGYeNblssR`0dZTGG`h`5f$tfk$`k37OjEI+5<96%7v>G zd_uE@X6yE_#?eEmY-d!;HKT`day|aTkbnRX{d?PE*pg}>S;{I$&;&qU!wP}yAz?@K zz6LrRYBh^by1Zh7m&^@CsGpY%T~D&BVrPm2HR3x6Gveiz{~!Oy?@Twofr;`!%1pr= z7@~xC>uSTta{v>(o;~_ru4y2R2$h|>clZUj1H#1_v-9_Witk<{mqFdQW^2N3?Bns% z;Fr0co~U!i?l!326LUHyr{1sqo*O@^8^r>dqD<2HDW?de?%Z7og5a=Qkwyf$3|J4& z=xcx`aeI-tgli$7y-H6EPz4jxC2$-s;2oQkOeUk;qu*eTKXdc}r|=ngKB`=%GKr^T z3wR4|WPYepBLel>ErwVWB&jk%2LP)PrbXSqXwgy6geCPCB_jS?jPm~*wCF!$^q&;T z@t@c%O0t$k;)fskbfm7*f&xVb!9_qYr^E_<5%40BkfMnHCVdd4zTTAC>^b%D?Omoj zR)~0R5Uw|bVR}2OXcef9DP{9&diu@9#KqG+9H7DF6lNxa^LPkA36hBD>(Y!xFk&bkDU{nYGW#NQBAa+h; zVxvj=v^s2x;)h%FuSB6HGA+#*LH*5*dQ5<*JgUtW#PlGawAXgxMaVg*K>w`LZs00{ zigIaq(^*?{Ih7GBIQ_0lBcpW!fJYRY-^!WK$xq~gySJs7CM*I_6q@S%AkW@W9%W+i zGvyOrBR9M7Q4NfAljIwoSQW(9zBB{?oX5&hC4Ntwmcj#(ldX-E);2}m<5lh`LA zX^RwUd)rGm4$m*OnJ}b?+b~yAkHS)wK-;Gpd_wpo$)~HldG4Q= z&>ydZLw!5?izTVRJGJf2_5Sj9mM{48eLZn`R`8szEcAOa%#SlPBfPilVMO0!J08IH zv5F;n9j$co@^|Y-x=50NUib;7eIOBmVaw*(Xj32kduyIY81MA~N!?H2?mK@&e+#5k z2>*81CEaMdshJ=VUN}P*X68@e6~_=I#TmJ@2$AMi3zjhxzN0cCo1jkd<+*cGhi7l8 z%lG5m^ce`C>E}-_8%NG)wi($0gCnXI*S#0#^kFZp9rJ}&s5{!f5P*rPNt*Cg#r1uO zQ~!0AlKA%#AYg1_>tOsZ($u9Qk}3SBwS%1-JMp|gkuWATBoTlxoEE;w0K_ti53?xh zk8$+AIa|gieM-c=?rF}ftxGYsRhmkV^Dymu496BJJ5j14dn4m}>;?C$OGnR^r%TTJ z-D}P_hbI-`NV}~Eir_YVjr0D}G`Ju`E03e$;2Y=)3rx@!6~?Tl{L4_|g$gL!r}U@k zhrxi$rFR8G=GY23@H+%W%v<&YzhzXE(P;Cc}?VPY{#Z?_XlL{iOTBK zZ0ecsig9CzC68L<8{XY{%{ zg$UunqJVHf+;UvslZ}BX#Bn~Ui|i1{uE^l}rgOLJrgTX`&A|$LSpnR&+2~&7MT{fB zRR0~}Rx@m!`*I3r>5uW6J!(0dZ0dGR4|~mgDa1(5XCI=pDR-+F8~T$hXp3m`csY4X zSu6&-Hg1|6y|h^ODmljQ*8IKSIPy?3*bPsqv_{><7GJXjC-A*T-K{A9Mk`?YF+`R=-uVe@AtY>5m4#sZKcT=7bJHTg*7&`h%zBmzCh|UpA zZRA7TpnUH0U|_gAz;zDS?LFh&l^%Hmjm&h|9BKq*p@+{i5^mUcU<@tWz(@pL=|iA; z2r^bk&qVx*yHBz3KSiFAW3u}IiOZf!2p_nvDLDHH94 zE5yTJH8X-uIK{15mQdE6YQc)}txQF4W&jnsPa0N}$9r6sp2!ZaMeblRrs0AGC6A~t zJ0;*iL@l3AIm3+lI)90Ji%z3UI{M9f`Rx;nxR~)Gz-_;EAQNbzLuy}2H4&{_PebK^ zg>8^_#V+S`B|P&(@Cj`FA48CfpD#OGUupFl%-;nSrGG!D2n0`@UJN+*W=l^lNSNvkZ{69ZTTAYfkikkhxRpjJ70+Dqk(^E-Eq%nxdPhaJN z8H_kLCT-4bZ+Y*BP^G-+cwTclGuO8Ga?uR?2QJzs*>}D^4ZJ?y?_hdxoUl~EfqZb> zJV$|yyHQU{WY+~p`*pM1y(v1JXi=>krxL`lZLmSx7I$(^N@Pl8gzU&&Vv@{-we%}o zkd1-Rg1ecrZO4wx_dHlR5&Q>u6Sat*q;dCM6>h;7-AW7=^s7l0Irs~(GD5H{$y#i! zBehoHxKbh`0=Tz^x^zD~`utwKtBdLsO-VQs@CWoCZJwFXGx@FLqb?;L!_^VjG9Q&V z*%NEe^bXgF;x$n%-A3gk=wkw=%%`vk3=$?+ z&UWN2X4`u2)*gmW4-DZB5mr*n&nV+n#+}bys*l!-`3ky*U8pEa<0pp#%k%sn{?Uv6 zRro4O@s$8;{?-^@^54I!k~Uu!yZ@ws|0IQ9uj@D&|C0<}s-3$2R7UyiDima%0yZR^ z^OG$59zQ*w8)`r&2r_5}u9s>Qz-U&~AqB~)Y_04J)Ed1(MPXx)zc)R4!e*!^6@q5J z6P>;PgZCBpX{d*7V65p zM!s$goLOKyI&OWFlSa#aSR%rX6%4eQN&ObxLst6biOW<}F|*N?SVyq&3t?9E(HW!w zP=*A6f)61!nRWSeJ$Q}OPWm+y@BMNL2+E7ipScHPR5Z6M0#Py06y#=*w4@K7FKBfqn2lqI;VaF3EN1 z$kGXt*DM~5!JHP5SJgGqs-5p{B>_}gl=kQ}sA}^i9V?d8iA?zxF#(hyEN>bRZC#p= zq)s!*bk#pwxzy%T^NGuKtrnLkvz9F8=dqU>wU(A_R+Oidn!e?V_koQck{i0-Uo9XV zjGAZ_=-h0|xl}N{pxD}SR_shXiAf2mM8D7+!G^CR0CNnR z5h`iW#VPhDby{YT%5*&U#bg#@hg||H%b*8Gk)ySBS(a)nIH1W(P6!GDU%L(5ym=$< z+LauP;6x-QfER)(NhN{kj~$T+IijW_xg{BHB<$U9)3BbKMgum@G+odZa4Yh;+RNbm z*!ga*fJdoFJz6Fg_nKb99)4;HL>hv8xg3OLC*l#8QD;fNZb{ zSwn~S<5EB$+@A1HPg~nVsj|0jw^?CgOPkC z_hRN)7VS|Edlcq)e@s5aNs~5@WsCGybyN1certPGTQapAvtU`XUgn7#JRGH4Xd_K# z(i7%~3{#pC9msT96=kLA1TdeGyaj1D?&9Rm)KHP!uYa-)Kn+$eq z6*>Xc!nJgr40;9eE!chVy5*V_9|#c)WjahKoLG=jaNjZp+_Zmn zX6C?%N`r06GckE6d<+3?rv`bhTTSo&p7xdO9HW8gNajWF^S#jpwLPvjXOvEP!sXNy zzmpp`Dv6fBG6f7BEKW<=!lxs=;Tl42Be!~7jid{q*h5lJT+~i#J;$2UqB?FpI97n% zm)~X+3OIdyfTW(Mxus*$?{tG~9}m1>wI4ZYpmXNwn%-=6lz%G>D~Rk|H%V{rVlQHc zRh=u}n^ocn&mI6WMfD7nCC%ZIkS{Q6RSeFe-O)E8+4 zjg5(WB0IB;t?;I(3bq>n!(=dZFZ4=7gKX0g(gwmaY&2+r7?x-Q#C5~;i-YBYhHA(a z)o6m3_HgtfJ*e?kp$I}>W^g1rxsS^TUz|KNX^(g@B1K--ayc#=Ap42&y+(QWw3m9h zbJMc!3cqG(T{YH4I`r6Csq+E5j%qnlZ-{95L1-mJX16^1_i9IL;T=&m&PXxbdBf^g zcSeOx+PcXH$lCaXBp18Doidpbc+Z(^kIeiNOeM_F48b;Ey7N$7?oe_@bJkzxf@BNi zBQygDyF=WQevj$%%WKOuq#P1%TeMi%b53AzgzPbs}fyN4h5FyHXHAKa9?<=3(rJitUP<(-AwOX1`k)*!&OOuqV#zrKIN zoo78F?xl-U6sZZ(_k#Y27^dTaxSjGu%L#}n)AvFNfBQ$bRm78=Mdqu9mHv86^8eR5 zR@&Ur@rz{sGm(Fdng3H3uS)s{(H!|)NKU5Fq=76Y3x`~Vs^4S&E{xEJMj5MTvA5AE zHJ-k;X5!+X`jlVegN#cgFXnXv{FE1I>XM?7ODiqDkj0+h=ySK@X#V;3{(#wwg<}${ z=eAqcK7`rXOih(?4QYN!zHJ6>yo9x(@kc9VqN<*H2u!tGF2UAnr^VR23t=@|OW|S4 z?*~EP&jGKHLNkbc^m!d{BR8EZcqDRje`@&4I_gWkIDO(B?rJ%GF=|iEem+ER5(YZ- zY!_&r=*fV*9U-`RXyA2A>yawRhh$ak@!__5)qR;DO3jU1eTvm)@8BgFS{qGziTT_K zUKLPv3bmL7-gsKyUmcqct(BS?{`s{*{hoo1(dn&z`oTIIybdxB$}0-8!gL2*?vWi^ z!5c@#SC+$zc#$rgU85(=ehZG}@D{mA9?!L^kPY1IEETveoXt$@noZ z5qGwf_gjq#V#;oiOD{O9W4|kNd*u|2BDV<#O`fBCdVm!;uvDp+oMs3my@c}rajPKcF zcF{O+1s8_qzl(G;yRXAZHQCKe%V40|lOM=I4QH-6^`PWXe(+s=QfP$;OgYkbOwoS@k{xSSNxfhmEl9NzC25I>! z1tg90p2Xz^G3ZEO;uo3xTTWf5B-gi0T%cYQ-#}k}1(M{8Ao}|8j=PmmHHlYDz1+^P zaXw}{UT3zszFfYh)O<_6#S4ZX2qvrmv$b=SMRWGSv)Y&YEg~p3P^m7mUAEGuO|I6w zdNhyB@Z7ko9^K_LTGXR>-%4r_N>>S1c-G4ZoyPkPEg7`q&=i{lJB%f`xY4kAprC7| ziVfLevX=Z9+m{Ik39DgNJ_tX?iA)b3+`wUgHVZGJHG+o-9TR{uTSgE^xh#)u3U3@* zQCelvMRmZaQyo`X06KEegqV_C|2`Fp7`iNz|$u33p5Zy#A z+J%NQk$u)xf+__jE!UW2=km`UHW%>$ECu?>$BB96ifASjF$$aXe(Q%9iC;d_m&0}m z__%UK569WD8Ocsv1`QV-%tm^p-9-Z1Z;yQt9yMJQB)5j638^TKnjsO)LYv&9y4 z3~!nD7@1vDo31azBy$NxD-8=L()Mo=vZq_0J`FAbi6pZ7_>_O0>1j999*1oE;w?E7%djRY=bPCjA=C8@9rRO(Vl_v znd00!^T9Rf+wVvRgwVsMP*t=^ghH9e^rwt9@m2Zxr$I5TH_cP1#Jp zPKBCKZu7HiYxyb(P1^F3VlQed(RCzD<&|2gX4N2St%+rLg8Qp*?!1&B z^?BB27vs65SsApOO6C=^^O6t1AL0R~O=AJNxJ@EJ{cB6#Q618~` zf~K+f$&9A3{Pxf0yg%La2UlpY`qZB}Fh<}Sa~t;a27B|TXYLGDFQkt8LiDBjez6wR zECKjhqyWkJD$LgRZXpI=NJsv5bQnSaswz;?uk~Pcjv$gq{Y|+RhlHE6-=lAgrX+3X z!`7Q~bRuYP3HXN$NTSZjecGFb9!k&&wdtcd$BAP(qSarWGF&MmE|O{ZMxEH;SHJ@b z=QyP3k(6k9^e%>HJ$f}7B4jmD-YkO_D$%1|gg!-pjug^fn3thsnu#RC$U$?brx=on z=HZ$6&DUUUM?@paU;+QZ*Aa&K`Gi$-@f$o&InN9DOpRCtm>zf*7aC}V3F@JwAUB+# zbR4-dWL|+@qSWs4Vr$QOyr#)Y;g2HXm+e5@X7@$Fo^ zPr#&#WjwOwxH?3pjid9;HJ`D#-BueBtX7 z{Oooct%R)v-F4Ysb?M=OUhIp1^)I)eOe z+H1`#C6Y27GAkTeZc!`7KJ)~@Lxvzk&Gd?*h?yWPb?)twW!HQZ79t_K<9Pm6HyT*Xvdqj6S9x3}c_ zkQzVoE3epwrsbTQ9-5@pA%$!|pgx+=h4HB&9NN=2K=iC^zfSp9U%h}qT*eI7Vw;qg zw_5Dd>p!~a;#Ka@iOqo5eC*KlpW#w%CI@5ORD|`$WZop#s>On&xO5vn3j}n7jwW7M65zzw=eYBUs8u8*pMy{3=VHY7A zfRGJo6EtW0*FAvDO`1>qQkNC|t-9=gF-HAo9Q|jT_dmv{ivKjIQ`Bg#v@8RKdvw$@xny(4)K$Q3$rF@Ul8{9H*Q=&IMncCnsvzNE!6DmO zo?kIEt!=^9Gp7eARZvEqNeV|?ROiv z0J;=S3*pG4N`CKcI>tQUEqE0%nXX=EX)2I1vBb!HfN#4%qK zaH?RdMXOMaG!Nw?cyLQP z9qtv@A~?|}3Lx`36gmgQ86GM6RP8e8-^mB}lTO93;Y5gQTqoKNNp;Mv6Kp_5VQ?&oYj3eo1i;BU%d!_}hcZ!kcU z9)r_LkWP4Rf}K--nCMNEL~G<4$Of0^!$+EClLmsS0`j1lHwl$KWg$w~N`peCUZ+1d zenMKTtx=RJAKM1g+ho3T?5I2!?}1HeXEK<?(?#_q_Jh9@80{mAi@dI`h&E*L50 zWR5lr4zF1bJ(Nt5Xpn)+M1B`skYv042GG*43~acj{{Tg*2&&W8(x=<$Q~GfTM>c$N zfq%c>o!$!cFa#NCGc5GIY)PVA{Y|+$a*h9ijNQ{O^ECeMSD2RU=mM2;Q)L=srGd>W zT^1?*Pr^}Cd!!b``9&XL6z@WU?$6?zYZtHSeOTXzR43HOynpI>Pzuo&T)u*x$(N?* zUsss_Yd-xiW*JJ-Uwj(P$E4lZSeA|w=h`9RxqtmvbkaE z3dI*Hv(vp6l{VJQfa|p&^4f|LJSdjLgpu=lrSpz!X7Z|LXUFH8+CV$R^vyh+MYLJC zg*FF_JyFsfJ^WNLbY}T6hj7mv{8{b=#j^@X9yE$;M%;OY#Lw1pk+ouQp@Sb^52DWr z+7n^PmZmp!_>xt`g?$emV_Of`nrSOFMO$@B!nCF*Vp!t_D>%UwgMxrwqq7geFM8ZG zXZ~Y+BXi*yuits08q`7xr24v*i?Fm6yX*YiGfPI7y0lubJdySrG*8Ajk2RiJJ+(J6 zY)lf!sh*|SKBd(|VJY8ZvdL%a^k>z(tf#M z60Be^-$b+{TXq5h2X1RF!g&Y`@s|S_yLan2RhcASnW3hDoaEi@9BiXq6 zsRu%yp2qWewMV%RNarhUAi~N$)C4KD&S~0X;W6Ja-MBS;g!J?W56D;j8J-brLYy?3Ob=hn<_&Z z8&!q;wT6-O2V{6Goj-fpF!}86{x$0z_^;s3T6pa4`*rWb{`TJU{rgG&zo++vDVqhp zpFuOyn;g!H@n9I^zIndlga&oQ$aund_`=pjO6s*4Yz2c!+|k1}2-kzbFk)10-*6!9 zM_^-_))Ljy+Z-=Fd2<-kpP$cP;l9z@84-%Qp`Yea4)UG_a zM}uTOd6LM6z?SSv39=zTG#?;Hq#aDM1I`P+GXE6tfY5(fRrUED9$T~I2~vR#Qy9p# z9InVn}BhBM(PE5#4g5~<5s~_chQZ_ZMtAOS6fU3AXU2aF|{QfxdnvBklbxTbdve z3js5sUL#*GkT@%I_O9t=jp!MXg0GKP^XPN@D`r_)e2WF2o3m}hsYe#2xxcD55esFr zDs3W;ts2W)a2Ja9ON+sh!!KNU*Z%b;-=<%iQ;VDgzi~8r;gB|app6ns&)%P7%T$N( zG81BS5^d{!oKXMATRc$SK|&u{w>MR7!YOwx)n@LZNCu-dGcN}7&EwRJ)pd0%%9QAm z(>Lf2EXvOHf$tW{>>xi%HK+eU8(y6h124X4L+#&=-lYHio26`H`8UJ2A?5!l!&KE! z$iyi3V33=%A)&Tu0b<}35d4UZaNXGJ3H9q&EF07}XP>wHh%{kvAKyN{>)?3Z4xzpS z53_H;C_$!&hrw-qDb*)g8Fwbxx1MkRVEfr`h7k(69kdE7Dhc_hryJK1SUWBuCxtsr zd5tlYWI3p>2C+{hL-kilxSpiF&%3Bxk|KXeBwx1kNx9HP0XMnAwIn~kjD}K>9H|YM zb2mu=L7GJO(_QeC{ZtSgW;7nv#;DJwggw`iXs_N_xJ|S0QQr-alC9kn0IH`ZZz{Xd ziY6qYp;DZnW%!Xc*w|w5Ci7JSEYAM^8+{k2p#{?B-W=}|!RmDz=iS)19!l&FQOXMwTl#(0!DkY3F?i+P${SQZn z>8wQ_ylK-|A(~MU*o*exkHOOo8|;gSmh^rb@c!w?N3yG|1+XCGTDAr_VW~nCR81vD zLqiLcko`1S=2ATnDjN zhj^*9mUL|=DpyvI+F#EZwdu#d$NG)3O|<#yDe)BbV!Y(5G=_E%Fz}WhGFNd$Y}s`- z{St3WHGw7&4%d8spq!?PkSre9JZB7Tn?EvYvj?EB4?Ez%1=z*+kb?nV5=dO<&HZp% z5WSxxy}wJ7|Iteh{jIwgD88mF7GGC!4B-CAG!2s1K$WCS_S=J2M(T3b6j!w-2dov) zz=rCU?r*xd+W*Y@BeREzP4qF7c2D7#SBmio*>O#`W4TSOs{BtuAK^k9-*1d#EEGch zt9s2WvkgHLRPnE&K8N=(*!NxdMMBuj-);Ks{E|$-(sUcq2iD@OjaJ}{n$BNC-gZhq zndyfe{Lo?Ua33E+gE6UWg%>+x_E{Qp{))}ATxWA5U$J@VZws)0XRYI(LHR$pwg!}! z(o))|@0iimL{KV2m$^<9$BgQH^FaFv3^kkzyX^?tYf`KB-2uUPd>Tx6mnI_ z0n6jfEKM57Nq|(yWDUwImzIt#8|^L8vMZaVD(}{-^`)Tq98OoAF+Av}*EGj#_MKO+ zolmc0SX&&=XFR_MqncnUeqp*rd2=Vssw@-Zl)5o!B^y}utu2$%`laiNN+=g+bpK}K zZojsE@Sv$!un;`kz`C*0drbf6@k46N$>mTzt6I)ID6oDg&kO>5-P}ON399n!&>7t| zJttMDP3i7vKZH|=w1DEprF3J6IQj2O6EaJ+hsj50&x>@^H?M&im|mKb%_mlBcom@g zRnO-@n81Bg{!bD<8Ee=KnlKOT0UU%TVsh|JgQ%5_#VSEwnZ+l!3sH3MEo!LG{UQ-N zR!FTDW>USVX|$s>X>AI>N}-z5a3V$hgB#ER08M8f$XWSNN01 zRmA#r7=P6fG&d#qQ$0gj)d=)2F=yf2L_k%rZxO=pGFQ)ebwl*LU9P^t)b|Nbf-P{?&(w%y>xJUz@p8Sg3*I&xrXs0AF!J`i>SUErT2D#?;!k0S}V zs+_V?3G@b#By3Sip*R^_?XH_dRQoexjwE;G} zokgl3()>m7j}*P){0^y*=mxZ?I1@;R_9TVAy?EvVMaLu544bu$l|G7Y>MmCU^Y zq~nw;ur>1wAXOcx#0^SK$&`GuqI=q^!c?yW((xcwN)(g9AdXCJ{DO^11DoZgFfc@o zl@rj%h9JGf2bcpI6!0WPk*bYrS9 zlJnK&5vr%fd!Q(pwP@86)z_qS&6?FjNgnxe^LK zpGp^hw$aU&=LM0maqw`g_bNG-0kIJ93%Ki#M=x&NEw!Ea$qJfENsO0A!>o!$LZ)h} z@tKYg5uwHgS1WyMs$UGmvss*_E*+oUILVdy_5u&2`2Uo46;M?zZJUFFfJk?Dcej9m z(%s#C=nxSEq`MKMOH#T8qy^~~P`X>Q=bf27d*;l3 z=Xt2#_pDkobE5`{Akqrki{oHvYzc=G6*NrWd(5#h;jKfF_1rLg4l!IW5dG5P5$D5v zR<_crGkVzsj2c(xp}-Th8r^$3TnX2&dDu-0WLX#t{qQ5n)zqUM<2Zv zv+9(2#Cu=$Z5|!IZmW+sa}{lX4|P{yO`g8g5zT|!=XB)r)`o9j z954Z@f9*@u57-!0NDaCqQ1(1r^J&BnUjtDP2xdK+^TInOaF zZ5OJmHdbXQwYtDe!aD8ASlLmb8Ts#8D^bnvjPLO)D}l4gky|lg7};O2yQd=Y4-NV9 z3wS1B+s|QFy`}5Q-7TbvT9J{fqeX8Qz-OElb-{6`pwu?<1bNmshlL z0897JHXSJm!WXt9VeE%Gn5~bE152ZX8^+Nde0Id0$gIkLyN+4hlT-auLD>|Cv9{dl&YG)S(J( zTo!2#Ya9VIIg<=AXF{-N3-4r@0%Hv z#tzvIQQ}{QUdrnYj;U-MdkQgrV!h~ReqAoREM2m=OMt4+MkZ-{Xzu5dB}Lnn8vG<4&yJ-(z;W*_e*6#uOWJ5+jlzCUooF6! z!ITD%5==qEV zD+-%M7eI>XM>2|So@k#cHB6nbF`e8x^lv6C<2i12>$$+8;|6ZZONoRdh$IdL7mfTi zQa%BIx)aX>*~u8FHT|?4$BR5-FA7}~kI=r)N6rL3WfC7paD;2M`io?81K7%B$hmD! zjB_V+XeP&2s?H@^J;t=NSQE*7*O;YeDy49}#b*q}^*mpaOy=8}k z%>;8#C|us^$r3g|3GN`6u%AYrzs^cQRZ~`NW~rsXJyIqf$+=)XM34xJdVWt4O{%Ob zP1I{b$}*IY2G?8aU`3WtGZkS;^yzxVcjeV?YjJlYC4{jV|2DALWNq z3!5pQAGgxWa)%v`jJF)%GutdvTQs%XcgtCK@0d^*%Ww^%^sQJk;tC>^jo2`|4};>M zFfUio2i*E9JKPU(;UhSO&ga{YnX|Bi`Rqd!A1m4`U556=0~U9@mRJ2rPer9BOal|T znq-2r;8rwPXQ#5bPWiYBM@zF?6Q<7~bKo;vk;q|9!rRLNG%z(G52(0J(m!J;TLjmo zWT0(j4GG04Y8ihgV)zn1>E4j_e2i#$yE7ZM_D2?U@!~fNiTa?j4f-Fx^4L}Ghgl2u z7B$fbN)APS4&95-tA40se5iLE1JGeL&#ydnorxhE!N*+BX>ltKK9TpIia^4dd0h~@ zyO}(lpmhS8WQyZJ%uKI6ApcCzV*in-7`H7R*RM3Ewn7TJw90M_X-rW=rm11Q7hAY~ zRvA=)x1`ev0r_$t|2^qrmRHg*yKpFulcEn4s{fEgtnO)d--4NBdI@AT2i`jOMU_ZVw}jmGvc-mjdKEJ)7MLs zQ?V!QFYoK~jlR7+Ao56~N+oQTw3t>|+OLDQO+);lqxxC(ix<9yTMe^maeb-+9@I8u z^}dNqETwovC&psnLmc4rdK_eR(C)_%o!>S)Q8ethCgqX}35|8lA~SZ?cn>u)zci%6 z9pM#F6E-Iq(CnZ9WG^>r68s&LK?p9#h&?yCZ5YaZl4esgXs{Pr~as-$x*b$aIza+WKUl*kK=) zkHoWsp#VNb-kd(Qz5A7{>YS<6DeOMO1&(KHTPlUk+zLqZM2q}%*98RCL(-;j^5V;O zmaO6~A_zT|z_d4?JJe(CXg$|8_3JsL%n91k!0I6J8R-h!iK7quN=7B{bPYeLk6a*b zb}D8wCtPtCftwGMi}gj#`J!C8_|n_6a?k{!NExa)mgi_;JUg&19C9qPaHcZ~LNfTC z3eXMN|7oDBH_1^B`W|irE_x(?FacEn7fZ-7TE{sr^IUaX5MrYG+$DFwH_o_DAww9? z?v{Lg_imh1UEx+F0a635(>+vWLkt6VomkYR$6_8(tWDM3yrzO>*1*BU2kpHLqZyki z9&E1scFlG#W(uJ@Q4{8!$Q8cgOaCC=lM{JNF=-hoZz&PgeX-F0TzkQI6M+3Ra70IL zY0pjPX>op|&x__t*td4BGe!}ClSYnTLgG#Od-r(!7WtOmv&e09)^@8+37QIp<@fqL zSae6R;24*0XCV#DrtKifhO6|;`fN~LAq&nQhz(PXn0Z^IGNH!#sEhnyql{%~%(XVe z1ASmUe~*Q6U0Vjusk}ALY_C0ZLY;%?;Zgg;3kI~ko#Qu!vaLNsZ$2rcgql7F^7)Ut zrWiMd8=pZ|&57~_#6PP|MZ4#k?C{w1Q&XruUOVC*vd+g&whOKlbWVieFv1?G1_}o* zhUN`>{>sxmziD4md&I9X@!ZEa9uzhuWXk(nvx7bNgE6;|G1i20QZ;pAdDPpK4!ggX zsl^R!PnJm*8%gk(EkS&ZDoAK4*vlGmP|mF6U9wQ`KGMfs?qR`rlca(n>2v6awj!QM z!>0iXLMz8|Fv|s^XGW_OM=zCmmnp032-nINjki^F$G>CGFQ6{o(1>%6`OahSHnQ^g zi)C_Y*--vR-MyJlc55Yt1}2=GT+96@)NB+JWkbY=jM1)H2Kg_uRJbX2yy~sSUKMI9 zi#5a@u2aA8fUgK|>ZNBxNzhKWh&Ed$iFzKuW*ET!qwU?p0P$?cb73AuQKP)MX_S_h z+A2%7ry5zK7U*#jxc8c;FxK11P)y|&iW~)JE*J&V9Z~A>U<=E*IBgrE=e}Wy!eYzD z$40*_Pb{!U=yNjaS5;18eeUc7>CbXHaexq~5&~)1#t=wh4$YMcOL+XOF zR17{|Q^)ZGZjWU2eWuQ*cy_cXuU6Zvf;#OOcAtFM^ue%})*QZc{c3if!9(bMJgF^riCR!pzC zEP}o&LJzmP%Eu=UGbm3Rb;u|J`guSha%QAYd8PE!re!>xqwd-R0kVF@w3ytUY600( z!bF=N*yi6}n3743alx|bvbk^{Paij|@IO7)O}$vMfk2oIN4|gmgq}BkEZEIt@EPM; zj~MZJOc7?VUvl4K?*a7;1NJ1drMWnx5oND8vd}ZV@1if&WmgV`8TcJ8G9vBnbEu1U zR={Ns#;SL6JmfBxqA4gdTpm_*vy;ikxa{+ zPZvw^LB>1d%jRUl{;2QO>O$H=g^*6#4fg`9z{^F0)QeqUU{&x?zPl94Cn}TJE7S=W z8Gd{;kv12@9X%5{WJH}c6sKdC300de+SeZ?l#Hzy#6XqIiQ!Q9>JRQ2j-_c$ce_VPfgnn$Go5`^RiqIuVBb{V`*F847$9smDGW# zF*hb?+=ZnU!#Ff$co(Zt6vz-u--$(SB&$JDD-Jq-*Abk-}q;&QlXT(fE=+{WEuao$3(z#Lf&X_FhtZ#kcNR0lbtV+&rY)Hp}Lm< z;dE%V>vk7&APWBws-z9Uw=8$b?hG;~4QD0%jxl~&hjKRH7jg~QoPxYX{=agNvdDFrEz-18ovbap3B3K zHF4da4EZ*&OYzkp+9)1#Vx!aR+v6c#$a)KwUQ2toMuqfFMpJm-a_q+_6Uzo+$D7oL zZ4cv6`*AQ6LaaQ|Y!)c@2`^@x?lo;Egh+ z4CvD^<~0mSDpPmiz}t$~D~(Qs+dsz>sduNh!7x0ro$XXmG;w+)>E!&4}5LB4(@3u{FzfASkUA^`R!`G?@P$b zX0N;{uC|#6(877RT=oy|LEe*AE#)6uRL8@Gp}c&pMu+!srbX2|A)dDrs`%`9LBPiL zoD_ZVzUOHmUITiT)B?&G`BFr;)Skz#nlt|f7r zixzM7f*>B14}G?pzIF#XB_&>jWo!kqMunU)A3iKP$_^@Xm|xC!utOw89@rF&nXlv_ zTkGkYiPZGL@#JN8UlFRA* zQSzY*@xgwEDr}mM9(jVG^oacqJ5;!*lgUL3e589u!&8D?k~1{rEb& zYUa4@=pK5*hUi{NJg+o?3EJaFdKa1O<}adndd9P)3r+@iX`zrn>>dlx>0H;=^B0UY z@>{lunwHRC7I0}Q4`4KjF0V$_QLeeY`#PL$q46$KbFXOAF)E5k^)k&}`<(FV1w#Pw zir@w`1cV!SLjC)s2cBQOqWjZi`g>x}?^9~>RZ7oQ&Tt%%TG9(rxiJeyShIpQ=r9vq zwC!~ka&#h&EAAKZdZT*lDowP@K!GICyblG95X#{sp>acbeTAwUpk9B& zNm1yPzH2I>ZSSm!=+xIKO!6dR*XGbTvmy1b+*Xry=}U(>$;PXUtLV{F^pZ?;Z+ZKc zc-5e$hI%EOLnXYqXGXTzp*=~|jbN-sdPv|OLn|MU7U~7)SKO-rs-MYa_#%JQdyJJq zbAn`q74(^I__#p!BdL$zD8# zL(LXPS4_y6rm=CWWt%ScrUmD+2hq18p##yZfWf_-TpM7wfeXF(fN zUvFzo=1ZOsY^~Un&#WPoZsKIY_iFsep@QKf%d68!yvRNcjC`xBs1jB)N!aH>S#T{f zo^WeL0+yR-7|h5;`iViYHE!09^4D#I6ne+9`q_Popq3#goyp8ibgDNgrF8XDg?6oN z04i&+>wk$F)kZ(IO;wXWdy7JlyU3t@?^q&>+|8D}?+Lo)ehb=%T6N0_fA~4!55}== ztlDlSCtxjKa;)Xmj`M>F7;TJ zylFy|T;HIE#-VGU*txvUjoFIEE_3~Xg-xjAv`!`0K(tYjJHAZ3 z%*P|I>VwzHOLY+rb6+5Np=!FF{>$=F*C)3##H-;m%TN4d9Q=Mx@Ta*G&we?4>}@QJ z4ITdp8Ekf>v@D4blHWI!`yyw;+D;4l4PLNPe5a9ACbCB4A6;$y?9EtN`OR_QFC}Ct z2+jZ(REGVpI!dCoqqm-X7>zy7lX3dc$lnCP)zVLb0fQ-VS{&fZsmF83R{o;k9;WWVTA7%?pylx}VIA%e@O zPl)&)mLL-r5(K?tuf;`clVd%@!LM2cMoxRXu-Q+O=+dkViRpm`lO5e7dQRl+s-+x0 zxU5{N+MWqdTpH7AWh)ZBfPI;#Z&sb5PYen8y98nD^DKApuX=w+*xEk;oZrCy)3nH^ zzv}(F!f!WzliXoV%#Q4l!DUACyL=*y0!bfXpBuc!kQWnDXYf%}I8ZIkHd4WOM@ojW z*rbCr95tqzhL#M2PmmC94jWQ<4Tg9$*x~yn{^^7#;SM3hS+frI!`F)0FIC%Ga7%H6 zV4rS>V%UBDatOJ0loL3&t3j*D6ct?s_XPP-JHb)p^|FTS7^6qilch1}hwn5ryMx&2H0$^2 zN-h-rJUiCmfqk>W)?58}b%)UO4H=bq2R(dsslmYIG|lhtqJ2=DSw^=}uX_Kz^Lkkz zxc4c3>wN$u8Uz{y3=EhS8iE-74*>ywEc|9*F`GLkEvh6$FD@enCTO``49+Ik{T=wr zv|zVrw+eBCKmAcmT1ZA*44i|)AT4&+!&RYcV8mM8mHje^j-u`und#zNt%oB~pKF zAb>X*>u*E;N0Rk-D{@n6^-Agd+CYFj=6{Oy*R{AQJa{G5dTk(pF!-_ax3#!?bKVke zy&L^ajkDWhE)3joe~a;-9ssWg?g|>-1-PjzawU~^Z6JWZ@UH=G3#a|&`){VTD`k@Z znj<(b{%?c7(kZzc=1m=iD z*qbccx9cdU^b6P@{ z+b2dcyU8 z1ipLV`7Y$mbAnf=Laz-3z;gc|A%7pqH%K||V%|Kqb$jFzdHj!dnOSs{1_G%1{cE(JAJ@3s!Z+XV-kxLL z1pZs-zh39v-1NSUP?Y!!gg77lfS?EeDT03^v*%rX`af|( Be7*nx diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 9e0264d04..44f3cf2c1 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip \ No newline at end of file +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/com.microsoft.java.debug.core/.classpath b/com.microsoft.java.debug.core/.classpath index 9ba41a249..b6fe6b96c 100644 --- a/com.microsoft.java.debug.core/.classpath +++ b/com.microsoft.java.debug.core/.classpath @@ -13,7 +13,7 @@ - + diff --git a/com.microsoft.java.debug.plugin/.classpath b/com.microsoft.java.debug.plugin/.classpath index de9b5e366..b2be945c8 100644 --- a/com.microsoft.java.debug.plugin/.classpath +++ b/com.microsoft.java.debug.plugin/.classpath @@ -1,6 +1,6 @@ - + diff --git a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF index 442e9bb17..f2ceffb53 100644 --- a/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF +++ b/com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Java Debug Server Plugin Bundle-SymbolicName: com.microsoft.java.debug.plugin;singleton:=true Bundle-Version: 0.53.2 -Bundle-RequiredExecutionEnvironment: JavaSE-11 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy Bundle-Activator: com.microsoft.java.debug.plugin.internal.JavaDebuggerServerPlugin Bundle-Vendor: Microsoft diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index baf2807df..deabb9f3e 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -1,4 +1,6 @@ - + + + @@ -14,4 +16,5 @@ + diff --git a/mvnw b/mvnw index e96ccd5fb..e9cf8d330 100755 --- a/mvnw +++ b/mvnw @@ -19,209 +19,277 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.3 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac -fi +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 fi fi - ;; -esac + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" +} - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - saveddir=`pwd` +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac - M2_HOME=`dirname "$PRG"`/.. +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} - cd "$saveddir" - # echo Using m2 at $M2_HOME +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" fi -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" fi -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true fi else - JAVACMD="`which java`" + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 fi fi -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" fi +fi - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi fi - # end of workaround done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; + set -f fi -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" fi -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 019bd74d7..3fd2be860 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,143 +1,189 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.3 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 9f59485d9..b8320ef8f 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.6 + 5.0.0 ${basedir} From dabcc49979f45e34216a83dd9fbd126852f6ec1f Mon Sep 17 00:00:00 2001 From: mozhuanzuojing <63572041+mozhuanzuojing@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:14:04 +0800 Subject: [PATCH 140/144] Update build.yml add distribution: 'temurin' of `JDK` (#600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update build.yml add distribution: 'temurin' of `JDK` add distribution: 'temurin' of JavaSE-21 * upgrade actions v4 、v5 * format yml --------- Co-authored-by: Changyong Gong Co-authored-by: wenyt <75360946+wenytang-ms@users.noreply.github.com> --- .github/workflows/build.yml | 115 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71d726f63..553d95a9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,80 +12,83 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - - name: Set up JDK 21 - uses: actions/setup-java@v1 - with: - java-version: '21' + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + - name: Cache local Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - - name: Verify - run: ./mvnw clean verify -U + - name: Verify + run: ./mvnw clean verify -U - - name: Checkstyle - run: ./mvnw checkstyle:check + - name: Checkstyle + run: ./mvnw checkstyle:check windows: name: Windows runs-on: windows-latest timeout-minutes: 30 steps: - - name: Set git to use LF - run: | - git config --global core.autocrlf false - git config --global core.eol lf + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - - name: Set up JDK 21 - uses: actions/setup-java@v1 - with: - java-version: '21' + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: $HOME/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- + - name: Cache local Maven repository + uses: actions/cache@v4 + with: + path: $HOME/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - - name: Verify - run: ./mvnw.cmd clean verify + - name: Verify + run: ./mvnw.cmd clean verify - - name: Checkstyle - run: ./mvnw.cmd checkstyle:check + - name: Checkstyle + run: ./mvnw.cmd checkstyle:check darwin: name: macOS runs-on: macos-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v2 - - - name: Set up JDK 21 - uses: actions/setup-java@v1 - with: - java-version: '21' - - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - - name: Verify - run: ./mvnw clean verify -U - - - name: Checkstyle - run: ./mvnw checkstyle:check + - uses: actions/checkout@v5 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + + - name: Cache local Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Verify + run: ./mvnw clean verify -U + + - name: Checkstyle + run: ./mvnw checkstyle:check From 905c7ba292bf52e7ed324a8fab80669b3806a6e5 Mon Sep 17 00:00:00 2001 From: Changyong Gong Date: Tue, 11 Nov 2025 13:46:33 +0800 Subject: [PATCH 141/144] chore: add triage open issues workflow (#613) --- .github/workflows/triage-agent.yml | 5 +- .github/workflows/triage-all-open-issues.yml | 145 +++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/triage-all-open-issues.yml diff --git a/.github/workflows/triage-agent.yml b/.github/workflows/triage-agent.yml index 185cf8f32..f1df6e117 100644 --- a/.github/workflows/triage-agent.yml +++ b/.github/workflows/triage-agent.yml @@ -1,4 +1,4 @@ -name: AI Triage - Label and Comment on New Issues +name: AI Triage on: issues: types: [opened] @@ -8,6 +8,9 @@ on: description: 'Issue number to triage (manual run). e.g. 123' required: true +run-name: >- + AI Triage for Issue #${{ github.event.issue.number || github.event.inputs.issue_number }} + permissions: issues: write contents: read diff --git a/.github/workflows/triage-all-open-issues.yml b/.github/workflows/triage-all-open-issues.yml new file mode 100644 index 000000000..092f4ef70 --- /dev/null +++ b/.github/workflows/triage-all-open-issues.yml @@ -0,0 +1,145 @@ +name: AI Triage - Process All Open Issues +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Dry run mode - only list issues without processing' + required: false + default: false + type: boolean + max_issues: + description: 'Maximum number of issues to process (0 = all)' + required: false + default: '0' + type: string + +permissions: + issues: write + contents: read + actions: write + +jobs: + get_open_issues: + runs-on: ubuntu-latest + outputs: + issue_numbers: ${{ steps.get_issues.outputs.issue_numbers }} + total_count: ${{ steps.get_issues.outputs.total_count }} + + steps: + - name: Get all open issues + id: get_issues + uses: actions/github-script@v6 + with: + script: | + // Use Search API to filter issues at API level + const { data } = await github.rest.search.issuesAndPullRequests({ + q: `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open -label:ai-triaged -label:invalid`, + sort: 'created', + order: 'asc', + per_page: 100 + }); + + const actualIssues = data.items; + + let issuesToProcess = actualIssues; + const maxIssues = parseInt('${{ inputs.max_issues }}' || '0'); + + if (maxIssues > 0 && actualIssues.length > maxIssues) { + issuesToProcess = actualIssues.slice(0, maxIssues); + console.log(`Limiting to first ${maxIssues} issues out of ${actualIssues.length} total`); + } + + const issueNumbers = issuesToProcess.map(issue => issue.number); + const totalCount = issuesToProcess.length; + + console.log(`Found ${actualIssues.length} open issues, processing ${totalCount}:`); + issuesToProcess.forEach(issue => { + console.log(` #${issue.number}: ${issue.title}`); + }); + + core.setOutput('issue_numbers', JSON.stringify(issueNumbers)); + core.setOutput('total_count', totalCount); + + process_issues: + runs-on: ubuntu-latest + needs: get_open_issues + if: needs.get_open_issues.outputs.total_count > 0 + + strategy: + # Process issues one by one (max-parallel: 1) + max-parallel: 1 + matrix: + issue_number: ${{ fromJSON(needs.get_open_issues.outputs.issue_numbers) }} + + steps: + - name: Log current issue being processed + run: | + echo "🔄 Processing issue #${{ matrix.issue_number }}" + echo "Total issues to process: ${{ needs.get_open_issues.outputs.total_count }}" + + - name: Check if dry run mode + if: inputs.dry_run == true + run: | + echo "🔍 DRY RUN MODE: Would process issue #${{ matrix.issue_number }}" + echo "Skipping actual triage processing" + + - name: Trigger triage workflow for issue + if: inputs.dry_run != true + uses: actions/github-script@v6 + with: + script: | + const issueNumber = '${{ matrix.issue_number }}'; + + try { + console.log(`Triggering triage workflow for issue #${issueNumber}`); + + const response = await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'triage-agent.yml', + ref: 'main', + inputs: { + issue_number: issueNumber + } + }); + + console.log(`✅ Successfully triggered triage workflow for issue #${issueNumber}`); + + } catch (error) { + console.error(`❌ Failed to trigger triage workflow for issue #${issueNumber}:`, error); + core.setFailed(`Failed to process issue #${issueNumber}: ${error.message}`); + } + + - name: Wait for workflow completion + if: inputs.dry_run != true + run: | + echo "⏳ Waiting for triage workflow to complete for issue #${{ matrix.issue_number }}..." + echo "Timeout: ${{ vars.TRIAGE_AGENT_TIMEOUT }} seconds" + sleep ${{ vars.TRIAGE_AGENT_TIMEOUT }} # Wait for triage workflow completion + + summary: + runs-on: ubuntu-latest + needs: [get_open_issues, process_issues] + if: always() + + steps: + - name: Print summary + run: | + echo "## Triage Processing Summary" + echo "Total open issues found: ${{ needs.get_open_issues.outputs.total_count }}" + + if [ "${{ inputs.dry_run }}" == "true" ]; then + echo "Mode: DRY RUN (no actual processing performed)" + else + echo "Mode: FULL PROCESSING" + fi + + if [ "${{ needs.process_issues.result }}" == "success" ]; then + echo "✅ All issues processed successfully" + elif [ "${{ needs.process_issues.result }}" == "failure" ]; then + echo "❌ Some issues failed to process" + elif [ "${{ needs.process_issues.result }}" == "skipped" ]; then + echo "⏭️ Processing was skipped (no open issues found)" + else + echo "⚠️ Processing completed with status: ${{ needs.process_issues.result }}" + fi From b62897e444a5b522b68dfb2c73aae21614d1fe30 Mon Sep 17 00:00:00 2001 From: Karl-Erik Enkelmann <110300169+playdohface@users.noreply.github.com> Date: Mon, 24 Nov 2025 07:49:45 +0100 Subject: [PATCH 142/144] Set source to null when unavailable in a StackTrace (#614) --- .../debug/core/adapter/handler/StackTraceRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 3fa0a9a9b..3bb319a01 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -220,7 +220,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrameInfo jdiFra } else { // For other unavailable method, such as lambda expression's built-in methods run/accept/apply, // display "Unknown Source" in the Call Stack View. - clientSource = new Types.Source("Unknown Source", "unknown", 0); + clientSource = null; } // DAP specifies lineNumber to be set to 0 when unavailable clientLineNumber = 0; From 81339de8108b50dfc93d2dc47dc48c72d8904902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Fu=C3=9Fenegger?= Date: Tue, 9 Dec 2025 02:13:35 +0100 Subject: [PATCH 143/144] Fix lspFrame.source NPE on stackTrace request (#616) * Fix lspFrame.source NPE on stackTrace request Follow up to: - https://github.com/microsoft/java-debug/pull/614 - https://github.com/microsoft/java-debug/pull/609 With the change to set the line number to 0 the jdiLineNumber != lspFrame.line comparison can evaluate to true: dap> lspFrame Types$StackFrame@78 column: 1 id: 6 line: 0 name: "0x000000002f0bc000.invokeVirtual(Object,Object)" presentationHint: "subtle" source: null dap> jdiLineNumber -1 `source` being null caused an NPE * Fix line number comparison in StackTraceRequestHandler --------- Co-authored-by: Changyong Gong --- .../core/adapter/handler/StackTraceRequestHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index 3bb319a01..f63d1b24e 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -29,9 +29,9 @@ import com.google.gson.JsonObject; import com.microsoft.java.debug.core.AsyncJdwpUtils; import com.microsoft.java.debug.core.DebugSettings; +import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.DebugUtility; import com.microsoft.java.debug.core.IBreakpoint; -import com.microsoft.java.debug.core.DebugSettings.Switch; import com.microsoft.java.debug.core.adapter.AdapterUtils; import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; @@ -40,13 +40,13 @@ import com.microsoft.java.debug.core.adapter.SourceType; import com.microsoft.java.debug.core.adapter.formatter.SimpleTypeFormatter; import com.microsoft.java.debug.core.adapter.variables.StackFrameReference; +import com.microsoft.java.debug.core.protocol.Events.TelemetryEvent; import com.microsoft.java.debug.core.protocol.Messages.Response; import com.microsoft.java.debug.core.protocol.Requests.Arguments; import com.microsoft.java.debug.core.protocol.Requests.Command; import com.microsoft.java.debug.core.protocol.Requests.StackTraceArguments; import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; -import com.microsoft.java.debug.core.protocol.Events.TelemetryEvent; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.LocalVariable; @@ -114,7 +114,7 @@ public CompletableFuture handle(Command command, Arguments arguments, result.add(lspFrame); frameReference.setSource(lspFrame.source); int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); - if (jdiLineNumber != lspFrame.line) { + if (jdiLineNumber >= 0 && jdiLineNumber != lspFrame.line) { decompiledClasses.add(lspFrame.source.path); } } From 9623b8ff69b188c5d0325110a5977a2623a67ea1 Mon Sep 17 00:00:00 2001 From: Changyong Gong Date: Tue, 16 Dec 2025 15:42:19 +0800 Subject: [PATCH 144/144] fix: lspFrame.source.path is null (#618) * fix: lspFrame.source.path is null * fix: update bundle location --- .../debug/core/adapter/handler/StackTraceRequestHandler.java | 2 +- .../com.microsoft.java.debug.tp.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java index f63d1b24e..6f54bcedb 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StackTraceRequestHandler.java @@ -114,7 +114,7 @@ public CompletableFuture handle(Command command, Arguments arguments, result.add(lspFrame); frameReference.setSource(lspFrame.source); int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1()); - if (jdiLineNumber >= 0 && jdiLineNumber != lspFrame.line) { + if (jdiLineNumber != lspFrame.line && lspFrame.source != null && lspFrame.source.path != null) { decompiledClasses.add(lspFrame.source.path); } } diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index deabb9f3e..31462a770 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -4,7 +4,7 @@ - +