diff --git a/src/lib/cdt-controller.ts b/src/lib/cdt-controller.ts index af32ce9..db8bc0a 100644 --- a/src/lib/cdt-controller.ts +++ b/src/lib/cdt-controller.ts @@ -36,6 +36,8 @@ export class CDTController { // set, and before issuing further commands to the debugger public proxyServer?: ChromeDevToolsProxyServer; private scripts: Array = []; + private pendingException: boolean = false; + private pendingExceptionStr?: string; private pendingBreakpoint?: Breakpoint; // stores resolve and reject functions from promised evaluations private evalResolvers: Array = []; @@ -62,12 +64,23 @@ export class CDTController { // this can happen before the proxy is connected if (this.proxyServer) { this.pendingBreakpoint = message.breakpoint; + this.pendingException = false; + this.protocolHandler!.requestBacktrace(); + } + } + + onExceptionHit(message: JerryMessageBreakpointHit, exception?: string) { + // this can happen before the proxy is connected + if (this.proxyServer) { + this.pendingBreakpoint = message.breakpoint; + this.pendingException = true; + this.pendingExceptionStr = exception; this.protocolHandler!.requestBacktrace(); } } onBacktrace(backtrace: Array) { - this.sendPaused(this.pendingBreakpoint, backtrace); + this.sendPaused(this.pendingBreakpoint, backtrace, this.pendingException, this.pendingExceptionStr); this.pendingBreakpoint = undefined; } @@ -215,10 +228,11 @@ export class CDTController { } // 'report' functions are events from Debugger to CDT - private sendPaused(breakpoint: Breakpoint | undefined, backtrace: Array) { + private sendPaused(breakpoint: Breakpoint | undefined, backtrace: Array, + isException: boolean, exception?: string) { // Node uses 'Break on start' but this is not allowable in crdp.d.ts - const reason = breakpoint ? 'debugCommand' : 'other'; - this.proxyServer!.sendPaused(breakpoint, backtrace, reason); + const reason = isException ? 'exception' : (breakpoint ? 'debugCommand' : 'other'); + this.proxyServer!.sendPaused(breakpoint, backtrace, reason, exception); } /** diff --git a/src/lib/cdt-proxy.ts b/src/lib/cdt-proxy.ts index a73bcaf..5bfe391 100644 --- a/src/lib/cdt-proxy.ts +++ b/src/lib/cdt-proxy.ts @@ -23,6 +23,9 @@ import { Breakpoint } from './breakpoint'; import { onHttpRequest } from './cdt-proxy-http'; import { JerryMessageScriptParsed } from './protocol-handler'; +export type PausedReason = 'exception' | 'debugCommand' | 'other'; +export type PauseForExceptions = 'none' | 'uncaught' | 'all'; + export interface CDTDelegate { requestScripts: () => void; requestBreakpoint: () => void; @@ -63,7 +66,7 @@ export class ChromeDevToolsProxyServer { readonly uuid: string; readonly jsfile: string; private skipAllPauses: boolean = false; - private pauseOnExceptions: ('none' | 'uncaught' | 'all') = 'none'; + private pauseOnExceptions: PauseForExceptions = 'none'; private asyncCallStackDepth: number = 0; // 0 is unlimited private delegate: CDTDelegate; private api: Crdp.CrdpServer; @@ -186,7 +189,7 @@ export class ChromeDevToolsProxyServer { * Sends Debugger.paused event for the current debugger location */ sendPaused(breakpoint: Breakpoint | undefined, backtrace: Array, - reason: 'exception' | 'debugCommand' | 'other') { + reason: PausedReason, exception?: string) { const callFrames: Array = []; let nextFrameId = 0; for (const bp of backtrace) { @@ -209,10 +212,12 @@ export class ChromeDevToolsProxyServer { hitBreakpoints.push(String(breakpoint.activeIndex)); } + const data = exception ? { message: exception } : undefined; this.api.Debugger.emitPaused({ - hitBreakpoints, - reason, callFrames, + reason, + data, + hitBreakpoints, }); } diff --git a/src/lib/protocol-handler.ts b/src/lib/protocol-handler.ts index a19a49a..c35978c 100644 --- a/src/lib/protocol-handler.ts +++ b/src/lib/protocol-handler.ts @@ -40,6 +40,7 @@ export interface JerryDebugProtocolDelegate { onBacktrace?(backtrace: Array): void; onBreakpointHit?(message: JerryMessageBreakpointHit): void; onEvalResult?(subType: number, result: string): void; + onExceptionHit?(message: JerryMessageBreakpointHit, exception?: string): void; onError?(code: number, message: string): void; onResume?(): void; onScriptParsed?(message: JerryMessageScriptParsed): void; @@ -96,13 +97,14 @@ export class JerryDebugProtocolHandler { private sourceNameData?: Uint8Array; private functionName?: string; private functionNameData?: Uint8Array; + private exceptionStr?: string; + private exceptionData?: Uint8Array; + private backtrace: Array = []; private evalResultData?: Uint8Array; private functions: FunctionMap = {}; private newFunctions: FunctionMap = {}; - private backtrace: Array = []; private nextScriptID: number = 1; - private exceptionData?: Uint8Array; private evalsPending: number = 0; private lastBreakpointHit?: Breakpoint; private lastBreakpointExact: boolean = true; @@ -131,6 +133,9 @@ export class JerryDebugProtocolHandler { [SP.JERRY_DEBUGGER_FUNCTION_NAME_END]: this.onFunctionName, [SP.JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP]: this.onReleaseByteCodeCP, [SP.JERRY_DEBUGGER_BREAKPOINT_HIT]: this.onBreakpointHit, + [SP.JERRY_DEBUGGER_EXCEPTION_HIT]: this.onBreakpointHit, + [SP.JERRY_DEBUGGER_EXCEPTION_STR]: this.onExceptionStr, + [SP.JERRY_DEBUGGER_EXCEPTION_STR_END]: this.onExceptionStr, [SP.JERRY_DEBUGGER_BACKTRACE]: this.onBacktrace, [SP.JERRY_DEBUGGER_BACKTRACE_END]: this.onBacktrace, [SP.JERRY_DEBUGGER_EVAL_RESULT]: this.onEvalResult, @@ -435,9 +440,8 @@ export class JerryDebugProtocolHandler { if (data[0] === SP.JERRY_DEBUGGER_EXCEPTION_HIT) { console.log('Exception throw detected'); - if (this.exceptionData) { - console.log('Exception hint:', cesu8ToString(this.exceptionData)); - this.exceptionData = undefined; + if (this.exceptionStr) { + console.log('Exception hint:', this.exceptionStr); } } @@ -452,9 +456,24 @@ export class JerryDebugProtocolHandler { const atAround = breakpointRef.exact ? 'at' : 'around'; console.log(`Stopped ${atAround} ${breakpointInfo}${breakpoint}`); - // TODO: handle exception case differently - if (this.delegate.onBreakpointHit) { - this.delegate.onBreakpointHit(breakpointRef); + if (data[0] === SP.JERRY_DEBUGGER_EXCEPTION_HIT) { + // TODO: handle exception case differently + if (this.delegate.onExceptionHit) { + this.delegate.onExceptionHit(breakpointRef, this.exceptionStr); + } + } else { + if (this.delegate.onBreakpointHit) { + this.delegate.onBreakpointHit(breakpointRef); + } + } + } + + onExceptionStr(data: Uint8Array) { + this.logPacket('Exception Str'); + this.exceptionData = assembleUint8Arrays(this.evalResultData, data); + if (data[0] === SP.JERRY_DEBUGGER_EXCEPTION_STR_END) { + this.exceptionStr = cesu8ToString(this.exceptionData); + this.exceptionData = undefined; } }