@@ -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 ;
@@ -407,7 +445,7 @@ function getConstructorName(obj) {
407445 return '' ;
408446}
409447
410- function getPrefix ( constructor , tag ) {
448+ function getPrefix ( constructor , tag , fallback ) {
411449 if ( constructor !== '' ) {
412450 if ( tag !== '' && constructor !== tag ) {
413451 return `${ constructor } [${ tag } ] ` ;
@@ -418,9 +456,42 @@ function getPrefix(constructor, tag) {
418456 if ( tag !== '' )
419457 return `[${ tag } ] ` ;
420458
459+ if ( fallback !== undefined )
460+ return `${ fallback } ` ;
461+
421462 return '' ;
422463}
423464
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+
424495function formatValue ( ctx , value , recurseTimes ) {
425496 // Primitive types cannot have properties
426497 if ( typeof value !== 'object' && typeof value !== 'function' ) {
@@ -511,7 +582,7 @@ function formatValue(ctx, value, recurseTimes) {
511582 }
512583
513584 if ( symbols . length !== 0 )
514- symbols = symbols . filter ( ( key ) => propertyIsEnumerable . call ( value , key ) ) ;
585+ symbols = symbols . filter ( ( key ) => propertyIsEnumerable ( value , key ) ) ;
515586 }
516587
517588 const keyLength = keys . length + symbols . length ;
@@ -524,8 +595,8 @@ function formatValue(ctx, value, recurseTimes) {
524595 let formatter = formatObject ;
525596 let braces ;
526597 let noIterator = true ;
527- let raw ;
528598 let extra ;
599+ let i = 0 ;
529600
530601 // Iterators and the rest are split to reduce checks
531602 if ( value [ Symbol . iterator ] ) {
@@ -559,34 +630,16 @@ function formatValue(ctx, value, recurseTimes) {
559630 braces = [ `[${ tag } ] {` , '}' ] ;
560631 formatter = formatSetIterator ;
561632 } 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 ;
581634 }
582635 }
583636 if ( noIterator ) {
584637 braces = [ '{' , '}' ] ;
585638 if ( constructor === 'Object' ) {
586639 if ( isArgumentsObject ( value ) ) {
587- braces [ 0 ] = '[Arguments] {' ;
588640 if ( keyLength === 0 )
589641 return '[Arguments] {}' ;
642+ braces [ 0 ] = '[Arguments] {' ;
590643 } else if ( tag !== '' ) {
591644 braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
592645 if ( keyLength === 0 ) {
@@ -596,24 +649,24 @@ function formatValue(ctx, value, recurseTimes) {
596649 return '{}' ;
597650 }
598651 } 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 } ` : '' } ` ;
601654 if ( keyLength === 0 )
602655 return ctx . stylize ( `[${ name } ]` , 'special' ) ;
603656 base = `[${ name } ]` ;
604657 } else if ( isRegExp ( value ) ) {
605658 // Make RegExps say that they are RegExps
606659 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 ) } ` ;
609662 } else if ( isDate ( value ) ) {
663+ // Make dates with properties first say the date
610664 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' ) ;
614668 }
615- // Make dates with properties first say the date
616- base = dateToISOString . call ( value ) ;
669+ base = dateToISOString ( value ) ;
617670 } else if ( isError ( value ) ) {
618671 // Make error with message first say the error
619672 base = formatError ( value ) ;
@@ -638,28 +691,31 @@ function formatValue(ctx, value, recurseTimes) {
638691 // Fast path for ArrayBuffer and SharedArrayBuffer.
639692 // Can't do the same for DataView because it has a non-primitive
640693 // .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+ }
642698 if ( keyLength === 0 )
643699 return prefix +
644700 `{ byteLength: ${ formatNumber ( ctx . stylize , value . byteLength ) } }` ;
645701 braces [ 0 ] = `${ prefix } {` ;
646702 keys . unshift ( 'byteLength' ) ;
647703 } else if ( isDataView ( value ) ) {
648- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
704+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'DataView' ) } {` ;
649705 // .buffer goes last, it's not a primitive like the others.
650706 keys . unshift ( 'byteLength' , 'byteOffset' , 'buffer' ) ;
651707 } else if ( isPromise ( value ) ) {
652- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
708+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'Promise' ) } {` ;
653709 formatter = formatPromise ;
654710 } else if ( isWeakSet ( value ) ) {
655- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
711+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakSet' ) } {` ;
656712 if ( ctx . showHidden ) {
657713 formatter = formatWeakSet ;
658714 } else {
659715 extra = '[items unknown]' ;
660716 }
661717 } else if ( isWeakMap ( value ) ) {
662- braces [ 0 ] = `${ getPrefix ( constructor , tag ) } {` ;
718+ braces [ 0 ] = `${ getPrefix ( constructor , tag , 'WeakMap' ) } {` ;
663719 if ( ctx . showHidden ) {
664720 formatter = formatWeakMap ;
665721 } else {
@@ -668,43 +724,67 @@ function formatValue(ctx, value, recurseTimes) {
668724 } else if ( types . isModuleNamespaceObject ( value ) ) {
669725 braces [ 0 ] = `[${ tag } ] {` ;
670726 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 ) } {}` ;
671786 } 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 ) } {` ;
708788 }
709789 }
710790
@@ -737,7 +817,7 @@ function formatValue(ctx, value, recurseTimes) {
737817 if ( extra !== undefined )
738818 output . unshift ( extra ) ;
739819
740- for ( var i = 0 ; i < symbols . length ; i ++ ) {
820+ for ( i = 0 ; i < symbols . length ; i ++ ) {
741821 output . push ( formatProperty ( ctx , value , recurseTimes , symbols [ i ] , 0 ) ) ;
742822 }
743823
@@ -803,7 +883,7 @@ function formatPrimitive(fn, value, ctx) {
803883}
804884
805885function formatError ( value ) {
806- return value . stack || errorToString . call ( value ) ;
886+ return value . stack || errorToString ( value ) ;
807887}
808888
809889function formatObject ( ctx , value , recurseTimes , keys ) {
0 commit comments