Skip to content

Commit

Permalink
First time breakpoints can be sent over protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdp committed Aug 4, 2020
1 parent c435238 commit 0bbb978
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 14 deletions.
5 changes: 4 additions & 1 deletion src/adapters/DebugProtocolAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Debugger, ProtocolVersionDetails } from '../debugProtocol/Debugger';
import { Debugger, ProtocolVersionDetails, BreakpointRequestObject } from '../debugProtocol/Debugger';
import * as eol from 'eol';
import * as EventEmitter from 'events';
import { Socket } from 'net';
Expand Down Expand Up @@ -533,6 +533,9 @@ export class DebugProtocolAdapter {
}
}

public async addBreakpoints(breakpoints: Array<BreakpointRequestObject> = []) {
return await this.socketDebugger.addBreakpoints(breakpoints);
}
/**
* Disconnect from the telnet session and unset all objects
*/
Expand Down
40 changes: 37 additions & 3 deletions src/debugProtocol/Debugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from './responses';
import { PROTOCOL_ERROR_CODES, COMMANDS, STEP_TYPE } from './Constants';
import { SmartBuffer } from 'smart-buffer';
import { ListOrAddBreakpointsResponse } from './responses/ListOrAddBreakpointsResponse';

export class Debugger {

Expand Down Expand Up @@ -252,12 +253,31 @@ export class Debugger {
return -1;
}

public async getBreakpointsList() {
return this.makeRequest(new SmartBuffer({ size: 12 }), COMMANDS.LIST_BREAKPOINTS);
}

public async addBreakpoints(breakpoints: Array<BreakpointRequestObject> = []) {
const numBreakpoints = breakpoints.length;
if (numBreakpoints > 0) {
let buffer = new SmartBuffer();
buffer.writeUInt32LE(numBreakpoints); // num_breakpoints - The number of breakpoints in the breakpoints array.
breakpoints.forEach((breakpoint) => {
buffer.writeStringNT(breakpoint.filePath); // file_path - The path of the source file where the breakpoint is to be inserted.
buffer.writeUInt32LE(breakpoint.lineNumber); // line_number - The line number in the channel application code where the breakpoint is to be executed.
buffer.writeUInt32LE(breakpoint.hitCount); // ignore_count - The number of times to ignore the breakpoint condition before executing the breakpoint. This number is decremented each time the channel application reaches the breakpoint.
});
return this.makeRequest(buffer, COMMANDS.ADD_BREAKPOINTS);
}
return -1;
}

private async makeRequest(buffer: SmartBuffer, command: COMMANDS, extraData?) {
this.totalRequests++;
let requestId = this.totalRequests;
buffer.insertUInt32LE(command, 0); // command_code
buffer.insertUInt32LE(requestId, 0); // request_id
buffer.insertUInt32LE(buffer.writeOffset + 4, 0); // packet_length
buffer.insertUInt32LE(command, 0); // command_code - An enum representing the debugging command being sent. See the COMMANDS enum
buffer.insertUInt32LE(requestId, 0); // request_id - The ID of the debugger request (must be >=1). This ID is included in the debugger response.
buffer.insertUInt32LE(buffer.writeOffset + 4, 0); // packet_length - The size of the packet to be sent.

this.activeRequests[requestId] = {
commandType: command,
Expand Down Expand Up @@ -297,6 +317,14 @@ export class Debugger {
return true;
}

if (commandType === COMMANDS.LIST_BREAKPOINTS || commandType === COMMANDS.ADD_BREAKPOINTS) {
let debuggerListOrAddBreakpointsResponse = new ListOrAddBreakpointsResponse(unhandledData);
if (debuggerListOrAddBreakpointsResponse.success) {
this.removedProcessedBytes(debuggerListOrAddBreakpointsResponse, unhandledData);
return true;
}
}

if (commandType === COMMANDS.VARIABLES) {
let debuggerVariableRequestResponse = new VariableResponse(unhandledData);
if (debuggerVariableRequestResponse.success) {
Expand Down Expand Up @@ -498,6 +526,12 @@ export interface ProtocolVersionDetails {
errorCode: PROTOCOL_ERROR_CODES;
}

export interface BreakpointRequestObject {
filePath: string;
lineNumber: number;
hitCount: number;
}

export interface ConstructorOptions {
/**
* The host/ip address of the Roku
Expand Down
65 changes: 65 additions & 0 deletions src/debugProtocol/responses/ListOrAddBreakpointsResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { SmartBuffer } from 'smart-buffer';
import { ERROR_CODES } from '../Constants';

export class ListOrAddBreakpointsResponse {

constructor(buffer: Buffer) {
// The minimum size of a connect to IO port request
if (buffer.byteLength >= 8) {
try {
let bufferReader = SmartBuffer.fromBuffer(buffer);
this.requestId = bufferReader.readUInt32LE(); // request_id

// Any request id less then one is an update and we should not process it here
if (this.requestId > 0) {
this.errorCode = ERROR_CODES[bufferReader.readUInt32LE()];
this.numBreakpoints = bufferReader.readUInt32LE(); // num_breakpoints - The number of breakpoints in the breakpoints array.

// iterate over each variable in the buffer data and create a Variable Info object
for (let i = 0; i < this.numBreakpoints; i++) {
let breakpointInfo = new BreakpointInfo(bufferReader);
if (breakpointInfo.success) {
// All the necessary variable data was present. Push to the variables array.
this.breakpoints.push(breakpointInfo);
}
}

this.readOffset = bufferReader.readOffset;
this.success = (this.breakpoints.length === this.numBreakpoints);
}
} catch (error) {
// Could not parse
}
}
}
public success = false;
public readOffset = 0;

// response fields
public requestId = -1;
public numBreakpoints: number;
public breakpoints = [];
public data = -1;
public errorCode: string;
}

export class BreakpointInfo {
constructor(bufferReader: SmartBuffer) {
// breakpoint_id - The ID assigned to the breakpoint. An ID greater than 0 indicates an active breakpoint. An ID of 0 denotes that the breakpoint has an error.
this.breakpointId = bufferReader.readUInt32LE();
// error_code - Indicates whether the breakpoint was successfully returned.
this.errorCode = ERROR_CODES[bufferReader.readUInt32LE()];

if (this.breakpointId > 0) {
// This argument is only present if the breakpoint_id is valid.
// ignore_count - Current state, decreases as breakpoint is executed.
this.hitCount = bufferReader.readUInt32LE();
}
this.success = true;
}

public success = false;
public breakpointId: number;
public errorCode: string;
public hitCount: number;
}
68 changes: 58 additions & 10 deletions src/debugSession/BrightScriptDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { standardizePath as s } from '../FileUtils';
import { EvaluateContainer, DebugProtocolAdapter } from '../adapters/DebugProtocolAdapter';
import { TelnetAdapter } from '../adapters/TelnetAdapter';
import { BrightScriptDebugCompileError } from '../CompileErrorProcessor';
import { fileUtils } from '../FileUtils';
import {
LaunchStartEvent,
LogOutputEvent,
Expand All @@ -37,6 +38,7 @@ import { FileManager } from '../managers/FileManager';
import { SourceMapManager } from '../managers/SourceMapManager';
import { LocationManager } from '../managers/LocationManager';
import { BreakpointManager } from '../managers/BreakpointManager';
import { BreakpointRequestObject } from '../debugProtocol/Debugger';

export class BrightScriptDebugSession extends BaseDebugSession {
public constructor() {
Expand Down Expand Up @@ -425,17 +427,29 @@ export class BrightScriptDebugSession extends BaseDebugSession {
/**
* Called every time a breakpoint is created, modified, or deleted, for each file. This receives the entire list of breakpoints every time.
*/
public setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments) {
let sanitizedBreakpoints = this.breakpointManager.replaceBreakpoints(args.source.path, args.breakpoints);
//sort the breakpoints
var sortedAndFilteredBreakpoints = orderBy(sanitizedBreakpoints, [x => x.line, x => x.column])
//filter out the inactive breakpoints
.filter(x => x.isHidden === false);
public async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments) {
if (!this.rokuAdapter.connected) {
let sanitizedBreakpoints = this.breakpointManager.replaceBreakpoints(args.source.path, args.breakpoints);
//sort the breakpoints
var sortedAndFilteredBreakpoints = orderBy(sanitizedBreakpoints, [x => x.line, x => x.column])
//filter out the inactive breakpoints
.filter(x => x.isHidden === false);

response.body = {
breakpoints: sortedAndFilteredBreakpoints
};
this.sendResponse(response);
response.body = {
breakpoints: sortedAndFilteredBreakpoints
};
this.sendResponse(response);
} else {
for (let breakpoint of args.breakpoints) {
let breakpoints = await this.getBreakpointRequests(args.source.path, breakpoint, this.projectManager.mainProject, 'pkg:');

for (let project of this.projectManager.componentLibraryProjects) {
breakpoints = breakpoints.concat(await this.getBreakpointRequests(args.source.path, breakpoint, project, 'libpkg:'));
}
console.log(breakpoints);
let result = await (this.rokuAdapter as DebugProtocolAdapter).addBreakpoints(breakpoints);
}
}

//set a small timeout so the user sees the breakpoints disappear before reappearing
//This is disabled because I'm not sure anyone actually wants this functionality, but I didn't want to lose it.
Expand All @@ -453,6 +467,40 @@ export class BrightScriptDebugSession extends BaseDebugSession {
// }, 100);
}

private async getBreakpointRequests(path: string, breakpoint: DebugProtocol.SourceBreakpoint, project: ComponentLibraryProject | Project, fileProtocol: string) {
let breakpoints = [];
console.log(project);
let stagingLocationsResult = await this.locationManager.getStagingLocations(
path,
breakpoint.line,
breakpoint.column,
[
...project.sourceDirs,
project.rootDir
],
project.stagingFolderPath,
project.fileMappings
);

for (let stagingLocation of stagingLocationsResult.locations) {
let relativeStagingPath = fileUtils.replaceCaseInsensitive(
stagingLocation.filePath,
fileUtils.standardizePath(
fileUtils.removeTrailingSlash(project.stagingFolderPath) + '/'
),
''
);

let breakpointRequest: BreakpointRequestObject = {
filePath: fileProtocol + '/' + relativeStagingPath,
lineNumber: breakpoint.line,
hitCount: breakpoint.hitCondition ? parseInt(breakpoint.hitCondition) : 0
};
breakpoints.push(breakpointRequest);
}
return breakpoints;
}

protected async exceptionInfoRequest(response: DebugProtocol.ExceptionInfoResponse, args: DebugProtocol.ExceptionInfoArguments) {
util.logDebug('exceptionInfoRequest');
}
Expand Down

0 comments on commit 0bbb978

Please sign in to comment.