Skip to content

Commit e38bade

Browse files
committed
events: don't inherit from Object.prototype
This commit safely allows event names that are named the same as properties that are ordinarily inherited from Object.prototype such as __proto__. Fixes: #728 PR-URL: #6092 Reviewed-By: Evan Lucas <evanlucas@me.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent dba245f commit e38bade

5 files changed

+52
-21
lines changed

lib/events.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
var domain;
44

5+
// This constructor is used to store event handlers. Instantiating this is
6+
// faster than explicitly calling `Object.create(null)` to get a "clean" empty
7+
// object (tested with v8 v4.9).
8+
function EventHandlers() {}
9+
EventHandlers.prototype = Object.create(null);
10+
511
function EventEmitter() {
612
EventEmitter.init.call(this);
713
}
@@ -44,7 +50,7 @@ EventEmitter.init = function() {
4450
}
4551

4652
if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
47-
this._events = {};
53+
this._events = new EventHandlers();
4854
this._eventsCount = 0;
4955
}
5056

@@ -211,7 +217,7 @@ EventEmitter.prototype.addListener = function addListener(type, listener) {
211217

212218
events = this._events;
213219
if (!events) {
214-
events = this._events = {};
220+
events = this._events = new EventHandlers();
215221
this._eventsCount = 0;
216222
} else {
217223
// To avoid recursion in the case that type === "newListener"! Before
@@ -296,7 +302,7 @@ EventEmitter.prototype.removeListener =
296302

297303
if (list === listener || (list.listener && list.listener === listener)) {
298304
if (--this._eventsCount === 0)
299-
this._events = {};
305+
this._events = new EventHandlers();
300306
else {
301307
delete events[type];
302308
if (events.removeListener)
@@ -319,7 +325,7 @@ EventEmitter.prototype.removeListener =
319325
if (list.length === 1) {
320326
list[0] = undefined;
321327
if (--this._eventsCount === 0) {
322-
this._events = {};
328+
this._events = new EventHandlers();
323329
return this;
324330
} else {
325331
delete events[type];
@@ -346,11 +352,11 @@ EventEmitter.prototype.removeAllListeners =
346352
// not listening for removeListener, no need to emit
347353
if (!events.removeListener) {
348354
if (arguments.length === 0) {
349-
this._events = {};
355+
this._events = new EventHandlers();
350356
this._eventsCount = 0;
351357
} else if (events[type]) {
352358
if (--this._eventsCount === 0)
353-
this._events = {};
359+
this._events = new EventHandlers();
354360
else
355361
delete events[type];
356362
}
@@ -366,7 +372,7 @@ EventEmitter.prototype.removeAllListeners =
366372
this.removeAllListeners(key);
367373
}
368374
this.removeAllListeners('removeListener');
369-
this._events = {};
375+
this._events = new EventHandlers();
370376
this._eventsCount = 0;
371377
return this;
372378
}

src/node.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -3228,7 +3228,9 @@ void SetupProcessObject(Environment* env,
32283228
env->SetMethod(process, "_setupDomainUse", SetupDomainUse);
32293229

32303230
// pre-set _events object for faster emit checks
3231-
process->Set(env->events_string(), Object::New(env->isolate()));
3231+
Local<Object> events_obj = Object::New(env->isolate());
3232+
events_obj->SetPrototype(env->context(), Null(env->isolate()));
3233+
process->Set(env->events_string(), events_obj);
32323234
}
32333235

32343236

test/known_issues/test-events-known-properties.js

-12
This file was deleted.

test/parallel/test-event-emitter-listeners-side-effects.js

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ assert(fl.length === 1);
2121
assert(fl[0] === assert.fail);
2222

2323
e.listeners('bar');
24-
assert(!e._events.hasOwnProperty('bar'));
2524

2625
e.on('foo', assert.ok);
2726
fl = e.listeners('foo');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const EventEmitter = require('events');
5+
const assert = require('assert');
6+
7+
const ee = new EventEmitter();
8+
const handler = () => {};
9+
10+
assert.strictEqual(ee._events.hasOwnProperty, undefined);
11+
assert.strictEqual(ee._events.toString, undefined);
12+
13+
ee.on('__proto__', handler);
14+
ee.on('__defineGetter__', handler);
15+
ee.on('toString', handler);
16+
17+
assert.deepStrictEqual(ee.eventNames(), [
18+
'__proto__',
19+
'__defineGetter__',
20+
'toString'
21+
]);
22+
23+
assert.deepStrictEqual(ee.listeners('__proto__'), [handler]);
24+
assert.deepStrictEqual(ee.listeners('__defineGetter__'), [handler]);
25+
assert.deepStrictEqual(ee.listeners('toString'), [handler]);
26+
27+
ee.on('__proto__', common.mustCall(function(val) {
28+
assert.strictEqual(val, 1);
29+
}));
30+
ee.emit('__proto__', 1);
31+
32+
process.on('__proto__', common.mustCall(function(val) {
33+
assert.strictEqual(val, 1);
34+
}));
35+
process.emit('__proto__', 1);
36+

0 commit comments

Comments
 (0)