Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
first cut of functionBreakpoints. fixes #35
Browse files Browse the repository at this point in the history
  • Loading branch information
weinand committed Feb 4, 2016
1 parent 1551446 commit 7f72d16
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 12 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"vscode": "^0.10.1"
},
"dependencies": {
"vscode-debugprotocol": "^1.5.0",
"vscode-debugadapter": "^1.5.0",
"vscode-debugprotocol": "~1.6.0-pre2",
"vscode-debugadapter": "~1.6.0-pre2",
"source-map": "*"
},
"repository": {
Expand Down
107 changes: 105 additions & 2 deletions src/node/nodeDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ export class NodeDebugSession extends DebugSession {
// This debug adapter does not (yet) support a side effect free evaluate request for data hovers.
response.body.supportsEvaluateForHovers = true;

// This debug adapter does not (yet) support function breakpoints.
response.body.supportsFunctionBreakpoints = false;
// This debug adapter supports function breakpoints.
response.body.supportsFunctionBreakpoints = true;

this.sendResponse(response);
}
Expand Down Expand Up @@ -1217,6 +1217,109 @@ export class NodeDebugSession extends DebugSession {
return pathRegex;
}

//--- set function breakpoints request ------------------------------------------------------------------------------------

protected setFunctionBreakPointsRequest(response: DebugProtocol.SetFunctionBreakpointsResponse, args: DebugProtocol.SetFunctionBreakpointsArguments): void {

// clear all existing breakpoints for the given path or script ID
this._node.command('listbreakpoints', null, (nodeResponse: NodeV8Response) => {

if (nodeResponse.success) {
const toClear = new Array<number>();

// try to match breakpoints
for (let breakpoint of nodeResponse.body.breakpoints) {
switch (breakpoint.type) {
case 'function':
toClear.push(breakpoint.number);
break;
}
}

this._clearBreakpoints(toClear, 0, () => {
this._finishSetFunctionBreakpoints(response, args.breakpoints);
});

} else {
this._sendNodeResponse(response, nodeResponse);
}
});
}

/*
* Finish the setBreakpointsRequest: set the breakpooints and send the verification response back to client
*/
private _finishSetFunctionBreakpoints(response: DebugProtocol.SetBreakpointsResponse, bps: DebugProtocol.FunctionBreakpoint[]): void {

const breakpoints = new Array<Breakpoint>();

this._setFunctionBreakpoints(0, bps, breakpoints, () => {

response.body = {
breakpoints: breakpoints
};
this.sendResponse(response);

this.log('bp', `_finishSetFunctionBreakpoints: result ${JSON.stringify(breakpoints)}`);
});
}

/**
* Recursive function for setting function breakpoints.
*/
private _setFunctionBreakpoints(ix: number, bps: DebugProtocol.FunctionBreakpoint[], results: Breakpoint[], done: () => void) : void {

if (bps.length == 0) { // nothing to do
done();
return;
}

this._setFunctionBreakpoint(bps[ix], (result: Breakpoint) => {

results[ix] = result;

if (ix+1 < bps.length) {
setImmediate(() => {
// recurse
this._setFunctionBreakpoints(ix+1, bps, results, done);
});
} else {
done();
}
});
}

/*
* Register a single function breakpoint with node.
*/
private _setFunctionBreakpoint(fb: DebugProtocol.FunctionBreakpoint, done: (result: Breakpoint) => void): void {

let args: any = {
type: 'function',
target: fb.name
};
if (fb.condition) {
args.condition = fb.condition;
}

this._node.command('setbreakpoint', args, (resp: NodeV8Response) => {
this.log('bp', `_setFunctionBreakpoint: ${JSON.stringify(args)}: ${resp.success}`);
if (resp.success) {
const locations = resp.body.actual_locations;
if (locations && locations.length > 0) {
const actualLine = this.convertDebuggerLineToClient(locations[0].line);
const actualColumn = this.convertDebuggerColumnToClient(this._adjustColumn(actualLine, locations[0].column));
done(new Breakpoint(true, actualLine, actualColumn)); // TODO@AW add source
} else {
done(new Breakpoint(true));
}
} else {
// TODO@AW use message
done(new Breakpoint(false));
}
});
}

//--- set exception request -----------------------------------------------------------------------------------------------

protected setExceptionBreakPointsRequest(response: DebugProtocol.SetExceptionBreakpointsResponse, args: DebugProtocol.SetExceptionBreakpointsArguments): void {
Expand Down
8 changes: 6 additions & 2 deletions src/tests/DebugClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ export class DebugClient extends ProtocolClient {
return this.send('setBreakpoints', args);
}

public setFunctionBreakpointsRequest(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise<DebugProtocol.SetFunctionBreakpointsResponse> {
return this.send('setFunctionBreakpoints', args);
}

public setExceptionBreakpointsRequest(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise<DebugProtocol.SetExceptionBreakpointsResponse> {
return this.send('setExceptionBreakpoints', args);
}
Expand Down Expand Up @@ -237,7 +241,7 @@ export class DebugClient extends ProtocolClient {
});
}

private configurationDone() : Promise<any> {
private configurationDone() : Promise<DebugProtocol.Response> {
if (this._supportsConfigurationDoneRequest) {
return this.configurationDoneRequest();
} else {
Expand Down Expand Up @@ -279,7 +283,7 @@ export class DebugClient extends ProtocolClient {
const e = <DebugProtocol.OutputEvent> event;
if (e.body.category === category) {
output += e.body.output;
if (output === expected) {
if (output.indexOf(expected) === 0) {
resolve(event);
} else if (expected.indexOf(output) !== 0) {
const sanitize = (s: string) => s.toString().replace(/\r/mg, '\\r').replace(/\n/mg, '\\n');
Expand Down
44 changes: 38 additions & 6 deletions src/tests/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ suite('Node Debug Adapter', () => {
dc.waitForEvent('initialized').then(event => {
return dc.setBreakpointsRequest({
breakpoints: [ { line: COND_BREAKPOINT_LINE, condition: "i === 3" } ],
source: { path: PROGRAM },

source: { path: PROGRAM }
});
}).then(response => {
assert.deepEqual(response.body.breakpoints[0], {
Expand Down Expand Up @@ -186,9 +185,9 @@ suite('Node Debug Adapter', () => {
}, JS_SOURCE, JS_LINE, TS_SOURCE, TS_LINE);
});

test.only('should stop on a breakpoint in TypeScript even if program\'s entry point is in JavaScript', () => {
test('should stop on a breakpoint in TypeScript even if program\'s entry point is in JavaScript', () => {

const PROGRAM = Path.join(PROJECT_ROOT, 'src/tests/data/sourcemaps-js-entrypoint/src/entry.js');
const PROGRAM = Path.join(PROJECT_ROOT, 'src/tests/data/sourcemaps-js-entrypoint/out/entry.js');
const OUT_DIR = Path.join(PROJECT_ROOT, 'src/tests/data/sourcemaps-js-entrypoint/out');
const TS_SOURCE = Path.join(PROJECT_ROOT, 'src/tests/data/sourcemaps-js-entrypoint/src/classes.ts');
const TS_LINE = 17;
Expand All @@ -202,6 +201,39 @@ suite('Node Debug Adapter', () => {
});
});

suite('function setBreakpoints', () => {

const PROGRAM = Path.join(PROJECT_ROOT, 'src/tests/data/programWithFunction.js');
const FUNCTION_NAME = 'foo';
const FUNCTION_LINE = 4;

test('should stop on a function breakpoint', () => {

return Promise.all<DebugProtocol.ProtocolMessage>([

dc.launch({ program: PROGRAM }),

dc.configurationSequence(),

// since we can only set a function breakpoint for *known* functions,
// we use the program output as an indication that function 'foo' has been defined.
dc.assertOutput('stdout', 'foo defined').then(event => {

return dc.setFunctionBreakpointsRequest({
breakpoints: [ { name: FUNCTION_NAME } ]
}).then(response => {
const bp = response.body.breakpoints[0];
assert.equal(bp.verified, true);
assert.equal(bp.line, FUNCTION_LINE);
return response;
});
}),

dc.assertStoppedLocation('breakpoint', PROGRAM, FUNCTION_LINE)
]);
});
});

suite('setExceptionBreakpoints', () => {

const PROGRAM = Path.join(PROJECT_ROOT, 'src/tests/data/programWithException.js');
Expand Down Expand Up @@ -255,8 +287,8 @@ suite('Node Debug Adapter', () => {
return Promise.all([
dc.configurationSequence(),
dc.launch({ program: PROGRAM }),
dc.assertOutput('stdout', "Hello stdout 0\nHello stdout 1\nHello stdout 2\n"),
//dc.assertOutput('stderr', "Hello stderr 0\nHello stderr 1\nHello stderr 2\n")
dc.assertOutput('stdout', 'Hello stdout 0\nHello stdout 1\nHello stdout 2\n'),
//dc.assertOutput('stderr', 'Hello stderr 0\nHello stderr 1\nHello stderr 2\n')
]);
});
});
Expand Down
11 changes: 11 additions & 0 deletions src/tests/data/programWithFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

// global function
foo = function(n) {
return n;
}

console.log('foo defined');

setInterval(function() {
foo(123);
}, 300);

0 comments on commit 7f72d16

Please sign in to comment.