Skip to content

Commit 266798f

Browse files
committed
Add watchpoint support
1 parent 6793e16 commit 266798f

File tree

2 files changed

+111
-1
lines changed

2 files changed

+111
-1
lines changed

src/debug/debugSession.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
112112
supportsSetVariable: true,
113113
supportsConditionalBreakpoints: false, // TODO
114114
supportsStepBack: false,
115+
supportsDataBreakpoints: true,
115116
};
116117

117118
try {
@@ -315,6 +316,81 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
315316
this.sendResponse(response);
316317
}
317318

319+
protected dataBreakpointInfoRequest(
320+
response: DebugProtocol.DataBreakpointInfoResponse,
321+
args: DebugProtocol.DataBreakpointInfoArguments
322+
): void {
323+
if (args.variablesReference !== undefined && (args.variablesReference === 1 || args.variablesReference === 2)) {
324+
// This is a private or public local variable
325+
response.body = {
326+
dataId: args.name,
327+
description: args.name,
328+
};
329+
} else {
330+
// This is an object property or array element, or args.variablesReference is undefined
331+
response.body = {
332+
dataId: null,
333+
description: "Can only set a watchpoint on a local variable",
334+
};
335+
}
336+
337+
this.sendResponse(response);
338+
}
339+
340+
protected async setDataBreakpointsRequest(
341+
response: DebugProtocol.SetDataBreakpointsResponse,
342+
args: DebugProtocol.SetDataBreakpointsArguments
343+
): Promise<void> {
344+
try {
345+
await this._debugTargetSet.wait(1000);
346+
347+
const currentList = await this._connection.sendBreakpointListCommand();
348+
currentList.breakpoints
349+
.filter((breakpoint) => {
350+
if (breakpoint instanceof xdebug.Watchpoint) {
351+
return true;
352+
}
353+
return false;
354+
})
355+
.map((breakpoint) => {
356+
this._connection.sendBreakpointRemoveCommand(breakpoint);
357+
});
358+
359+
let xdebugWatchpoints: xdebug.Watchpoint[] = [];
360+
xdebugWatchpoints = await Promise.all(
361+
args.breakpoints.map(async (breakpoint) => {
362+
return new xdebug.Watchpoint(breakpoint.dataId);
363+
})
364+
);
365+
366+
const vscodeWatchpoints: DebugProtocol.Breakpoint[] = [];
367+
await Promise.all(
368+
xdebugWatchpoints.map(async (breakpoint, index) => {
369+
try {
370+
await this._connection.sendBreakpointSetCommand(breakpoint);
371+
vscodeWatchpoints[index] = { verified: true, instructionReference: breakpoint.variable };
372+
} catch (error) {
373+
vscodeWatchpoints[index] = {
374+
verified: false,
375+
instructionReference: breakpoint.variable,
376+
message: error.message,
377+
};
378+
}
379+
})
380+
);
381+
382+
// send back the watchpoints
383+
response.body = {
384+
breakpoints: vscodeWatchpoints,
385+
};
386+
} catch (error) {
387+
this.sendErrorResponse(response, error);
388+
return;
389+
}
390+
391+
this.sendResponse(response);
392+
}
393+
318394
protected threadsRequest(response: DebugProtocol.ThreadsResponse): void {
319395
// runtime supports now threads so just return a default thread.
320396
response.body = {

src/debug/xdebugConnection.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ export abstract class Breakpoint {
128128
case "conditional":
129129
// eslint-disable-next-line @typescript-eslint/no-use-before-define
130130
return new ConditionalBreakpoint(breakpointNode, connection);
131+
case "watch":
132+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
133+
return new Watchpoint(breakpointNode, connection);
131134
default:
132135
throw new Error(`Invalid type ${breakpointNode.getAttribute("type")}`);
133136
}
@@ -260,6 +263,29 @@ export class ConditionalBreakpoint extends Breakpoint {
260263
}
261264
}
262265

266+
/** class for watch breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
267+
export class Watchpoint extends Breakpoint {
268+
/** The variable to watch */
269+
public variable: string;
270+
/** Constructs a breakpoint object from an XML node from a XDebug response */
271+
public constructor(breakpointNode: Element, connection: Connection);
272+
/** Contructs a breakpoint object for passing to sendSetBreakpointCommand */
273+
public constructor(variable: string);
274+
public constructor(...rest: any[]) {
275+
if (typeof rest[0] === "object") {
276+
// from XML
277+
const breakpointNode: Element = rest[0];
278+
const connection: Connection = rest[1];
279+
super(breakpointNode, connection);
280+
this.variable = breakpointNode.getAttribute("expression"); // Base64 encoded?
281+
} else {
282+
// from arguments
283+
super("watch");
284+
this.variable = rest[0];
285+
}
286+
}
287+
}
288+
263289
/** Response to a breakpoint_set command */
264290
export class BreakpointSetResponse extends Response {
265291
public breakpointId: number;
@@ -738,7 +764,7 @@ export class Connection extends DbgpConnection {
738764

739765
/**
740766
* Sends a breakpoint_set command that sets a breakpoint.
741-
* @param {Breakpoint} breakpoint - an instance of LineBreakpoint, ConditionalBreakpoint or ExceptionBreakpoint
767+
* @param {Breakpoint} breakpoint - an instance of LineBreakpoint, ConditionalBreakpoint, ExceptionBreakpoint or Watchpoint
742768
* @returns Promise.<BreakpointSetResponse>
743769
*/
744770
public async sendBreakpointSetCommand(breakpoint: Breakpoint): Promise<BreakpointSetResponse> {
@@ -760,6 +786,14 @@ export class Connection extends DbgpConnection {
760786
args += ` -n ${breakpoint.line}`;
761787
}
762788
data = breakpoint.expression;
789+
} else if (breakpoint instanceof Watchpoint) {
790+
data = breakpoint.variable;
791+
792+
// These placeholders are needed due to a bug on the server
793+
// They have no effect on the watchpoint functionality
794+
args += ` -f PLACEHOLDER`;
795+
args += ` -m PLACEHOLDER`;
796+
args += ` -n PLACEHOLDER`;
763797
}
764798
return new BreakpointSetResponse(await this._enqueueCommand("breakpoint_set", args, data), this);
765799
}

0 commit comments

Comments
 (0)