Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

events: make sure console functions exist #4479

Conversation

davidvgalbraith
Copy link

If there's no global console cached, these calls will initialize it,
which in some cases can cause a circular dependency, making the console
received here an empty object. The program shouldn't crash in this case.

Fixes #4467

if there's no global console cached, these calls will initialize it,
which in some cases can cause a circular dependency, making the console
received here an empty object. The program shouldn't crash in this case.

Fixes nodejs#4467
@JungMinu JungMinu added the events Issues and PRs related to the events subsystem / EventEmitter. label Dec 30, 2015
@davidvgalbraith
Copy link
Author

A more general fix would be to always eagerly cache the global console object. I am not sure what the startup performance tradeoff equation is with that, so I went with this minimal fix for the issue at hand.

@JungMinu JungMinu added lib / src Issues and PRs related to general changes in the lib or src directory. util Issues and PRs related to the built-in util module. and removed lib / src Issues and PRs related to general changes in the lib or src directory. labels Dec 30, 2015
@jasnell
Copy link
Member

jasnell commented Dec 30, 2015

@nodejs/ctc

const e = new events.EventEmitter();
e.on('hello', function() {});

assert.ok(!e._events['hello'].hasOwnProperty('warned'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be better to use a public API to check, such as:

assert.ok(!e.listeners('hello')[0].hasOwnProperty('warned'));

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree that it's the most efficient approach, using an internal API in a test is a bit troublesome. The "correct" way to do this would likely be to direct the stderr/stdout output to a stream and apply a regex to ensure that the result is as the user should expect it to be. It's a bit of a workaround but it ensures that we're testing exactly what the user would see.

@davidvgalbraith
Copy link
Author

I thought up a better way to do this: now I include a reference to console in the setter for EventEmitter.defaultMaxListeners, so it'll always be compiled after you change the property. The advantage of this over my old approach is the old way wouldn't log the warning it got where console was uninitialized, whereas this approach always has the console initialized by the time it gets to logging the warning. It also was a strange leap of logic to have util.js and events.js accounting for a potentially uninitialized console, so I like this better.

@jasnell
Copy link
Member

jasnell commented Jan 4, 2016

Much better approach. I always get a bit concerned when replacing bare properties with getter/setter but in this instance I cannot imagine that there'd be any issue. The actual code change LGTM but I think the test needs a bit more work to avoid using the internal API.

@jasnell jasnell added the semver-minor PRs that contain new features and should be released in the next minor version. label Jan 4, 2016
@jasnell
Copy link
Member

jasnell commented Jan 4, 2016

Marking as a semver-minor due to the switch to using a getter/setter

@davidvgalbraith
Copy link
Author

I refactored the test as per your suggestion @jasnell.

@jasnell
Copy link
Member

jasnell commented Jan 8, 2016

@jasnell
Copy link
Member

jasnell commented Jan 8, 2016

LGTM if CI is green


const assert = require('assert');
const events = require('events');
const leak_warning = /EventEmitter memory leak detected. 2 hello listeners/;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this dot meant to be matched as a dot? You should escape it in that case.

@silverwind
Copy link
Contributor

LGTM besides the two nits above.

eslint-disable required-modules
escape regex dot
@davidvgalbraith
Copy link
Author

Good ideas @silverwind, I've incorporated them.

@silverwind
Copy link
Contributor

const old_default = events.defaultMaxListeners;
events.defaultMaxListeners = 1;

const e = new events.EventEmitter();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant EventEmitter

@davidvgalbraith
Copy link
Author

Thanks @thefourtheye, updated.

@silverwind
Copy link
Contributor

CI likes it. Still LGTM.

e.on('hello', function() {});
e.on('hello', function() {});

assert.equal(write_calls, 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also assert if the console object is properly initialized by now?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's any way we can do that without incidentally initializing it. Our prior logging would have failed if console didn't initialize properly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about checking if console.error function exists by the time control reaches here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such a reference to console.error will compile console if it isn't already compiled.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we could inspect require('native_module')._cache.console.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scratch that -- Error: Cannot find module 'native_module'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, my office time has started. I'll think about this when I find time today. Let's not block this PR because of this. Perhaps you might want to leave a TODO or something in the test for the timebeing.

else if (write_calls === 1)
assert.ok(data.match(/Trace/));
else
assert.ok(false, 'stderr.write should be called only twice');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why we have assert.equal(write_calls, 2); at the end, right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I felt like I should have some sort of catch-all else statement here though.

e.on('hello', function() {});
e.on('hello', function() {});

// TODO validate console
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add the comment vs. adding the validation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per earlier discussion (#4479 (diff)) I don't think it's actually possible to validate the console here in any significant way, but @thefourtheye requested a comment about validating it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh :-) lol completely missed that one

@jasnell
Copy link
Member

jasnell commented Jan 15, 2016

LGTM

jasnell pushed a commit that referenced this pull request Jan 15, 2016
If there's no global console cached, initialize it.

Fixes: #4467
PR-URL: #4479
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@jasnell
Copy link
Member

jasnell commented Jan 15, 2016

Landed in f9a59c1...

@davidvgalbraith .. I squashed the commits and reworked the commit message to make sure it fit within our style guidelines. I also went ahead and expanded that TODO comment in the test case to make sure it describes exactly why we're leaving it as a TODO

@jasnell jasnell closed this Jan 15, 2016
@davidvgalbraith
Copy link
Author

Thanks, @jasnell! I'm glad we got this one in.

evanlucas pushed a commit that referenced this pull request Jan 18, 2016
If there's no global console cached, initialize it.

Fixes: #4467
PR-URL: #4479
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: James M Snell <[email protected]>
evanlucas added a commit that referenced this pull request Jan 20, 2016
Notable changes:

* events: make sure console functions exist (Dave) #4479
* fs: add autoClose option to fs.createWriteStream (Saquib) #3679
* http: improves expect header handling (Daniel Sellers) #4501
* node: allow preload modules with -i (Evan Lucas) #4696
* v8,src: expose statistics about heap spaces (`v8.getHeapSpaceStatistics()`) (Ben Ripkens) #4463
* Minor performance improvements:
  - lib: Use arrow functions instead of bind where possible (Minwoo Jung) #3622
  - module: cache stat() results more aggressively (Ben Noordhuis) #4575
  - querystring: improve parse() performance (Brian White) #4675

PR-URL: #4742
evanlucas added a commit that referenced this pull request Jan 21, 2016
Notable changes:

* events: make sure console functions exist (Dave) #4479
* fs: add autoClose option to fs.createWriteStream (Saquib) #3679
* http: improves expect header handling (Daniel Sellers) #4501
* node: allow preload modules with -i (Evan Lucas) #4696
* v8,src: expose statistics about heap spaces (`v8.getHeapSpaceStatistics()`) (Ben Ripkens) #4463
* Minor performance improvements:
  - lib: Use arrow functions instead of bind where possible (Minwoo Jung) #3622
  - module: cache stat() results more aggressively (Ben Noordhuis) #4575
  - querystring: improve parse() performance (Brian White) #4675

PR-URL: #4742
scovetta pushed a commit to scovetta/node that referenced this pull request Apr 2, 2016
If there's no global console cached, initialize it.

Fixes: nodejs#4467
PR-URL: nodejs#4479
Reviewed-By: Roman Reiss <[email protected]>
Reviewed-By: James M Snell <[email protected]>
scovetta pushed a commit to scovetta/node that referenced this pull request Apr 2, 2016
Notable changes:

* events: make sure console functions exist (Dave) nodejs#4479
* fs: add autoClose option to fs.createWriteStream (Saquib) nodejs#3679
* http: improves expect header handling (Daniel Sellers) nodejs#4501
* node: allow preload modules with -i (Evan Lucas) nodejs#4696
* v8,src: expose statistics about heap spaces (`v8.getHeapSpaceStatistics()`) (Ben Ripkens) nodejs#4463
* Minor performance improvements:
  - lib: Use arrow functions instead of bind where possible (Minwoo Jung) nodejs#3622
  - module: cache stat() results more aggressively (Ben Noordhuis) nodejs#4575
  - querystring: improve parse() performance (Brian White) nodejs#4675

PR-URL: nodejs#4742
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
events Issues and PRs related to the events subsystem / EventEmitter. semver-minor PRs that contain new features and should be released in the next minor version. util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants