@@ -27,6 +27,7 @@ import { getArrayProperties } from './templates/getArrayProperties';
2727import { getArraySlots } from './templates/getArraySlots' ;
2828import { getNodeChildren } from './templates/getNodeChildren' ;
2929import {
30+ DescriptionSymbols ,
3031 getCustomProperties ,
3132 getDescriptionSymbols ,
3233 getStringyProps ,
@@ -174,6 +175,8 @@ interface IContextInit {
174175 presentationHint ?: Dap . VariablePresentationHint ;
175176 /** How this variable should be sorted in results, in ascending numeric order. */
176177 sortOrder ?: number ;
178+ /** Indicates if this variable represents a custom property from debug.properties. */
179+ isCustomProperty ?: boolean ;
177180}
178181
179182interface IContextSettings {
@@ -195,6 +198,10 @@ class VariableContext {
195198 public readonly presentationHint ?: Dap . VariablePresentationHint ;
196199 /** Sort order set from the parent. */
197200 public readonly sortOrder : number ;
201+ /**
202+ * Indicates if this variable represents a custom property from debug.properties.
203+ */
204+ public readonly isCustomProperty ?: boolean ;
198205
199206 public get customDescriptionGenerator ( ) {
200207 return this . settings . customDescriptionGenerator ;
@@ -213,6 +220,7 @@ class VariableContext {
213220 this . name = ctx . name ;
214221 this . presentationHint = ctx . presentationHint ;
215222 this . sortOrder = ctx . sortOrder || SortOrder . Default ;
223+ this . isCustomProperty = ctx . isCustomProperty ;
216224 }
217225
218226 /**
@@ -355,7 +363,6 @@ class VariableContext {
355363 ) : Promise < Variable [ ] > {
356364 const properties : ( Promise < Variable [ ] > | Variable [ ] ) [ ] = [ ] ;
357365 let originalObject : Cdp . Runtime . RemoteObject | undefined ;
358- let hasCustomProperties = false ;
359366
360367 if ( this . settings . customPropertiesGenerator ) {
361368 const { result, errorDescription } = await this . evaluateCodeForObject (
@@ -394,40 +401,33 @@ class VariableContext {
394401 ) ;
395402 if ( ! accessorsProperties || ! ownProperties ) return [ ] ;
396403
397- // Check for Symbol.for("debug.properties") custom property replacement
398- // Only do this if we haven't already applied customPropertiesGenerator and if not explicitly skipped
399- if ( ! this . settings . customPropertiesGenerator && ! skipSymbolBasedCustomProperties ) {
400- // Check if the object has Symbol.for("debug.properties") by looking for it in the properties
401- const hasDebugPropertiesSymbol = [ ...accessorsProperties . result , ...ownProperties . result ]
402- . some (
403- p => p . symbol ?. description === 'Symbol(debug.properties)' ,
404- ) ;
404+ if (
405+ ! this . settings . customPropertiesGenerator && stringyProps . customProps
406+ && ! skipSymbolBasedCustomProperties
407+ ) {
408+ try {
409+ const customPropsResult = await this . cdp . Runtime . callFunctionOn ( {
410+ functionDeclaration : getCustomProperties . decl ( ) ,
411+ arguments : [ await this . getDescriptionSymbols ( object . objectId ) ] ,
412+ objectId : object . objectId ,
413+ throwOnSideEffect : true ,
414+ } ) ;
405415
406- if ( hasDebugPropertiesSymbol ) {
407- try {
408- const customPropsResult = await this . cdp . Runtime . callFunctionOn ( {
409- functionDeclaration : getCustomProperties . decl ( ) ,
410- arguments : [ await this . getDescriptionSymbols ( object . objectId ) ] ,
411- objectId : object . objectId ,
412- throwOnSideEffect : true ,
413- } ) ;
414-
415- if ( customPropsResult && customPropsResult . result . objectId ) {
416- // Store the original object for the escape hatch
417- originalObject = object ;
418- hasCustomProperties = true ;
419- // Replace with the custom properties object and re-fetch its properties
420- object = customPropsResult . result ;
421-
422- // Re-fetch properties for the custom properties object
423- [ accessorsProperties , ownProperties , stringyProps ] = await this . fetchObjectProperties (
424- object . objectId ! ,
425- ) ;
426- if ( ! accessorsProperties || ! ownProperties ) return [ ] ;
427- }
428- } catch {
429- // If anything goes wrong, just use the original object
416+ if ( customPropsResult && customPropsResult . result . objectId ) {
417+ // Store the original object for the escape hatch
418+ originalObject = object ;
419+
420+ // Replace with the custom properties object and re-fetch its properties
421+ object = customPropsResult . result ;
422+
423+ // Re-fetch properties for the custom properties object
424+ [ accessorsProperties , ownProperties , stringyProps ] = await this . fetchObjectProperties (
425+ object . objectId ! ,
426+ ) ;
427+ if ( ! accessorsProperties || ! ownProperties ) return [ ] ;
430428 }
429+ } catch {
430+ // If anything goes wrong, just use the original object
431431 }
432432 }
433433
@@ -462,15 +462,22 @@ class VariableContext {
462462 // Push own properties & accessors and symbols
463463 for ( const propertiesCollection of [ propertiesMap . values ( ) , propertySymbols . values ( ) ] ) {
464464 for ( const p of propertiesCollection ) {
465- const contextInit = hasCustomProperties
466- ? { presentationHint : { kind : 'virtual' as const } }
465+ if ( 'symbol' in p && p . symbol ?. description === `Symbol(${ DescriptionSymbols . Properties } )` ) {
466+ continue ;
467+ }
468+
469+ const contextInit = originalObject
470+ ? {
471+ presentationHint : { kind : 'virtual' as const } ,
472+ isCustomProperty : ! ! originalObject ,
473+ }
467474 : undefined ;
468475 properties . push (
469476 this . createPropertyVar (
470477 p ,
471478 object ,
472- stringyProps ?. hasOwnProperty ( p . name )
473- ? localizeIndescribable ( stringyProps [ p . name ] )
479+ stringyProps . out ?. hasOwnProperty ( p . name )
480+ ? localizeIndescribable ( stringyProps . out [ p . name ] )
474481 : undefined ,
475482 contextInit ,
476483 ) ,
@@ -479,14 +486,19 @@ class VariableContext {
479486 }
480487
481488 for ( const property of ownProperties . privateProperties ?? [ ] ) {
482- const presentationHint = hasCustomProperties
483- ? { kind : 'virtual' as const , visibility : 'private' as const }
484- : { visibility : 'private' as const } ;
485- properties . push (
486- this . createPropertyVar ( property , object , undefined , {
487- presentationHint,
489+ const contextInit = originalObject
490+ ? {
491+ presentationHint : { kind : 'virtual' as const , visibility : 'private' as const } ,
488492 sortOrder : SortOrder . Private ,
489- } ) ,
493+ isCustomProperty : ! ! originalObject ,
494+ }
495+ : {
496+ presentationHint : { visibility : 'private' as const } ,
497+ sortOrder : SortOrder . Private ,
498+ isCustomProperty : ! ! originalObject ,
499+ } ;
500+ properties . push (
501+ this . createPropertyVar ( property , object , undefined , contextInit ) ,
490502 ) ;
491503 }
492504
@@ -562,6 +574,7 @@ class VariableContext {
562574 const ctx : Required < IContextInit > = {
563575 name : p . name ,
564576 sortOrder : SortOrder . Default ,
577+ isCustomProperty : false ,
565578 ...contextInit ,
566579 presentationHint : {
567580 ...contextInit ?. presentationHint ,
@@ -654,7 +667,7 @@ class Variable implements IVariable {
654667 * Gets the accessor though which this object can be read.
655668 */
656669 public get accessor ( ) : string {
657- const { parent, name } = this . context ;
670+ const { parent, name, isCustomProperty } = this . context ;
658671 if ( parent instanceof AccessorVariable ) {
659672 return parent . accessor ;
660673 }
@@ -663,6 +676,19 @@ class Variable implements IVariable {
663676 return this . context . name ;
664677 }
665678
679+ if ( isCustomProperty ) {
680+ const prefix = `${ parent . accessor } [Symbol.for(${
681+ JSON . stringify ( DescriptionSymbols . Properties )
682+ } )]()`;
683+ if ( isNumberOrNumeric ( name ) ) {
684+ return `${ prefix } [${ name } ]` ;
685+ }
686+ if ( identifierRe . test ( name ) ) {
687+ return `${ prefix } .${ name } ` ;
688+ }
689+ return `${ prefix } [${ JSON . stringify ( name ) } ]` ;
690+ }
691+
666692 // Maps and sets:
667693 const grandparent = parent . context . parent ;
668694 if ( grandparent instanceof Variable ) {
@@ -953,6 +979,19 @@ class ObjectVariable extends Variable implements IMemoryReadable {
953979 * Used as the "..." escape hatch to view raw object properties.
954980 */
955981class PropertiesGeneratorEscapeHatchVariable extends ObjectVariable {
982+ /**
983+ * Override accessor to return the parent's accessor (the original object).
984+ * This ensures children of the escape hatch use the correct accessor path
985+ * (e.g., `c._b` instead of `c["..."]._b`).
986+ */
987+ public override get accessor ( ) : string {
988+ const { parent } = this . context ;
989+ if ( parent instanceof Variable ) {
990+ return parent . accessor ;
991+ }
992+ return super . accessor ;
993+ }
994+
956995 public override getChildren ( _params : Dap . VariablesParamsExtended ) {
957996 return this . context . createObjectPropertyVars (
958997 this . remoteObject ,
0 commit comments