@@ -448,14 +448,20 @@ function getEmptyFormatArray() {
448
448
return [ ] ;
449
449
}
450
450
451
- function getConstructorName ( obj , ctx , recurseTimes ) {
451
+ function getConstructorName ( obj , ctx , recurseTimes , protoProps ) {
452
452
let firstProto ;
453
453
const tmp = obj ;
454
454
while ( obj ) {
455
455
const descriptor = ObjectGetOwnPropertyDescriptor ( obj , 'constructor' ) ;
456
456
if ( descriptor !== undefined &&
457
457
typeof descriptor . value === 'function' &&
458
458
descriptor . value . name !== '' ) {
459
+ if ( protoProps !== undefined &&
460
+ ! builtInObjects . has ( descriptor . value . name ) ) {
461
+ const isProto = firstProto !== undefined ;
462
+ addPrototypeProperties (
463
+ ctx , tmp , obj , recurseTimes , isProto , protoProps ) ;
464
+ }
459
465
return descriptor . value . name ;
460
466
}
461
467
@@ -475,7 +481,8 @@ function getConstructorName(obj, ctx, recurseTimes) {
475
481
return `${ res } <Complex prototype>` ;
476
482
}
477
483
478
- const protoConstr = getConstructorName ( firstProto , ctx , recurseTimes + 1 ) ;
484
+ const protoConstr = getConstructorName (
485
+ firstProto , ctx , recurseTimes + 1 , protoProps ) ;
479
486
480
487
if ( protoConstr === null ) {
481
488
return `${ res } <${ inspect ( firstProto , {
@@ -488,6 +495,68 @@ function getConstructorName(obj, ctx, recurseTimes) {
488
495
return `${ res } <${ protoConstr } >` ;
489
496
}
490
497
498
+ // This function has the side effect of adding prototype properties to the
499
+ // `output` argument (which is an array). This is intended to highlight user
500
+ // defined prototype properties.
501
+ function addPrototypeProperties ( ctx , main , obj , recurseTimes , isProto , output ) {
502
+ let depth = 0 ;
503
+ let keys ;
504
+ let keySet ;
505
+ do {
506
+ if ( ! isProto ) {
507
+ obj = ObjectGetPrototypeOf ( obj ) ;
508
+ // Stop as soon as a null prototype is encountered.
509
+ if ( obj === null ) {
510
+ return ;
511
+ }
512
+ // Stop as soon as a built-in object type is detected.
513
+ const descriptor = ObjectGetOwnPropertyDescriptor ( obj , 'constructor' ) ;
514
+ if ( descriptor !== undefined &&
515
+ typeof descriptor . value === 'function' &&
516
+ builtInObjects . has ( descriptor . value . name ) ) {
517
+ return ;
518
+ }
519
+ } else {
520
+ isProto = false ;
521
+ }
522
+
523
+ if ( depth === 0 ) {
524
+ keySet = new Set ( ) ;
525
+ } else {
526
+ keys . forEach ( ( key ) => keySet . add ( key ) ) ;
527
+ }
528
+ // Get all own property names and symbols.
529
+ keys = ObjectGetOwnPropertyNames ( obj ) ;
530
+ const symbols = ObjectGetOwnPropertySymbols ( obj ) ;
531
+ if ( symbols . length !== 0 ) {
532
+ keys . push ( ...symbols ) ;
533
+ }
534
+ for ( const key of keys ) {
535
+ // Ignore the `constructor` property and keys that exist on layers above.
536
+ if ( key === 'constructor' ||
537
+ ObjectPrototypeHasOwnProperty ( main , key ) ||
538
+ ( depth !== 0 && keySet . has ( key ) ) ) {
539
+ continue ;
540
+ }
541
+ const desc = ObjectGetOwnPropertyDescriptor ( obj , key ) ;
542
+ if ( typeof desc . value === 'function' ) {
543
+ continue ;
544
+ }
545
+ const value = formatProperty (
546
+ ctx , obj , recurseTimes , key , kObjectType , desc ) ;
547
+ if ( ctx . colors ) {
548
+ // Faint!
549
+ output . push ( `\u001b[2m${ value } \u001b[22m` ) ;
550
+ } else {
551
+ output . push ( value ) ;
552
+ }
553
+ }
554
+ // Limit the inspection to up to three prototype layers. Using `recurseTimes`
555
+ // is not a good choice here, because it's as if the properties are declared
556
+ // on the current object from the users perspective.
557
+ } while ( ++ depth !== 3 ) ;
558
+ }
559
+
491
560
function getPrefix ( constructor , tag , fallback ) {
492
561
if ( constructor === null ) {
493
562
if ( tag !== '' ) {
@@ -691,8 +760,17 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
691
760
692
761
function formatRaw ( ctx , value , recurseTimes , typedArray ) {
693
762
let keys ;
763
+ let protoProps ;
764
+ if ( ctx . showHidden && ( recurseTimes <= ctx . depth || ctx . depth === null ) ) {
765
+ protoProps = [ ] ;
766
+ }
767
+
768
+ const constructor = getConstructorName ( value , ctx , recurseTimes , protoProps ) ;
769
+ // Reset the variable to check for this later on.
770
+ if ( protoProps !== undefined && protoProps . length === 0 ) {
771
+ protoProps = undefined ;
772
+ }
694
773
695
- const constructor = getConstructorName ( value , ctx , recurseTimes ) ;
696
774
let tag = value [ SymbolToStringTag ] ;
697
775
// Only list the tag in case it's non-enumerable / not an own property.
698
776
// Otherwise we'd print this twice.
@@ -722,21 +800,21 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
722
800
// Only set the constructor for non ordinary ("Array [...]") arrays.
723
801
const prefix = getPrefix ( constructor , tag , 'Array' ) ;
724
802
braces = [ `${ prefix === 'Array ' ? '' : prefix } [` , ']' ] ;
725
- if ( value . length === 0 && keys . length === 0 )
803
+ if ( value . length === 0 && keys . length === 0 && protoProps === undefined )
726
804
return `${ braces [ 0 ] } ]` ;
727
805
extrasType = kArrayExtrasType ;
728
806
formatter = formatArray ;
729
807
} else if ( isSet ( value ) ) {
730
808
keys = getKeys ( value , ctx . showHidden ) ;
731
809
const prefix = getPrefix ( constructor , tag , 'Set' ) ;
732
- if ( value . size === 0 && keys . length === 0 )
810
+ if ( value . size === 0 && keys . length === 0 && protoProps === undefined )
733
811
return `${ prefix } {}` ;
734
812
braces = [ `${ prefix } {` , '}' ] ;
735
813
formatter = formatSet ;
736
814
} else if ( isMap ( value ) ) {
737
815
keys = getKeys ( value , ctx . showHidden ) ;
738
816
const prefix = getPrefix ( constructor , tag , 'Map' ) ;
739
- if ( value . size === 0 && keys . length === 0 )
817
+ if ( value . size === 0 && keys . length === 0 && protoProps === undefined )
740
818
return `${ prefix } {}` ;
741
819
braces = [ `${ prefix } {` , '}' ] ;
742
820
formatter = formatMap ;
@@ -771,12 +849,12 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
771
849
} else if ( tag !== '' ) {
772
850
braces [ 0 ] = `${ getPrefix ( constructor , tag , 'Object' ) } {` ;
773
851
}
774
- if ( keys . length === 0 ) {
852
+ if ( keys . length === 0 && protoProps === undefined ) {
775
853
return `${ braces [ 0 ] } }` ;
776
854
}
777
855
} else if ( typeof value === 'function' ) {
778
856
base = getFunctionBase ( value , constructor , tag ) ;
779
- if ( keys . length === 0 )
857
+ if ( keys . length === 0 && protoProps === undefined )
780
858
return ctx . stylize ( base , 'special' ) ;
781
859
} else if ( isRegExp ( value ) ) {
782
860
// Make RegExps say that they are RegExps
@@ -786,8 +864,10 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
786
864
const prefix = getPrefix ( constructor , tag , 'RegExp' ) ;
787
865
if ( prefix !== 'RegExp ' )
788
866
base = `${ prefix } ${ base } ` ;
789
- if ( keys . length === 0 || ( recurseTimes > ctx . depth && ctx . depth !== null ) )
867
+ if ( ( keys . length === 0 && protoProps === undefined ) ||
868
+ ( recurseTimes > ctx . depth && ctx . depth !== null ) ) {
790
869
return ctx . stylize ( base , 'regexp' ) ;
870
+ }
791
871
} else if ( isDate ( value ) ) {
792
872
// Make dates with properties first say the date
793
873
base = NumberIsNaN ( DatePrototypeGetTime ( value ) ) ?
@@ -796,12 +876,12 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
796
876
const prefix = getPrefix ( constructor , tag , 'Date' ) ;
797
877
if ( prefix !== 'Date ' )
798
878
base = `${ prefix } ${ base } ` ;
799
- if ( keys . length === 0 ) {
879
+ if ( keys . length === 0 && protoProps === undefined ) {
800
880
return ctx . stylize ( base , 'date' ) ;
801
881
}
802
882
} else if ( isError ( value ) ) {
803
883
base = formatError ( value , constructor , tag , ctx ) ;
804
- if ( keys . length === 0 )
884
+ if ( keys . length === 0 && protoProps === undefined )
805
885
return base ;
806
886
} else if ( isAnyArrayBuffer ( value ) ) {
807
887
// Fast path for ArrayBuffer and SharedArrayBuffer.
@@ -812,7 +892,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
812
892
const prefix = getPrefix ( constructor , tag , arrayType ) ;
813
893
if ( typedArray === undefined ) {
814
894
formatter = formatArrayBuffer ;
815
- } else if ( keys . length === 0 ) {
895
+ } else if ( keys . length === 0 && protoProps === undefined ) {
816
896
return prefix +
817
897
`{ byteLength: ${ formatNumber ( ctx . stylize , value . byteLength ) } }` ;
818
898
}
@@ -836,7 +916,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
836
916
formatter = formatNamespaceObject ;
837
917
} else if ( isBoxedPrimitive ( value ) ) {
838
918
base = getBoxedBase ( value , ctx , keys , constructor , tag ) ;
839
- if ( keys . length === 0 ) {
919
+ if ( keys . length === 0 && protoProps === undefined ) {
840
920
return base ;
841
921
}
842
922
} else {
@@ -856,7 +936,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
856
936
formatter = formatIterator ;
857
937
// Handle other regular objects again.
858
938
} else {
859
- if ( keys . length === 0 ) {
939
+ if ( keys . length === 0 && protoProps === undefined ) {
860
940
if ( isExternal ( value ) )
861
941
return ctx . stylize ( '[External]' , 'special' ) ;
862
942
return `${ getCtxStyle ( value , constructor , tag ) } {}` ;
@@ -884,6 +964,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
884
964
output . push (
885
965
formatProperty ( ctx , value , recurseTimes , keys [ i ] , extrasType ) ) ;
886
966
}
967
+ if ( protoProps !== undefined ) {
968
+ output . push ( ...protoProps ) ;
969
+ }
887
970
} catch ( err ) {
888
971
const constructorName = getCtxStyle ( value , constructor , tag ) . slice ( 0 , - 1 ) ;
889
972
return handleMaxCallStackSize ( ctx , err , constructorName , indentationLvl ) ;
@@ -1350,6 +1433,7 @@ function formatTypedArray(ctx, value, recurseTimes) {
1350
1433
}
1351
1434
if ( ctx . showHidden ) {
1352
1435
// .buffer goes last, it's not a primitive like the others.
1436
+ // All besides `BYTES_PER_ELEMENT` are actually getters.
1353
1437
ctx . indentationLvl += 2 ;
1354
1438
for ( const key of [
1355
1439
'BYTES_PER_ELEMENT' ,
@@ -1498,10 +1582,10 @@ function formatPromise(ctx, value, recurseTimes) {
1498
1582
return output ;
1499
1583
}
1500
1584
1501
- function formatProperty ( ctx , value , recurseTimes , key , type ) {
1585
+ function formatProperty ( ctx , value , recurseTimes , key , type , desc ) {
1502
1586
let name , str ;
1503
1587
let extra = ' ' ;
1504
- const desc = ObjectGetOwnPropertyDescriptor ( value , key ) ||
1588
+ desc = desc || ObjectGetOwnPropertyDescriptor ( value , key ) ||
1505
1589
{ value : value [ key ] , enumerable : true } ;
1506
1590
if ( desc . value !== undefined ) {
1507
1591
const diff = ( type !== kObjectType || ctx . compact !== true ) ? 2 : 3 ;
0 commit comments