@@ -43,6 +43,7 @@ const types = internalBinding('types');
43
43
Object . assign ( types , require ( 'internal/util/types' ) ) ;
44
44
const {
45
45
isAnyArrayBuffer,
46
+ isArrayBuffer,
46
47
isArgumentsObject,
47
48
isDataView,
48
49
isExternal,
@@ -55,7 +56,23 @@ const {
55
56
isWeakSet,
56
57
isRegExp,
57
58
isDate,
58
- isTypedArray
59
+ isTypedArray,
60
+ isStringObject,
61
+ isNumberObject,
62
+ isBooleanObject,
63
+ isSymbolObject,
64
+ isBigIntObject,
65
+ isUint8Array,
66
+ isUint8ClampedArray,
67
+ isUint16Array,
68
+ isUint32Array,
69
+ isInt8Array,
70
+ isInt16Array,
71
+ isInt32Array,
72
+ isFloat32Array,
73
+ isFloat64Array,
74
+ isBigInt64Array,
75
+ isBigUint64Array
59
76
} = types ;
60
77
61
78
const {
@@ -79,10 +96,31 @@ const inspectDefaultOptions = Object.seal({
79
96
compact : true
80
97
} ) ;
81
98
82
- const propertyIsEnumerable = Object . prototype . propertyIsEnumerable ;
83
- const regExpToString = RegExp . prototype . toString ;
84
- const dateToISOString = Date . prototype . toISOString ;
85
- const errorToString = Error . prototype . toString ;
99
+ const ReflectApply = Reflect . apply ;
100
+
101
+ // This function is borrowed from the function with the same name on V8 Extras'
102
+ // `utils` object. V8 implements Reflect.apply very efficiently in conjunction
103
+ // with the spread syntax, such that no additional special case is needed for
104
+ // function calls w/o arguments.
105
+ // Refs: https://github.com/v8/v8/blob/d6ead37d265d7215cf9c5f768f279e21bd170212/src/js/prologue.js#L152-L156
106
+ function uncurryThis ( func ) {
107
+ return ( thisArg , ...args ) => ReflectApply ( func , thisArg , args ) ;
108
+ }
109
+
110
+ const propertyIsEnumerable = uncurryThis ( Object . prototype . propertyIsEnumerable ) ;
111
+ const regExpToString = uncurryThis ( RegExp . prototype . toString ) ;
112
+ const dateToISOString = uncurryThis ( Date . prototype . toISOString ) ;
113
+ const errorToString = uncurryThis ( Error . prototype . toString ) ;
114
+
115
+ const bigIntValueOf = uncurryThis ( BigInt . prototype . valueOf ) ;
116
+ const booleanValueOf = uncurryThis ( Boolean . prototype . valueOf ) ;
117
+ const numberValueOf = uncurryThis ( Number . prototype . valueOf ) ;
118
+ const symbolValueOf = uncurryThis ( Symbol . prototype . valueOf ) ;
119
+ const stringValueOf = uncurryThis ( String . prototype . valueOf ) ;
120
+
121
+ const setValues = uncurryThis ( Set . prototype . values ) ;
122
+ const mapEntries = uncurryThis ( Map . prototype . entries ) ;
123
+ const dateGetTime = uncurryThis ( Date . prototype . getTime ) ;
86
124
87
125
let CIRCULAR_ERROR_MESSAGE ;
88
126
let internalDeepEqual ;
@@ -445,7 +483,7 @@ function getConstructorName(obj) {
445
483
return '' ;
446
484
}
447
485
448
- function getPrefix ( constructor , tag ) {
486
+ function getPrefix ( constructor , tag , fallback ) {
449
487
if ( constructor !== '' ) {
450
488
if ( tag !== '' && constructor !== tag ) {
451
489
return `${ constructor } [${ tag } ] ` ;
@@ -456,9 +494,42 @@ function getPrefix(constructor, tag) {
456
494
if ( tag !== '' )
457
495
return `[${ tag } ] ` ;
458
496
497
+ if ( fallback !== undefined )
498
+ return `${ fallback } ` ;
499
+
459
500
return '' ;
460
501
}
461
502
503
+ function addExtraKeys ( source , target , keys ) {
504
+ for ( const key of keys ) {
505
+ target [ key ] = source [ key ] ;
506
+ }
507
+ return target ;
508
+ }
509
+
510
+ function findTypedConstructor ( value ) {
511
+ for ( const [ check , clazz ] of [
512
+ [ isUint8Array , Uint8Array ] ,
513
+ [ isUint8ClampedArray , Uint8ClampedArray ] ,
514
+ [ isUint16Array , Uint16Array ] ,
515
+ [ isUint32Array , Uint32Array ] ,
516
+ [ isInt8Array , Int8Array ] ,
517
+ [ isInt16Array , Int16Array ] ,
518
+ [ isInt32Array , Int32Array ] ,
519
+ [ isFloat32Array , Float32Array ] ,
520
+ [ isFloat64Array , Float64Array ] ,
521
+ [ isBigInt64Array , BigInt64Array ] ,
522
+ [ isBigUint64Array , BigUint64Array ]
523
+ ] ) {
524
+ if ( check ( value ) ) {
525
+ return new clazz ( value ) ;
526
+ }
527
+ }
528
+ return value ;
529
+ }
530
+
531
+ const getBoxedValue = formatPrimitive . bind ( null , stylizeNoColor ) ;
532
+
462
533
function formatValue ( ctx , value , recurseTimes ) {
463
534
// Primitive types cannot have properties
464
535
if ( typeof value !== 'object' && typeof value !== 'function' ) {
@@ -539,7 +610,7 @@ function formatValue(ctx, value, recurseTimes) {
539
610
}
540
611
541
612
if ( symbols . length !== 0 )
542
- symbols = symbols . filter ( ( key ) => propertyIsEnumerable . call ( value , key ) ) ;
613
+ symbols = symbols . filter ( ( key ) => propertyIsEnumerable ( value , key ) ) ;
543
614
}
544
615
545
616
const keyLength = keys . length + symbols . length ;
@@ -552,8 +623,8 @@ function formatValue(ctx, value, recurseTimes) {
552
623
let formatter = formatObject ;
553
624
let braces ;
554
625
let noIterator = true ;
555
- let raw ;
556
626
let extra ;
627
+ let i = 0 ;
557
628
558
629
// Iterators and the rest are split to reduce checks
559
630
if ( value [ Symbol . iterator ] ) {
@@ -587,34 +658,16 @@ function formatValue(ctx, value, recurseTimes) {
587
658
braces = [ `[${ tag } ] {` , '}' ] ;
588
659
formatter = formatSetIterator ;
589
660
} else {
590
- // Check for boxed strings with valueOf()
591
- // The .valueOf() call can fail for a multitude of reasons
592
- try {
593
- raw = value . valueOf ( ) ;
594
- } catch ( e ) { /* ignore */ }
595
-
596
- if ( typeof raw === 'string' ) {
597
- const formatted = formatPrimitive ( stylizeNoColor , raw , ctx ) ;
598
- if ( keyLength === raw . length )
599
- return ctx . stylize ( `[String: ${ formatted } ]` , 'string' ) ;
600
- base = `[String: ${ formatted } ]` ;
601
- // For boxed Strings, we have to remove the 0-n indexed entries,
602
- // since they just noisy up the output and are redundant
603
- // Make boxed primitive Strings look like such
604
- keys = keys . slice ( value . length ) ;
605
- braces = [ '{' , '}' ] ;
606
- } else {
607
- noIterator = true ;
608
- }
661
+ noIterator = true ;
609
662
}
610
663
}
611
664
if ( noIterator ) {
612
665
braces = [ '{' , '}' ] ;
613
666
if ( constructor === 'Object' ) {
614
667
if ( isArgumentsObject ( value ) ) {
615
- braces [ 0 ] = '[Arguments] {' ;
616
668
if ( keyLength === 0 )
617
669
return '[Arguments] {}' ;
670
+ braces [ 0 ] = '[Arguments] {' ;
618
671
} else if ( tag !== '' ) {
619
672
braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
620
673
if ( keyLength === 0 ) {
@@ -624,24 +677,24 @@ function formatValue(ctx, value, recurseTimes) {
624
677
return '{}' ;
625
678
}
626
679
} else if ( typeof value === 'function' ) {
627
- const name =
628
- `${ constructor || tag } ${ value . name ? `: ${ value . name } ` : '' } ` ;
680
+ const type = constructor || tag || 'Function' ;
681
+ const name = `${ type } ${ value . name ? `: ${ value . name } ` : '' } ` ;
629
682
if ( keyLength === 0 )
630
683
return ctx . stylize ( `[${ name } ]` , 'special' ) ;
631
684
base = `[${ name } ]` ;
632
685
} else if ( isRegExp ( value ) ) {
633
686
// Make RegExps say that they are RegExps
634
687
if ( keyLength === 0 || recurseTimes < 0 )
635
- return ctx . stylize ( regExpToString . call ( value ) , 'regexp' ) ;
636
- base = `${ regExpToString . call ( value ) } ` ;
688
+ return ctx . stylize ( regExpToString ( value ) , 'regexp' ) ;
689
+ base = `${ regExpToString ( value ) } ` ;
637
690
} else if ( isDate ( value ) ) {
691
+ // Make dates with properties first say the date
638
692
if ( keyLength === 0 ) {
639
- if ( Number . isNaN ( value . getTime ( ) ) )
640
- return ctx . stylize ( value . toString ( ) , 'date' ) ;
641
- return ctx . stylize ( dateToISOString . call ( value ) , 'date' ) ;
693
+ if ( Number . isNaN ( dateGetTime ( value ) ) )
694
+ return ctx . stylize ( String ( value ) , 'date' ) ;
695
+ return ctx . stylize ( dateToISOString ( value ) , 'date' ) ;
642
696
}
643
- // Make dates with properties first say the date
644
- base = dateToISOString . call ( value ) ;
697
+ base = dateToISOString ( value ) ;
645
698
} else if ( isError ( value ) ) {
646
699
// Make error with message first say the error
647
700
base = formatError ( value ) ;
@@ -666,28 +719,31 @@ function formatValue(ctx, value, recurseTimes) {
666
719
// Fast path for ArrayBuffer and SharedArrayBuffer.
667
720
// Can't do the same for DataView because it has a non-primitive
668
721
// .buffer property that we need to recurse for.
669
- const prefix = getPrefix ( constructor , tag ) ;
722
+ let prefix = getPrefix ( constructor , tag ) ;
723
+ if ( prefix === '' ) {
724
+ prefix = isArrayBuffer ( value ) ? 'ArrayBuffer ' : 'SharedArrayBuffer ' ;
725
+ }
670
726
if ( keyLength === 0 )
671
727
return prefix +
672
728
`{ byteLength: ${ formatNumber ( ctx . stylize , value . byteLength ) } }` ;
673
729
braces [ 0 ] = `${ prefix } {` ;
674
730
keys . unshift ( 'byteLength' ) ;
675
731
} else if ( isDataView ( value ) ) {
676
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
732
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'DataView' ) } {` ;
677
733
// .buffer goes last, it's not a primitive like the others.
678
734
keys . unshift ( 'byteLength' , 'byteOffset' , 'buffer' ) ;
679
735
} else if ( isPromise ( value ) ) {
680
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
736
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'Promise' ) } {` ;
681
737
formatter = formatPromise ;
682
738
} else if ( isWeakSet ( value ) ) {
683
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
739
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakSet' ) } {` ;
684
740
if ( ctx . showHidden ) {
685
741
formatter = formatWeakSet ;
686
742
} else {
687
743
extra = '<items unknown>' ;
688
744
}
689
745
} else if ( isWeakMap ( value ) ) {
690
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
746
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakMap' ) } {` ;
691
747
if ( ctx . showHidden ) {
692
748
formatter = formatWeakMap ;
693
749
} else {
@@ -696,43 +752,67 @@ function formatValue(ctx, value, recurseTimes) {
696
752
} else if ( types . isModuleNamespaceObject ( value ) ) {
697
753
braces [ 0 ] = `[${ tag } ] {` ;
698
754
formatter = formatNamespaceObject ;
755
+ } else if ( isNumberObject ( value ) ) {
756
+ base = `[Number: ${ getBoxedValue ( numberValueOf ( value ) ) } ]` ;
757
+ if ( keyLength === 0 )
758
+ return ctx . stylize ( base , 'number' ) ;
759
+ } else if ( isBooleanObject ( value ) ) {
760
+ base = `[Boolean: ${ getBoxedValue ( booleanValueOf ( value ) ) } ]` ;
761
+ if ( keyLength === 0 )
762
+ return ctx . stylize ( base , 'boolean' ) ;
763
+ } else if ( isBigIntObject ( value ) ) {
764
+ base = `[BigInt: ${ getBoxedValue ( bigIntValueOf ( value ) ) } ]` ;
765
+ if ( keyLength === 0 )
766
+ return ctx . stylize ( base , 'bigint' ) ;
767
+ } else if ( isSymbolObject ( value ) ) {
768
+ base = `[Symbol: ${ getBoxedValue ( symbolValueOf ( value ) ) } ]` ;
769
+ if ( keyLength === 0 )
770
+ return ctx . stylize ( base , 'symbol' ) ;
771
+ } else if ( isStringObject ( value ) ) {
772
+ const raw = stringValueOf ( value ) ;
773
+ base = `[String: ${ getBoxedValue ( raw , ctx ) } ]` ;
774
+ if ( keyLength === raw . length )
775
+ return ctx . stylize ( base , 'string' ) ;
776
+ // For boxed Strings, we have to remove the 0-n indexed entries,
777
+ // since they just noisy up the output and are redundant
778
+ // Make boxed primitive Strings look like such
779
+ keys = keys . slice ( value . length ) ;
780
+ braces = [ '{' , '}' ] ;
781
+ // The input prototype got manipulated. Special handle these.
782
+ // We have to rebuild the information so we are able to display everything.
783
+ } else if ( isSet ( value ) ) {
784
+ const newVal = addExtraKeys ( value , new Set ( setValues ( value ) ) , keys ) ;
785
+ return formatValue ( ctx , newVal , recurseTimes ) ;
786
+ } else if ( isMap ( value ) ) {
787
+ const newVal = addExtraKeys ( value , new Map ( mapEntries ( value ) ) , keys ) ;
788
+ return formatValue ( ctx , newVal , recurseTimes ) ;
789
+ } else if ( Array . isArray ( value ) ) {
790
+ // The prefix is not always possible to fully reconstruct.
791
+ const prefix = getPrefix ( constructor , tag ) ;
792
+ braces = [ `${ prefix === 'Array ' ? '' : prefix } [` , ']' ] ;
793
+ formatter = formatArray ;
794
+ const newValue = [ ] ;
795
+ newValue . length = value . length ;
796
+ value = addExtraKeys ( value , newValue , keys ) ;
797
+ } else if ( isTypedArray ( value ) ) {
798
+ const newValue = findTypedConstructor ( value ) ;
799
+ value = addExtraKeys ( value , newValue , keys . slice ( newValue . length ) ) ;
800
+ // The prefix is not always possible to fully reconstruct.
801
+ braces = [ `${ getPrefix ( getConstructorName ( value ) , tag ) } [` , ']' ] ;
802
+ formatter = formatTypedArray ;
803
+ } else if ( isMapIterator ( value ) ) {
804
+ braces = [ `[${ tag || 'Map Iterator' } ] {` , '}' ] ;
805
+ formatter = formatMapIterator ;
806
+ } else if ( isSetIterator ( value ) ) {
807
+ braces = [ `[${ tag || 'Set Iterator' } ] {` , '}' ] ;
808
+ formatter = formatSetIterator ;
809
+ // Handle other regular objects again.
810
+ } else if ( keyLength === 0 ) {
811
+ if ( isExternal ( value ) )
812
+ return ctx . stylize ( '[External]' , 'special' ) ;
813
+ return `${ getPrefix ( constructor , tag ) } {}` ;
699
814
} else {
700
- // Check boxed primitives other than string with valueOf()
701
- // NOTE: `Date` has to be checked first!
702
- // The .valueOf() call can fail for a multitude of reasons
703
- try {
704
- raw = value . valueOf ( ) ;
705
- } catch ( e ) { /* ignore */ }
706
-
707
- if ( typeof raw === 'number' ) {
708
- // Make boxed primitive Numbers look like such
709
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
710
- if ( keyLength === 0 )
711
- return ctx . stylize ( `[Number: ${ formatted } ]` , 'number' ) ;
712
- base = `[Number: ${ formatted } ]` ;
713
- } else if ( typeof raw === 'boolean' ) {
714
- // Make boxed primitive Booleans look like such
715
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
716
- if ( keyLength === 0 )
717
- return ctx . stylize ( `[Boolean: ${ formatted } ]` , 'boolean' ) ;
718
- base = `[Boolean: ${ formatted } ]` ;
719
- // eslint-disable-next-line valid-typeof
720
- } else if ( typeof raw === 'bigint' ) {
721
- // Make boxed primitive BigInts look like such
722
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
723
- if ( keyLength === 0 )
724
- return ctx . stylize ( `[BigInt: ${ formatted } ]` , 'bigint' ) ;
725
- base = `[BigInt: ${ formatted } ]` ;
726
- } else if ( typeof raw === 'symbol' ) {
727
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
728
- return ctx . stylize ( `[Symbol: ${ formatted } ]` , 'symbol' ) ;
729
- } else if ( keyLength === 0 ) {
730
- if ( isExternal ( value ) )
731
- return ctx . stylize ( '[External]' , 'special' ) ;
732
- return `${ getPrefix ( constructor , tag ) } {}` ;
733
- } else {
734
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
735
- }
815
+ braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
736
816
}
737
817
}
738
818
@@ -765,7 +845,7 @@ function formatValue(ctx, value, recurseTimes) {
765
845
if ( extra !== undefined )
766
846
output . unshift ( extra ) ;
767
847
768
- for ( var i = 0 ; i < symbols . length ; i ++ ) {
848
+ for ( i = 0 ; i < symbols . length ; i ++ ) {
769
849
output . push ( formatProperty ( ctx , value , recurseTimes , symbols [ i ] , 0 ) ) ;
770
850
}
771
851
@@ -835,7 +915,7 @@ function formatPrimitive(fn, value, ctx) {
835
915
}
836
916
837
917
function formatError ( value ) {
838
- return value . stack || errorToString . call ( value ) ;
918
+ return value . stack || errorToString ( value ) ;
839
919
}
840
920
841
921
function formatObject ( ctx , value , recurseTimes , keys ) {
0 commit comments