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