exceptions = Collections.synchronizedMap(new HashMap<>());
+
+ @Override
+ public JdiExceptionReference getException(long threadId) {
+ return exceptions.get(threadId);
+ }
+
+ @Override
+ public JdiExceptionReference removeException(long threadId) {
+ return exceptions.remove(threadId);
+ }
+
+ @Override
+ public JdiExceptionReference setException(long threadId, JdiExceptionReference exception) {
+ return exceptions.put(threadId, exception);
+ }
+
+ @Override
+ public void removeAllExceptions() {
+ exceptions.clear();
+ }
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/HotCodeReplaceEvent.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/HotCodeReplaceEvent.java
new file mode 100644
index 000000000..7046b0ac6
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/HotCodeReplaceEvent.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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 HotCodeReplaceEvent {
+
+ public enum EventType {
+ ERROR(-1),
+
+ WARNING(-2),
+
+ STARTING(1),
+
+ END(2),
+
+ BUILD_COMPLETE(3);
+
+ private int value;
+
+ private EventType(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+ }
+
+ private EventType eventType;
+
+ private String message;
+
+ private Object data;
+
+ public HotCodeReplaceEvent(EventType eventType, String message) {
+ this.eventType = eventType;
+ this.message = message;
+ }
+
+ public HotCodeReplaceEvent(EventType eventType, String message, Object data) {
+ this(eventType, message);
+ this.data = data;
+ }
+
+ public EventType getEventType() {
+ return eventType;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public Object getData() {
+ return data;
+ }
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java
new file mode 100644
index 000000000..196714d52
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IBreakpointManager.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+* Copyright (c) 2019 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.debug.core.adapter;
+
+import com.microsoft.java.debug.core.IBreakpoint;
+import com.microsoft.java.debug.core.IMethodBreakpoint;
+import com.microsoft.java.debug.core.IWatchpoint;
+
+public interface IBreakpointManager {
+
+ /**
+ * Update the breakpoints associated with the source file.
+ *
+ * @see #setBreakpoints(String, IBreakpoint[], boolean)
+ * @param source
+ * source path of breakpoints
+ * @param breakpoints
+ * full list of breakpoints that locates in this source file
+ * @return the full breakpoint list that locates in the source file
+ */
+ IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints);
+
+ /**
+ * Update the breakpoints associated with the source file. If the requested breakpoints already registered in the breakpoint manager,
+ * reuse the cached one. Otherwise register the requested breakpoint as a new breakpoint. Besides, delete those not existed any more.
+ *
+ * If the source file is modified, delete all cached breakpoints associated the file first and re-register the new breakpoints.
+ *
+ * @param source
+ * source path of breakpoints
+ * @param breakpoints
+ * full list of breakpoints that locates in this source file
+ * @param sourceModified
+ * the source file is modified or not.
+ * @return the full breakpoint list that locates in the source file
+ */
+ IBreakpoint[] setBreakpoints(String source, IBreakpoint[] breakpoints, boolean sourceModified);
+
+ /**
+ * Update the watchpoint list. If the requested watchpoint already registered in the breakpoint manager,
+ * reuse the cached one. Otherwise register the requested watchpoint as a new watchpoint.
+ * Besides, delete those not existed any more.
+ *
+ * @param watchpoints
+ * the watchpoints requested by client
+ * @return the full registered watchpoints list
+ */
+ IWatchpoint[] setWatchpoints(IWatchpoint[] watchpoints);
+
+ /**
+ * Returns all registered breakpoints.
+ */
+ IBreakpoint[] getBreakpoints();
+
+ /**
+ * Returns the registered breakpoints at the source file.
+ */
+ IBreakpoint[] getBreakpoints(String source);
+
+ /**
+ * Returns all registered watchpoints.
+ */
+ IWatchpoint[] getWatchpoints();
+
+ /**
+ * 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/ICompletionsProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ICompletionsProvider.java
new file mode 100644
index 000000000..6597ff07b
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ICompletionsProvider.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2018 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.List;
+
+import com.microsoft.java.debug.core.protocol.Types.CompletionItem;
+import com.sun.jdi.StackFrame;
+
+public interface ICompletionsProvider extends IProvider {
+
+ /**
+ * Complete the code snippet on the target frame.
+ *
+ * @param frame
+ * the target frame that the completions on
+ * @param snippet
+ * the code snippet text
+ * @param line
+ * the line number of the operation happens inside the snippet
+ * @param column
+ * the column number of the operation happens inside the snippet
+ * @return a list of {@link CompletionItem}
+ */
+ List codeComplete(StackFrame frame, String snippet, int line, int column);
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapter.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapter.java
index b275c27f1..72405da2e 100644
--- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapter.java
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapter.java
@@ -11,8 +11,10 @@
package com.microsoft.java.debug.core.adapter;
+import java.util.concurrent.CompletableFuture;
+
import com.microsoft.java.debug.core.protocol.Messages;
public interface IDebugAdapter {
- Messages.Response dispatchRequest(Messages.Request request);
+ CompletableFuture dispatchRequest(Messages.Request request);
}
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 c51ab5158..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
@@ -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,28 +12,16 @@
package com.microsoft.java.debug.core.adapter;
import java.nio.charset.Charset;
+import java.nio.file.Path;
import java.util.Map;
import com.microsoft.java.debug.core.IDebugSession;
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
-import com.microsoft.java.debug.core.protocol.Events;
+import com.microsoft.java.debug.core.protocol.IProtocolServer;
+import com.microsoft.java.debug.core.protocol.Requests.StepFilters;
public interface IDebugAdapterContext {
- /**
- * Send debug event synchronously.
- *
- * @param event
- * the debug event
- */
- void sendEvent(Events.DebugEvent event);
-
- /**
- * Send debug event asynchronously.
- *
- * @param event
- * the debug event
- */
- void sendEventAsync(Events.DebugEvent event);
+ IProtocolServer getProtocolServer();
T getProvider(Class clazz);
@@ -63,10 +51,20 @@ public interface IDebugAdapterContext {
void setClientLinesStartAt1(boolean clientLinesStartAt1);
+ boolean isClientColumnsStartAt1();
+
+ void setClientColumnsStartAt1(boolean clientColumnsStartAt1);
+
+ boolean isDebuggerColumnsStartAt1();
+
boolean isClientPathsAreUri();
void setClientPathsAreUri(boolean clientPathsAreUri);
+ void setSupportsRunInTerminalRequest(boolean supportsRunInTerminalRequest);
+
+ boolean supportsRunInTerminalRequest();
+
boolean isAttached();
void setAttached(boolean attached);
@@ -87,7 +85,7 @@ public interface IDebugAdapterContext {
void setVariableFormatter(IVariableFormatter variableFormatter);
- Map getSourceLookupCache();
+ Map getSourceLookupCache();
void setDebuggeeEncoding(Charset encoding);
@@ -104,4 +102,60 @@ public interface IDebugAdapterContext {
void setMainClass(String mainClass);
String getMainClass();
+
+ void setStepFilters(StepFilters stepFilters);
+
+ StepFilters getStepFilters();
+
+ IStackFrameManager getStackFrameManager();
+
+ LaunchMode getLaunchMode();
+
+ void setLaunchMode(LaunchMode launchMode);
+
+ Process getDebuggeeProcess();
+
+ void setDebuggeeProcess(Process debuggeeProcess);
+
+ void setClasspathJar(Path classpathJar);
+
+ Path getClasspathJar();
+
+ void setArgsfile(Path argsfile);
+
+ Path getArgsfile();
+
+ IExceptionManager getExceptionManager();
+
+ IBreakpointManager getBreakpointManager();
+
+ IStepResultManager getStepResultManager();
+
+ void setShellProcessId(long shellProcessId);
+
+ long getShellProcessId();
+
+ void setProcessId(long processId);
+
+ long getProcessId();
+
+ void setThreadCache(ThreadCache cache);
+
+ ThreadCache getThreadCache();
+
+ boolean asyncJDWP();
+
+ boolean asyncJDWP(long usableLatency/**ms*/);
+
+ boolean isLocalDebugging();
+
+ void setLocalDebugging(boolean local);
+
+ 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/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/IDebugRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugRequestHandler.java
index bb8254771..69ad9cbd6 100644
--- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugRequestHandler.java
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugRequestHandler.java
@@ -12,13 +12,19 @@
package com.microsoft.java.debug.core.adapter;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
-import com.microsoft.java.debug.core.protocol.Messages;
+import com.microsoft.java.debug.core.protocol.Messages.Response;
import com.microsoft.java.debug.core.protocol.Requests;
+import com.microsoft.java.debug.core.protocol.Requests.Arguments;
+import com.microsoft.java.debug.core.protocol.Requests.Command;
public interface IDebugRequestHandler {
List getTargetCommands();
- void handle(Requests.Command command, Requests.Arguments arguments, Messages.Response response, IDebugAdapterContext context);
+ default void initialize(IDebugAdapterContext context) {
+ }
+
+ CompletableFuture handle(Command command, Arguments arguments, Response response, IDebugAdapterContext context);
}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java
new file mode 100644
index 000000000..8cece2f58
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2017-2019 Microsoft Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * 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.concurrent.CompletableFuture;
+
+import com.microsoft.java.debug.core.IEvaluatableBreakpoint;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+
+/**
+ * An evaluation engine performs an evaluation of a code snippet or expression
+ * in a specified thread of a debug target. An evaluation engine is associated
+ * with a specific debug target and Java project on creation.
+ */
+public interface IEvaluationProvider extends IProvider {
+ /**
+ * This method provides the event hub the ability to exclude the breakpoint event raised during evaluation.
+ * @param thread the thread to be checked against evaluation work.
+ * @return whether or not the thread is performing evaluation
+ */
+ boolean isInEvaluation(ThreadReference thread);
+
+ /**
+ * Evaluate the expression in the context of the specified stack frame, return the promise which is to be resolved/rejected when
+ * the evaluation finishes.
+ *
+ * @param expression The expression to be evaluated
+ * @param thread The suspended thread the evaluation will be executed at
+ * @param depth The stack frame depth in the suspended thread
+ * @return the evaluation result future
+ */
+ CompletableFuture evaluate(String expression, ThreadReference thread, int depth);
+
+ /**
+ * Evaluate the expression in the context of the specified 'this' object, return the promise which is to be resolved/rejected when
+ * the evaluation finishes.
+ * @param expression The expression to be evaluated
+ * @param thisContext The 'this' context for the evaluation
+ * @param thread The suspended thread which the evaluation will be executed at
+ * @return the evaluation result future
+ */
+ CompletableFuture evaluate(String expression, ObjectReference thisContext, ThreadReference thread);
+
+ /**
+ * Evaluate the conditional breakpoint or logpoint at the given thread and return the promise which is to be resolved/rejected when
+ * the evaluation finishes.
+ *
+ * @param breakpoint The evaluatable breakpoint
+ * @param thread The jdi thread to the expression will be executed at
+ * @return the evaluation result future
+ */
+ CompletableFuture evaluateForBreakpoint(IEvaluatableBreakpoint breakpoint, ThreadReference thread);
+
+ /**
+ * Invoke the specified method with the given arguments at this object and the given thread, and return the result.
+ * The given thread is resumed to perform the method invocation. The thread will suspend in its originallocation when the method invocation is complete.
+ * @param thisContext The 'this' context for the invocation
+ * @param methodName The method to be invoked
+ * @param methodSignature The JNI style signature of the method to be invoked
+ * @param args The arguments of the method, which can be null or empty if there are none
+ * @param thread The thread in which to invoke the method
+ * @param invokeSuper true if the method lookup should begin in thisobject's superclass
+ * @return The result of invoking the method
+ */
+ CompletableFuture invokeMethod(ObjectReference thisContext, String methodName, String methodSignature,
+ Value[] args, ThreadReference thread, boolean invokeSuper);
+
+ /**
+ * Call this method when the thread is to be resumed by user, it will first cancel ongoing evaluation tasks on specified thread and
+ * ensure the inner states is cleaned.
+ *
+ * @param thread the JDI thread reference where the evaluation task is executing at
+ */
+ void clearState(ThreadReference thread);
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IExceptionManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IExceptionManager.java
new file mode 100644
index 000000000..eca1c80bb
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IExceptionManager.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+* Copyright (c) 2019 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.debug.core.adapter;
+
+import com.microsoft.java.debug.core.JdiExceptionReference;
+
+public interface IExceptionManager {
+ /**
+ * Returns the Exception associated with the thread.
+ */
+ JdiExceptionReference getException(long threadId);
+
+ /**
+ * Removes the Exception associated with the thread. Returns the previous Exception mapping to the thread,
+ * null if no mapping exists.
+ */
+ JdiExceptionReference removeException(long threadId);
+
+ /**
+ * Associates an Exception with the thread. Returns the previous Exception mapping to the thread,
+ * null if no mapping exists before.
+ */
+ JdiExceptionReference setException(long threadId, JdiExceptionReference exception);
+
+ /**
+ * Clear all Exceptions.
+ */
+ void removeAllExceptions();
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IHotCodeReplaceProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IHotCodeReplaceProvider.java
new file mode 100644
index 000000000..0760c432e
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IHotCodeReplaceProvider.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Microsoft Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Microsoft Corporation - initial API and implementation
+ *******************************************************************************/
+
+package com.microsoft.java.debug.core.adapter;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+import io.reactivex.Observable;
+
+public interface IHotCodeReplaceProvider extends IProvider {
+ void onClassRedefined(Consumer> consumer);
+
+ CompletableFuture> redefineClasses();
+
+ Observable getEventHub();
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java
index 17b137b2e..86c30929c 100644
--- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IProvider.java
@@ -13,16 +13,21 @@
import java.util.Map;
-import com.microsoft.java.debug.core.IDebugSession;
-
public interface IProvider {
/**
* Initialize this provider.
- * @param debugSession
- * The associated debug session
+ *
+ * @param debugContext
+ * The associated debug context
* @param options
* the options
*/
- default void initialize(IDebugSession debugSession, Map options) {
+ default void initialize(IDebugAdapterContext debugContext, Map options) {
+ }
+
+ /**
+ * Close the provider and free all associated resources.
+ */
+ default void close() {
}
}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ISourceLookUpProvider.java
index 12f9756ab..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
@@ -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
@@ -8,17 +8,137 @@
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/
-
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;
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;
+
+ /**
+ * 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
+ * the specified project name
+ * @return the Java runtime version the specified project used. null if projectName is empty or doesn't exist.
+ */
+ default String getJavaRuntimeVersion(String projectName) {
+ return null;
+ }
+
+ /**
+ * Return method invocation found in the statement as the given line number of
+ * the source file.
+ *
+ * @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(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;
+ public String methodSignature;
+ public String methodGenericSignature;
+ public String declaringTypeName;
+ public int lineStart;
+ public int lineEnd;
+ public int columnStart;
+ public int columnEnd;
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(expression, methodName, methodSignature, methodGenericSignature, declaringTypeName,
+ lineStart, lineEnd, columnStart, columnEnd);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof MethodInvocation)) {
+ return false;
+ }
+ MethodInvocation other = (MethodInvocation) obj;
+ 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/IStackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java
new file mode 100644
index 000000000..8d6c74486
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStackFrameManager.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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
+ * 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.adapter.variables.StackFrameReference;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+
+public interface IStackFrameManager {
+ /**
+ * Get a jdi stack frame from stack frame reference.
+ *
+ * @param ref the stackframe reference
+ * @return the jdi stackframe
+ */
+ StackFrame getStackFrame(StackFrameReference ref);
+
+ /**
+ * Refresh all stackframes from jdi thread.
+ *
+ * @param thread the jdi thread
+ * @return all the stackframes in the specified thread
+ */
+ 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.
+ *
+ * @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);
+
+ /**
+ * Clear the stackframes cache from the specified thread.
+ *
+ * @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/IStepResultManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java
new file mode 100644
index 000000000..c19c0ecd6
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IStepResultManager.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+* Copyright (c) 2020 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.debug.core.adapter;
+
+import com.microsoft.java.debug.core.JdiMethodResult;
+
+public interface IStepResultManager {
+ JdiMethodResult setMethodResult(long threadId, JdiMethodResult methodResult);
+
+ JdiMethodResult getMethodResult(long threadId);
+
+ JdiMethodResult removeMethodResult(long threadId);
+
+ void removeAllMethodResults();
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java
new file mode 100644
index 000000000..095c070bf
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IVirtualMachineManager.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Microsoft Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Microsoft Corporation - initial API and implementation
+ *******************************************************************************/
+
+package com.microsoft.java.debug.core.adapter;
+
+public interface IVirtualMachineManager extends com.sun.jdi.VirtualMachineManager {
+ boolean connectVirtualMachine(com.sun.jdi.VirtualMachine vm);
+
+ boolean disconnectVirtualMachine(com.sun.jdi.VirtualMachine vm);
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/LaunchMode.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/LaunchMode.java
new file mode 100644
index 000000000..0c3b4ee48
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/LaunchMode.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+* Copyright (c) 2018 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 LaunchMode {
+ DEBUG,
+ NO_DEBUG
+}
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 de066bcaa..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
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2017 Microsoft Corporation and others.
+* Copyright (c) 2017-2019 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -17,18 +17,18 @@
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.stream.Stream;
-import io.reactivex.functions.Consumer;
+import com.microsoft.java.debug.core.protocol.Events.OutputEvent.Category;
+
+import io.reactivex.Observable;
+import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
public class ProcessConsole {
- private Process process;
- private String name;
- private Charset encoding;
- private PublishSubject stdoutSubject = PublishSubject.create();
- private PublishSubject stderrSubject = PublishSubject.create();
- private Thread stdoutThread = null;
- private Thread stderrThread = null;
+ private InputStreamObservable stdoutStream;
+ private InputStreamObservable stderrStream;
+ private Observable observable = null;
public ProcessConsole(Process process) {
this(process, "Process", StandardCharsets.UTF_8);
@@ -44,76 +44,126 @@ public ProcessConsole(Process process) {
* the process encoding format
*/
public ProcessConsole(Process process, String name, Charset encoding) {
- this.process = process;
- this.name = name;
- this.encoding = encoding;
+ this.stdoutStream = new InputStreamObservable(name + " Stdout Handler", process.getInputStream(), encoding);
+ this.stderrStream = new InputStreamObservable(name + " Stderr Handler", process.getErrorStream(), encoding);
+ Observable stdout = this.stdoutStream.messages().map((message) -> new ConsoleMessage(message, Category.stdout));
+ Observable stderr = this.stderrStream.messages().map((message) -> new ConsoleMessage(message, Category.stderr));
+ this.observable = Observable.mergeArrayDelayError(stdout, stderr).observeOn(Schedulers.newThread());
}
/**
- * Start two separate threads to monitor the messages from stdout and stderr streams of the target process.
+ * Start monitoring the stdout/stderr streams of the target process.
*/
public void start() {
- this.stdoutThread = new Thread(this.name + " Stdout Handler") {
- public void run() {
- monitor(process.getInputStream(), stdoutSubject);
- }
- };
- stdoutThread.setDaemon(true);
- stdoutThread.start();
-
- this.stderrThread = new Thread(this.name + " Stderr Handler") {
- public void run() {
- monitor(process.getErrorStream(), stderrSubject);
- }
- };
- stderrThread.setDaemon(true);
- stderrThread.start();
+ stdoutStream.start();
+ stderrStream.start();
}
/**
- * Stop the process console handlers.
+ * Stop monitoring the process console.
*/
public void stop() {
- if (this.stdoutThread != null) {
- this.stdoutThread.interrupt();
- this.stdoutThread = null;
- }
+ stdoutStream.stop();
+ stderrStream.stop();
+ }
- if (this.stderrThread != null) {
- this.stderrThread.interrupt();
- this.stderrThread = null;
- }
+ public Observable messages() {
+ return observable;
}
- public void onStdout(Consumer callback) {
- stdoutSubject.subscribe(callback);
+ public Observable stdoutMessages() {
+ return this.messages().filter((message) -> message.category == Category.stdout);
}
- public void onStderr(Consumer callback) {
- stderrSubject.subscribe(callback);
+ public Observable stderrMessages() {
+ return this.messages().filter((message) -> message.category == Category.stderr);
}
- private void monitor(InputStream input, PublishSubject subject) {
- BufferedReader reader = new BufferedReader(new InputStreamReader(input, encoding));
- final int BUFFERSIZE = 4096;
- char[] buffer = new char[BUFFERSIZE];
- while (true) {
- try {
- if (Thread.interrupted()) {
- subject.onComplete();
- return;
+ /**
+ * Split the stdio message to lines, and return them as a new Observable.
+ */
+ public Observable lineMessages() {
+ return this.messages().map((message) -> {
+ String[] lines = message.output.split("(?<=\n)");
+ return Stream.of(lines).map((line) -> new ConsoleMessage(line, message.category)).toArray(ConsoleMessage[]::new);
+ }).concatMap((lines) -> Observable.fromArray(lines));
+ }
+
+ public static class InputStreamObservable {
+ private PublishSubject rxSubject = PublishSubject.create();
+ private String name;
+ private InputStream inputStream;
+ private Charset encoding;
+ private Thread loopingThread;
+
+ /**
+ * Constructor.
+ */
+ public InputStreamObservable(String name, InputStream inputStream, Charset encoding) {
+ this.name = name;
+ this.inputStream = inputStream;
+ this.encoding = encoding;
+ }
+
+ /**
+ * Starts the stream.
+ */
+ public void start() {
+ loopingThread = new Thread(name) {
+ public void run() {
+ monitor(inputStream, rxSubject);
}
- int read = reader.read(buffer, 0, BUFFERSIZE);
- if (read == -1) {
- subject.onComplete();
+ };
+ loopingThread.setDaemon(true);
+ loopingThread.start();
+ }
+
+ /**
+ * Stops the stream.
+ */
+ public void stop() {
+ if (loopingThread != null) {
+ loopingThread.interrupt();
+ loopingThread = null;
+ }
+ }
+
+ private void monitor(InputStream input, PublishSubject subject) {
+ BufferedReader reader = new BufferedReader(encoding == null ? new InputStreamReader(input) : new InputStreamReader(input, encoding));
+ final int BUFFERSIZE = 4096;
+ char[] buffer = new char[BUFFERSIZE];
+ while (true) {
+ try {
+ if (Thread.interrupted()) {
+ subject.onComplete();
+ return;
+ }
+ int read = reader.read(buffer, 0, BUFFERSIZE);
+ if (read == -1) {
+ subject.onComplete();
+ return;
+ }
+
+ subject.onNext(new String(buffer, 0, read));
+ } catch (IOException e) {
+ subject.onError(e);
return;
}
-
- subject.onNext(new String(buffer, 0, read));
- } catch (IOException e) {
- subject.onError(e);
- return;
}
}
+
+ public Observable messages() {
+ return rxSubject;
+ }
+ }
+
+ public static class ConsoleMessage {
+ public String output;
+ public Category category;
+
+ public ConsoleMessage(String message, Category category) {
+ this.output = message;
+ this.category = category;
+ }
}
}
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 d1850a9aa..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
@@ -13,15 +13,31 @@
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+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.UsageDataSession;
import com.microsoft.java.debug.core.protocol.AbstractProtocolServer;
+import com.microsoft.java.debug.core.protocol.Events.DebugEvent;
+import com.microsoft.java.debug.core.protocol.Events.StoppedEvent;
import com.microsoft.java.debug.core.protocol.Messages;
+import com.sun.jdi.VMDisconnectedException;
public class ProtocolServer extends AbstractProtocolServer {
+ private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
+
private IDebugAdapter debugAdapter;
private UsageDataSession usageDataSession = new UsageDataSession();
+ private Object lock = new Object();
+ private boolean isDispatchingRequest = false;
+ private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue<>();
+
/**
* Constructs a protocol server instance based on the given input stream and output stream.
* @param input
@@ -33,42 +49,130 @@ public class ProtocolServer extends AbstractProtocolServer {
*/
public ProtocolServer(InputStream input, OutputStream output, IProviderContext context) {
super(input, output);
- this.debugAdapter = new DebugAdapter((debugEvent, willSendLater) -> {
- // If the protocolServer has been stopped, it'll no longer receive any event.
- if (!terminateSession) {
- if (willSendLater) {
- sendEventLater(debugEvent.type, debugEvent);
- } else {
- sendEvent(debugEvent.type, debugEvent);
- }
- }
- }, context);
+ debugAdapter = new DebugAdapter(this, context);
}
/**
- * A while-loop to parse input data and send output data constantly.
+ * 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 void start() {
- usageDataSession.reportStart();
- super.start();
+ public ProtocolServer(InputStream input, OutputStream output, IDebugAdapterFactory debugAdapterFactory) {
+ super(input, output);
+ debugAdapter = debugAdapterFactory.create(this);
}
/**
- * Sets terminateSession flag to true. And the dispatcher loop will be terminated after current dispatching operation finishes.
+ * A while-loop to parse input data and send output data constantly.
*/
- public void stop() {
+ @Override
+ public void run() {
+ usageDataSession.reportStart();
+ super.run();
usageDataSession.reportStop();
- super.stop();
usageDataSession.submitUsageData();
}
- protected void dispatchRequest(Messages.Request request) {
- Messages.Response response = this.debugAdapter.dispatchRequest(request);
- if (request.command.equals("disconnect")) {
- stop();
- }
- sendMessage(response);
+ @Override
+ public void sendResponse(Messages.Response response) {
usageDataSession.recordResponse(response);
+ super.sendResponse(response);
+ }
+
+ @Override
+ public CompletableFuture sendRequest(Messages.Request request) {
+ usageDataSession.recordRequest(request);
+ return super.sendRequest(request);
+ }
+
+ @Override
+ public CompletableFuture sendRequest(Messages.Request request, long timeout) {
+ usageDataSession.recordRequest(request);
+ return super.sendRequest(request, timeout);
+ }
+
+ @Override
+ public void sendEvent(DebugEvent event) {
+ // See the two bugs https://github.com/Microsoft/java-debug/issues/134 and https://github.com/Microsoft/vscode/issues/58327,
+ // it requires the java-debug to send the StoppedEvent after ContinueResponse/StepResponse is received by DA.
+ if (event instanceof StoppedEvent) {
+ sendEventLater(event);
+ } else {
+ super.sendEvent(event);
+ }
+
+ }
+
+ /**
+ * If the the dispatcher is idle, then send the event to the DA immediately.
+ * Else add the new event to an eventQueue first and send them when dispatcher becomes idle again.
+ */
+ private void sendEventLater(DebugEvent event) {
+ synchronized (lock) {
+ if (this.isDispatchingRequest) {
+ this.eventQueue.offer(event);
+ } else {
+ super.sendEvent(event);
+ }
+ }
}
+ @Override
+ protected void dispatchRequest(Messages.Request request) {
+ usageDataSession.recordRequest(request);
+ try {
+ synchronized (lock) {
+ this.isDispatchingRequest = true;
+ }
+
+ debugAdapter.dispatchRequest(request).thenCompose((response) -> {
+ CompletableFuture future = new CompletableFuture<>();
+ if (response != null) {
+ sendResponse(response);
+ future.complete(null);
+ } else {
+ future.completeExceptionally(new DebugException("The request dispatcher should not return null response.",
+ ErrorCode.UNKNOWN_FAILURE.getId()));
+ }
+ return future;
+ }).exceptionally((ex) -> {
+ Messages.Response response = new Messages.Response(request.seq, request.command);
+ if (ex instanceof CompletionException && ex.getCause() != null) {
+ ex = ex.getCause();
+ }
+
+ if (ex instanceof VMDisconnectedException) {
+ // mark it success to avoid reporting error on VSCode.
+ response.success = true;
+ sendResponse(response);
+ } else {
+ String exceptionMessage = ex.getMessage() != null ? ex.getMessage() : ex.toString();
+ ErrorCode errorCode = ex instanceof DebugException ? ErrorCode.parse(((DebugException) ex).getErrorCode()) : ErrorCode.UNKNOWN_FAILURE;
+ boolean isUserError = ex instanceof DebugException && ((DebugException) ex).isUserError();
+ if (isUserError) {
+ usageDataSession.recordUserError(errorCode);
+ } else {
+ logger.log(Level.SEVERE, String.format("[error response][%s]: %s", request.command, exceptionMessage), ex);
+ }
+
+ sendResponse(AdapterUtils.setErrorResponse(response,
+ errorCode,
+ exceptionMessage));
+ }
+ return null;
+ }).join();
+ } finally {
+ synchronized (lock) {
+ this.isDispatchingRequest = false;
+ }
+
+ while (this.eventQueue.peek() != null) {
+ super.sendEvent(this.eventQueue.poll());
+ }
+ }
+ }
}
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/StackFrameManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java
new file mode 100644
index 000000000..518cc7761
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StackFrameManager.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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
+ * 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.HashMap;
+import java.util.Map;
+
+import com.microsoft.java.debug.core.adapter.variables.StackFrameReference;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+
+public class StackFrameManager implements IStackFrameManager {
+ private Map threadStackFrameMap = new HashMap<>();
+
+ @Override
+ public synchronized StackFrame getStackFrame(StackFrameReference ref) {
+ ThreadReference thread = ref.getThread();
+ int depth = ref.getDepth();
+ StackFrame[] frames = threadStackFrameMap.get(thread.uniqueID());
+ return frames == null || frames.length < depth ? null : frames[depth];
+ }
+
+ @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) {
+ 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]);
+ }
+ } 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];
+ }
+ }
+
+ @Override
+ 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/StepResultManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java
new file mode 100644
index 000000000..dd3937181
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/StepResultManager.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+* Copyright (c) 2020 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.debug.core.adapter;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.microsoft.java.debug.core.JdiMethodResult;
+
+public class StepResultManager implements IStepResultManager {
+ private Map methodResults = Collections.synchronizedMap(new HashMap<>());
+
+ @Override
+ public JdiMethodResult setMethodResult(long threadId, JdiMethodResult methodResult) {
+ return this.methodResults.put(threadId, methodResult);
+ }
+
+ @Override
+ public JdiMethodResult getMethodResult(long threadId) {
+ return this.methodResults.get(threadId);
+ }
+
+ @Override
+ public JdiMethodResult removeMethodResult(long threadId) {
+ return this.methodResults.remove(threadId);
+ }
+
+ @Override
+ public void removeAllMethodResults() {
+ this.methodResults.clear();
+ }
+}
diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/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..57d39154e
--- /dev/null
+++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ThreadCache.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+* 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.HashMap;
+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;
+
+public class ThreadCache {
+ private List allThreads = new ArrayList<>();
+ private Map