@@ -2,7 +2,44 @@ import * as vscode from 'vscode';
22import * as dap from "./dap" ;
33import { Features } from './configuration' ;
44import { PgVariablesViewProvider } from './variables' ;
5- import { EvaluationError } from './error' ;
5+ import { PghhError } from './error' ;
6+
7+ /**
8+ * Evaluation of some expression resulted in some error
9+ */
10+ export class EvaluationError extends PghhError {
11+ constructor ( obj : string | Error ) {
12+ if ( obj instanceof Error ) {
13+ super ( obj . message ) ;
14+ this . stack = obj . stack ;
15+ } else {
16+ super ( obj ) ;
17+ }
18+
19+ this . name = 'EvaluationError' ;
20+ }
21+ }
22+
23+ /**
24+ * DAP request was send, but debugger can not handle it.
25+ *
26+ * You must note that this error is used even if we provided debugger
27+ * outdated descriptors (variablesReference/stackFrameId), because
28+ * this error must be handled only at the top-level, not ordinal Variable
29+ * logic (it must handle only EvaluationError).
30+ */
31+ export class DebuggerNotAvailableError extends PghhError {
32+ constructor ( obj : string | Error ) {
33+ if ( obj instanceof Error ) {
34+ super ( obj . message ) ;
35+ this . stack = obj . stack ;
36+ } else {
37+ super ( obj ) ;
38+ }
39+
40+ this . name = 'DebuggerNotAvailableError' ;
41+ }
42+ }
643
744/* Simple representation of a variable obtained from debugger */
845export interface IDebugVariable {
@@ -283,7 +320,7 @@ export interface IDebuggerFacade {
283320 * otherwise some debuggers will throw error in such cases
284321 * like CodeLLDB. Default - 'false'.
285322 * @returns Result of evaluation
286- * @throws @see {@link error. EvaluationError } if evaluation failed
323+ * @throws @see {@link EvaluationError } if evaluation failed
287324 */
288325 evaluate : ( expression : string , frameId : number | undefined ,
289326 context ?: string , noReturn ?: boolean ) => Promise < dap . EvaluateResponse > ;
@@ -454,6 +491,92 @@ export abstract class GenericDebuggerFacade implements IDebuggerFacade, vscode.D
454491
455492 return threadId ;
456493 }
494+
495+ /*
496+ * Debugger specific filter for errors because debugger
497+ * can not handle requests. It is not available in short.
498+ */
499+ abstract isDebuggerNotAvailableError ( message : string ) : boolean ;
500+ protected handleDebuggerError ( error : Error ) {
501+ /*
502+ * This is VS Code error thrown if we attempted to send
503+ * DAP request when no process is running.
504+ */
505+ if ( error . message . lastIndexOf ( 'process is running' ) !== - 1 ) {
506+ throw new DebuggerNotAvailableError ( error . message ) ;
507+ }
508+
509+ /*
510+ * Each DAP adapter has it's own error messages when it
511+ * failed to handle request.
512+ */
513+ if ( this . isDebuggerNotAvailableError ( error . message ) ) {
514+ throw new DebuggerNotAvailableError ( error . message ) ;
515+ }
516+ }
517+
518+ async evaluate ( expression : string , frameId : number | undefined , context ?: string ) {
519+ try {
520+ context ??= 'watch' ;
521+ const response : dap . EvaluateResponse = await this . getSession ( ) . customRequest ( 'evaluate' , {
522+ expression,
523+ context,
524+ frameId,
525+ } as dap . EvaluateArguments ) ;
526+
527+ return response ;
528+ } catch ( error ) {
529+ if ( error instanceof Error ) {
530+ this . handleDebuggerError ( error ) ;
531+ }
532+
533+ throw error ;
534+ }
535+ }
536+
537+ async getMembers ( variablesReference : number ) : Promise < dap . DebugVariable [ ] > {
538+ try {
539+ const response : dap . VariablesResponse = await this . getSession ( )
540+ . customRequest ( 'variables' , {
541+ variablesReference,
542+ } as dap . VariablesArguments ) ;
543+ return response . variables ;
544+ } catch ( error ) {
545+ if ( error instanceof Error ) {
546+ this . handleDebuggerError ( error ) ;
547+ }
548+
549+ throw error ;
550+ }
551+ }
552+
553+ async getVariables ( frameId : number ) : Promise < dap . DebugVariable [ ] > {
554+ try {
555+ const scopes = await this . getScopes ( frameId ) ;
556+ if ( scopes === undefined ) {
557+ return [ ] ;
558+ }
559+
560+ const variables : dap . DebugVariable [ ] = [ ] ;
561+
562+ /*
563+ * Show only Locals - not Registers. Also do not
564+ * use 'presentationHint' - it might be undefined
565+ * in old versions of VS Code.
566+ */
567+ for ( const scope of scopes . filter ( this . shouldShowScope ) ) {
568+ const members = await this . getMembers ( scope . variablesReference ) ;
569+ variables . push ( ...members ) ;
570+ }
571+ return variables ;
572+ } catch ( error ) {
573+ if ( error instanceof Error ) {
574+ this . handleDebuggerError ( error ) ;
575+ }
576+
577+ throw error ;
578+ }
579+ }
457580
458581 async getArrayVariables ( array : string , length : number ,
459582 frameId : number | undefined ) {
@@ -571,34 +694,6 @@ export abstract class GenericDebuggerFacade implements IDebuggerFacade, vscode.D
571694 } as dap . StackTraceArguments ) as dap . StackTraceResponse ;
572695 }
573696
574- async getMembers ( variablesReference : number ) : Promise < dap . DebugVariable [ ] > {
575- const response : dap . VariablesResponse = await this . getSession ( )
576- . customRequest ( 'variables' , {
577- variablesReference,
578- } as dap . VariablesArguments ) ;
579- return response . variables ;
580- }
581-
582- async getVariables ( frameId : number ) : Promise < dap . DebugVariable [ ] > {
583- const scopes = await this . getScopes ( frameId ) ;
584- if ( scopes === undefined ) {
585- return [ ] ;
586- }
587-
588- const variables : dap . DebugVariable [ ] = [ ] ;
589-
590- /*
591- * Show only Locals - not Registers. Also do not
592- * use 'presentationHint' - it might be undefined
593- * in old versions of VS Code.
594- */
595- for ( const scope of scopes . filter ( this . shouldShowScope ) ) {
596- const members = await this . getMembers ( scope . variablesReference ) ;
597- variables . push ( ...members ) ;
598- }
599- return variables ;
600- }
601-
602697 async getTopStackFrameId ( threadId : number ) : Promise < number | undefined > {
603698 const response : dap . StackTraceResponse = await this . getStackTrace ( threadId , 1 ) ;
604699 return response . stackFrames ?. [ 0 ] ?. id ;
@@ -656,7 +751,6 @@ export abstract class GenericDebuggerFacade implements IDebuggerFacade, vscode.D
656751 abstract readonly type : DebuggerType ;
657752 abstract maybeCalcFrameIndex ( frameId : number ) : number | undefined ;
658753 abstract shouldShowScope ( scope : dap . Scope ) : boolean ;
659- abstract evaluate ( expression : string , frameId : number | undefined , context ?: string ) : Promise < dap . EvaluateResponse > ;
660754 abstract extractVariableProperties ( dv : IDebugVariable ) : IVariableProperties ;
661755 abstract isNull ( variable : IDebugVariable | dap . EvaluateResponse ) : boolean ;
662756 abstract isValueStruct ( variable : IDebugVariable , type ?: string ) : boolean ;
@@ -702,14 +796,14 @@ export class CppDbgDebuggerFacade extends GenericDebuggerFacade {
702796 switchToManualArrayExpansion ( ) {
703797 this . getArrayVariables = super . getArrayVariables ;
704798 }
705-
799+
800+ isDebuggerNotAvailableError ( message : string ) {
801+ /* 'Cannot evaluate expression on the specified stack frame' */
802+ return message . startsWith ( 'Cannot evaluate expression' ) ;
803+ }
804+
706805 async evaluate ( expression : string , frameId : number | undefined , context ?: string ) {
707- context ??= 'watch' ;
708- const response : dap . EvaluateResponse = await this . getSession ( ) . customRequest ( 'evaluate' , {
709- expression,
710- context,
711- frameId,
712- } as dap . EvaluateArguments ) ;
806+ const response = await super . evaluate ( expression , frameId , context ) ;
713807
714808 if ( this . isFailedVar ( response ) ) {
715809 throw new EvaluationError ( response . result ) ;
@@ -718,15 +812,7 @@ export class CppDbgDebuggerFacade extends GenericDebuggerFacade {
718812 return response ;
719813 }
720814
721- async getMembers ( variablesReference : number ) : Promise < dap . DebugVariable [ ] > {
722- const response : dap . VariablesResponse = await this . getSession ( )
723- . customRequest ( 'variables' , {
724- variablesReference,
725- } as dap . VariablesArguments ) ;
726- return response . variables ;
727- }
728-
729- isFailedVar ( response : dap . EvaluateResponse ) : boolean {
815+ isFailedVar ( response : dap . EvaluateResponse ) {
730816 /*
731817 * gdb/mi has many error types for different operations.
732818 * In common - when error occurs 'result' has message in form
@@ -744,7 +830,25 @@ export class CppDbgDebuggerFacade extends GenericDebuggerFacade {
744830 */
745831 return response . result . startsWith ( '-var-create' ) ;
746832 }
747-
833+
834+ async getMembers ( variablesReference : number ) {
835+ const response = await super . getMembers ( variablesReference ) ;
836+ if ( ! response ?. length ) {
837+ /*
838+ * When debugger is not available and we requested members, then
839+ * cppdbg will not throw error, but it will return empty array.
840+ * This may seem bad, because we can not distinguish between
841+ * member-less object and error, but in real life this is actually
842+ * not a problem as this method will be invoked only in
843+ * variables view ('getChildren()') because we will not use such
844+ * structure.
845+ * Even if we will throw Error everything ok, because in that case
846+ * nothing will be displayed, just like for member-less array.
847+ */
848+ throw new DebuggerNotAvailableError ( `No members obtained from variablesReference: ${ variablesReference } ` ) ;
849+ }
850+ return response ;
851+ }
748852 private isNullInternal ( value : string ) {
749853 return value === '0x0' ;
750854 }
@@ -1008,10 +1112,56 @@ export class CodeLLDBDebuggerFacade extends GenericDebuggerFacade {
10081112 return scope . name === 'Local' ;
10091113 }
10101114
1011- async evaluate ( expression : string , frameId : number | undefined , context ?: string , noReturn ?: boolean ) : Promise < dap . EvaluateResponse > {
1115+ isDebuggerNotAvailableError ( message : string ) {
1116+ /*
1117+ * CodeLLDB error messages have format:
1118+ *
1119+ * Internal debugger error: DESCRIPTION
1120+ *
1121+ * where DESCRIPTION is actual error. It can be anything, but
1122+ * we do not check this and treat all such errors that debugger
1123+ * is not available. One example of such message is:
1124+ *
1125+ * Internal debugger error: Invalid variable reference: 1242
1126+ *
1127+ * or
1128+ *
1129+ * Internal debugger error: Invalid stack frame: 1001
1130+ */
1131+ return message . startsWith ( 'Internal debugger error' ) ;
1132+ }
1133+
1134+ handleCodeLLDBDebuggerError ( error : unknown ) {
1135+ /* DebuggerNotAvailableError may be already thrown by base class */
1136+ if ( ! ( error && error instanceof Error ) ) {
1137+ return ;
1138+ }
1139+
1140+ if ( error . name === 'CodeExpectedError' && ! ( error instanceof DebuggerNotAvailableError ) ) {
1141+ throw new EvaluationError ( error . message ) ;
1142+ }
1143+ }
1144+
1145+ async getVariables ( frameId : number ) : Promise < dap . DebugVariable [ ] > {
10121146 try {
1013- context ??= 'watch' ;
1147+ return await super . getVariables ( frameId ) ;
1148+ } catch ( err ) {
1149+ this . handleCodeLLDBDebuggerError ( err ) ;
1150+ throw err ;
1151+ }
1152+ }
1153+
1154+ async getMembers ( variablesReference : number ) {
1155+ try {
1156+ return await super . getMembers ( variablesReference ) ;
1157+ } catch ( err ) {
1158+ this . handleCodeLLDBDebuggerError ( err ) ;
1159+ throw err ;
1160+ }
1161+ }
10141162
1163+ async evaluate ( expression : string , frameId : number | undefined , context ?: string , noReturn ?: boolean ) : Promise < dap . EvaluateResponse > {
1164+ try {
10151165 /*
10161166 * CodeLLDB has many expression evaluators: simple, python and native.
10171167 * https://github.com/vadimcn/codelldb/blob/master/MANUAL.md#expressions
@@ -1020,29 +1170,27 @@ export class CodeLLDBDebuggerFacade extends GenericDebuggerFacade {
10201170 * so add '/nat' for each expression for sure.
10211171 */
10221172 expression = `/nat ${ expression } ` ;
1023- return await this . getSession ( ) . customRequest ( 'evaluate' , {
1024- expression,
1025- context,
1026- frameId,
1027- } as dap . EvaluateArguments ) ;
1173+ return await super . evaluate ( expression , frameId , context ) ;
10281174 } catch ( err ) {
1029- if ( err instanceof Error ) {
1030- if ( noReturn && err . message === 'unknown error' ) {
1031- /*
1032- * CodeLLDB don't like 'void' returning expressions and
1033- * throws such strange errors, but call actually succeeds
1034- */
1035- return {
1036- memoryReference : '' ,
1037- result : '' ,
1038- type : '' ,
1039- variablesReference : - 1 ,
1040- } ;
1041- }
1175+ if ( ! ( err instanceof Error ) ) {
1176+ throw err ;
1177+ }
10421178
1043- throw new EvaluationError ( err . message ) ;
1179+ if ( noReturn && err . message === 'unknown error' ) {
1180+ /*
1181+ * CodeLLDB don't like 'void' returning expressions and
1182+ * throws such strange errors, but call actually succeeds
1183+ */
1184+ return {
1185+ memoryReference : '' ,
1186+ result : '' ,
1187+ type : '' ,
1188+ variablesReference : - 1 ,
1189+ } ;
10441190 }
10451191
1192+ this . handleCodeLLDBDebuggerError ( err ) ;
1193+
10461194 throw err ;
10471195 }
10481196 }
0 commit comments