提交 64980ea1 编写于 作者: I isidor

debug: better cancelation support

fixes #80374
上级 aa0231b0
...@@ -361,23 +361,24 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { ...@@ -361,23 +361,24 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter {
super(); super();
} }
public fireError(handle: number, err: Error) { fireError(handle: number, err: Error) {
this._onError.fire(err); this._onError.fire(err);
} }
public fireExit(handle: number, code: number, signal: string) { fireExit(handle: number, code: number, signal: string) {
this._onExit.fire(code); this._onExit.fire(code);
} }
public startSession(): Promise<void> { startSession(): Promise<void> {
return Promise.resolve(this._proxy.$startDASession(this._handle, this._ds.getSessionDto(this._session))); return Promise.resolve(this._proxy.$startDASession(this._handle, this._ds.getSessionDto(this._session)));
} }
public sendMessage(message: DebugProtocol.ProtocolMessage): void { sendMessage(message: DebugProtocol.ProtocolMessage): void {
this._proxy.$sendDAMessage(this._handle, convertToDAPaths(message, true)); this._proxy.$sendDAMessage(this._handle, convertToDAPaths(message, true));
} }
public stopSession(): Promise<void> { async stopSession(): Promise<void> {
await this.cancelPendingRequests();
return Promise.resolve(this._proxy.$stopDASession(this._handle)); return Promise.resolve(this._proxy.$stopDASession(this._handle));
} }
} }
...@@ -33,6 +33,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; ...@@ -33,6 +33,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IOpenerService } from 'vs/platform/opener/common/opener';
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView'; import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
export class DebugSession implements IDebugSession { export class DebugSession implements IDebugSession {
...@@ -43,6 +44,7 @@ export class DebugSession implements IDebugSession { ...@@ -43,6 +44,7 @@ export class DebugSession implements IDebugSession {
private sources = new Map<string, Source>(); private sources = new Map<string, Source>();
private threads = new Map<number, Thread>(); private threads = new Map<number, Thread>();
private cancellationMap = new Map<number, CancellationTokenSource[]>();
private rawListeners: IDisposable[] = []; private rawListeners: IDisposable[] = [];
private fetchThreadsScheduler: RunOnceScheduler | undefined; private fetchThreadsScheduler: RunOnceScheduler | undefined;
private repl: ReplModel; private repl: ReplModel;
...@@ -235,6 +237,7 @@ export class DebugSession implements IDebugSession { ...@@ -235,6 +237,7 @@ export class DebugSession implements IDebugSession {
*/ */
terminate(restart = false): Promise<void> { terminate(restart = false): Promise<void> {
if (this.raw) { if (this.raw) {
this.cancelAllRequests();
if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') { if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') {
return this.raw.terminate(restart).then(response => { return this.raw.terminate(restart).then(response => {
return undefined; return undefined;
...@@ -252,6 +255,7 @@ export class DebugSession implements IDebugSession { ...@@ -252,6 +255,7 @@ export class DebugSession implements IDebugSession {
*/ */
disconnect(restart = false): Promise<void> { disconnect(restart = false): Promise<void> {
if (this.raw) { if (this.raw) {
this.cancelAllRequests();
return this.raw.disconnect(restart).then(response => { return this.raw.disconnect(restart).then(response => {
return undefined; return undefined;
}); });
...@@ -264,6 +268,7 @@ export class DebugSession implements IDebugSession { ...@@ -264,6 +268,7 @@ export class DebugSession implements IDebugSession {
*/ */
restart(): Promise<void> { restart(): Promise<void> {
if (this.raw) { if (this.raw) {
this.cancelAllRequests();
return this.raw.restart().then(() => undefined); return this.raw.restart().then(() => undefined);
} }
return Promise.reject(new Error('no debug adapter')); return Promise.reject(new Error('no debug adapter'));
...@@ -380,7 +385,8 @@ export class DebugSession implements IDebugSession { ...@@ -380,7 +385,8 @@ export class DebugSession implements IDebugSession {
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse> { stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse> {
if (this.raw) { if (this.raw) {
return this.raw.stackTrace({ threadId, startFrame, levels }); const token = this.getNewCancellationToken(threadId);
return this.raw.stackTrace({ threadId, startFrame, levels }, token);
} }
return Promise.reject(new Error('no debug adapter')); return Promise.reject(new Error('no debug adapter'));
} }
...@@ -402,16 +408,18 @@ export class DebugSession implements IDebugSession { ...@@ -402,16 +408,18 @@ export class DebugSession implements IDebugSession {
return Promise.reject(new Error('no debug adapter')); return Promise.reject(new Error('no debug adapter'));
} }
scopes(frameId: number): Promise<DebugProtocol.ScopesResponse> { scopes(frameId: number, threadId: number): Promise<DebugProtocol.ScopesResponse> {
if (this.raw) { if (this.raw) {
return this.raw.scopes({ frameId }); const token = this.getNewCancellationToken(threadId);
return this.raw.scopes({ frameId }, token);
} }
return Promise.reject(new Error('no debug adapter')); return Promise.reject(new Error('no debug adapter'));
} }
variables(variablesReference: number, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse> { variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse> {
if (this.raw) { if (this.raw) {
return this.raw.variables({ variablesReference, filter, start, count }); const token = threadId ? this.getNewCancellationToken(threadId) : undefined;
return this.raw.variables({ variablesReference, filter, start, count }, token);
} }
return Promise.reject(new Error('no debug adapter')); return Promise.reject(new Error('no debug adapter'));
} }
...@@ -541,14 +549,14 @@ export class DebugSession implements IDebugSession { ...@@ -541,14 +549,14 @@ export class DebugSession implements IDebugSession {
return Promise.reject(new Error('no debug adapter')); return Promise.reject(new Error('no debug adapter'));
} }
completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number): Promise<CompletionItem[]> { completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<CompletionItem[]> {
if (this.raw) { if (this.raw) {
return this.raw.completions({ return this.raw.completions({
frameId, frameId,
text, text,
column: position.column, column: position.column,
line: position.lineNumber line: position.lineNumber,
}).then(response => { }, token).then(response => {
const result: CompletionItem[] = []; const result: CompletionItem[] = [];
if (response && response.body && response.body.targets) { if (response && response.body && response.body.targets) {
...@@ -757,6 +765,16 @@ export class DebugSession implements IDebugSession { ...@@ -757,6 +765,16 @@ export class DebugSession implements IDebugSession {
this.rawListeners.push(this.raw.onDidContinued(event => { this.rawListeners.push(this.raw.onDidContinued(event => {
const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
if (threadId) {
const tokens = this.cancellationMap.get(threadId);
this.cancellationMap.delete(threadId);
if (tokens) {
tokens.forEach(t => t.cancel());
}
} else {
this.cancelAllRequests();
}
this.model.clearThreads(this.getId(), false, threadId); this.model.clearThreads(this.getId(), false, threadId);
this._onDidChangeState.fire(); this._onDidChangeState.fire();
})); }));
...@@ -787,7 +805,7 @@ export class DebugSession implements IDebugSession { ...@@ -787,7 +805,7 @@ export class DebugSession implements IDebugSession {
source: this.getSource(event.body.source) source: this.getSource(event.body.source)
} : undefined; } : undefined;
if (event.body.variablesReference) { if (event.body.variablesReference) {
const container = new ExpressionContainer(this, event.body.variablesReference, generateUuid()); const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
outpuPromises.push(container.getChildren().then(children => { outpuPromises.push(container.getChildren().then(children => {
return Promise.all(waitFor).then(() => children.forEach(child => { return Promise.all(waitFor).then(() => children.forEach(child => {
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
...@@ -896,6 +914,20 @@ export class DebugSession implements IDebugSession { ...@@ -896,6 +914,20 @@ export class DebugSession implements IDebugSession {
return source; return source;
} }
private getNewCancellationToken(threadId: number): CancellationToken {
const tokenSource = new CancellationTokenSource();
const tokens = this.cancellationMap.get(threadId) || [];
tokens.push(tokenSource);
this.cancellationMap.set(threadId, tokens);
return tokenSource.token;
}
private cancelAllRequests(): void {
this.cancellationMap.forEach(tokens => tokens.forEach(t => t.cancel()));
this.cancellationMap.clear();
}
private getUriKey(uri: URI): string { private getUriKey(uri: URI): string {
// TODO: the following code does not make sense if uri originates from a different platform // TODO: the following code does not make sense if uri originates from a different platform
return platform.isLinux ? uri.toString() : uri.toString().toLowerCase(); return platform.isLinux ? uri.toString() : uri.toString().toLowerCase();
......
...@@ -19,6 +19,7 @@ import { IProcessEnvironment } from 'vs/base/common/platform'; ...@@ -19,6 +19,7 @@ import { IProcessEnvironment } from 'vs/base/common/platform';
import { env as processEnv } from 'vs/base/common/process'; import { env as processEnv } from 'vs/base/common/process';
import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
/** /**
* This interface represents a single command line argument split into a "prefix" and a "path" half. * This interface represents a single command line argument split into a "prefix" and a "path" half.
...@@ -112,8 +113,6 @@ export class RawDebugSession implements IDisposable { ...@@ -112,8 +113,6 @@ export class RawDebugSession implements IDisposable {
} }
})); }));
this.toDispose.push(this.onDidContinued(() => this.cancelPendingRequests()));
this.debugAdapter.onEvent(event => { this.debugAdapter.onEvent(event => {
switch (event.event) { switch (event.event) {
case 'initialized': case 'initialized':
...@@ -346,9 +345,9 @@ export class RawDebugSession implements IDisposable { ...@@ -346,9 +345,9 @@ export class RawDebugSession implements IDisposable {
return Promise.reject(new Error('restartFrame not supported')); return Promise.reject(new Error('restartFrame not supported'));
} }
completions(args: DebugProtocol.CompletionsArguments): Promise<DebugProtocol.CompletionsResponse> { completions(args: DebugProtocol.CompletionsArguments, token: CancellationToken): Promise<DebugProtocol.CompletionsResponse> {
if (this.capabilities.supportsCompletionsRequest) { if (this.capabilities.supportsCompletionsRequest) {
return this.send<DebugProtocol.CompletionsResponse>('completions', args); return this.send<DebugProtocol.CompletionsResponse>('completions', args, token);
} }
return Promise.reject(new Error('completions not supported')); return Promise.reject(new Error('completions not supported'));
} }
...@@ -389,8 +388,8 @@ export class RawDebugSession implements IDisposable { ...@@ -389,8 +388,8 @@ export class RawDebugSession implements IDisposable {
return Promise.reject(new Error('configurationDone not supported')); return Promise.reject(new Error('configurationDone not supported'));
} }
stackTrace(args: DebugProtocol.StackTraceArguments): Promise<DebugProtocol.StackTraceResponse> { stackTrace(args: DebugProtocol.StackTraceArguments, token: CancellationToken): Promise<DebugProtocol.StackTraceResponse> {
return this.send<DebugProtocol.StackTraceResponse>('stackTrace', args); return this.send<DebugProtocol.StackTraceResponse>('stackTrace', args, token);
} }
exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise<DebugProtocol.ExceptionInfoResponse> { exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise<DebugProtocol.ExceptionInfoResponse> {
...@@ -400,12 +399,12 @@ export class RawDebugSession implements IDisposable { ...@@ -400,12 +399,12 @@ export class RawDebugSession implements IDisposable {
return Promise.reject(new Error('exceptionInfo not supported')); return Promise.reject(new Error('exceptionInfo not supported'));
} }
scopes(args: DebugProtocol.ScopesArguments): Promise<DebugProtocol.ScopesResponse> { scopes(args: DebugProtocol.ScopesArguments, token: CancellationToken): Promise<DebugProtocol.ScopesResponse> {
return this.send<DebugProtocol.ScopesResponse>('scopes', args); return this.send<DebugProtocol.ScopesResponse>('scopes', args, token);
} }
variables(args: DebugProtocol.VariablesArguments): Promise<DebugProtocol.VariablesResponse> { variables(args: DebugProtocol.VariablesArguments, token?: CancellationToken): Promise<DebugProtocol.VariablesResponse> {
return this.send<DebugProtocol.VariablesResponse>('variables', args); return this.send<DebugProtocol.VariablesResponse>('variables', args, token);
} }
source(args: DebugProtocol.SourceArguments): Promise<DebugProtocol.SourceResponse> { source(args: DebugProtocol.SourceArguments): Promise<DebugProtocol.SourceResponse> {
...@@ -482,7 +481,7 @@ export class RawDebugSession implements IDisposable { ...@@ -482,7 +481,7 @@ export class RawDebugSession implements IDisposable {
if (!this.inShutdown) { if (!this.inShutdown) {
this.inShutdown = true; this.inShutdown = true;
if (this.debugAdapter) { if (this.debugAdapter) {
return this.send('disconnect', { restart }, 500).then(() => { return this.send('disconnect', { restart }, undefined, 500).then(() => {
this.stopAdapter(error); this.stopAdapter(error);
}, () => { }, () => {
// ignore error // ignore error
...@@ -494,23 +493,10 @@ export class RawDebugSession implements IDisposable { ...@@ -494,23 +493,10 @@ export class RawDebugSession implements IDisposable {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }
private cancelPendingRequests(): void {
if (this.debugAdapter) {
if (this.capabilities.supportsCancelRequest) {
this.debugAdapter.getPendingRequestIds().forEach(requestId => {
this.cancel({ requestId });
});
} else {
this.debugAdapter.cancelPendingRequests();
}
}
}
private stopAdapter(error?: Error): Promise<any> { private stopAdapter(error?: Error): Promise<any> {
if (this.debugAdapter) { if (this.debugAdapter) {
const da = this.debugAdapter; const da = this.debugAdapter;
this.debugAdapter = null; this.debugAdapter = null;
this.cancelPendingRequests();
return da.stopSession().then(_ => { return da.stopSession().then(_ => {
this.debugAdapterStopped = true; this.debugAdapterStopped = true;
this.fireAdapterExitEvent(error); this.fireAdapterExitEvent(error);
...@@ -642,20 +628,34 @@ export class RawDebugSession implements IDisposable { ...@@ -642,20 +628,34 @@ export class RawDebugSession implements IDisposable {
return this.windowsService.openExtensionDevelopmentHostWindow(args, env); return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
} }
private send<R extends DebugProtocol.Response>(command: string, args: any, timeout?: number): Promise<R> { private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number): Promise<R> {
return new Promise<R>((completeDispatch, errorDispatch) => { return new Promise<R>((completeDispatch, errorDispatch) => {
if (!this.debugAdapter) { if (!this.debugAdapter) {
errorDispatch(new Error('no debug adapter found')); errorDispatch(new Error('no debug adapter found'));
return; return;
} }
this.debugAdapter.sendRequest(command, args, (response: R) => { let cancelationListener: IDisposable;
const requestId = this.debugAdapter.sendRequest(command, args, (response: R) => {
if (cancelationListener) {
cancelationListener.dispose();
}
if (response.success) { if (response.success) {
completeDispatch(response); completeDispatch(response);
} else { } else {
errorDispatch(response); errorDispatch(response);
} }
}, timeout); }, timeout);
}).then(response => response, err => Promise.reject(this.handleErrorResponse(err)));
if (token) {
cancelationListener = token.onCancellationRequested(() => {
cancelationListener.dispose();
if (this.capabilities.supportsCancelRequest) {
this.cancel({ requestId });
}
});
}
}).then(undefined, err => Promise.reject(this.handleErrorResponse(err)));
} }
private handleErrorResponse(errorResponse: DebugProtocol.Response): Error { private handleErrorResponse(errorResponse: DebugProtocol.Response): Error {
......
...@@ -149,7 +149,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati ...@@ -149,7 +149,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
const text = model.getLineContent(position.lineNumber); const text = model.getLineContent(position.lineNumber);
const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame;
const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined;
const suggestions = await session.completions(frameId, text, position, overwriteBefore); const suggestions = await session.completions(frameId, text, position, overwriteBefore, token);
return { suggestions }; return { suggestions };
} }
......
...@@ -71,7 +71,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { ...@@ -71,7 +71,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
} }
} }
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void { sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): number {
const request: any = { const request: any = {
command: command command: command
}; };
...@@ -101,6 +101,8 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { ...@@ -101,6 +101,8 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
// store callback for this request // store callback for this request
this.pendingRequests.set(request.seq, clb); this.pendingRequests.set(request.seq, clb);
} }
return request.seq;
} }
acceptMessage(message: DebugProtocol.ProtocolMessage): void { acceptMessage(message: DebugProtocol.ProtocolMessage): void {
...@@ -137,7 +139,11 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { ...@@ -137,7 +139,11 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
this.sendMessage(message); this.sendMessage(message);
} }
async cancelPendingRequests(): Promise<void> { protected async cancelPendingRequests(): Promise<void> {
if (this.pendingRequests.size === 0) {
return Promise.resolve();
}
const pending = new Map<number, (e: DebugProtocol.Response) => void>(); const pending = new Map<number, (e: DebugProtocol.Response) => void>();
this.pendingRequests.forEach((value, key) => pending.set(key, value)); this.pendingRequests.forEach((value, key) => pending.set(key, value));
await timeout(500); await timeout(500);
......
...@@ -210,8 +210,8 @@ export interface IDebugSession extends ITreeElement { ...@@ -210,8 +210,8 @@ export interface IDebugSession extends ITreeElement {
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse>; stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse>;
exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined>; exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined>;
scopes(frameId: number): Promise<DebugProtocol.ScopesResponse>; scopes(frameId: number, threadId: number): Promise<DebugProtocol.ScopesResponse>;
variables(variablesReference: number, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse>; variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse>;
evaluate(expression: string, frameId?: number, context?: string): Promise<DebugProtocol.EvaluateResponse>; evaluate(expression: string, frameId?: number, context?: string): Promise<DebugProtocol.EvaluateResponse>;
customRequest(request: string, args: any): Promise<DebugProtocol.Response>; customRequest(request: string, args: any): Promise<DebugProtocol.Response>;
...@@ -225,7 +225,7 @@ export interface IDebugSession extends ITreeElement { ...@@ -225,7 +225,7 @@ export interface IDebugSession extends ITreeElement {
pause(threadId: number): Promise<void>; pause(threadId: number): Promise<void>;
terminateThreads(threadIds: number[]): Promise<void>; terminateThreads(threadIds: number[]): Promise<void>;
completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number): Promise<CompletionItem[]>; completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<CompletionItem[]>;
setVariable(variablesReference: number | undefined, name: string, value: string): Promise<DebugProtocol.SetVariableResponse>; setVariable(variablesReference: number | undefined, name: string, value: string): Promise<DebugProtocol.SetVariableResponse>;
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse>; loadSource(resource: uri): Promise<DebugProtocol.SourceResponse>;
getLoadedSources(): Promise<Source[]>; getLoadedSources(): Promise<Source[]>;
...@@ -504,10 +504,8 @@ export interface IDebugAdapter extends IDisposable { ...@@ -504,10 +504,8 @@ export interface IDebugAdapter extends IDisposable {
startSession(): Promise<void>; startSession(): Promise<void>;
sendMessage(message: DebugProtocol.ProtocolMessage): void; sendMessage(message: DebugProtocol.ProtocolMessage): void;
sendResponse(response: DebugProtocol.Response): void; sendResponse(response: DebugProtocol.Response): void;
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void; sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): number;
stopSession(): Promise<void>; stopSession(): Promise<void>;
cancelPendingRequests(): void;
getPendingRequestIds(): number[];
} }
export interface IDebugAdapterFactory extends ITerminalLauncher { export interface IDebugAdapterFactory extends ITerminalLauncher {
......
...@@ -37,6 +37,7 @@ export class ExpressionContainer implements IExpressionContainer { ...@@ -37,6 +37,7 @@ export class ExpressionContainer implements IExpressionContainer {
constructor( constructor(
protected session: IDebugSession | undefined, protected session: IDebugSession | undefined,
protected threadId: number | undefined,
private _reference: number | undefined, private _reference: number | undefined,
private id: string, private id: string,
public namedVariables: number | undefined = 0, public namedVariables: number | undefined = 0,
...@@ -85,7 +86,7 @@ export class ExpressionContainer implements IExpressionContainer { ...@@ -85,7 +86,7 @@ export class ExpressionContainer implements IExpressionContainer {
for (let i = 0; i < numberOfChunks; i++) { for (let i = 0; i < numberOfChunks; i++) {
const start = (this.startOfVariables || 0) + i * chunkSize; const start = (this.startOfVariables || 0) + i * chunkSize;
const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize); const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize);
children.push(new Variable(this.session, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, true, start)); children.push(new Variable(this.session, this.threadId, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, true, start));
} }
return children; return children;
...@@ -109,12 +110,12 @@ export class ExpressionContainer implements IExpressionContainer { ...@@ -109,12 +110,12 @@ export class ExpressionContainer implements IExpressionContainer {
} }
private fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise<Variable[]> { private fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise<Variable[]> {
return this.session!.variables(this.reference || 0, filter, start, count).then(response => { return this.session!.variables(this.reference || 0, this.threadId, filter, start, count).then(response => {
return response && response.body && response.body.variables return response && response.body && response.body.variables
? distinct(response.body.variables.filter(v => !!v && isString(v.name)), (v: DebugProtocol.Variable) => v.name).map((v: DebugProtocol.Variable) => ? distinct(response.body.variables.filter(v => !!v && isString(v.name)), (v: DebugProtocol.Variable) => v.name).map((v: DebugProtocol.Variable) =>
new Variable(this.session, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type)) new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type))
: []; : [];
}, (e: Error) => [new Variable(this.session, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]); }, (e: Error) => [new Variable(this.session, this.threadId, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]);
} }
// The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked. // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked.
...@@ -171,7 +172,7 @@ export class Expression extends ExpressionContainer implements IExpression { ...@@ -171,7 +172,7 @@ export class Expression extends ExpressionContainer implements IExpression {
public available: boolean; public available: boolean;
constructor(public name: string, id = generateUuid()) { constructor(public name: string, id = generateUuid()) {
super(undefined, 0, id); super(undefined, undefined, 0, id);
this.available = false; this.available = false;
// name is not set if the expression is just being added // name is not set if the expression is just being added
// in that case do not set default value to prevent flashing #14499 // in that case do not set default value to prevent flashing #14499
...@@ -196,6 +197,7 @@ export class Variable extends ExpressionContainer implements IExpression { ...@@ -196,6 +197,7 @@ export class Variable extends ExpressionContainer implements IExpression {
constructor( constructor(
session: IDebugSession | undefined, session: IDebugSession | undefined,
threadId: number | undefined,
public parent: IExpressionContainer, public parent: IExpressionContainer,
reference: number | undefined, reference: number | undefined,
public name: string, public name: string,
...@@ -208,7 +210,7 @@ export class Variable extends ExpressionContainer implements IExpression { ...@@ -208,7 +210,7 @@ export class Variable extends ExpressionContainer implements IExpression {
public available = true, public available = true,
startOfVariables = 0 startOfVariables = 0
) { ) {
super(session, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables); super(session, threadId, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables);
this.value = value || ''; this.value = value || '';
} }
...@@ -248,7 +250,7 @@ export class Scope extends ExpressionContainer implements IScope { ...@@ -248,7 +250,7 @@ export class Scope extends ExpressionContainer implements IScope {
indexedVariables?: number, indexedVariables?: number,
public range?: IRange public range?: IRange
) { ) {
super(stackFrame.thread.session, reference, `scope:${name}:${index}`, namedVariables, indexedVariables); super(stackFrame.thread.session, stackFrame.thread.threadId, reference, `scope:${name}:${index}`, namedVariables, indexedVariables);
} }
toString(): string { toString(): string {
...@@ -276,7 +278,7 @@ export class StackFrame implements IStackFrame { ...@@ -276,7 +278,7 @@ export class StackFrame implements IStackFrame {
getScopes(): Promise<IScope[]> { getScopes(): Promise<IScope[]> {
if (!this.scopes) { if (!this.scopes) {
this.scopes = this.thread.session.scopes(this.frameId).then(response => { this.scopes = this.thread.session.scopes(this.frameId, this.thread.threadId).then(response => {
return response && response.body && response.body.scopes ? return response && response.body && response.body.scopes ?
response.body.scopes.map((rs, index) => new Scope(this, index, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables, response.body.scopes.map((rs, index) => new Scope(this, index, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables,
rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : undefined)) : []; rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : undefined)) : [];
......
...@@ -97,7 +97,7 @@ export class ReplEvaluationInput implements IReplElement { ...@@ -97,7 +97,7 @@ export class ReplEvaluationInput implements IReplElement {
export class ReplEvaluationResult extends ExpressionContainer implements IReplElement { export class ReplEvaluationResult extends ExpressionContainer implements IReplElement {
constructor() { constructor() {
super(undefined, 0, generateUuid()); super(undefined, undefined, 0, generateUuid());
} }
toString(): string { toString(): string {
......
...@@ -127,8 +127,8 @@ export class SocketDebugAdapter extends StreamDebugAdapter { ...@@ -127,8 +127,8 @@ export class SocketDebugAdapter extends StreamDebugAdapter {
}); });
} }
stopSession(): Promise<void> { async stopSession(): Promise<void> {
await this.cancelPendingRequests();
if (this.socket) { if (this.socket) {
this.socket.end(); this.socket.end();
this.socket = undefined; this.socket = undefined;
...@@ -249,7 +249,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { ...@@ -249,7 +249,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter {
} }
} }
stopSession(): Promise<void> { async stopSession(): Promise<void> {
if (!this.serverProcess) { if (!this.serverProcess) {
return Promise.resolve(undefined); return Promise.resolve(undefined);
...@@ -258,6 +258,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { ...@@ -258,6 +258,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter {
// when killing a process in windows its child // when killing a process in windows its child
// processes are *not* killed but become root // processes are *not* killed but become root
// processes. Therefore we use TASKKILL.EXE // processes. Therefore we use TASKKILL.EXE
await this.cancelPendingRequests();
if (platform.isWindows) { if (platform.isWindows) {
return new Promise<void>((c, e) => { return new Promise<void>((c, e) => {
const killer = cp.exec(`taskkill /F /T /PID ${this.serverProcess!.pid}`, function (err, stdout, stderr) { const killer = cp.exec(`taskkill /F /T /PID ${this.serverProcess!.pid}`, function (err, stdout, stderr) {
......
...@@ -76,7 +76,7 @@ suite('Debug - Base Debug View', () => { ...@@ -76,7 +76,7 @@ suite('Debug - Base Debug View', () => {
const stackFrame = new StackFrame(thread, 1, null!, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: undefined!, endColumn: undefined! }, 0); const stackFrame = new StackFrame(thread, 1, null!, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: undefined!, endColumn: undefined! }, 0);
const scope = new Scope(stackFrame, 1, 'local', 1, false, 10, 10); const scope = new Scope(stackFrame, 1, 'local', 1, false, 10, 10);
let variable = new Variable(session, scope, 2, 'foo', 'bar.foo', undefined!, 0, 0, {}, 'string'); let variable = new Variable(session, 1, scope, 2, 'foo', 'bar.foo', undefined!, 0, 0, {}, 'string');
let expression = $('.'); let expression = $('.');
let name = $('.'); let name = $('.');
let value = $('.'); let value = $('.');
...@@ -104,7 +104,7 @@ suite('Debug - Base Debug View', () => { ...@@ -104,7 +104,7 @@ suite('Debug - Base Debug View', () => {
assert.ok(value.querySelector('a')); assert.ok(value.querySelector('a'));
assert.equal(value.querySelector('a')!.textContent, variable.value); assert.equal(value.querySelector('a')!.textContent, variable.value);
variable = new Variable(session, scope, 2, 'console', 'console', '5', 0, 0, { kind: 'virtual' }); variable = new Variable(session, 1, scope, 2, 'console', 'console', '5', 0, 0, { kind: 'virtual' });
expression = $('.'); expression = $('.');
name = $('.'); name = $('.');
value = $('.'); value = $('.');
......
...@@ -264,7 +264,7 @@ export class MockSession implements IDebugSession { ...@@ -264,7 +264,7 @@ export class MockSession implements IDebugSession {
scopes(frameId: number): Promise<DebugProtocol.ScopesResponse> { scopes(frameId: number): Promise<DebugProtocol.ScopesResponse> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise<DebugProtocol.VariablesResponse> { variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named', start: number, count: number): Promise<DebugProtocol.VariablesResponse> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
evaluate(expression: string, frameId: number, context?: string): Promise<DebugProtocol.EvaluateResponse> { evaluate(expression: string, frameId: number, context?: string): Promise<DebugProtocol.EvaluateResponse> {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册