From a5b11ed1169661641e42394bc967b5b35889286e Mon Sep 17 00:00:00 2001 From: Rob Date: Tue, 20 Oct 2015 22:44:33 -0700 Subject: [PATCH] Factor out sourcemaps and add tests --- .vscode/launch.json | 12 +- .vscode/tasks.json | 5 +- adapter/adapterProxy.ts | 4 +- .../sourceMaps}/pathUtilities.ts | 0 adapter/sourceMaps/sourceMapTransformer.ts | 80 +++++++ {webkit => adapter/sourceMaps}/sourceMaps.ts | 0 gulpfile.js | 9 +- package.json | 1 + test/adapter/lineNumberTransformer.test.ts | 16 +- .../sourceMaps/sourceMapTransformer.test.ts | 199 ++++++++++++++++++ testapp/.vscode/launch.json | 2 +- tsconfig.json | 24 +-- typings/mockery/mockery.d.ts | 33 +++ webkit/webKitAdapterInterfaces.d.ts | 34 +-- webkit/webKitDebugAdapter.ts | 58 +---- webkit/webKitDebugSession.ts | 6 +- 16 files changed, 367 insertions(+), 116 deletions(-) rename {webkit => adapter/sourceMaps}/pathUtilities.ts (100%) create mode 100644 adapter/sourceMaps/sourceMapTransformer.ts rename {webkit => adapter/sourceMaps}/sourceMaps.ts (100%) create mode 100644 test/adapter/sourceMaps/sourceMapTransformer.test.ts create mode 100644 typings/mockery/mockery.d.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 4153f06..24f20b9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,18 +13,10 @@ }, { "name": "test", - "type": "webkit", - "program": "./webkit/test/index.html", - "stopOnEntry": false, - "sourceMaps": false, - "outDir": "out" - }, - { - "name": "opendebug-node", "type": "node", - "program": "./out/node/openDebugNode.js", + "program": "./node_modules/gulp/bin/gulp.js", "stopOnEntry": false, - "args": [ "--server=4712" ], + "args": [ "test" ], "sourceMaps": false, "outDir": "out" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 55775de..0b6433d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,8 +13,9 @@ ] }, { - "taskName": "build-test-watch", - "isWatching": true + "taskName": "watch-build-test", + "isWatching": true, + "isTestCommand": true } ] } \ No newline at end of file diff --git a/adapter/adapterProxy.ts b/adapter/adapterProxy.ts index 8ad3465..53e2b7f 100644 --- a/adapter/adapterProxy.ts +++ b/adapter/adapterProxy.ts @@ -35,7 +35,7 @@ export class AdapterProxy { (p, transformer) => { // If the transformer implements this command, give it a chance to modify the args. Otherwise skip it return request.command in transformer ? - p.then(() => transformer[request.command](request.arguments)) : + p.then(() => transformer[request.command](request.arguments, request.seq)) : p; }, Promise.resolve()); } @@ -54,7 +54,7 @@ export class AdapterProxy { // If the transformer implements this command, give it a chance to modify the args. Otherwise skip it const bodyTransformMethodName = request.command + "Response"; return bodyTransformMethodName in transformer ? - p.then(() => transformer[bodyTransformMethodName](body)) : + p.then(() => transformer[bodyTransformMethodName](body, request.seq)) : p; }, Promise.resolve()); } diff --git a/webkit/pathUtilities.ts b/adapter/sourceMaps/pathUtilities.ts similarity index 100% rename from webkit/pathUtilities.ts rename to adapter/sourceMaps/pathUtilities.ts diff --git a/adapter/sourceMaps/sourceMapTransformer.ts b/adapter/sourceMaps/sourceMapTransformer.ts new file mode 100644 index 0000000..f235d25 --- /dev/null +++ b/adapter/sourceMaps/sourceMapTransformer.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import {ISourceMaps, SourceMaps} from './sourceMaps'; + +/** + * If sourcemaps are enabled, converts from source files on the client side to runtime files on the target side + */ +export class SourceMapTransformer implements IDebugTransformer { + private _sourceMaps: ISourceMaps; + private _generatedCodeDirectory: string; + private _requestSeqToSetBreakpointsArgs: Map; + + public initialize(args: IInitializeRequestArgs): void { + if (args.sourceMaps) { + this._sourceMaps = new SourceMaps(args.generatedCodeDirectory); + this._generatedCodeDirectory = args.generatedCodeDirectory; + this._requestSeqToSetBreakpointsArgs = new Map(); + } + } + + public launch(args: ILaunchRequestArgs): void { + // Can html files be sourcemapped? May as well try. + if (this._sourceMaps && args.program) { + const generatedPath = this._sourceMaps.MapPathFromSource(args.program); + if (generatedPath) { + args.program = generatedPath; + } + } + } + + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments, requestSeq: number): void { + if (this._sourceMaps && args.source.path) { + const argsPath = args.source.path; + + args.source.path = this._sourceMaps.MapPathFromSource(argsPath) || argsPath; + args.lines = args.lines.map(line => { + const mapped = this._sourceMaps.MapFromSource(argsPath, line, /*column=*/0); + return mapped ? mapped.line : line; + }); + + this._requestSeqToSetBreakpointsArgs.set(requestSeq, JSON.parse(JSON.stringify(args))); + } + } + + public setBreakpointsResponse(response: SetBreakpointsResponseBody, requestSeq: number): void { + if (this._sourceMaps && this._requestSeqToSetBreakpointsArgs.has(requestSeq)) { + const args = this._requestSeqToSetBreakpointsArgs.get(requestSeq); + response.breakpoints.forEach(bp => { + const mapped = this._sourceMaps.MapToSource(args.source.path, bp.line, (bp).column); + delete (bp).column; + if (mapped) { + bp.line = mapped.line; + } + + this._requestSeqToSetBreakpointsArgs.delete(requestSeq); + }); + } else { + // Cleanup column, which is passed in here in case it's needed for sourcemaps, but isn't actually + // part of the DebugProtocol + response.breakpoints.forEach(bp => { + delete (bp).column; + }); + } + } + + public stackTraceResponse(response: StackTraceResponseBody): void { + if (this._sourceMaps) { + response.stackFrames.forEach(stackFrame => { + const mapped = this._sourceMaps.MapToSource(stackFrame.source.path, stackFrame.line, stackFrame.column); + if (mapped) { + stackFrame.source.path = mapped.path; + stackFrame.line = mapped.line; + stackFrame.column = mapped.column; + } + }); + } + } +} diff --git a/webkit/sourceMaps.ts b/adapter/sourceMaps/sourceMaps.ts similarity index 100% rename from webkit/sourceMaps.ts rename to adapter/sourceMaps/sourceMaps.ts diff --git a/gulpfile.js b/gulpfile.js index e70f0a6..b89f647 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -44,13 +44,16 @@ gulp.task('default', ['build']); function test() { return gulp.src('out/test/**/*.test.js', { read: false }) - .pipe(mocha()) - .on('error', function() { }); + .pipe(mocha({ ui: 'tdd' })) + .on('error', function(e) { + log(e ? e.toString() : 'error in test task!'); + this.emit('end'); + }); } gulp.task('build-test', ['build'], test); gulp.task('test', test); -gulp.task('watch-build-test', ['build', 'build-test'], function(cb) { +gulp.task('watch-build-test', ['build', 'build-test'], function() { return gulp.watch(sources, ['build', 'build-test']); }); diff --git a/package.json b/package.json index 23c597e..791f975 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "gulp-typescript": "^2.8.0", "gulp-util": "^3.0.5", "mocha": "^2.3.3", + "mockery": "^1.4.0", "tsd": "^0.6.3", "typescript": "^1.6.2" }, diff --git a/test/adapter/lineNumberTransformer.test.ts b/test/adapter/lineNumberTransformer.test.ts index 771b1e5..3d63164 100644 --- a/test/adapter/lineNumberTransformer.test.ts +++ b/test/adapter/lineNumberTransformer.test.ts @@ -14,13 +14,13 @@ function createTransformer(clientLinesStartAt1: boolean, targetLinesStartAt1: bo return transformer; } -describe('LineNumberTransformer', () => { +suite('LineNumberTransformer', () => { const c0t0Transformer = createTransformer(false, false); const c0t1Transformer = createTransformer(false, true); const c1t0Transformer = createTransformer(true, false); const c1t1Transformer = createTransformer(true, true); - describe('setBreakpoints()', () => { + suite('setBreakpoints()', () => { function getArgs(lines: number[]): DebugProtocol.SetBreakpointsArguments { return { source: { path: "test/path" }, @@ -34,7 +34,7 @@ describe('LineNumberTransformer', () => { assert.deepEqual(args, getArgs(tLines)); } - it('fixes args.lines', () => { + test('fixes args.lines', () => { testSetBreakpoints(c0t0Transformer, [0, 1, 2]); testSetBreakpoints(c0t1Transformer, [0, 1, 2], [1, 2, 3]); testSetBreakpoints(c1t0Transformer, [1, 2, 3], [0, 1, 2]); @@ -42,7 +42,7 @@ describe('LineNumberTransformer', () => { }); }); - describe('setBreakpointsResponse()', () => { + suite('setBreakpointsResponse()', () => { function getResponse(lines: number[]): SetBreakpointsResponseBody { return { breakpoints: lines.map(line => ({ verified: true, line: line })) @@ -55,7 +55,7 @@ describe('LineNumberTransformer', () => { assert.deepEqual(response, getResponse(cLines)); } - it('fixes the breakpoints\' lines', () => { + test('fixes the breakpoints\' lines', () => { testSetBreakpointsResponse(c0t0Transformer, [0, 1, 2]); testSetBreakpointsResponse(c0t1Transformer, [1, 2, 3], [0, 1, 2]); testSetBreakpointsResponse(c1t0Transformer, [0, 1, 2], [1, 2, 3]); @@ -63,7 +63,7 @@ describe('LineNumberTransformer', () => { }); }); - describe('stackTraceResponse', () => { + suite('stackTraceResponse()', () => { function getResponse(lines: number[]): StackTraceResponseBody { return { stackFrames: lines.map(line => ({ id: 0, name: '', line, column: 0 })) @@ -76,11 +76,11 @@ describe('LineNumberTransformer', () => { assert.deepEqual(response, getResponse(cLines)); } - it('fixes the stackFrames\' lines', () => { + test('fixes the stackFrames\' lines', () => { testStackTraceResponse(c0t0Transformer, [0, 1, 2]); testStackTraceResponse(c0t1Transformer, [1, 2, 3], [0, 1, 2]); testStackTraceResponse(c1t0Transformer, [0, 1, 2], [1, 2, 3]); testStackTraceResponse(c1t1Transformer, [1, 2, 3]); - }) + }); }); }); \ No newline at end of file diff --git a/test/adapter/sourceMaps/sourceMapTransformer.test.ts b/test/adapter/sourceMaps/sourceMapTransformer.test.ts new file mode 100644 index 0000000..ab79cf2 --- /dev/null +++ b/test/adapter/sourceMaps/sourceMapTransformer.test.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import assert = require('assert'); +import mockery = require('mockery'); +import { ISourceMaps, MappingResult } from '../../../adapter/sourceMaps/sourceMaps'; + +const MODULE_UNDER_TEST = '../../../adapter/sourceMaps/sourceMapTransformer'; +const AUTHORED_PATH = 'authored.ts'; +const RUNTIME_PATH = 'runtime.js'; +const AUTHORED_LINES = [1, 2, 3]; +const RUNTIME_LINES = [2, 5, 8]; + +suite('SourceMapTransformer', () => { + let transformer: IDebugTransformer; + let transformerSMDisabled: IDebugTransformer; + + setup(() => { + // Set up mockery with SourceMaps mock + mockery.enable(); + mockery.registerMock('./sourceMaps', { SourceMaps: MockSourceMaps }); + mockery.registerAllowable(MODULE_UNDER_TEST); + + // Grab the test class with mock injected, instantiate with and without sourcemaps enabled + let SourceMapTransformer = require(MODULE_UNDER_TEST).SourceMapTransformer; + transformer = new SourceMapTransformer(); + transformer.initialize({ + sourceMaps: true, + generatedCodeDirectory: 'test' + }); + + transformerSMDisabled = new SourceMapTransformer(); + transformerSMDisabled.initialize({ + sourceMaps: false, + generatedCodeDirectory: 'test' + }); + }); + + teardown(() => { + mockery.deregisterAll(); + mockery.disable(); + transformer = null; + transformerSMDisabled = null; + }); + + suite('launch()', () => { + test('modifies args.path when present', () => { + const args = { workingDirectory: 'C:/code', program: 'authored.ts' }; + const expected = { workingDirectory: 'C:/code', program: 'runtime.js' }; + + transformer.launch(args); + assert.deepEqual(args, expected); + }); + + test('doesn\'t do anything when args.path is missing, e.g. args.url was set', () => { + const args = { workingDirectory: 'C:/code' }; + const expected = { workingDirectory: 'C:/code' }; + + transformer.launch(args); + assert.deepEqual(args, expected); + }); + + test('doesn\'t do anything when sourcemaps are disabled', () => { + const args = { workingDirectory: 'C:/code' }; + const expected = { workingDirectory: 'C:/code' }; + + transformerSMDisabled.launch(args); + assert.deepEqual(args, expected); + }); + }); + + suite('setBreakpoints()', () => { + function createArgs(path: string, lines:number[]): DebugProtocol.SetBreakpointsArguments { + return { + source: { path }, + lines + }; + } + + test('modifies the source and lines', () => { + const args = createArgs(AUTHORED_PATH, AUTHORED_LINES); + const expected = createArgs(RUNTIME_PATH, RUNTIME_LINES); + + transformer.setBreakpoints(args); + assert.deepEqual(args, expected); + }); + + test('doesn\'t do anything when sourcemaps are disabled', () => { + const args = createArgs(RUNTIME_PATH, RUNTIME_LINES); + const expected = createArgs(RUNTIME_PATH, RUNTIME_LINES); + + transformerSMDisabled.setBreakpoints(args); + assert.deepEqual(args, expected); + }); + + suite('setBreakpointsResponse()', () => { + function getResponseBody(lines: number[], column?: number): SetBreakpointsResponseBody { + return { + breakpoints: lines.map(line => { + const bp = { line, verified: true }; + if (column !== undefined) { + (bp).column = column; + } + + return bp; + }) + }; + } + + test('modifies the response source and lines', () => { + const response = getResponseBody(RUNTIME_LINES, /*column=*/0); + const expected = getResponseBody(AUTHORED_LINES); + + transformer.setBreakpoints({ + source: { path: AUTHORED_PATH }, + lines: AUTHORED_LINES + }); + transformer.setBreakpointsResponse(response); + assert.deepEqual(response, expected); + }); + + test('doesn\'t do anything when sourcemaps are disabled except remove the column', () => { + const response = getResponseBody(RUNTIME_LINES, /*column=*/0); + const expected = getResponseBody(RUNTIME_LINES); + + transformerSMDisabled.setBreakpoints({ + source: { path: RUNTIME_PATH }, + lines: RUNTIME_LINES + }); + transformerSMDisabled.setBreakpointsResponse(response); + assert.deepEqual(response, expected); + }); + }); + }); + + suite('stackTraceResponse', () => { + function getResponseBody(path: string, lines: number[]): StackTraceResponseBody { + return { + stackFrames: lines.map((line, i) => ({ + id: i, + name: 'line ' + i, + line, + column: 0, + source: { path } + })) + }; + } + + test('modifies the response stackFrames', () => { + const response = getResponseBody(RUNTIME_PATH, RUNTIME_LINES); + const expected = getResponseBody(AUTHORED_PATH, AUTHORED_LINES); + + transformer.stackTraceResponse(response); + assert.deepEqual(response, expected); + }); + + test('does nothing when there are no sourcemaps', () => { + const response = getResponseBody(RUNTIME_PATH, RUNTIME_LINES); + const expected = getResponseBody(RUNTIME_PATH, RUNTIME_LINES); + + transformerSMDisabled.stackTraceResponse(response); + assert.deepEqual(response, expected); + }); + }); +}); + +class MockSourceMaps implements ISourceMaps { + constructor(private generatedCodeDirectory: string) { } + + public MapPathFromSource(path: string): string { + assert.equal(path, AUTHORED_PATH); + return RUNTIME_PATH; + } + + /* + * Map location in source language to location in generated code. + * line and column are 0 based. + */ + public MapFromSource(path: string, line: number, column: number): MappingResult { + assert.equal(path, AUTHORED_PATH); + assert.equal(column, 0); + + const mappedLine = RUNTIME_LINES[AUTHORED_LINES.indexOf(line)]; + return { path: RUNTIME_PATH, line: mappedLine, column: 0 }; + } + + /* + * Map location in generated code to location in source language. + * line and column are 0 based. + */ + public MapToSource(path: string, line: number, column: number): MappingResult { + assert.equal(path, RUNTIME_PATH); + assert.equal(column, 0); + + const mappedLine = AUTHORED_LINES[RUNTIME_LINES.indexOf(line)]; + return { path: AUTHORED_PATH, line: mappedLine, column: 0 }; + } +} diff --git a/testapp/.vscode/launch.json b/testapp/.vscode/launch.json index db1ce88..853d642 100644 --- a/testapp/.vscode/launch.json +++ b/testapp/.vscode/launch.json @@ -9,7 +9,7 @@ "runtimeArgs": ["http://localhost:8080/out/client/index.html?2"], "cwd": ".", "stopOnEntry": false, - "sourceMaps": false, + "sourceMaps": true, "outDir": "out" }, { diff --git a/tsconfig.json b/tsconfig.json index a408875..634c4d6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,27 +6,5 @@ "target": "ES5", "sourceMap": true, "outDir": "out" - }, - "files": [ - "adapter/adapterProxy.ts", - "adapter/lineNumberTransformer.ts", - "webkit/openDebugWebKit.ts", - "webkit/pathUtilities.ts", - "webkit/sourceMaps.ts", - "webkit/utilities.ts", - "webkit/webKitConnection.ts", - "webkit/webKitDebugAdapter.ts", - "webkit/webKitDebugSession.ts", - "webkit/webKitProtocol.d.ts", - "webkit/webKitAdapterInterfaces.d.ts", - "common/debugProtocol.d.ts", - "common/debugSession.ts", - "common/handles.ts", - "common/v8Protocol.ts", - "typings/es6-collections/es6-collections.d.ts", - "typings/es6-promise/es6-promise.d.ts", - "typings/node/node.d.ts", - "typings/source-map/source-map.d.ts", - "typings/ws/ws.d.ts" - ] + } } diff --git a/typings/mockery/mockery.d.ts b/typings/mockery/mockery.d.ts new file mode 100644 index 0000000..f4d98de --- /dev/null +++ b/typings/mockery/mockery.d.ts @@ -0,0 +1,33 @@ +// Type definitions for mockery 1.4.0 +// Project: https://github.com/mfncooper/mockery +// Definitions by: jt000 +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module "mockery" { + + interface MockeryEnableArgs { + useCleanCache?: boolean; + warnOnReplace?: boolean; + warnOnUnregistered?: boolean; + } + + export function enable(args?: MockeryEnableArgs): void; + export function disable(): void; + + export function registerMock(name: string, mock: any): void; + export function deregisterMock(name: string): void; + + export function registerSubstitute(name: string, substitute: string): void; + export function deregisterSubstitute(name: string): void; + + export function registerAllowable(name: string, unhook?: boolean): void; + export function deregisterAllowable(name: string): void; + + export function registerAllowables(names: string[]): void; + export function deregisterAllowables(names: string[]): void; + + export function deregisterAll(): void; + export function resetCache(): void; + export function warnOnUnregistered(value: boolean): void; + export function warnOnReplace(value: boolean): void; +} \ No newline at end of file diff --git a/webkit/webKitAdapterInterfaces.d.ts b/webkit/webKitAdapterInterfaces.d.ts index f2b58c0..1fd21f0 100644 --- a/webkit/webKitAdapterInterfaces.d.ts +++ b/webkit/webKitAdapterInterfaces.d.ts @@ -77,23 +77,23 @@ interface IDebugAdapter { declare type PromiseOrNot = T | Promise; interface IDebugTransformer { - initialize?(args: IInitializeRequestArgs): PromiseOrNot; - launch?(args: ILaunchRequestArgs): PromiseOrNot; - attach?(args: IAttachRequestArgs): PromiseOrNot; - setBreakpoints?(args: DebugProtocol.SetBreakpointsArguments): PromiseOrNot; - setExceptionBreakpoints?(args: DebugProtocol.SetExceptionBreakpointsArguments): PromiseOrNot; + initialize?(args: IInitializeRequestArgs, requestSeq?: number): PromiseOrNot; + launch?(args: ILaunchRequestArgs, requestSeq?: number): PromiseOrNot; + attach?(args: IAttachRequestArgs, requestSeq?: number): PromiseOrNot; + setBreakpoints?(args: DebugProtocol.SetBreakpointsArguments, requestSeq?: number): PromiseOrNot; + setExceptionBreakpoints?(args: DebugProtocol.SetExceptionBreakpointsArguments, requestSeq?: number): PromiseOrNot; - stackTrace?(args: DebugProtocol.StackTraceArguments): PromiseOrNot; - scopes?(args: DebugProtocol.ScopesArguments): PromiseOrNot; - variables?(args: DebugProtocol.VariablesArguments): PromiseOrNot; - source?(args: DebugProtocol.SourceArguments): PromiseOrNot; - evaluate?(args: DebugProtocol.EvaluateArguments): PromiseOrNot; + stackTrace?(args: DebugProtocol.StackTraceArguments, requestSeq?: number): PromiseOrNot; + scopes?(args: DebugProtocol.ScopesArguments, requestSeq?: number): PromiseOrNot; + variables?(args: DebugProtocol.VariablesArguments, requestSeq?: number): PromiseOrNot; + source?(args: DebugProtocol.SourceArguments, requestSeq?: number): PromiseOrNot; + evaluate?(args: DebugProtocol.EvaluateArguments, requestSeq?: number): PromiseOrNot; - setBreakpointsResponse?(response: SetBreakpointsResponseBody): PromiseOrNot; - stackTraceResponse?(response: StackTraceResponseBody): PromiseOrNot; - scopesResponse?(response: ScopesResponseBody): PromiseOrNot; - variablesResponse?(response: VariablesResponseBody): PromiseOrNot; - sourceResponse?(response: SourceResponseBody): PromiseOrNot; - threadsResponse?(response: ThreadsResponseBody): PromiseOrNot; - evaluateResponse?(response: EvaluateResponseBody): PromiseOrNot; + setBreakpointsResponse?(response: SetBreakpointsResponseBody, requestSeq?: number): PromiseOrNot; + stackTraceResponse?(response: StackTraceResponseBody, requestSeq?: number): PromiseOrNot; + scopesResponse?(response: ScopesResponseBody, requestSeq?: number): PromiseOrNot; + variablesResponse?(response: VariablesResponseBody, requestSeq?: number): PromiseOrNot; + sourceResponse?(response: SourceResponseBody, requestSeq?: number): PromiseOrNot; + threadsResponse?(response: ThreadsResponseBody, requestSeq?: number): PromiseOrNot; + evaluateResponse?(response: EvaluateResponseBody, requestSeq?: number): PromiseOrNot; } diff --git a/webkit/webKitDebugAdapter.ts b/webkit/webKitDebugAdapter.ts index ad199b7..f98ea78 100644 --- a/webkit/webKitDebugAdapter.ts +++ b/webkit/webKitDebugAdapter.ts @@ -6,7 +6,6 @@ import {DebugSession, StoppedEvent, InitializedEvent, TerminatedEvent} from '../ import {Handles} from '../common/handles'; import {WebKitConnection} from './webKitConnection'; import Utilities = require('./utilities'); -import {ISourceMaps, SourceMaps} from './sourceMaps'; import {spawn, ChildProcess} from 'child_process'; import nodeUrl = require('url'); @@ -32,8 +31,6 @@ export class WebKitDebugAdapter implements IDebugAdapter { private _currentStack: WebKitProtocol.Debugger.CallFrame[]; private _pendingBreakpointsByUrl: Map; private _committedBreakpointsByScriptId: Map; - private _sourceMaps: ISourceMaps; - private _generatedCodeDirectory: string; private _overlayHelper: Utilities.DebounceHelper; private _chromeProc: ChildProcess; @@ -75,11 +72,6 @@ export class WebKitDebugAdapter implements IDebugAdapter { public initialize(args: IInitializeRequestArgs): Promise { this._clientLinesStartAt1 = args.linesStartAt1; - if (args.sourceMaps) { - this._sourceMaps = new SourceMaps(args.generatedCodeDirectory); - this._generatedCodeDirectory = args.generatedCodeDirectory; - } - return Promise.resolve(); } @@ -98,17 +90,11 @@ export class WebKitDebugAdapter implements IDebugAdapter { } if (args.program) { - // Can html files be sourcemapped? May as well try. - if (this._sourceMaps) { - const generatedPath = this._sourceMaps.MapPathFromSource(args.program); - if (generatedPath) { - args.program = generatedPath; - } - } - chromeArgs.push(args.program); } else if (args.url) { chromeArgs.push(args.url); + } else { + // TODO fail } if (args.arguments) { @@ -234,9 +220,7 @@ export class WebKitDebugAdapter implements IDebugAdapter { private _setAllBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { let targetScript: WebKitProtocol.Debugger.Script; if (args.source.path) { - const path = (this._sourceMaps && this._sourceMaps.MapPathFromSource(args.source.path)) || args.source.path; - const canPath = canonicalizeUrl(path); - targetScript = this._scriptsByUrl.get(canPath); + targetScript = this._scriptsByUrl.get(canonicalizeUrl(args.source.path)); } else if (args.source.sourceReference) { targetScript = this._scriptsById.get(sourceReferenceToScriptId(args.source.sourceReference)); } else if (args.source.name) { @@ -263,15 +247,6 @@ export class WebKitDebugAdapter implements IDebugAdapter { private _addBreakpoints(sourcePath: string, scriptId: WebKitProtocol.Debugger.ScriptId, lines: number[]): Promise { // Adjust lines for sourcemaps, call setBreakpoint for all breakpoints in the script simultaneously const responsePs = lines - .map(debuggerLine => { - // Sourcemap lines - if (this._sourceMaps) { - const mapped = this._sourceMaps.MapFromSource(sourcePath, debuggerLine, /*column=*/0); - return mapped ? mapped.line : debuggerLine; - } else { - return debuggerLine; - } - }) .map(lineNumber => this._webKitConnection.debugger_setBreakpoint({ scriptId: scriptId, lineNumber })); // Join all setBreakpoint requests to a single promise @@ -290,18 +265,11 @@ export class WebKitDebugAdapter implements IDebugAdapter { return successfulResponses .map(response => { let line = response.result.actualLocation.lineNumber; - if (this._sourceMaps) { - const clientUrl = this.webkitUrlToClientUrl(script.url); - const mapped = this._sourceMaps.MapToSource(clientUrl, response.result.actualLocation.lineNumber, response.result.actualLocation.columnNumber); - if (mapped) { - line = mapped.line; - } - } - return { verified: true, - line: line - } + line, + column: response.result.actualLocation.columnNumber + }; }); } @@ -350,15 +318,8 @@ export class WebKitDebugAdapter implements IDebugAdapter { let path = this.webkitUrlToClientUrl(script.url); let line = callFrame.location.lineNumber; let column = callFrame.location.columnNumber; - if (this._sourceMaps) { - const mapped = this._sourceMaps.MapToSource(path, line, column); - if (mapped) { - path = mapped.path; - line = mapped.line; - column = mapped.column; - } - } + // Both? const source = { path, sourceReference: scriptIdToSourceReference(script.scriptId) @@ -398,7 +359,7 @@ export class WebKitDebugAdapter implements IDebugAdapter { return { variables }; }); } else { - return Promise.resolve(); + return Promise.resolve(null); } } @@ -506,8 +467,7 @@ export class WebKitDebugAdapter implements IDebugAdapter { const pathParts = pathName.split('/'); while (pathParts.length > 0) { - const rootDir = this._sourceMaps ? this._generatedCodeDirectory : this._clientCWD; - const clientUrl = path.join(rootDir, pathParts.join('/')); + const clientUrl = path.join(this._clientCWD, pathParts.join('/')); if (fs.existsSync(clientUrl)) { return canonicalizeUrl(clientUrl); // path.join will change / to \ } diff --git a/webkit/webKitDebugSession.ts b/webkit/webKitDebugSession.ts index 9fce7f2..42fe15c 100644 --- a/webkit/webKitDebugSession.ts +++ b/webkit/webKitDebugSession.ts @@ -8,6 +8,7 @@ import {WebKitDebugAdapter} from './webKitDebugAdapter'; import {AdapterProxy} from '../adapter/adapterProxy'; import {LineNumberTransformer} from '../adapter/lineNumberTransformer'; +import {SourceMapTransformer} from '../adapter/sourceMaps/sourceMapTransformer'; export class WebKitDebugSession extends DebugSession { private _adapterProxy: AdapterProxy; @@ -16,7 +17,10 @@ export class WebKitDebugSession extends DebugSession { super(targetLinesStartAt1, isServer); this._adapterProxy = new AdapterProxy( - [new LineNumberTransformer(targetLinesStartAt1)], + [ + new LineNumberTransformer(targetLinesStartAt1), + new SourceMapTransformer() + ], new WebKitDebugAdapter(), event => this.sendEvent(event)); }