@@ -43,6 +43,7 @@ const types = internalBinding('types');
4343Object . assign ( types , require ( 'internal/util/types' ) ) ;
4444const {
4545 isAnyArrayBuffer,
46+ isArrayBuffer,
4647 isArgumentsObject,
4748 isDataView,
4849 isExternal,
@@ -55,7 +56,23 @@ const {
5556 isWeakSet,
5657 isRegExp,
5758 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
5976} = types ;
6077
6178const {
@@ -79,10 +96,31 @@ const inspectDefaultOptions = Object.seal({
7996 compact : true
8097} ) ;
8198
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 ) ;
86124
87125let CIRCULAR_ERROR_MESSAGE ;
88126let internalDeepEqual ;
@@ -445,7 +483,7 @@ function getConstructorName(obj) {
445483 return '' ;
446484}
447485
448- function getPrefix ( constructor , tag ) {
486+ function getPrefix ( constructor , tag , fallback ) {
449487 if ( constructor !== '' ) {
450488 if ( tag !== '' && constructor !== tag ) {
451489 return `${ constructor } [${ tag } ] ` ;
@@ -456,9 +494,42 @@ function getPrefix(constructor, tag) {
456494 if ( tag !== '' )
457495 return `[${ tag } ] ` ;
458496
497+ if ( fallback !== undefined )
498+ return `${ fallback } ` ;
499+
459500 return '' ;
460501}
461502
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+
462533function formatValue ( ctx , value , recurseTimes ) {
463534 // Primitive types cannot have properties
464535 if ( typeof value !== 'object' && typeof value !== 'function' ) {
@@ -539,7 +610,7 @@ function formatValue(ctx, value, recurseTimes) {
539610 }
540611
541612 if ( symbols . length !== 0 )
542- symbols = symbols . filter ( ( key ) => propertyIsEnumerable . call ( value , key ) ) ;
613+ symbols = symbols . filter ( ( key ) => propertyIsEnumerable ( value , key ) ) ;
543614 }
544615
545616 const keyLength = keys . length + symbols . length ;
@@ -552,8 +623,8 @@ function formatValue(ctx, value, recurseTimes) {
552623 let formatter = formatObject ;
553624 let braces ;
554625 let noIterator = true ;
555- let raw ;
556626 let extra ;
627+ let i = 0 ;
557628
558629 // Iterators and the rest are split to reduce checks
559630 if ( value [ Symbol . iterator ] ) {
@@ -587,34 +658,16 @@ function formatValue(ctx, value, recurseTimes) {
587658 braces = [ `[${ tag } ] {` , '}' ] ;
588659 formatter = formatSetIterator ;
589660 } 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 ;
609662 }
610663 }
611664 if ( noIterator ) {
612665 braces = [ '{' , '}' ] ;
613666 if ( constructor === 'Object' ) {
614667 if ( isArgumentsObject ( value ) ) {
615- braces [ 0 ] = '[Arguments] {' ;
616668 if ( keyLength === 0 )
617669 return '[Arguments] {}' ;
670+ braces [ 0 ] = '[Arguments] {' ;
618671 } else if ( tag !== '' ) {
619672 braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
620673 if ( keyLength === 0 ) {
@@ -624,24 +677,24 @@ function formatValue(ctx, value, recurseTimes) {
624677 return '{}' ;
625678 }
626679 } 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 } ` : '' } ` ;
629682 if ( keyLength === 0 )
630683 return ctx . stylize ( `[${ name } ]` , 'special' ) ;
631684 base = `[${ name } ]` ;
632685 } else if ( isRegExp ( value ) ) {
633686 // Make RegExps say that they are RegExps
634687 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 ) } ` ;
637690 } else if ( isDate ( value ) ) {
691+ // Make dates with properties first say the date
638692 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' ) ;
642696 }
643- // Make dates with properties first say the date
644- base = dateToISOString . call ( value ) ;
697+ base = dateToISOString ( value ) ;
645698 } else if ( isError ( value ) ) {
646699 // Make error with message first say the error
647700 base = formatError ( value ) ;
@@ -666,28 +719,31 @@ function formatValue(ctx, value, recurseTimes) {
666719 // Fast path for ArrayBuffer and SharedArrayBuffer.
667720 // Can't do the same for DataView because it has a non-primitive
668721 // .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+ }
670726 if ( keyLength === 0 )
671727 return prefix +
672728 `{ byteLength: ${ formatNumber ( ctx . stylize , value . byteLength ) } }` ;
673729 braces [ 0 ] = `${ prefix } {` ;
674730 keys . unshift ( 'byteLength' ) ;
675731 } else if ( isDataView ( value ) ) {
676- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
732+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'DataView' ) } {` ;
677733 // .buffer goes last, it's not a primitive like the others.
678734 keys . unshift ( 'byteLength' , 'byteOffset' , 'buffer' ) ;
679735 } else if ( isPromise ( value ) ) {
680- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
736+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'Promise' ) } {` ;
681737 formatter = formatPromise ;
682738 } else if ( isWeakSet ( value ) ) {
683- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
739+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakSet' ) } {` ;
684740 if ( ctx . showHidden ) {
685741 formatter = formatWeakSet ;
686742 } else {
687743 extra = '<items unknown>' ;
688744 }
689745 } else if ( isWeakMap ( value ) ) {
690- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
746+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakMap' ) } {` ;
691747 if ( ctx . showHidden ) {
692748 formatter = formatWeakMap ;
693749 } else {
@@ -696,43 +752,67 @@ function formatValue(ctx, value, recurseTimes) {
696752 } else if ( types . isModuleNamespaceObject ( value ) ) {
697753 braces [ 0 ] = `[${ tag } ] {` ;
698754 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 ) } {}` ;
699814 } 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 ) } {` ;
736816 }
737817 }
738818
@@ -765,7 +845,7 @@ function formatValue(ctx, value, recurseTimes) {
765845 if ( extra !== undefined )
766846 output . unshift ( extra ) ;
767847
768- for ( var i = 0 ; i < symbols . length ; i ++ ) {
848+ for ( i = 0 ; i < symbols . length ; i ++ ) {
769849 output . push ( formatProperty ( ctx , value , recurseTimes , symbols [ i ] , 0 ) ) ;
770850 }
771851
@@ -835,7 +915,7 @@ function formatPrimitive(fn, value, ctx) {
835915}
836916
837917function formatError ( value ) {
838- return value . stack || errorToString . call ( value ) ;
918+ return value . stack || errorToString ( value ) ;
839919}
840920
841921function formatObject ( ctx , value , recurseTimes , keys ) {
0 commit comments