diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 42095e745e750076666f7262c641e2b56861cbe6..d1b4eef6a8d4b611f903768b30bb607c5b817171 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -100,7 +100,8 @@ export interface ISession extends IBaseSession, ITreeElement { } export interface IProcess extends IBaseSession, ITreeElement { - + getThread(threadId: number): IThread; + getAllThreads(): IThread[]; } export interface IThread extends ITreeElement { @@ -223,7 +224,6 @@ export interface IViewModel extends ITreeElement { export interface IModel extends ITreeElement { getProcesses(): IProcess[]; - getThreads(sessionId): { [threadId: number]: IThread; }; getBreakpoints(): IBreakpoint[]; areBreakpointsActivated(): boolean; getFunctionBreakpoints(): IFunctionBreakpoint[]; diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 615f66e596f19370176ebe1d8174e3b4356e74f3..b8015e774d99eb67a3f01dfa65f01379afed7553 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -401,12 +401,20 @@ export class Thread implements debug.IThread { export class Process implements debug.IProcess { - public threads: { [reference: number]: debug.IThread; }; + private threads: { [reference: number]: debug.IThread; }; constructor(public session: debug.ISession) { this.threads = {}; } + public getThread(threadId: number): debug.IThread { + return this.threads[threadId]; + } + + public getAllThreads(): debug.IThread[] { + return Object.keys(this.threads).map(key => this.threads[key]); + } + public getId(): string { return this.session.getId(); } @@ -621,15 +629,6 @@ export class Model implements debug.IModel { return this._onDidChangeREPLElements.event; } - public getThreads(processId: string): { [reference: number]: debug.IThread; } { - const process = this.processes.filter(p => p.getId() === processId).pop(); - if (!process) { - return {}; - } - - return process.threads; - } - public rawUpdate(data: debug.IRawModelUpdate): void { let process = this.processes.filter(p => p.getId() === data.rawSession.getId()).pop(); if (!process) { diff --git a/src/vs/workbench/parts/debug/common/debugSource.ts b/src/vs/workbench/parts/debug/common/debugSource.ts index 1d38b5de0b8cc89a5374fd14a31082e236f40498..dc213bf40a956675b434e9e0eaa08ebaf12c33b5 100644 --- a/src/vs/workbench/parts/debug/common/debugSource.ts +++ b/src/vs/workbench/parts/debug/common/debugSource.ts @@ -40,7 +40,7 @@ export class Source { // first try to find the raw source amongst the stack frames - since that represenation has more data (source reference), const processes = model.getProcesses(); for (let i = 0; i < processes.length; i++) { - const threads = model.getThreads(processes[i].getId()); + const threads = process[i].getThreads(); for (let threadId in threads) { if (threads.hasOwnProperty(threadId) && threads[threadId].getCachedCallStack()) { const found = threads[threadId].getCachedCallStack().filter(sf => sf.source.uri.toString() === uri.toString()).pop(); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index fd7cfa47c61af78ed1d3666723559d5a31961767..62c540886e3f99e175e4fecebcfaa3dc879ef78f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -268,7 +268,8 @@ export class DebugService implements debug.IDebugService { allThreadsStopped: event.body.allThreadsStopped }); - const thread = this.model.getThreads(session.getId())[threadId]; + const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop(); + const thread = process.getThread(threadId); thread.getCallStack().then(callStack => { if (callStack.length > 0) { // focus first stack frame from top that has source location @@ -981,9 +982,9 @@ export class DebugService implements debug.IDebugService { this.model.clearThreads(session.getId(), false, threadId); // Get a top stack frame of a stopped thread if there is any. - const threads = this.model.getThreads(session.getId()); - const stoppedReference = Object.keys(threads).filter(ref => threads[ref].stopped).pop(); - const stoppedThread = stoppedReference ? threads[parseInt(stoppedReference)] : null; + + const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop(); + const stoppedThread = process && process.getAllThreads().filter(t => t.stopped).pop(); const callStack = stoppedThread ? stoppedThread.getCachedCallStack() : null; const stackFrameToFocus = callStack && callStack.length > 0 ? callStack[0] : null; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index 30c207032a0f90814f3007f0f9ce8c330df646ab..78fe9fa12ccd2ca4d35dd66aa804c85721f1209a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -212,14 +212,14 @@ export class BaseDebugController extends treedefaults.DefaultController { // call stack -class ThreadAndSessionId { - constructor(public sessionId: string, public threadId: number) { } +class ThreadAndProcessIds { + constructor(public processId: string, public threadId: number) { } } export class CallStackController extends BaseDebugController { protected onLeftClick(tree: tree.ITree, element: any, event: IMouseEvent): boolean { - if (element instanceof ThreadAndSessionId) { + if (element instanceof ThreadAndProcessIds) { return this.showMoreStackFrames(tree, element); } if (element instanceof model.StackFrame) { @@ -231,7 +231,7 @@ export class CallStackController extends BaseDebugController { protected onEnter(tree: tree.ITree, event: IKeyboardEvent): boolean { const element = tree.getFocus(); - if (element instanceof ThreadAndSessionId) { + if (element instanceof ThreadAndProcessIds) { return this.showMoreStackFrames(tree, element); } if (element instanceof model.StackFrame) { @@ -270,8 +270,9 @@ export class CallStackController extends BaseDebugController { } // user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree. - private showMoreStackFrames(tree: tree.ITree, threadAndSessionId: ThreadAndSessionId): boolean { - const thread = this.debugService.getModel().getThreads(threadAndSessionId.sessionId)[threadAndSessionId.threadId]; + private showMoreStackFrames(tree: tree.ITree, threadAndProcessIds: ThreadAndProcessIds): boolean { + const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop(); + const thread = process && process.getThread(threadAndProcessIds.threadId); if (thread) { thread.getCallStack(true) .done(() => tree.refresh(), errors.onUnexpectedError); @@ -338,10 +339,6 @@ export class CallStackActionProvider implements renderer.IActionProvider { export class CallStackDataSource implements tree.IDataSource { - constructor( @debug.IDebugService private debugService: debug.IDebugService) { - // noop - } - public getId(tree: tree.ITree, element: any): string { if (typeof element === 'number') { return element.toString(); @@ -366,8 +363,7 @@ export class CallStackDataSource implements tree.IDataSource { } const process = element; - const threads = this.debugService.getModel().getThreads(process.getId()); - return TPromise.as(Object.keys(threads).map(ref => threads[ref])); + return TPromise.as(process.getAllThreads()); } private getThreadChildren(thread: debug.IThread): TPromise { @@ -376,7 +372,7 @@ export class CallStackDataSource implements tree.IDataSource { return callStack.concat([thread.stoppedDetails.framesErrorMessage]); } if (thread.stoppedDetails && thread.stoppedDetails.totalFrames > callStack.length) { - return callStack.concat([new ThreadAndSessionId(thread.process.getId(), thread.threadId)]); + return callStack.concat([new ThreadAndProcessIds(thread.process.getId(), thread.threadId)]); } return callStack; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts index fc82187acbb93c522e2e3a0d7b40b27becdcb74f..7a4e6e4cc4e6cf44718dacddfad3a83377c8a63d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts @@ -293,10 +293,9 @@ export class CallStackView extends viewlet.CollapsibleViewletView { let newTreeInput: any = this.debugService.getModel(); const processes = this.debugService.getModel().getProcesses(); if (processes.length === 1) { - const threads = processes[0] ? model.getThreads(processes[0].getId()) : Object.create(null); - const threadsArray = Object.keys(threads).map(ref => threads[ref]); + const threads = processes[0].getAllThreads(); // Only show the threads in the call stack if there is more than 1 thread. - newTreeInput = threadsArray.length === 1 ? threadsArray[0] : processes[0]; + newTreeInput = threads.length === 1 ? threads[0] : processes[0]; } if (this.tree.getInput() === newTreeInput) { diff --git a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts b/src/vs/workbench/parts/debug/test/node/debugModel.test.ts index d58706849664ed91ddecae478720e2a02f576d88..919e04fbe227ed8bb1177683251935911412034d 100644 --- a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugModel.test.ts @@ -12,13 +12,11 @@ import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; suite('Debug - Model', () => { let model: debugmodel.Model; - let process: debugmodel.Process; let rawSession: MockSession; setup(() => { model = new debugmodel.Model([], true, [], [], []); rawSession = new MockSession(); - process = new debugmodel.Process(rawSession); }); teardown(() => { @@ -91,12 +89,12 @@ suite('Debug - Model', () => { name: threadName } }); + const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop(); - var threads = model.getThreads(process.getId()); - assert.equal(threads[threadId].name, threadName); + assert.equal(process.getThread(threadId).name, threadName); - model.clearThreads(process.getId(),true); - assert.equal(model.getThreads[threadId], null); + model.clearThreads(process.getId(), true); + assert.equal(process.getThread(threadId), null); }); test('threads multiple wtih allThreadsStopped', () => { @@ -137,11 +135,13 @@ suite('Debug - Model', () => { }, allThreadsStopped: true }); + const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop(); - const thread1 = model.getThreads(process.getId())[threadId1]; - const thread2 = model.getThreads(process.getId())[threadId2]; + const thread1 = process.getThread(threadId1); + const thread2 = process.getThread(threadId2); // at the beginning, callstacks are obtainable but not available + assert.equal(process.getAllThreads().length, 2); assert.equal(thread1.name, threadName1); assert.equal(thread1.stopped, true); assert.equal(thread1.getCachedCallStack(), undefined); @@ -183,8 +183,9 @@ suite('Debug - Model', () => { assert.equal(thread2.getCachedCallStack(), undefined); model.clearThreads(process.getId(), true); - assert.equal(model.getThreads[threadId1], null); - assert.equal(model.getThreads[threadId2], null); + assert.equal(process.getThread(threadId1), null); + assert.equal(process.getThread(threadId2), null); + assert.equal(process.getAllThreads().length, 0); }); test('threads mutltiple without allThreadsStopped', () => { @@ -225,14 +226,16 @@ suite('Debug - Model', () => { }, allThreadsStopped: false }); + const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop(); - const stoppedThread = model.getThreads(process.getId())[stoppedThreadId]; - const runningThread = model.getThreads(process.getId())[runningThreadId]; + const stoppedThread = process.getThread(stoppedThreadId); + const runningThread = process.getThread(runningThreadId); // the callstack for the stopped thread is obtainable but not available // the callstack for the running thread is not obtainable nor available assert.equal(stoppedThread.name, stoppedThreadName); assert.equal(stoppedThread.stopped, true); + assert.equal(process.getAllThreads().length, 2); assert.equal(stoppedThread.getCachedCallStack(), undefined); assert.equal(stoppedThread.stoppedDetails.reason, stoppedReason); assert.equal(runningThread.name, runningThreadName); @@ -268,8 +271,9 @@ suite('Debug - Model', () => { assert.equal(stoppedThread.getCachedCallStack(), undefined); model.clearThreads(process.getId(), true); - assert.equal(model.getThreads[stoppedThreadId], null); - assert.equal(model.getThreads[runningThreadId], null); + assert.equal(process.getThread(stoppedThreadId), null); + assert.equal(process.getThread(runningThreadId), null); + assert.equal(process.getAllThreads().length, 0 ); }); // Expressions @@ -285,6 +289,7 @@ suite('Debug - Model', () => { test('watch expressions', () => { assert.equal(model.getWatchExpressions().length, 0); + const process = new debugmodel.Process(rawSession); const thread = new debugmodel.Thread(process, 'mockthread', 1); const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1); model.addWatchExpression(stackFrame, 'console').done(); @@ -305,6 +310,7 @@ suite('Debug - Model', () => { test('repl expressions', () => { assert.equal(model.getReplElements().length, 0); + const process = new debugmodel.Process(rawSession); const thread = new debugmodel.Thread(process, 'mockthread', 1); const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1); model.addReplExpression(stackFrame, 'myVariable').done(); @@ -364,6 +370,7 @@ suite('Debug - Model', () => { assert.equal(debugmodel.getFullExpressionName(new debugmodel.Expression(null, false), type), null); assert.equal(debugmodel.getFullExpressionName(new debugmodel.Expression('son', false), type), 'son'); + const process = new debugmodel.Process(rawSession); const thread = new debugmodel.Thread(process, 'mockthread', 1); const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1); const scope = new debugmodel.Scope(stackFrame, 'myscope', 1, false, 1, 0);