@@ -20,6 +20,7 @@ import { getConfiguredEnvironment } from './envfile'
2020import { XdebugCloudConnection } from './cloud'
2121import { shouldIgnoreException } from './ignore'
2222import { varExportProperty } from './varExport'
23+ import { supportedEngine } from './xdebugUtils'
2324
2425if ( process . env [ 'VSCODE_NLS_CONFIG' ] ) {
2526 try {
@@ -249,6 +250,7 @@ class PhpDebugSession extends vscode.DebugSession {
249250 supportTerminateDebuggee : true ,
250251 supportsDelayedStackTraceLoading : false ,
251252 supportsClipboardContext : true ,
253+ supportsExceptionInfoRequest : true ,
252254 }
253255 this . sendResponse ( response )
254256 }
@@ -522,29 +524,24 @@ class PhpDebugSession extends vscode.DebugSession {
522524
523525 // support for breakpoints
524526 let feat : xdebug . FeatureGetResponse
525- const supportedEngine =
526- initPacket . engineName === 'Xdebug' &&
527- semver . valid ( initPacket . engineVersion , { loose : true } ) &&
528- semver . gte ( initPacket . engineVersion , '3.0.0' , { loose : true } )
529- const supportedEngine32 =
530- initPacket . engineName === 'Xdebug' &&
531- semver . valid ( initPacket . engineVersion , { loose : true } ) &&
532- semver . gte ( initPacket . engineVersion , '3.2.0' , { loose : true } )
527+ const supportedEngine30 = supportedEngine ( initPacket , '3.0.0' )
528+ const supportedEngine32 = supportedEngine ( initPacket , '3.2.0' )
529+ const supportedEngine35 = supportedEngine ( initPacket , '3.5.0' )
533530 if (
534- supportedEngine ||
531+ supportedEngine30 ||
535532 ( ( feat = await connection . sendFeatureGetCommand ( 'resolved_breakpoints' ) ) && feat . supported === '1' )
536533 ) {
537534 await connection . sendFeatureSetCommand ( 'resolved_breakpoints' , '1' )
538535 }
539536 if (
540- supportedEngine ||
537+ supportedEngine30 ||
541538 ( ( feat = await connection . sendFeatureGetCommand ( 'notify_ok' ) ) && feat . supported === '1' )
542539 ) {
543540 await connection . sendFeatureSetCommand ( 'notify_ok' , '1' )
544541 connection . on ( 'notify_user' , ( notify : xdebug . UserNotify ) => this . handleUserNotify ( notify , connection ) )
545542 }
546543 if (
547- supportedEngine ||
544+ supportedEngine30 ||
548545 ( ( feat = await connection . sendFeatureGetCommand ( 'extended_properties' ) ) && feat . supported === '1' )
549546 ) {
550547 await connection . sendFeatureSetCommand ( 'extended_properties' , '1' )
@@ -556,6 +553,12 @@ class PhpDebugSession extends vscode.DebugSession {
556553 ) {
557554 await connection . sendFeatureSetCommand ( 'breakpoint_include_return_value' , '1' )
558555 }
556+ if (
557+ supportedEngine35 ||
558+ ( ( feat = await connection . sendFeatureGetCommand ( 'virtual_exception_value' ) ) && feat . supported === '1' )
559+ ) {
560+ await connection . sendFeatureSetCommand ( 'virtual_exception_value' , '1' )
561+ }
559562
560563 // override features from launch.json
561564 try {
@@ -1529,6 +1532,79 @@ class PhpDebugSession extends vscode.DebugSession {
15291532 this . sendResponse ( response )
15301533 }
15311534 }
1535+
1536+ protected async exceptionInfoRequest (
1537+ response : VSCodeDebugProtocol . ExceptionInfoResponse ,
1538+ args : VSCodeDebugProtocol . ExceptionInfoArguments
1539+ ) : Promise < void > {
1540+ const connection = this . _connections . get ( args . threadId )
1541+ if ( ! connection ) {
1542+ throw new Error ( 'Unknown thread ID' )
1543+ }
1544+
1545+ const status = this . _statuses . get ( connection ) !
1546+
1547+ let ex : VSCodeDebugProtocol . ExceptionDetails | undefined
1548+
1549+ if ( connection . featureSet ( 'virtual_exception_value' ) ) {
1550+ let old_max_depth : number = 3
1551+ try {
1552+ const { stack } = await connection . sendStackGetCommand ( ) // CACHE?
1553+
1554+ if ( stack . length > 0 ) {
1555+ const ctx = await stack [ 0 ] . getContexts ( ) // CACHE
1556+ old_max_depth = < number > connection . featureSet ( 'max_depth' ) ?? 1
1557+ if ( old_max_depth < 3 ) {
1558+ await connection . sendFeatureSetCommand ( 'max_depth' , 3 )
1559+ }
1560+
1561+ const res = await connection . sendPropertyGetNameCommand ( '$__EXCEPTION' , ctx [ 0 ] )
1562+
1563+ const s = res . property . children . find (
1564+ p => p . name === 'trace' || p . name === '*Exception*trace' || p . name === '*Error*trace'
1565+ )
1566+
1567+ const at = `Created at ${ res . property . children . find ( p => p . name == 'file' ) ?. value ?? '**UNKNOWN**' } :${
1568+ res . property . children . find ( p => p . name == 'line' ) ?. value ?? '?'
1569+ } \n`
1570+ const st = s ?. children
1571+ . map (
1572+ ( t , i ) =>
1573+ `at ${
1574+ t . children . find ( p => p . name == 'class' ) ?. value ?? '' } ${
1575+ t . children . find ( p => p . name == 'type' ) ?. value ?? ''
1576+ } ${ t . children . find ( p => p . name == 'function' ) ?. value ?? '?' } () (${
1577+ t . children . find ( p => p . name == 'file' ) ?. value ?? '**UNKNOWN**' } :${
1578+ t . children . find ( p => p . name == 'line' ) ?. value ?? '?'
1579+ } )`
1580+ )
1581+ . join ( '\n' )
1582+ ex = {
1583+ message : res . property . children . find ( p => p . name === 'message' ) ?. value ?? undefined ,
1584+ typeName : res . property . class ,
1585+ fullTypeName : res . property . class ,
1586+ evaluateName : '$__EXCEPTION' ,
1587+ stackTrace : `${ at } ${ st } ` ,
1588+ // TODO process inner/previous exception
1589+ }
1590+ }
1591+ } catch {
1592+ // Probably $__EXCEPTION not present
1593+ } finally {
1594+ if ( old_max_depth < 3 ) {
1595+ await connection . sendFeatureSetCommand ( 'max_depth' , old_max_depth )
1596+ }
1597+ }
1598+ }
1599+
1600+ response . body = {
1601+ exceptionId : status . exception . name ,
1602+ breakMode : 'always' ,
1603+ description : status . exception . message ,
1604+ details : ex ,
1605+ }
1606+ this . sendResponse ( response )
1607+ }
15321608}
15331609
15341610vscode . DebugSession . run ( PhpDebugSession )
0 commit comments