@@ -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 ;
@@ -407,7 +445,7 @@ function getConstructorName(obj) {
407
445
return '' ;
408
446
}
409
447
410
- function getPrefix ( constructor , tag ) {
448
+ function getPrefix ( constructor , tag , fallback ) {
411
449
if ( constructor !== '' ) {
412
450
if ( tag !== '' && constructor !== tag ) {
413
451
return `${ constructor } [${ tag } ] ` ;
@@ -418,9 +456,42 @@ function getPrefix(constructor, tag) {
418
456
if ( tag !== '' )
419
457
return `[${ tag } ] ` ;
420
458
459
+ if ( fallback !== undefined )
460
+ return `${ fallback } ` ;
461
+
421
462
return '' ;
422
463
}
423
464
465
+ function addExtraKeys ( source , target , keys ) {
466
+ for ( const key of keys ) {
467
+ target [ key ] = source [ key ] ;
468
+ }
469
+ return target ;
470
+ }
471
+
472
+ function findTypedConstructor ( value ) {
473
+ for ( const [ check , clazz ] of [
474
+ [ isUint8Array , Uint8Array ] ,
475
+ [ isUint8ClampedArray , Uint8ClampedArray ] ,
476
+ [ isUint16Array , Uint16Array ] ,
477
+ [ isUint32Array , Uint32Array ] ,
478
+ [ isInt8Array , Int8Array ] ,
479
+ [ isInt16Array , Int16Array ] ,
480
+ [ isInt32Array , Int32Array ] ,
481
+ [ isFloat32Array , Float32Array ] ,
482
+ [ isFloat64Array , Float64Array ] ,
483
+ [ isBigInt64Array , BigInt64Array ] ,
484
+ [ isBigUint64Array , BigUint64Array ]
485
+ ] ) {
486
+ if ( check ( value ) ) {
487
+ return new clazz ( value ) ;
488
+ }
489
+ }
490
+ return value ;
491
+ }
492
+
493
+ const getBoxedValue = formatPrimitive . bind ( null , stylizeNoColor ) ;
494
+
424
495
function formatValue ( ctx , value , recurseTimes ) {
425
496
// Primitive types cannot have properties
426
497
if ( typeof value !== 'object' && typeof value !== 'function' ) {
@@ -511,7 +582,7 @@ function formatValue(ctx, value, recurseTimes) {
511
582
}
512
583
513
584
if ( symbols . length !== 0 )
514
- symbols = symbols . filter ( ( key ) => propertyIsEnumerable . call ( value , key ) ) ;
585
+ symbols = symbols . filter ( ( key ) => propertyIsEnumerable ( value , key ) ) ;
515
586
}
516
587
517
588
const keyLength = keys . length + symbols . length ;
@@ -524,8 +595,8 @@ function formatValue(ctx, value, recurseTimes) {
524
595
let formatter = formatObject ;
525
596
let braces ;
526
597
let noIterator = true ;
527
- let raw ;
528
598
let extra ;
599
+ let i = 0 ;
529
600
530
601
// Iterators and the rest are split to reduce checks
531
602
if ( value [ Symbol . iterator ] ) {
@@ -559,34 +630,16 @@ function formatValue(ctx, value, recurseTimes) {
559
630
braces = [ `[${ tag } ] {` , '}' ] ;
560
631
formatter = formatSetIterator ;
561
632
} else {
562
- // Check for boxed strings with valueOf()
563
- // The .valueOf() call can fail for a multitude of reasons
564
- try {
565
- raw = value . valueOf ( ) ;
566
- } catch ( e ) { /* ignore */ }
567
-
568
- if ( typeof raw === 'string' ) {
569
- const formatted = formatPrimitive ( stylizeNoColor , raw , ctx ) ;
570
- if ( keyLength === raw . length )
571
- return ctx . stylize ( `[String: ${ formatted } ]` , 'string' ) ;
572
- base = `[String: ${ formatted } ]` ;
573
- // For boxed Strings, we have to remove the 0-n indexed entries,
574
- // since they just noisy up the output and are redundant
575
- // Make boxed primitive Strings look like such
576
- keys = keys . slice ( value . length ) ;
577
- braces = [ '{' , '}' ] ;
578
- } else {
579
- noIterator = true ;
580
- }
633
+ noIterator = true ;
581
634
}
582
635
}
583
636
if ( noIterator ) {
584
637
braces = [ '{' , '}' ] ;
585
638
if ( constructor === 'Object' ) {
586
639
if ( isArgumentsObject ( value ) ) {
587
- braces [ 0 ] = '[Arguments] {' ;
588
640
if ( keyLength === 0 )
589
641
return '[Arguments] {}' ;
642
+ braces [ 0 ] = '[Arguments] {' ;
590
643
} else if ( tag !== '' ) {
591
644
braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
592
645
if ( keyLength === 0 ) {
@@ -596,24 +649,24 @@ function formatValue(ctx, value, recurseTimes) {
596
649
return '{}' ;
597
650
}
598
651
} else if ( typeof value === 'function' ) {
599
- const name =
600
- `${ constructor || tag } ${ value . name ? `: ${ value . name } ` : '' } ` ;
652
+ const type = constructor || tag || 'Function' ;
653
+ const name = `${ type } ${ value . name ? `: ${ value . name } ` : '' } ` ;
601
654
if ( keyLength === 0 )
602
655
return ctx . stylize ( `[${ name } ]` , 'special' ) ;
603
656
base = `[${ name } ]` ;
604
657
} else if ( isRegExp ( value ) ) {
605
658
// Make RegExps say that they are RegExps
606
659
if ( keyLength === 0 || recurseTimes < 0 )
607
- return ctx . stylize ( regExpToString . call ( value ) , 'regexp' ) ;
608
- base = `${ regExpToString . call ( value ) } ` ;
660
+ return ctx . stylize ( regExpToString ( value ) , 'regexp' ) ;
661
+ base = `${ regExpToString ( value ) } ` ;
609
662
} else if ( isDate ( value ) ) {
663
+ // Make dates with properties first say the date
610
664
if ( keyLength === 0 ) {
611
- if ( Number . isNaN ( value . getTime ( ) ) )
612
- return ctx . stylize ( value . toString ( ) , 'date' ) ;
613
- return ctx . stylize ( dateToISOString . call ( value ) , 'date' ) ;
665
+ if ( Number . isNaN ( dateGetTime ( value ) ) )
666
+ return ctx . stylize ( String ( value ) , 'date' ) ;
667
+ return ctx . stylize ( dateToISOString ( value ) , 'date' ) ;
614
668
}
615
- // Make dates with properties first say the date
616
- base = dateToISOString . call ( value ) ;
669
+ base = dateToISOString ( value ) ;
617
670
} else if ( isError ( value ) ) {
618
671
// Make error with message first say the error
619
672
base = formatError ( value ) ;
@@ -638,28 +691,31 @@ function formatValue(ctx, value, recurseTimes) {
638
691
// Fast path for ArrayBuffer and SharedArrayBuffer.
639
692
// Can't do the same for DataView because it has a non-primitive
640
693
// .buffer property that we need to recurse for.
641
- const prefix = getPrefix ( constructor , tag ) ;
694
+ let prefix = getPrefix ( constructor , tag ) ;
695
+ if ( prefix === '' ) {
696
+ prefix = isArrayBuffer ( value ) ? 'ArrayBuffer ' : 'SharedArrayBuffer ' ;
697
+ }
642
698
if ( keyLength === 0 )
643
699
return prefix +
644
700
`{ byteLength: ${ formatNumber ( ctx . stylize , value . byteLength ) } }` ;
645
701
braces [ 0 ] = `${ prefix } {` ;
646
702
keys . unshift ( 'byteLength' ) ;
647
703
} else if ( isDataView ( value ) ) {
648
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
704
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'DataView' ) } {` ;
649
705
// .buffer goes last, it's not a primitive like the others.
650
706
keys . unshift ( 'byteLength' , 'byteOffset' , 'buffer' ) ;
651
707
} else if ( isPromise ( value ) ) {
652
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
708
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'Promise' ) } {` ;
653
709
formatter = formatPromise ;
654
710
} else if ( isWeakSet ( value ) ) {
655
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
711
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakSet' ) } {` ;
656
712
if ( ctx . showHidden ) {
657
713
formatter = formatWeakSet ;
658
714
} else {
659
715
extra = '[items unknown]' ;
660
716
}
661
717
} else if ( isWeakMap ( value ) ) {
662
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
718
+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakMap' ) } {` ;
663
719
if ( ctx . showHidden ) {
664
720
formatter = formatWeakMap ;
665
721
} else {
@@ -668,43 +724,67 @@ function formatValue(ctx, value, recurseTimes) {
668
724
} else if ( types . isModuleNamespaceObject ( value ) ) {
669
725
braces [ 0 ] = `[${ tag } ] {` ;
670
726
formatter = formatNamespaceObject ;
727
+ } else if ( isNumberObject ( value ) ) {
728
+ base = `[Number: ${ getBoxedValue ( numberValueOf ( value ) ) } ]` ;
729
+ if ( keyLength === 0 )
730
+ return ctx . stylize ( base , 'number' ) ;
731
+ } else if ( isBooleanObject ( value ) ) {
732
+ base = `[Boolean: ${ getBoxedValue ( booleanValueOf ( value ) ) } ]` ;
733
+ if ( keyLength === 0 )
734
+ return ctx . stylize ( base , 'boolean' ) ;
735
+ } else if ( isBigIntObject ( value ) ) {
736
+ base = `[BigInt: ${ getBoxedValue ( bigIntValueOf ( value ) ) } ]` ;
737
+ if ( keyLength === 0 )
738
+ return ctx . stylize ( base , 'bigint' ) ;
739
+ } else if ( isSymbolObject ( value ) ) {
740
+ base = `[Symbol: ${ getBoxedValue ( symbolValueOf ( value ) ) } ]` ;
741
+ if ( keyLength === 0 )
742
+ return ctx . stylize ( base , 'symbol' ) ;
743
+ } else if ( isStringObject ( value ) ) {
744
+ const raw = stringValueOf ( value ) ;
745
+ base = `[String: ${ getBoxedValue ( raw , ctx ) } ]` ;
746
+ if ( keyLength === raw . length )
747
+ return ctx . stylize ( base , 'string' ) ;
748
+ // For boxed Strings, we have to remove the 0-n indexed entries,
749
+ // since they just noisy up the output and are redundant
750
+ // Make boxed primitive Strings look like such
751
+ keys = keys . slice ( value . length ) ;
752
+ braces = [ '{' , '}' ] ;
753
+ // The input prototype got manipulated. Special handle these.
754
+ // We have to rebuild the information so we are able to display everything.
755
+ } else if ( isSet ( value ) ) {
756
+ const newVal = addExtraKeys ( value , new Set ( setValues ( value ) ) , keys ) ;
757
+ return formatValue ( ctx , newVal , recurseTimes ) ;
758
+ } else if ( isMap ( value ) ) {
759
+ const newVal = addExtraKeys ( value , new Map ( mapEntries ( value ) ) , keys ) ;
760
+ return formatValue ( ctx , newVal , recurseTimes ) ;
761
+ } else if ( Array . isArray ( value ) ) {
762
+ // The prefix is not always possible to fully reconstruct.
763
+ const prefix = getPrefix ( constructor , tag ) ;
764
+ braces = [ `${ prefix === 'Array ' ? '' : prefix } [` , ']' ] ;
765
+ formatter = formatArray ;
766
+ const newValue = [ ] ;
767
+ newValue . length = value . length ;
768
+ value = addExtraKeys ( value , newValue , keys ) ;
769
+ } else if ( isTypedArray ( value ) ) {
770
+ const newValue = findTypedConstructor ( value ) ;
771
+ value = addExtraKeys ( value , newValue , keys . slice ( newValue . length ) ) ;
772
+ // The prefix is not always possible to fully reconstruct.
773
+ braces = [ `${ getPrefix ( getConstructorName ( value ) , tag ) } [` , ']' ] ;
774
+ formatter = formatTypedArray ;
775
+ } else if ( isMapIterator ( value ) ) {
776
+ braces = [ `[${ tag || 'Map Iterator' } ] {` , '}' ] ;
777
+ formatter = formatMapIterator ;
778
+ } else if ( isSetIterator ( value ) ) {
779
+ braces = [ `[${ tag || 'Set Iterator' } ] {` , '}' ] ;
780
+ formatter = formatSetIterator ;
781
+ // Handle other regular objects again.
782
+ } else if ( keyLength === 0 ) {
783
+ if ( isExternal ( value ) )
784
+ return ctx . stylize ( '[External]' , 'special' ) ;
785
+ return `${ getPrefix ( constructor , tag ) } {}` ;
671
786
} else {
672
- // Check boxed primitives other than string with valueOf()
673
- // NOTE: `Date` has to be checked first!
674
- // The .valueOf() call can fail for a multitude of reasons
675
- try {
676
- raw = value . valueOf ( ) ;
677
- } catch ( e ) { /* ignore */ }
678
-
679
- if ( typeof raw === 'number' ) {
680
- // Make boxed primitive Numbers look like such
681
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
682
- if ( keyLength === 0 )
683
- return ctx . stylize ( `[Number: ${ formatted } ]` , 'number' ) ;
684
- base = `[Number: ${ formatted } ]` ;
685
- } else if ( typeof raw === 'boolean' ) {
686
- // Make boxed primitive Booleans look like such
687
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
688
- if ( keyLength === 0 )
689
- return ctx . stylize ( `[Boolean: ${ formatted } ]` , 'boolean' ) ;
690
- base = `[Boolean: ${ formatted } ]` ;
691
- // eslint-disable-next-line valid-typeof
692
- } else if ( typeof raw === 'bigint' ) {
693
- // Make boxed primitive BigInts look like such
694
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
695
- if ( keyLength === 0 )
696
- return ctx . stylize ( `[BigInt: ${ formatted } ]` , 'bigint' ) ;
697
- base = `[BigInt: ${ formatted } ]` ;
698
- } else if ( typeof raw === 'symbol' ) {
699
- const formatted = formatPrimitive ( stylizeNoColor , raw ) ;
700
- return ctx . stylize ( `[Symbol: ${ formatted } ]` , 'symbol' ) ;
701
- } else if ( keyLength === 0 ) {
702
- if ( isExternal ( value ) )
703
- return ctx . stylize ( '[External]' , 'special' ) ;
704
- return `${ getPrefix ( constructor , tag ) } {}` ;
705
- } else {
706
- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
707
- }
787
+ braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
708
788
}
709
789
}
710
790
@@ -737,7 +817,7 @@ function formatValue(ctx, value, recurseTimes) {
737
817
if ( extra !== undefined )
738
818
output . unshift ( extra ) ;
739
819
740
- for ( var i = 0 ; i < symbols . length ; i ++ ) {
820
+ for ( i = 0 ; i < symbols . length ; i ++ ) {
741
821
output . push ( formatProperty ( ctx , value , recurseTimes , symbols [ i ] , 0 ) ) ;
742
822
}
743
823
@@ -803,7 +883,7 @@ function formatPrimitive(fn, value, ctx) {
803
883
}
804
884
805
885
function formatError ( value ) {
806
- return value . stack || errorToString . call ( value ) ;
886
+ return value . stack || errorToString ( value ) ;
807
887
}
808
888
809
889
function formatObject ( ctx , value , recurseTimes , keys ) {
0 commit comments