|
@@ -41,6 +41,13 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
this._debuggerSources = options.debuggerSources;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Signal emitted for debug event messages.
|
|
|
+ */
|
|
|
+ get eventMessage(): ISignal<IDebugger, IDebugger.ISession.Event> {
|
|
|
+ return this._eventMessage;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Whether the debug service is disposed.
|
|
|
*/
|
|
@@ -55,6 +62,13 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
return this._session?.isStarted ?? false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the debugger model.
|
|
|
+ */
|
|
|
+ get model(): IDebugger.IModel {
|
|
|
+ return this._model;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Returns the current debug session.
|
|
|
*/
|
|
@@ -91,24 +105,37 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns the debugger model.
|
|
|
+ * Signal emitted upon session changed.
|
|
|
*/
|
|
|
- get model(): IDebugger.IModel {
|
|
|
- return this._model;
|
|
|
+ get sessionChanged(): ISignal<IDebugger, IDebugger.ISession> {
|
|
|
+ return this._sessionChanged;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Signal emitted upon session changed.
|
|
|
+ * Dispose the debug service.
|
|
|
*/
|
|
|
- get sessionChanged(): ISignal<IDebugger, IDebugger.ISession> {
|
|
|
- return this._sessionChanged;
|
|
|
+ dispose(): void {
|
|
|
+ if (this.isDisposed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this._isDisposed = true;
|
|
|
+ Signal.clearData(this);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Signal emitted for debug event messages.
|
|
|
+ * Computes an id based on the given code.
|
|
|
+ *
|
|
|
+ * @param code The source code.
|
|
|
*/
|
|
|
- get eventMessage(): ISignal<IDebugger, IDebugger.ISession.Event> {
|
|
|
- return this._eventMessage;
|
|
|
+ getCodeId(code: string): string {
|
|
|
+ return this._config.getCodeId(code, this.session.connection.kernel.name);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Whether there exists a thread in stopped state.
|
|
|
+ */
|
|
|
+ hasStoppedThreads(): boolean {
|
|
|
+ return this._model?.stoppedThreads.size > 0 ?? false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -129,49 +156,75 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Dispose the debug service.
|
|
|
+ * Clear all the breakpoints for the current session.
|
|
|
*/
|
|
|
- dispose(): void {
|
|
|
- if (this.isDisposed) {
|
|
|
+ async clearBreakpoints(): Promise<void> {
|
|
|
+ if (!this.session.isStarted) {
|
|
|
return;
|
|
|
}
|
|
|
- this._isDisposed = true;
|
|
|
- Signal.clearData(this);
|
|
|
+
|
|
|
+ this._model.breakpoints.breakpoints.forEach(
|
|
|
+ async (breakpoints, path, _) => {
|
|
|
+ await this._setBreakpoints([], path);
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ let bpMap = new Map<string, IDebugger.IBreakpoint[]>();
|
|
|
+ this._model.breakpoints.restoreBreakpoints(bpMap);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Computes an id based on the given code.
|
|
|
- *
|
|
|
- * @param code The source code.
|
|
|
+ * Continues the execution of the current thread.
|
|
|
*/
|
|
|
- getCodeId(code: string): string {
|
|
|
- return this._config.getCodeId(code, this.session.connection.kernel.name);
|
|
|
+ async continue(): Promise<void> {
|
|
|
+ try {
|
|
|
+ await this.session.sendRequest('continue', {
|
|
|
+ threadId: this._currentThread()
|
|
|
+ });
|
|
|
+ this._model.stoppedThreads.delete(this._currentThread());
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Error:', err.message);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Whether there exists a thread in stopped state.
|
|
|
+ * Retrieve the content of a source file.
|
|
|
+ *
|
|
|
+ * @param source The source object containing the path to the file.
|
|
|
*/
|
|
|
- hasStoppedThreads(): boolean {
|
|
|
- return this._model?.stoppedThreads.size > 0 ?? false;
|
|
|
+ async getSource(source: DebugProtocol.Source): Promise<IDebugger.Source> {
|
|
|
+ const reply = await this.session.sendRequest('source', {
|
|
|
+ source,
|
|
|
+ sourceReference: source.sourceReference
|
|
|
+ });
|
|
|
+ return { ...reply.body, path: source.path };
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Starts a debugger.
|
|
|
- * Precondition: !isStarted
|
|
|
+ * Makes the current thread run again for one step.
|
|
|
*/
|
|
|
- async start(): Promise<void> {
|
|
|
- await this.session.start();
|
|
|
+ async next(): Promise<void> {
|
|
|
+ try {
|
|
|
+ await this.session.sendRequest('next', {
|
|
|
+ threadId: this._currentThread()
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Error:', err.message);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Stops the debugger.
|
|
|
- * Precondition: isStarted
|
|
|
+ * Request variables for a given variable reference.
|
|
|
+ *
|
|
|
+ * @param variablesReference The variable reference to request.
|
|
|
*/
|
|
|
- async stop(): Promise<void> {
|
|
|
- await this.session.stop();
|
|
|
- if (this._model) {
|
|
|
- this._model.clear();
|
|
|
- }
|
|
|
+ async inspectVariable(
|
|
|
+ variablesReference: number
|
|
|
+ ): Promise<DebugProtocol.Variable[]> {
|
|
|
+ const reply = await this.session.sendRequest('variables', {
|
|
|
+ variablesReference
|
|
|
+ });
|
|
|
+ return reply.body.variables;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -244,30 +297,11 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Continues the execution of the current thread.
|
|
|
- */
|
|
|
- async continue(): Promise<void> {
|
|
|
- try {
|
|
|
- await this.session.sendRequest('continue', {
|
|
|
- threadId: this._currentThread()
|
|
|
- });
|
|
|
- this._model.stoppedThreads.delete(this._currentThread());
|
|
|
- } catch (err) {
|
|
|
- console.error('Error:', err.message);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Makes the current thread run again for one step.
|
|
|
+ * Starts a debugger.
|
|
|
+ * Precondition: !isStarted
|
|
|
*/
|
|
|
- async next(): Promise<void> {
|
|
|
- try {
|
|
|
- await this.session.sendRequest('next', {
|
|
|
- threadId: this._currentThread()
|
|
|
- });
|
|
|
- } catch (err) {
|
|
|
- console.error('Error:', err.message);
|
|
|
- }
|
|
|
+ start(): Promise<void> {
|
|
|
+ return this.session.start();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -296,6 +330,17 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Stops the debugger.
|
|
|
+ * Precondition: isStarted
|
|
|
+ */
|
|
|
+ async stop(): Promise<void> {
|
|
|
+ await this.session.stop();
|
|
|
+ if (this._model) {
|
|
|
+ this._model.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Update all breakpoints at once.
|
|
|
*
|
|
@@ -340,48 +385,56 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Clear all the breakpoints for the current session.
|
|
|
+ * Clear the current model.
|
|
|
*/
|
|
|
- async clearBreakpoints(): Promise<void> {
|
|
|
- if (!this.session.isStarted) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ private _clearModel(): void {
|
|
|
+ this._model.callstack.frames = [];
|
|
|
+ this._model.variables.scopes = [];
|
|
|
+ }
|
|
|
|
|
|
- this._model.breakpoints.breakpoints.forEach(
|
|
|
- async (breakpoints, path, _) => {
|
|
|
- await this._setBreakpoints([], path);
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Clear the signals set on the model.
|
|
|
+ */
|
|
|
+ private _clearSignals(): void {
|
|
|
+ this._model.callstack.currentFrameChanged.disconnect(
|
|
|
+ this._onCurrentFrameChanged,
|
|
|
+ this
|
|
|
+ );
|
|
|
+ this._model.variables.variableExpanded.disconnect(
|
|
|
+ this._onVariableExpanded,
|
|
|
+ this
|
|
|
);
|
|
|
-
|
|
|
- let bpMap = new Map<string, IDebugger.IBreakpoint[]>();
|
|
|
- this._model.breakpoints.restoreBreakpoints(bpMap);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Retrieve the content of a source file.
|
|
|
+ * Map a list of scopes to a list of variables.
|
|
|
*
|
|
|
- * @param source The source object containing the path to the file.
|
|
|
+ * @param scopes The list of scopes.
|
|
|
+ * @param variables The list of variables.
|
|
|
*/
|
|
|
- async getSource(source: DebugProtocol.Source): Promise<IDebugger.Source> {
|
|
|
- const reply = await this.session.sendRequest('source', {
|
|
|
- source,
|
|
|
- sourceReference: source.sourceReference
|
|
|
+ private _convertScopes(
|
|
|
+ scopes: DebugProtocol.Scope[],
|
|
|
+ variables: DebugProtocol.Variable[]
|
|
|
+ ): VariablesModel.IScope[] {
|
|
|
+ if (!variables || !scopes) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ return scopes.map(scope => {
|
|
|
+ return {
|
|
|
+ name: scope.name,
|
|
|
+ variables: variables.map(variable => {
|
|
|
+ return { ...variable };
|
|
|
+ })
|
|
|
+ };
|
|
|
});
|
|
|
- return { ...reply.body, path: source.path };
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Request variables for a given variable reference.
|
|
|
- *
|
|
|
- * @param variablesReference The variable reference to request.
|
|
|
+ * Get the current thread from the model.
|
|
|
*/
|
|
|
- async inspectVariable(
|
|
|
- variablesReference: number
|
|
|
- ): Promise<DebugProtocol.Variable[]> {
|
|
|
- const reply = await this.session.sendRequest('variables', {
|
|
|
- variablesReference
|
|
|
- });
|
|
|
- return reply.body.variables;
|
|
|
+ private _currentThread(): number {
|
|
|
+ // TODO: ask the model for the current thread ID
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -394,6 +447,7 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
): Promise<IDebugger.ISession.IDumpCellResponse> {
|
|
|
return this.session.sendRequest('dumpCell', { code });
|
|
|
}
|
|
|
+
|
|
|
/**
|
|
|
* Filter breakpoints and only return those associated with a known editor.
|
|
|
*
|
|
@@ -404,7 +458,7 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
breakpoints: Map<string, IDebugger.IBreakpoint[]>
|
|
|
): Map<string, IDebugger.IBreakpoint[]> {
|
|
|
let bpMapForRestore = new Map<string, IDebugger.IBreakpoint[]>();
|
|
|
- for (let collection of breakpoints) {
|
|
|
+ for (const collection of breakpoints) {
|
|
|
const [id, list] = collection;
|
|
|
list.forEach(() => {
|
|
|
each(
|
|
@@ -425,6 +479,72 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
return bpMapForRestore;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Get all the frames from the kernel.
|
|
|
+ */
|
|
|
+ private async _getAllFrames(): Promise<void> {
|
|
|
+ this._model.callstack.currentFrameChanged.connect(
|
|
|
+ this._onCurrentFrameChanged,
|
|
|
+ this
|
|
|
+ );
|
|
|
+ this._model.variables.variableExpanded.connect(
|
|
|
+ this._onVariableExpanded,
|
|
|
+ this
|
|
|
+ );
|
|
|
+
|
|
|
+ const stackFrames = await this._getFrames(this._currentThread());
|
|
|
+ this._model.callstack.frames = stackFrames;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get all the frames for the given thread id.
|
|
|
+ *
|
|
|
+ * @param threadId The thread id.
|
|
|
+ */
|
|
|
+ private async _getFrames(
|
|
|
+ threadId: number
|
|
|
+ ): Promise<DebugProtocol.StackFrame[]> {
|
|
|
+ const reply = await this.session.sendRequest('stackTrace', {
|
|
|
+ threadId
|
|
|
+ });
|
|
|
+ const stackFrames = reply.body.stackFrames;
|
|
|
+ return stackFrames;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get all the scopes for the given frame.
|
|
|
+ *
|
|
|
+ * @param frame The frame.
|
|
|
+ */
|
|
|
+ private async _getScopes(
|
|
|
+ frame: DebugProtocol.StackFrame
|
|
|
+ ): Promise<DebugProtocol.Scope[]> {
|
|
|
+ if (!frame) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const reply = await this.session.sendRequest('scopes', {
|
|
|
+ frameId: frame.id
|
|
|
+ });
|
|
|
+ return reply.body.scopes;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the variables for a given scope.
|
|
|
+ *
|
|
|
+ * @param scope The scope to get variables for.
|
|
|
+ */
|
|
|
+ private async _getVariables(
|
|
|
+ scope: DebugProtocol.Scope
|
|
|
+ ): Promise<DebugProtocol.Variable[]> {
|
|
|
+ if (!scope) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const reply = await this.session.sendRequest('variables', {
|
|
|
+ variablesReference: scope.variablesReference
|
|
|
+ });
|
|
|
+ return reply.body.variables;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Process the list of breakpoints from the server and return as a map.
|
|
|
*
|
|
@@ -453,30 +573,13 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Get all the frames from the kernel.
|
|
|
- */
|
|
|
- private async _getAllFrames(): Promise<void> {
|
|
|
- this._model.callstack.currentFrameChanged.connect(
|
|
|
- this._onChangeFrame,
|
|
|
- this
|
|
|
- );
|
|
|
- this._model.variables.variableExpanded.connect(
|
|
|
- this._onVariableExpanded,
|
|
|
- this
|
|
|
- );
|
|
|
-
|
|
|
- const stackFrames = await this._getFrames(this._currentThread());
|
|
|
- this._model.callstack.frames = stackFrames;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Handle a change of the current active frame.
|
|
|
*
|
|
|
* @param _ The callstack model
|
|
|
* @param frame The frame.
|
|
|
*/
|
|
|
- private async _onChangeFrame(
|
|
|
+ private async _onCurrentFrameChanged(
|
|
|
_: CallstackModel,
|
|
|
frame: CallstackModel.IFrame
|
|
|
): Promise<void> {
|
|
@@ -519,55 +622,6 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
return reply.body.variables;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Get all the frames for the given thread id.
|
|
|
- *
|
|
|
- * @param threadId The thread id.
|
|
|
- */
|
|
|
- private async _getFrames(
|
|
|
- threadId: number
|
|
|
- ): Promise<DebugProtocol.StackFrame[]> {
|
|
|
- const reply = await this.session.sendRequest('stackTrace', {
|
|
|
- threadId
|
|
|
- });
|
|
|
- const stackFrames = reply.body.stackFrames;
|
|
|
- return stackFrames;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get all the scopes for the given frame.
|
|
|
- *
|
|
|
- * @param frame The frame.
|
|
|
- */
|
|
|
- private async _getScopes(
|
|
|
- frame: DebugProtocol.StackFrame
|
|
|
- ): Promise<DebugProtocol.Scope[]> {
|
|
|
- if (!frame) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const reply = await this.session.sendRequest('scopes', {
|
|
|
- frameId: frame.id
|
|
|
- });
|
|
|
- return reply.body.scopes;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get the variables for a given scope.
|
|
|
- *
|
|
|
- * @param scope The scope to get variables for.
|
|
|
- */
|
|
|
- private async _getVariables(
|
|
|
- scope: DebugProtocol.Scope
|
|
|
- ): Promise<DebugProtocol.Variable[]> {
|
|
|
- if (!scope) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const reply = await this.session.sendRequest('variables', {
|
|
|
- variablesReference: scope.variablesReference
|
|
|
- });
|
|
|
- return reply.body.variables;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Set the breakpoints for a given file.
|
|
|
*
|
|
@@ -585,59 +639,6 @@ export class DebuggerService implements IDebugger, IDisposable {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Map a list of scopes to a list of variables.
|
|
|
- *
|
|
|
- * @param scopes The list of scopes.
|
|
|
- * @param variables The list of variables.
|
|
|
- */
|
|
|
- private _convertScopes(
|
|
|
- scopes: DebugProtocol.Scope[],
|
|
|
- variables: DebugProtocol.Variable[]
|
|
|
- ): VariablesModel.IScope[] {
|
|
|
- if (!variables || !scopes) {
|
|
|
- return;
|
|
|
- }
|
|
|
- return scopes.map(scope => {
|
|
|
- return {
|
|
|
- name: scope.name,
|
|
|
- variables: variables.map(variable => {
|
|
|
- return { ...variable };
|
|
|
- })
|
|
|
- };
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Clear the current model.
|
|
|
- */
|
|
|
- private _clearModel(): void {
|
|
|
- this._model.callstack.frames = [];
|
|
|
- this._model.variables.scopes = [];
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Clear the signals set on the model.
|
|
|
- */
|
|
|
- private _clearSignals(): void {
|
|
|
- this._model.callstack.currentFrameChanged.disconnect(
|
|
|
- this._onChangeFrame,
|
|
|
- this
|
|
|
- );
|
|
|
- this._model.variables.variableExpanded.disconnect(
|
|
|
- this._onVariableExpanded,
|
|
|
- this
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get the current thread from the model.
|
|
|
- */
|
|
|
- private _currentThread(): number {
|
|
|
- // TODO: ask the model for the current thread ID
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
private _config: IDebugger.IConfig;
|
|
|
private _debuggerSources: IDebugger.ISources | null;
|
|
|
private _eventMessage = new Signal<IDebugger, IDebugger.ISession.Event>(this);
|