@@ -65,7 +65,15 @@ const kFormatForStdout = Symbol('kFormatForStdout');
6565const kGetInspectOptions = Symbol ( 'kGetInspectOptions' ) ;
6666const kColorMode = Symbol ( 'kColorMode' ) ;
6767const kIsConsole = Symbol ( 'kIsConsole' ) ;
68-
68+ const kWriteToConsole = Symbol ( 'kWriteToConsole' ) ;
69+ const kBindProperties = Symbol ( 'kBindProperties' ) ;
70+ const kBindStreamsEager = Symbol ( 'kBindStreamsEager' ) ;
71+ const kBindStreamsLazy = Symbol ( 'kBindStreamsLazy' ) ;
72+ const kUseStdout = Symbol ( 'kUseStdout' ) ;
73+ const kUseStderr = Symbol ( 'kUseStderr' ) ;
74+
75+ // This constructor is not used to construct the global console.
76+ // It's exported for backwards compatibility.
6977function Console ( options /* or: stdout, stderr, ignoreErrors = true */ ) {
7078 // We have to test new.target here to see if this function is called
7179 // with new, because we need to define a custom instanceof to accommodate
@@ -74,7 +82,6 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
7482 return new Console ( ...arguments ) ;
7583 }
7684
77- this [ kIsConsole ] = true ;
7885 if ( ! options || typeof options . write === 'function' ) {
7986 options = {
8087 stdout : options ,
@@ -97,37 +104,9 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
97104 throw new ERR_CONSOLE_WRITABLE_STREAM ( 'stderr' ) ;
98105 }
99106
100- const prop = {
101- writable : true ,
102- enumerable : false ,
103- configurable : true
104- } ;
105- Object . defineProperty ( this , '_stdout' , { ...prop , value : stdout } ) ;
106- Object . defineProperty ( this , '_stderr' , { ...prop , value : stderr } ) ;
107- Object . defineProperty ( this , '_ignoreErrors' , {
108- ...prop ,
109- value : Boolean ( ignoreErrors ) ,
110- } ) ;
111- Object . defineProperty ( this , '_times' , { ...prop , value : new Map ( ) } ) ;
112- Object . defineProperty ( this , '_stdoutErrorHandler' , {
113- ...prop ,
114- value : createWriteErrorHandler ( stdout ) ,
115- } ) ;
116- Object . defineProperty ( this , '_stderrErrorHandler' , {
117- ...prop ,
118- value : createWriteErrorHandler ( stderr ) ,
119- } ) ;
120-
121107 if ( typeof colorMode !== 'boolean' && colorMode !== 'auto' )
122108 throw new ERR_INVALID_ARG_VALUE ( 'colorMode' , colorMode ) ;
123109
124- // Corresponds to https://console.spec.whatwg.org/#count-map
125- this [ kCounts ] = new Map ( ) ;
126- this [ kColorMode ] = colorMode ;
127-
128- Object . defineProperty ( this , kGroupIndent , { writable : true } ) ;
129- this [ kGroupIndent ] = '' ;
130-
131110 // Bind the prototype functions to this Console instance
132111 var keys = Object . keys ( Console . prototype ) ;
133112 for ( var v = 0 ; v < keys . length ; v ++ ) {
@@ -137,14 +116,92 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
137116 // from the prototype chain of the subclass.
138117 this [ k ] = this [ k ] . bind ( this ) ;
139118 }
119+
120+ this [ kBindStreamsEager ] ( stdout , stderr ) ;
121+ this [ kBindProperties ] ( ignoreErrors , colorMode ) ;
140122}
141123
124+ const consolePropAttributes = {
125+ writable : true ,
126+ enumerable : false ,
127+ configurable : true
128+ } ;
129+
130+ // Fixup global.console instanceof global.console.Console
131+ Object . defineProperty ( Console , Symbol . hasInstance , {
132+ value ( instance ) {
133+ return instance [ kIsConsole ] ;
134+ }
135+ } ) ;
136+
137+ // Eager version for the Console constructor
138+ Console . prototype [ kBindStreamsEager ] = function ( stdout , stderr ) {
139+ Object . defineProperties ( this , {
140+ '_stdout' : { ...consolePropAttributes , value : stdout } ,
141+ '_stderr' : { ...consolePropAttributes , value : stderr }
142+ } ) ;
143+ } ;
144+
145+ // Lazily load the stdout and stderr from an object so we don't
146+ // create the stdio streams when they are not even accessed
147+ Console . prototype [ kBindStreamsLazy ] = function ( object ) {
148+ let stdout ;
149+ let stderr ;
150+ Object . defineProperties ( this , {
151+ '_stdout' : {
152+ enumerable : false ,
153+ configurable : true ,
154+ get ( ) {
155+ if ( ! stdout ) stdout = object . stdout ;
156+ return stdout ;
157+ } ,
158+ set ( value ) { stdout = value ; }
159+ } ,
160+ '_stderr' : {
161+ enumerable : false ,
162+ configurable : true ,
163+ get ( ) {
164+ if ( ! stderr ) { stderr = object . stderr ; }
165+ return stderr ;
166+ } ,
167+ set ( value ) { stderr = value ; }
168+ }
169+ } ) ;
170+ } ;
171+
172+ Console . prototype [ kBindProperties ] = function ( ignoreErrors , colorMode ) {
173+ Object . defineProperties ( this , {
174+ '_stdoutErrorHandler' : {
175+ ...consolePropAttributes ,
176+ value : createWriteErrorHandler ( this , kUseStdout )
177+ } ,
178+ '_stderrErrorHandler' : {
179+ ...consolePropAttributes ,
180+ value : createWriteErrorHandler ( this , kUseStderr )
181+ } ,
182+ '_ignoreErrors' : {
183+ ...consolePropAttributes ,
184+ value : Boolean ( ignoreErrors )
185+ } ,
186+ '_times' : { ...consolePropAttributes , value : new Map ( ) }
187+ } ) ;
188+
189+ // TODO(joyeecheung): use consolePropAttributes for these
190+ // Corresponds to https://console.spec.whatwg.org/#count-map
191+ this [ kCounts ] = new Map ( ) ;
192+ this [ kColorMode ] = colorMode ;
193+ this [ kIsConsole ] = true ;
194+ this [ kGroupIndent ] = '' ;
195+ } ;
196+
142197// Make a function that can serve as the callback passed to `stream.write()`.
143- function createWriteErrorHandler ( stream ) {
198+ function createWriteErrorHandler ( instance , streamSymbol ) {
144199 return ( err ) => {
145200 // This conditional evaluates to true if and only if there was an error
146201 // that was not already emitted (which happens when the _write callback
147202 // is invoked asynchronously).
203+ const stream = streamSymbol === kUseStdout ?
204+ instance . _stdout : instance . _stderr ;
148205 if ( err !== null && ! stream . _writableState . errorEmitted ) {
149206 // If there was an error, it will be emitted on `stream` as
150207 // an `error` event. Adding a `once` listener will keep that error
@@ -158,7 +215,15 @@ function createWriteErrorHandler(stream) {
158215 } ;
159216}
160217
161- function write ( ignoreErrors , stream , string , errorhandler , groupIndent ) {
218+ Console . prototype [ kWriteToConsole ] = function ( streamSymbol , string ) {
219+ const ignoreErrors = this . _ignoreErrors ;
220+ const groupIndent = this [ kGroupIndent ] ;
221+
222+ const useStdout = streamSymbol === kUseStdout ;
223+ const stream = useStdout ? this . _stdout : this . _stderr ;
224+ const errorHandler = useStdout ?
225+ this . _stdoutErrorHandler : this . _stderrErrorHandler ;
226+
162227 if ( groupIndent . length !== 0 ) {
163228 if ( string . indexOf ( '\n' ) !== - 1 ) {
164229 string = string . replace ( / \n / g, `\n${ groupIndent } ` ) ;
@@ -176,7 +241,7 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) {
176241 // Add and later remove a noop error handler to catch synchronous errors.
177242 stream . once ( 'error' , noop ) ;
178243
179- stream . write ( string , errorhandler ) ;
244+ stream . write ( string , errorHandler ) ;
180245 } catch ( e ) {
181246 // Console is a debugging utility, so it swallowing errors is not desirable
182247 // even in edge cases such as low stack space.
@@ -186,7 +251,7 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) {
186251 } finally {
187252 stream . removeListener ( 'error' , noop ) ;
188253 }
189- }
254+ } ;
190255
191256const kColorInspectOptions = { colors : true } ;
192257const kNoColorInspectOptions = { } ;
@@ -212,23 +277,17 @@ Console.prototype[kFormatForStderr] = function(args) {
212277} ;
213278
214279Console . prototype . log = function log ( ...args ) {
215- write ( this . _ignoreErrors ,
216- this . _stdout ,
217- this [ kFormatForStdout ] ( args ) ,
218- this . _stdoutErrorHandler ,
219- this [ kGroupIndent ] ) ;
280+ this [ kWriteToConsole ] ( kUseStdout , this [ kFormatForStdout ] ( args ) ) ;
220281} ;
282+
221283Console . prototype . debug = Console . prototype . log ;
222284Console . prototype . info = Console . prototype . log ;
223285Console . prototype . dirxml = Console . prototype . log ;
224286
225287Console . prototype . warn = function warn ( ...args ) {
226- write ( this . _ignoreErrors ,
227- this . _stderr ,
228- this [ kFormatForStderr ] ( args ) ,
229- this . _stderrErrorHandler ,
230- this [ kGroupIndent ] ) ;
288+ this [ kWriteToConsole ] ( kUseStderr , this [ kFormatForStderr ] ( args ) ) ;
231289} ;
290+
232291Console . prototype . error = Console . prototype . warn ;
233292
234293Console . prototype . dir = function dir ( object , options ) {
@@ -237,11 +296,7 @@ Console.prototype.dir = function dir(object, options) {
237296 ...this [ kGetInspectOptions ] ( this . _stdout ) ,
238297 ...options
239298 } ;
240- write ( this . _ignoreErrors ,
241- this . _stdout ,
242- util . inspect ( object , options ) ,
243- this . _stdoutErrorHandler ,
244- this [ kGroupIndent ] ) ;
299+ this [ kWriteToConsole ] ( kUseStdout , util . inspect ( object , options ) ) ;
245300} ;
246301
247302Console . prototype . time = function time ( label = 'default' ) {
@@ -301,7 +356,7 @@ Console.prototype.trace = function trace(...args) {
301356Console . prototype . assert = function assert ( expression , ...args ) {
302357 if ( ! expression ) {
303358 args [ 0 ] = `Assertion failed${ args . length === 0 ? '' : `: ${ args [ 0 ] } ` } ` ;
304- this . warn ( this [ kFormatForStderr ] ( args ) ) ;
359+ this . warn ( ... args ) ; // the arguments will be formatted in warn() again
305360 }
306361} ;
307362
@@ -363,7 +418,6 @@ const valuesKey = 'Values';
363418const indexKey = '(index)' ;
364419const iterKey = '(iteration index)' ;
365420
366-
367421const isArray = ( v ) => ArrayIsArray ( v ) || isTypedArray ( v ) || isBuffer ( v ) ;
368422
369423// https://console.spec.whatwg.org/#table
@@ -490,38 +544,31 @@ function noop() {}
490544// we cannot actually use `new Console` to construct the global console.
491545// Therefore, the console.Console.prototype is not
492546// in the global console prototype chain anymore.
547+
548+ // TODO(joyeecheung):
549+ // - Move the Console constructor into internal/console.js
550+ // - Move the global console creation code along with the inspector console
551+ // wrapping code in internal/bootstrap/node.js into a separate file.
552+ // - Make this file a simple re-export of those two files.
493553// This is only here for v11.x conflict resolution.
494554const globalConsole = Object . create ( Console . prototype ) ;
495- const tempConsole = new Console ( {
496- stdout : process . stdout ,
497- stderr : process . stderr
498- } ) ;
499555
500556// Since Console is not on the prototype chain of the global console,
501557// the symbol properties on Console.prototype have to be looked up from
502- // the global console itself.
503- for ( const prop of Object . getOwnPropertySymbols ( Console . prototype ) ) {
504- globalConsole [ prop ] = Console . prototype [ prop ] ;
505- }
506-
507- // Reflect.ownKeys() is used here for retrieving Symbols
508- for ( const prop of Reflect . ownKeys ( tempConsole ) ) {
509- const desc = { ...( Reflect . getOwnPropertyDescriptor ( tempConsole , prop ) ) } ;
510- // Since Console would bind method calls onto the instance,
511- // make sure the methods are called on globalConsole instead of
512- // tempConsole.
513- if ( typeof Console . prototype [ prop ] === 'function' ) {
514- desc . value = Console . prototype [ prop ] . bind ( globalConsole ) ;
558+ // the global console itself. In addition, we need to make the global
559+ // console a namespace by binding the console methods directly onto
560+ // the global console with the receiver fixed.
561+ for ( const prop of Reflect . ownKeys ( Console . prototype ) ) {
562+ if ( prop === 'constructor' ) { continue ; }
563+ const desc = Reflect . getOwnPropertyDescriptor ( Console . prototype , prop ) ;
564+ if ( typeof desc . value === 'function' ) { // fix the receiver
565+ desc . value = desc . value . bind ( globalConsole ) ;
515566 }
516567 Reflect . defineProperty ( globalConsole , prop , desc ) ;
517568}
518569
519- globalConsole . Console = Console ;
520-
521- Object . defineProperty ( Console , Symbol . hasInstance , {
522- value ( instance ) {
523- return instance [ kIsConsole ] ;
524- }
525- } ) ;
570+ globalConsole [ kBindStreamsLazy ] ( process ) ;
571+ globalConsole [ kBindProperties ] ( true , 'auto' ) ;
526572
527573module . exports = globalConsole ;
574+ module . exports . Console = Console ;
0 commit comments