Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/adapter/breakpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Cdp from '../cdp/api';
import { Thread, ThreadManager, Script } from './threads';
import { Disposable } from 'vscode';

let lastBreakpointId = 0;

export class Breakpoint {
private _manager: BreakpointManager;
private _dapId: number;
Expand Down Expand Up @@ -277,11 +279,11 @@ export class BreakpointManager {
});
}

async setBreakpoints(params: Dap.SetBreakpointsParams, ids?: number[]): Promise<Dap.SetBreakpointsResult | Dap.Error> {
async setBreakpoints(params: Dap.SetBreakpointsParams): Promise<Dap.SetBreakpointsResult | Dap.Error> {
const breakpoints: Breakpoint[] = [];
const inBreakpoints = params.breakpoints || [];
for (let index = 0; index < inBreakpoints.length; index++) {
const id = ids ? ids[index] : generateBreakpointId();
const id = ++lastBreakpointId;
breakpoints.push(new Breakpoint(this, id, params.source, inBreakpoints[index]));
}
let previous: Breakpoint[] | undefined;
Expand Down Expand Up @@ -318,9 +320,3 @@ function logMessageToExpression(msg: string): string {
const argStr = args.length ? `, ${args.join(', ')}` : '';
return `console.log('${format}'${argStr});\n//# sourceURL=${kLogPointUrl}`;
}

let lastBreakpointId = 0;

export function generateBreakpointId(): number {
return ++lastBreakpointId;
}
85 changes: 28 additions & 57 deletions src/adapter/debugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,38 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

import { Disposable } from 'vscode';
import { Disposable, EventEmitter } from 'vscode';
import * as nls from 'vscode-nls';
import Dap from '../dap/api';
import * as sourceUtils from '../utils/sourceUtils';
import { BreakpointManager, generateBreakpointId } from './breakpoints';
import * as errors from './errors';
import { Location, SourceContainer } from './sources';
import { DummyThreadAdapter, ThreadAdapter } from './threadAdapter';
import { ExecutionContext, PauseOnExceptionsState, Thread, ThreadManager, ThreadManagerDelegate } from './threads';
import { ExecutionContext, PauseOnExceptionsState, Thread, ThreadManager } from './threads';
import { VariableStore } from './variables';
import { BreakpointManager } from './breakpoints';

const localize = nls.loadMessageBundle();
const defaultThreadId = 0;
const revealLocationThreadId = 1;

export type SetBreakpointRequest = {
params: Dap.SetBreakpointsParams;
generatedIds: number[];
};

export interface DebugAdapterDelegate extends ThreadManagerDelegate {
export interface DebugAdapterDelegate {
executionContextForest(): ExecutionContext[];
adapterDisposed: () => void;
}

// This class collects configuration issued before "launch" request,
// to be applied after launch.
export class DebugAdapter {
private _setBreakpointRequests: SetBreakpointRequest[] = [];
private _pausedOnExceptionsState: PauseOnExceptionsState = 'none';
private _dap: Dap.Api;
private _sourceContainer: SourceContainer | undefined;
private _threadManager: ThreadManager | undefined;
private _breakpointManager: BreakpointManager | undefined;
readonly sourceContainer: SourceContainer;
readonly threadManager: ThreadManager;
readonly breakpointManager: BreakpointManager;
private _delegate: DebugAdapterDelegate;
private _threadAdapter: ThreadAdapter | DummyThreadAdapter;
private _locationToReveal: Location | undefined;
private _onExecutionContextForestChangedEmitter = new EventEmitter<ExecutionContext[]>();
readonly onExecutionContextForestChanged = this._onExecutionContextForestChangedEmitter.event;

constructor(dap: Dap.Api) {
this._dap = dap;
Expand All @@ -52,7 +48,10 @@ export class DebugAdapter {
this._dap.on('variables', params => this._onVariables(params));
this._dap.on('setVariable', params => this._onSetVariable(params));
this._dap.thread({ reason: 'started', threadId: defaultThreadId });
this._threadAdapter = new DummyThreadAdapter(dap);
this.sourceContainer = new SourceContainer(this._dap);
this.threadManager = new ThreadManager(this._dap, this.sourceContainer);
this.breakpointManager = new BreakpointManager(this._dap, this.sourceContainer, this.threadManager);
this._threadAdapter = new DummyThreadAdapter(this._dap);
}

async _onInitialize(params: Dap.InitializeParams): Promise<Dap.InitializeResult | Dap.Error> {
Expand Down Expand Up @@ -104,27 +103,11 @@ export class DebugAdapter {
}

async _onSetBreakpoints(params: Dap.SetBreakpointsParams): Promise<Dap.SetBreakpointsResult | Dap.Error> {
if (this._breakpointManager)
return this._breakpointManager.setBreakpoints(params);

const request: SetBreakpointRequest = {
params,
generatedIds: []
};
this._setBreakpointRequests.push(request);
const result: Dap.SetBreakpointsResult = { breakpoints: [] };
for (const _ of params.breakpoints || []) {
const id = generateBreakpointId();
request.generatedIds.push(id);
result.breakpoints.push({ id, verified: false });
}
return result;
return this.breakpointManager.setBreakpoints(params);
}

async _onSetExceptionBreakpoints(params: Dap.SetExceptionBreakpointsParams): Promise<Dap.SetExceptionBreakpointsResult> {
this._pausedOnExceptionsState = DebugAdapter.resolvePausedOnExceptionsState(params);
if (this._threadManager)
await this._threadManager.setPauseOnExceptionsState(this._pausedOnExceptionsState);
await this.threadManager.setPauseOnExceptionsState(DebugAdapter.resolvePausedOnExceptionsState(params));
return {};
}

Expand All @@ -133,13 +116,11 @@ export class DebugAdapter {
}

async _onLoadedSources(_: Dap.LoadedSourcesParams): Promise<Dap.LoadedSourcesResult> {
if (!this._sourceContainer)
return { sources: [] };
return { sources: await Promise.all(this._sourceContainer.sources().map(source => source.toDap())) };
return { sources: await Promise.all(this.sourceContainer.sources().map(source => source.toDap())) };
}

async _onSource(params: Dap.SourceParams): Promise<Dap.SourceResult | Dap.Error> {
const source = this._sourceContainer ? this._sourceContainer.source(params.source!) : undefined;
const source = this.sourceContainer.source(params.source!);
if (!source)
return errors.createSilentError(localize('error.sourceNotFound', 'Source not found'));
const content = await source.content();
Expand All @@ -162,9 +143,7 @@ export class DebugAdapter {
}

_findVariableStore(variablesReference: number): VariableStore | undefined {
if (!this._threadManager)
return;
for (const thread of this._threadManager.threads()) {
for (const thread of this.threadManager.threads()) {
if (thread.pausedVariables() && thread.pausedVariables()!.hasVariables(variablesReference))
return thread.pausedVariables();
if (thread.replVariables.hasVariables(variablesReference))
Expand All @@ -189,17 +168,13 @@ export class DebugAdapter {

async launch(delegate: DebugAdapterDelegate): Promise<void> {
this._delegate = delegate;
this._sourceContainer = new SourceContainer(this._dap);
this._threadManager = new ThreadManager(this._dap, this._sourceContainer, delegate);
this._breakpointManager = new BreakpointManager(this._dap, this._sourceContainer, this._threadManager);

await this._threadManager.setPauseOnExceptionsState(this._pausedOnExceptionsState);
for (const request of this._setBreakpointRequests)
await this._breakpointManager.setBreakpoints(request.params, request.generatedIds);
this.threadManager.onExecutionContextsChanged(_ => {
this._onExecutionContextForestChangedEmitter.fire(delegate.executionContextForest());
});

// Select first thread once it is available.
const disposables: Disposable[] = [];
this._threadManager.onThreadAdded(thread => {
this.threadManager.onThreadAdded(thread => {
this._setExecutionContext(thread, undefined);
disposables[0].dispose();
}, undefined, disposables);
Expand Down Expand Up @@ -228,12 +203,16 @@ export class DebugAdapter {
this._locationToReveal = undefined;
}

executionContextForest(): ExecutionContext[] {
return this._delegate.executionContextForest();
}

selectExecutionContext(context: ExecutionContext | undefined) {
if (context) {
const description = context.description || context.thread.defaultExecutionContext();
this._setExecutionContext(context.thread, description ? description.id : undefined);
} else {
const thread = this._threadManager!.mainThread();
const thread = this.threadManager.mainThread();
const defaultContext = thread ? thread.defaultExecutionContext() : undefined;
this._setExecutionContext(thread, defaultContext ? defaultContext.id : undefined);
}
Expand Down Expand Up @@ -282,14 +261,6 @@ export class DebugAdapter {
return errors.createSilentError(localize('error.threadNotFound', 'Thread not found'));
}

threadManager(): ThreadManager {
return this._threadManager!;
}

sourceContainer(): SourceContainer {
return this._sourceContainer!;
}

dispose() {
this._delegate.adapterDisposed();
}
Expand Down
26 changes: 8 additions & 18 deletions src/adapter/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,10 @@ export interface ExecutionContext {

export type Script = { scriptId: string, hash: string, source: Source, thread: Thread };

export interface ThreadManagerDelegate {
executionContextForest(): ExecutionContext[] | undefined;
copyToClipboard: (text: string) => void;
}

export interface ThreadDelegate {
canStopThread(): boolean;
stopThread(): void;
copyToClipboard: (text: string) => void;

supportsCustomBreakpoints?: boolean;
defaultScriptOffset?: InlineScriptOffset;
Expand All @@ -67,7 +63,7 @@ export class ThreadManager {
private _threads: Map<string, Thread> = new Map();
private _dap: Dap.Api;

private _onExecutionContextsChangedEmitter = new EventEmitter<ExecutionContext[]>();
_onExecutionContextsChangedEmitter = new EventEmitter<Thread>();
_onThreadAddedEmitter = new EventEmitter<Thread>();
_onThreadRemovedEmitter = new EventEmitter<Thread>();
_onThreadPausedEmitter = new EventEmitter<Thread>();
Expand All @@ -78,19 +74,17 @@ export class ThreadManager {
readonly onThreadResumed = this._onThreadResumedEmitter.event;
readonly onExecutionContextsChanged = this._onExecutionContextsChangedEmitter.event;
readonly sourceContainer: SourceContainer;
_delegate: ThreadManagerDelegate;
_scriptWithSourceMapHandler?: ScriptWithSourceMapHandler;
_consoleIsDirty = false;

// url => (hash => Source)
private _scriptSources = new Map<string, Map<string, Source>>();

constructor(dap: Dap.Api, sourceContainer: SourceContainer, delegate: ThreadManagerDelegate) {
constructor(dap: Dap.Api, sourceContainer: SourceContainer) {
this._dap = dap;
this._pauseOnExceptionsState = 'none';
this._customBreakpoints = new Set();
this.sourceContainer = sourceContainer;
this._delegate = delegate;
}

mainThread(): Thread | undefined {
Expand All @@ -117,10 +111,6 @@ export class ThreadManager {
this._onThreadRemovedEmitter.fire(thread);
}

refreshExecutionContexts() {
this._onExecutionContextsChangedEmitter.fire(this._delegate.executionContextForest());
}

threads(): Thread[] {
return Array.from(this._threads.values());
}
Expand Down Expand Up @@ -437,23 +427,23 @@ export class Thread implements VariableStoreDelegate {

_executionContextCreated(context: Cdp.Runtime.ExecutionContextDescription) {
this._executionContexts.set(context.id, context);
this.manager.refreshExecutionContexts();
this.manager._onExecutionContextsChangedEmitter.fire(this);
}

_executionContextDestroyed(contextId: number) {
const context = this._executionContexts.get(contextId);
if (!context)
return;
this._executionContexts.delete(contextId);
this.manager.refreshExecutionContexts();
this.manager._onExecutionContextsChangedEmitter.fire(this);
}

_executionContextsCleared() {
this._removeAllScripts();
if (this._pausedDetails)
this._onResumed();
this._executionContexts.clear();
this.manager.refreshExecutionContexts();
this.manager._onExecutionContextsChangedEmitter.fire(this);
}

_onResumed() {
Expand Down Expand Up @@ -786,7 +776,7 @@ export class Thread implements VariableStoreDelegate {

async _copyObjectToClipboard(object: Cdp.Runtime.RemoteObject) {
if (!object.objectId) {
this.manager._delegate.copyToClipboard(objectPreview.renderValue(object, 1000000, false /* quote */));
this._delegate.copyToClipboard(objectPreview.renderValue(object, 1000000, false /* quote */));
return;
}

Expand All @@ -812,7 +802,7 @@ export class Thread implements VariableStoreDelegate {
returnByValue: true
});
if (response && response.result)
this.manager._delegate.copyToClipboard(String(response.result.value));
this._delegate.copyToClipboard(String(response.result.value));
this.cdp().Runtime.releaseObject({objectId: object.objectId});
}

Expand Down
2 changes: 1 addition & 1 deletion src/adapterFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class AdapterFactory implements vscode.DebugAdapterDescriptorFactory {
const adapter = factory.adapter(sessionId || '');
if (!adapter)
return { adapter: undefined, source: undefined };
return { adapter, source: adapter.sourceContainer().source(ref) };
return { adapter, source: adapter.sourceContainer.source(ref) };
}

dispose() {
Expand Down
3 changes: 1 addition & 2 deletions src/chrome/chromeAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,11 @@ export class ChromeAdapter {

await this._debugAdapter.launch({
adapterDisposed: () => this._dispose(),
copyToClipboard: (text: string) => vscode.env.clipboard.writeText(text),
executionContextForest: () => this.targetManager().executionContextForest()
});
this._debugAdapter[ChromeAdapter.symbol] = this;
const pathResolver = new ChromeSourcePathResolver(this._rootPath, params.url, params.webRoot);
this._targetManager = new TargetManager(this._connection, this._debugAdapter.threadManager(), pathResolver);
this._targetManager = new TargetManager(this._debugAdapter.threadManager, this._connection, pathResolver);
this._disposables.push(this._targetManager);

// Note: assuming first page is our main target breaks multiple debugging sessions
Expand Down
13 changes: 3 additions & 10 deletions src/chrome/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class TargetManager implements vscode.Disposable {
readonly onTargetAdded = this._onTargetAddedEmitter.event;
readonly onTargetRemoved = this._onTargetRemovedEmitter.event;

constructor(connection: CdpConnection, threadManager: ThreadManager, sourcePathResolver: SourcePathResolver) {
constructor(threadManager: ThreadManager, connection: CdpConnection, sourcePathResolver: SourcePathResolver) {
this._connection = connection;
this._threadManager = threadManager;
this._sourcePathResolver = sourcePathResolver;
Expand All @@ -50,7 +50,7 @@ export class TargetManager implements vscode.Disposable {
return Array.from(this._targets.values());
}

executionContextForest(): ExecutionContext[] | undefined {
executionContextForest(): ExecutionContext[] {
const reported: Set<string> = new Set();
const toExecutionContext = (thread: Thread, context: Cdp.Runtime.ExecutionContextDescription, isThread: boolean, type: string, name?: string): ExecutionContext => {
reported.add(thread.threadId() + ':' + context.id);
Expand Down Expand Up @@ -203,7 +203,6 @@ export class TargetManager implements vscode.Disposable {
return cleanupOnFailure();
this._onTargetAddedEmitter.fire(target);
debugTarget(`Attached to target ${targetInfo.targetId}`);
this._targetStructureChanged();
return target;
}

Expand All @@ -223,25 +222,18 @@ export class TargetManager implements vscode.Disposable {

this._onTargetRemovedEmitter.fire(target);
debugTarget(`Detached from target ${targetId}`);
this._targetStructureChanged();
}

_targetInfoChanged(targetInfo: Cdp.Target.TargetInfo) {
const target = this._targets.get(targetInfo.targetId);
if (!target)
return;
target._updateFromInfo(targetInfo);
this._targetStructureChanged();
}

_serviceWorkersStatusChanged() {
for (const target of this._targets.values())
target._updateThreadName();
this._targetStructureChanged();
}

_targetStructureChanged() {
this._threadManager.refreshExecutionContexts();
}

canStop(targetId: string): boolean {
Expand Down Expand Up @@ -279,6 +271,7 @@ export class Target {
this.parentTarget = parentTarget;
if (jsTypes.has(targetInfo.type))
this._thread = targetManager._threadManager.createThread(targetInfo.targetId, cdp, {
copyToClipboard: (text: string) => vscode.env.clipboard.writeText(text),
canStopThread: () => targetManager.canStop(this._thread!.threadId()),
stopThread: () => targetManager.stop(this._thread!.threadId()),
supportsCustomBreakpoints: domDebuggerTypes.has(targetInfo.type),
Expand Down
Loading