@@ -453,14 +453,20 @@ function getEmptyFormatArray() {
453
453
return [ ] ;
454
454
}
455
455
456
- function getConstructorName ( obj , ctx , recurseTimes ) {
456
+ function getConstructorName ( obj , ctx , recurseTimes , protoProps ) {
457
457
let firstProto ;
458
458
const tmp = obj ;
459
459
while ( obj ) {
460
460
const descriptor = ObjectGetOwnPropertyDescriptor ( obj , 'constructor' ) ;
461
461
if ( descriptor !== undefined &&
462
462
typeof descriptor . value === 'function' &&
463
463
descriptor . value . name !== '' ) {
464
+ if ( protoProps !== undefined &&
465
+ ! builtInObjects . has ( descriptor . value . name ) ) {
466
+ const isProto = firstProto !== undefined ;
467
+ addPrototypeProperties (
468
+ ctx , tmp , obj , recurseTimes , isProto , protoProps ) ;
469
+ }
464
470
return descriptor . value . name ;
465
471
}
466
472
@@ -480,7 +486,8 @@ function getConstructorName(obj, ctx, recurseTimes) {
480
486
return `${ res } <Complex prototype>` ;
481
487
}
482
488
483
- const protoConstr = getConstructorName ( firstProto , ctx , recurseTimes + 1 ) ;
489
+ const protoConstr = getConstructorName (
490
+ firstProto , ctx , recurseTimes + 1 , protoProps ) ;
484
491
485
492
if ( protoConstr === null ) {
486
493
return `${ res } <${ inspect ( firstProto , {
@@ -493,6 +500,68 @@ function getConstructorName(obj, ctx, recurseTimes) {
493
500
return `${ res } <${ protoConstr } >` ;
494
501
}
495
502
503
+ // This function has the side effect of adding prototype properties to the
504
+ // `output` argument (which is an array). This is intended to highlight user
505
+ // defined prototype properties.
506
+ function addPrototypeProperties ( ctx , main , obj , recurseTimes , isProto , output ) {
507
+ let depth = 0 ;
508
+ let keys ;
509
+ let keySet ;
510
+ do {
511
+ if ( ! isProto ) {
512
+ obj = ObjectGetPrototypeOf ( obj ) ;
513
+ // Stop as soon as a null prototype is encountered.
514
+ if ( obj === null ) {
515
+ return ;
516
+ }
517
+ // Stop as soon as a built-in object type is detected.
518
+ const descriptor = ObjectGetOwnPropertyDescriptor ( obj , 'constructor' ) ;
519
+ if ( descriptor !== undefined &&
520
+ typeof descriptor . value === 'function' &&
521
+ builtInObjects . has ( descriptor . value . name ) ) {
522
+ return ;
523
+ }
524
+ } else {
525
+ isProto = false ;
526
+ }
527
+
528
+ if ( depth === 0 ) {
529
+ keySet = new Set ( ) ;
530
+ } else {
531
+ keys . forEach ( ( key ) => keySet . add ( key ) ) ;
532
+ }
533
+ // Get all own property names and symbols.
534
+ keys = ObjectGetOwnPropertyNames ( obj ) ;
535
+ const symbols = ObjectGetOwnPropertySymbols ( obj ) ;
536
+ if ( symbols . length !== 0 ) {
537
+ keys . push ( ...symbols ) ;
538
+ }
539
+ for ( const key of keys ) {
540
+ // Ignore the `constructor` property and keys that exist on layers above.
541
+ if ( key === 'constructor' ||
542
+ ObjectPrototypeHasOwnProperty ( main , key ) ||
543
+ ( depth !== 0 && keySet . has ( key ) ) ) {
544
+ continue ;
545
+ }
546
+ const desc = ObjectGetOwnPropertyDescriptor ( obj , key ) ;
547
+ if ( typeof desc . value === 'function' ) {
548
+ continue ;
549
+ }
550
+ const value = formatProperty (
551
+ ctx , obj , recurseTimes , key , kObjectType , desc ) ;
552
+ if ( ctx . colors ) {
553
+ // Faint!
554
+ output . push ( `\u001b[2m${ value } \u001b[22m` ) ;
555
+ } else {
556
+ output . push ( value ) ;
557
+ }
558
+ }
559
+ // Limit the inspection to up to three prototype layers. Using `recurseTimes`
560
+ // is not a good choice here, because it's as if the properties are declared
561
+ // on the current object from the users perspective.
562
+ } while ( ++ depth !== 3 ) ;
563
+ }
564
+
496
565
function getPrefix ( constructor , tag , fallback ) {
497
566
if ( constructor === null ) {
498
567
if ( tag !== '' ) {
@@ -696,8 +765,17 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
696
765
697
766
function formatRaw ( ctx , value , recurseTimes , typedArray ) {
698
767
let keys ;
768
+ let protoProps ;
769
+ if ( ctx . showHidden && ( recurseTimes <= ctx . depth || ctx . depth === null ) ) {
770
+ protoProps = [ ] ;
771
+ }
772
+
773
+ const constructor = getConstructorName ( value , ctx , recurseTimes , protoProps ) ;
774
+ // Reset the variable to check for this later on.
775
+ if ( protoProps !== undefined && protoProps . length === 0 ) {
776
+ protoProps = undefined ;
777
+ }
699
778
700
- const constructor = getConstructorName ( value , ctx , recurseTimes ) ;
701
779
let tag = value [ SymbolToStringTag ] ;
702
780
// Only list the tag in case it's non-enumerable / not an own property.
703
781
// Otherwise we'd print this twice.
@@ -727,21 +805,21 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
727
805
// Only set the constructor for non ordinary ("Array [...]") arrays.
728
806
const prefix = getPrefix ( constructor , tag , 'Array' ) ;
729
807
braces = [ `${ prefix === 'Array ' ? '' : prefix } [` , ']' ] ;
730
- if ( value . length === 0 && keys . length === 0 )
808
+ if ( value . length === 0 && keys . length === 0 && protoProps === undefined )
731
809
return `${ braces [ 0 ] } ]` ;
732
810
extrasType = kArrayExtrasType ;
733
811
formatter = formatArray ;
734
812
} else if ( isSet ( value ) ) {
735
813
keys = getKeys ( value , ctx . showHidden ) ;
736
814
const prefix = getPrefix ( constructor , tag , 'Set' ) ;
737
- if ( value . size === 0 && keys . length === 0 )
815
+ if ( value . size === 0 && keys . length === 0 && protoProps === undefined )
738
816
return `${ prefix } {}` ;
739
817
braces = [ `${ prefix } {` , '}' ] ;
740
818
formatter = formatSet ;
741
819
} else if ( isMap ( value ) ) {
742
820
keys = getKeys ( value , ctx . showHidden ) ;
743
821
const prefix = getPrefix ( constructor , tag , 'Map' ) ;
744
- if ( value . size === 0 && keys . length === 0 )
822
+ if ( value . size === 0 && keys . length === 0 && protoProps === undefined )
745
823
return `${ prefix } {}` ;
746
824
braces = [ `${ prefix } {` , '}' ] ;
747
825
formatter = formatMap ;
@@ -776,12 +854,12 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
776
854
} else if ( tag !== '' ) {
777
855
braces [ 0 ] = `${ getPrefix ( constructor , tag , 'Object' ) } {` ;
778
856
}
779
- if ( keys . length === 0 ) {
857
+ if ( keys . length === 0 && protoProps === undefined ) {
780
858
return `${ braces [ 0 ] } }` ;
781
859
}
782
860
} else if ( typeof value === 'function' ) {
783
861
base = getFunctionBase ( value , constructor , tag ) ;
784
- if ( keys . length === 0 )
862
+ if ( keys . length === 0 && protoProps === undefined )
785
863
return ctx . stylize ( base , 'special' ) ;
786
864
} else if ( isRegExp ( value ) ) {
787
865
// Make RegExps say that they are RegExps
@@ -791,8 +869,10 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
791
869
const prefix = getPrefix ( constructor , tag , 'RegExp' ) ;
792
870
if ( prefix !== 'RegExp ' )
793
871
base = `${ prefix } ${ base } ` ;
794
- if ( keys . length === 0 || ( recurseTimes > ctx . depth && ctx . depth !== null ) )
872
+ if ( ( keys . length === 0 && protoProps === undefined ) ||
873
+ ( recurseTimes > ctx . depth && ctx . depth !== null ) ) {
795
874
return ctx . stylize ( base , 'regexp' ) ;
875
+ }
796
876
} else if ( isDate ( value ) ) {
797
877
// Make dates with properties first say the date
798
878
base = NumberIsNaN ( DatePrototypeGetTime ( value ) ) ?
@@ -801,12 +881,12 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
801
881
const prefix = getPrefix ( constructor , tag , 'Date' ) ;
802
882
if ( prefix !== 'Date ' )
803
883
base = `${ prefix } ${ base } ` ;
804
- if ( keys . length === 0 ) {
884
+ if ( keys . length === 0 && protoProps === undefined ) {
805
885
return ctx . stylize ( base , 'date' ) ;
806
886
}
807
887
} else if ( isError ( value ) ) {
808
888
base = formatError ( value , constructor , tag , ctx ) ;
809
- if ( keys . length === 0 )
889
+ if ( keys . length === 0 && protoProps === undefined )
810
890
return base ;
811
891
} else if ( isAnyArrayBuffer ( value ) ) {
812
892
// Fast path for ArrayBuffer and SharedArrayBuffer.
@@ -817,7 +897,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
817
897
const prefix = getPrefix ( constructor , tag , arrayType ) ;
818
898
if ( typedArray === undefined ) {
819
899
formatter = formatArrayBuffer ;
820
- } else if ( keys . length === 0 ) {
900
+ } else if ( keys . length === 0 && protoProps === undefined ) {
821
901
return prefix +
822
902
`{ byteLength: ${ formatNumber ( ctx . stylize , value . byteLength ) } }` ;
823
903
}
@@ -841,7 +921,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
841
921
formatter = formatNamespaceObject ;
842
922
} else if ( isBoxedPrimitive ( value ) ) {
843
923
base = getBoxedBase ( value , ctx , keys , constructor , tag ) ;
844
- if ( keys . length === 0 ) {
924
+ if ( keys . length === 0 && protoProps === undefined ) {
845
925
return base ;
846
926
}
847
927
} else {
@@ -861,7 +941,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
861
941
formatter = formatIterator ;
862
942
// Handle other regular objects again.
863
943
} else {
864
- if ( keys . length === 0 ) {
944
+ if ( keys . length === 0 && protoProps === undefined ) {
865
945
if ( isExternal ( value ) )
866
946
return ctx . stylize ( '[External]' , 'special' ) ;
867
947
return `${ getCtxStyle ( value , constructor , tag ) } {}` ;
@@ -889,6 +969,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
889
969
output . push (
890
970
formatProperty ( ctx , value , recurseTimes , keys [ i ] , extrasType ) ) ;
891
971
}
972
+ if ( protoProps !== undefined ) {
973
+ output . push ( ...protoProps ) ;
974
+ }
892
975
} catch ( err ) {
893
976
const constructorName = getCtxStyle ( value , constructor , tag ) . slice ( 0 , - 1 ) ;
894
977
return handleMaxCallStackSize ( ctx , err , constructorName , indentationLvl ) ;
@@ -1355,6 +1438,7 @@ function formatTypedArray(ctx, value, recurseTimes) {
1355
1438
}
1356
1439
if ( ctx . showHidden ) {
1357
1440
// .buffer goes last, it's not a primitive like the others.
1441
+ // All besides `BYTES_PER_ELEMENT` are actually getters.
1358
1442
ctx . indentationLvl += 2 ;
1359
1443
for ( const key of [
1360
1444
'BYTES_PER_ELEMENT' ,
@@ -1503,10 +1587,10 @@ function formatPromise(ctx, value, recurseTimes) {
1503
1587
return output ;
1504
1588
}
1505
1589
1506
- function formatProperty ( ctx , value , recurseTimes , key , type ) {
1590
+ function formatProperty ( ctx , value , recurseTimes , key , type , desc ) {
1507
1591
let name , str ;
1508
1592
let extra = ' ' ;
1509
- const desc = ObjectGetOwnPropertyDescriptor ( value , key ) ||
1593
+ desc = desc || ObjectGetOwnPropertyDescriptor ( value , key ) ||
1510
1594
{ value : value [ key ] , enumerable : true } ;
1511
1595
if ( desc . value !== undefined ) {
1512
1596
const diff = ( type !== kObjectType || ctx . compact !== true ) ? 2 : 3 ;
0 commit comments