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
54 changes: 52 additions & 2 deletions src/IotjsDebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
'use strict';

import {
DebugSession, InitializedEvent, OutputEvent, Thread, Source,
DebugSession, Handles, InitializedEvent, OutputEvent, Thread, Scope, Source,
StoppedEvent, StackFrame, TerminatedEvent, Event, ErrorDestination
} from 'vscode-debugadapter';
import { DebugProtocol } from 'vscode-debugprotocol';
Expand All @@ -30,7 +30,7 @@ import { IAttachRequestArguments, ILaunchRequestArguments, SourceSendingOptions,
import { JerryDebuggerClient, JerryDebuggerOptions } from './JerryDebuggerClient';
import {
JerryDebugProtocolDelegate, JerryDebugProtocolHandler, JerryMessageScriptParsed, JerryEvalResult,
JerryMessageExceptionHit, JerryMessageBreakpointHit, JerryBacktraceResult
JerryMessageExceptionHit, JerryMessageBreakpointHit, JerryBacktraceResult, JerryScopeVariable, JerryScopeChain
} from './JerryProtocolHandler';
import { EVAL_RESULT_SUBTYPE, CLIENT as CLIENT_PACKAGE } from './JerryProtocolConstants';
import { Breakpoint } from './JerryBreakpoints';
Expand All @@ -48,6 +48,7 @@ class IotjsDebugSession extends DebugSession {
private _debuggerClient: JerryDebuggerClient;
private _protocolhandler: JerryDebugProtocolHandler;
private _sourceSendingOptions: SourceSendingOptions;
private _variableHandles = new Handles<string>();

public constructor() {
super();
Expand Down Expand Up @@ -451,6 +452,55 @@ class IotjsDebugSession extends DebugSession {
}
}


protected async scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments
): Promise<void> {
try {
const scopesArray: Array<JerryScopeChain> = await this._protocolhandler.requestScopes();
const scopes = new Array<Scope>();

for (const scope of scopesArray) {
scopes.push(new Scope(scope.name,
this._variableHandles.create(scope.variablesReference.toString()),
scope.expensive));
}

response.body = {
scopes: scopes
};

this.sendResponse(response);
} catch (error) {
this.log(error.message, LOG_LEVEL.ERROR);
this.sendErrorResponse(response, 0, (<Error>error).message);
}
}

protected async variablesRequest(response: DebugProtocol.VariablesResponse,
args: DebugProtocol.VariablesArguments
): Promise<void> {
try {
const variables = new Array<DebugProtocol.Variable>();
const id = this._variableHandles.get(args.variablesReference);
const scopeVariables: Array<JerryScopeVariable> = await this._protocolhandler.requestVariables(Number(id));

for (const variable of scopeVariables) {
variables.push({name: variable.name,
type: variable.type,
value: variable.value,
variablesReference: 0});
}

response.body = {
variables: variables
};
this.sendResponse(response);
} catch (error) {
this.log(error.message, LOG_LEVEL.ERROR);
this.sendErrorResponse(response, 0, (<Error>error).message);
}
}

protected customRequest(command: string, response: DebugProtocol.Response, args: any): void {
switch (command) {
case 'sendSource': {
Expand Down
34 changes: 31 additions & 3 deletions src/JerryProtocolConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
'use strict';

// Expected JerryScript debugger protocol version.
export const JERRY_DEBUGGER_VERSION = 6;
export const JERRY_DEBUGGER_VERSION = 7;

// Packages sent from the server to the client.
export enum SERVER {
Expand Down Expand Up @@ -47,7 +47,11 @@ export enum SERVER {
JERRY_DEBUGGER_EVAL_RESULT_END = 24,
JERRY_DEBUGGER_WAIT_FOR_SOURCE = 25,
JERRY_DEBUGGER_OUTPUT_RESULT = 26,
JERRY_DEBUGGER_OUTPUT_RESULT_END = 27
JERRY_DEBUGGER_OUTPUT_RESULT_END = 27,
JERRY_DEBUGGER_SCOPE_CHAIN = 28,
JERRY_DEBUGGER_SCOPE_CHAIN_END = 29,
JERRY_DEBUGGER_SCOPE_VARIABLES = 30,
JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31
}

// Subtypes of eval.
Expand Down Expand Up @@ -91,5 +95,29 @@ export enum CLIENT {
JERRY_DEBUGGER_FINISH = 15,
JERRY_DEBUGGER_GET_BACKTRACE = 16,
JERRY_DEBUGGER_EVAL = 17,
JERRY_DEBUGGER_EVAL_PART = 18
JERRY_DEBUGGER_EVAL_PART = 18,
JERRY_DEBUGGER_GET_SCOPE_CHAIN = 19,
JERRY_DEBUGGER_GET_SCOPE_VARIABLES = 20
}

// Types of scope chain.
export enum JERRY_DEBUGGER_SCOPE_TYPE {
JERRY_DEBUGGER_SCOPE_WITH = 1,
JERRY_DEBUGGER_SCOPE_LOCAL = 2,
JERRY_DEBUGGER_SCOPE_CLOSURE = 3,
JERRY_DEBUGGER_SCOPE_GLOBAL = 4,
JERRY_DEBUGGER_SCOPE_NON_CLOSURE = 5
}

// Type of scope variables.
export enum JERRY_DEBUGGER_SCOPE_VARIABLES {
JERRY_DEBUGGER_VALUE_NONE = 1,
JERRY_DEBUGGER_VALUE_UNDEFINED = 2,
JERRY_DEBUGGER_VALUE_NULL = 3,
JERRY_DEBUGGER_VALUE_BOOLEAN = 4,
JERRY_DEBUGGER_VALUE_NUMBER = 5,
JERRY_DEBUGGER_VALUE_STRING = 6,
JERRY_DEBUGGER_VALUE_FUNCTION = 7,
JERRY_DEBUGGER_VALUE_ARRAY = 8,
JERRY_DEBUGGER_VALUE_OBJECT = 9
}
166 changes: 165 additions & 1 deletion src/JerryProtocolHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ class PendingRequest {
}
}

export interface JerryScopeChain {
name: string;
variablesReference: number;
expensive: boolean;
}

export interface JerryScopeVariable {
name: string;
type: string;
value: string;
}

// abstracts away the details of the protocol
export class JerryDebugProtocolHandler {
public debuggerClient?: JerryDebuggerClient;
Expand All @@ -149,6 +161,8 @@ export class JerryDebugProtocolHandler {
private functions: FunctionMap = {};
private newFunctions: FunctionMap = {};
private backtraceData: JerryBacktraceResult = {totalFrames : 0, backtrace: []};
private scopeMessage?: Array<number> = [];
private scopeVariableMessage?: string = '';

private nextScriptID: number = 1;
private exceptionData?: Uint8Array;
Expand Down Expand Up @@ -197,7 +211,11 @@ export class JerryDebugProtocolHandler {
[SP.SERVER.JERRY_DEBUGGER_BACKTRACE_END]: this.onBacktrace,
[SP.SERVER.JERRY_DEBUGGER_EVAL_RESULT]: this.onEvalResult,
[SP.SERVER.JERRY_DEBUGGER_EVAL_RESULT_END]: this.onEvalResult,
[SP.SERVER.JERRY_DEBUGGER_WAIT_FOR_SOURCE]: this.onWaitForSource
[SP.SERVER.JERRY_DEBUGGER_WAIT_FOR_SOURCE]: this.onWaitForSource,
[SP.SERVER.JERRY_DEBUGGER_SCOPE_CHAIN]: this.onScopeChain,
[SP.SERVER.JERRY_DEBUGGER_SCOPE_CHAIN_END]: this.onScopeChainEnd,
[SP.SERVER.JERRY_DEBUGGER_SCOPE_VARIABLES]: this.onScopeVariables,
[SP.SERVER.JERRY_DEBUGGER_SCOPE_VARIABLES_END]: this.onScopeVariablesEnd
};

this.requestQueue = [];
Expand Down Expand Up @@ -586,6 +604,132 @@ export class JerryDebugProtocolHandler {
return this.backtraceData;
}

public onScopeChain(data: Uint8Array): void {
this.logPacket('ScopeChain');

for (let i = 1; i < data.byteLength; i++) {
this.scopeMessage.push(data[i]);
}
}

public onScopeChainEnd(data: Uint8Array): Array<JerryScopeChain> {
this.logPacket('ScopeChainEnd');

for (let i = 1; i < data.byteLength; i++) {
this.scopeMessage.push(data[i]);
}

const scopes: Array<JerryScopeChain> = [];
for (let i = 0; i < this.scopeMessage.length; i++) {
switch (this.scopeMessage[i]) {
case SP.JERRY_DEBUGGER_SCOPE_TYPE.JERRY_DEBUGGER_SCOPE_WITH: {
scopes.push({name: 'with', variablesReference: i, expensive: true});
break;
}
case SP.JERRY_DEBUGGER_SCOPE_TYPE.JERRY_DEBUGGER_SCOPE_LOCAL: {
scopes.push({name: 'local', variablesReference: i, expensive: true});
break;
}
case SP.JERRY_DEBUGGER_SCOPE_TYPE.JERRY_DEBUGGER_SCOPE_CLOSURE: {
scopes.push({name: 'closure', variablesReference: i, expensive: true});
break;
}
case SP.JERRY_DEBUGGER_SCOPE_TYPE.JERRY_DEBUGGER_SCOPE_GLOBAL: {
scopes.push({name: 'global', variablesReference: i, expensive: true});
break;
}
case SP.JERRY_DEBUGGER_SCOPE_TYPE.JERRY_DEBUGGER_SCOPE_NON_CLOSURE: {
scopes.push({name: 'catch', variablesReference: i, expensive: true});
break;
}
default: {
throw new Error('Invalid scope chain type!');
}
}
}
this.scopeMessage = [];

return scopes;
}

public onScopeVariables(data: Uint8Array): void {
this.logPacket('ScopeVariables');
for (let i = 1; i < data.byteLength; i++) {
this.scopeVariableMessage += String.fromCharCode(data[i]);
}
}

public onScopeVariablesEnd(data: Uint8Array): Array<JerryScopeVariable> {
this.logPacket('ScopeVariablesEnd');

for (let i = 1; i < data.byteLength; i++) {
this.scopeVariableMessage += String.fromCharCode(data[i]);
}

let buff_pos = 0;
const scopeVariablesArray: Array<JerryScopeVariable> = [];

while (buff_pos < this.scopeVariableMessage.length) {
let scopeVariable: JerryScopeVariable = {name: '', type: '', value: ''};

// Process name.
const name_length = this.scopeVariableMessage[buff_pos++].charCodeAt(0);
scopeVariable.name = this.scopeVariableMessage.substring(buff_pos, buff_pos + name_length);

buff_pos += name_length;

// Process type
const value_type: SP.JERRY_DEBUGGER_SCOPE_VARIABLES = this.scopeVariableMessage[buff_pos++].charCodeAt(0);
const value_length = this.scopeVariableMessage[buff_pos++].charCodeAt(0);

scopeVariable.value = this.scopeVariableMessage.substring(buff_pos, buff_pos + value_length);
buff_pos += value_length;

switch (value_type) {
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_UNDEFINED): {
scopeVariable.type = 'undefined';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_NULL): {
scopeVariable.type = 'Null';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_BOOLEAN): {
scopeVariable.type = 'Boolean';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_NUMBER): {
scopeVariable.type = 'Number';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_STRING): {
scopeVariable.type = 'String';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_FUNCTION): {
scopeVariable.type = 'Function';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_ARRAY): {
scopeVariable.type = 'Array';
scopeVariable.value = '[' + scopeVariable.value + ']';
break;
}
case (SP.JERRY_DEBUGGER_SCOPE_VARIABLES.JERRY_DEBUGGER_VALUE_OBJECT): {
scopeVariable.type = 'Object';
break;
}
default: {
throw new Error('Invalid scope variable type!');
}
}
scopeVariablesArray.push(scopeVariable);
}
this.scopeVariableMessage = '';

return scopeVariablesArray;
}

public onEvalResult(data: Uint8Array): JerryEvalResult {
this.logPacket('Eval Result');

Expand Down Expand Up @@ -776,6 +920,26 @@ export class JerryDebugProtocolHandler {
1]));
}

public requestScopes(): Promise<any> {
if (!this.lastBreakpointHit) {
return Promise.reject(new Error('scope chain not allowed while app running'));
}

return this.sendRequest(encodeMessage(this.byteConfig, 'B',
[SP.CLIENT.JERRY_DEBUGGER_GET_SCOPE_CHAIN]));
}

public requestVariables(level?: number): Promise<any> {
if (!this.lastBreakpointHit) {
return Promise.reject(new Error('scope variables not allowed while app running'));
}

return this.sendRequest(encodeMessage(this.byteConfig, 'BI',
[SP.CLIENT.JERRY_DEBUGGER_GET_SCOPE_VARIABLES,
level]));

}

logPacket(description: string, ignorable: boolean = false) {
// certain packets are ignored while evals are pending
const ignored = (ignorable && this.evalsPending) ? 'Ignored: ' : '';
Expand Down