summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMatteo Collina <hello@matteocollina.com>2019-05-19 13:55:18 +0200
committerMatteo Collina <hello@matteocollina.com>2019-12-03 12:14:46 +0100
commitae8f20ec5eee55f648823392c9c4e9491c958b60 (patch)
treef9ef3febfd46bf4ac19678e2f5e14b45105e8f09 /lib
parent8cf8eb16ac873c11a6b1051c3a71a91659d431e3 (diff)
downloadandroid-node-v8-ae8f20ec5eee55f648823392c9c4e9491c958b60.tar.gz
android-node-v8-ae8f20ec5eee55f648823392c9c4e9491c958b60.tar.bz2
android-node-v8-ae8f20ec5eee55f648823392c9c4e9491c958b60.zip
events: add captureRejection option
PR-URL: https://github.com/nodejs/node/pull/27867 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaƫl Zasso <targos@protonmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/events.js112
1 files changed, 106 insertions, 6 deletions
diff --git a/lib/events.js b/lib/events.js
index a09333af44..ce93cd9f9b 100644
--- a/lib/events.js
+++ b/lib/events.js
@@ -23,6 +23,7 @@
const {
Array,
+ Boolean,
MathMin,
NumberIsNaN,
ObjectCreate,
@@ -32,6 +33,7 @@ const {
ReflectApply,
ReflectOwnKeys,
} = primordials;
+const kRejection = Symbol.for('nodejs.rejection');
let spliceOne;
@@ -49,8 +51,10 @@ const {
inspect
} = require('internal/util/inspect');
-function EventEmitter() {
- EventEmitter.init.call(this);
+const kCapture = Symbol('kCapture');
+
+function EventEmitter(opts) {
+ EventEmitter.init.call(this, opts);
}
module.exports = EventEmitter;
module.exports.once = once;
@@ -60,6 +64,29 @@ EventEmitter.EventEmitter = EventEmitter;
EventEmitter.usingDomains = false;
+EventEmitter.captureRejectionSymbol = kRejection;
+ObjectDefineProperty(EventEmitter, 'captureRejections', {
+ get() {
+ return EventEmitter.prototype[kCapture];
+ },
+ set(value) {
+ if (typeof value !== 'boolean') {
+ throw new ERR_INVALID_ARG_TYPE('EventEmitter.captureRejections',
+ 'boolean', value);
+ }
+
+ EventEmitter.prototype[kCapture] = value;
+ },
+ enumerable: true
+});
+
+// The default for captureRejections is false
+ObjectDefineProperty(EventEmitter.prototype, kCapture, {
+ value: false,
+ writable: true,
+ enumerable: false
+});
+
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;
@@ -89,7 +116,7 @@ ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', {
}
});
-EventEmitter.init = function() {
+EventEmitter.init = function(opts) {
if (this._events === undefined ||
this._events === ObjectGetPrototypeOf(this)._events) {
@@ -98,8 +125,64 @@ EventEmitter.init = function() {
}
this._maxListeners = this._maxListeners || undefined;
+
+
+ if (opts && opts.captureRejections) {
+ if (typeof opts.captureRejections !== 'boolean') {
+ throw new ERR_INVALID_ARG_TYPE('options.captureRejections',
+ 'boolean', opts.captureRejections);
+ }
+ this[kCapture] = Boolean(opts.captureRejections);
+ } else {
+ // Assigning it directly a prototype lookup, as it slighly expensive
+ // and it sits in a very sensitive hot path.
+ this[kCapture] = EventEmitter.prototype[kCapture];
+ }
};
+function addCatch(that, promise, type, args) {
+ if (!that[kCapture]) {
+ return;
+ }
+
+ // Handle Promises/A+ spec, then could be a getter
+ // that throws on second use.
+ try {
+ const then = promise.then;
+
+ if (typeof then === 'function') {
+ then.call(promise, undefined, function(err) {
+ // The callback is called with nextTick to avoid a follow-up
+ // rejection from this promise.
+ process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
+ });
+ }
+ } catch (err) {
+ that.emit('error', err);
+ }
+}
+
+function emitUnhandledRejectionOrErr(ee, err, type, args) {
+ if (typeof ee[kRejection] === 'function') {
+ ee[kRejection](err, type, ...args);
+ } else {
+ // We have to disable the capture rejections mechanism, otherwise
+ // we might end up in an infinite loop.
+ const prev = ee[kCapture];
+
+ // If the error handler throws, it is not catcheable and it
+ // will end up in 'uncaughtException'. We restore the previous
+ // value of kCapture in case the uncaughtException is present
+ // and the exception is handled.
+ try {
+ ee[kCapture] = false;
+ ee.emit('error', err);
+ } finally {
+ ee[kCapture] = prev;
+ }
+ }
+}
+
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
@@ -216,12 +299,29 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
return false;
if (typeof handler === 'function') {
- ReflectApply(handler, this, args);
+ const result = ReflectApply(handler, this, args);
+
+ // We check if result is undefined first because that
+ // is the most common case so we do not pay any perf
+ // penalty
+ if (result !== undefined && result !== null) {
+ addCatch(this, result, type, args);
+ }
} else {
const len = handler.length;
const listeners = arrayClone(handler, len);
- for (let i = 0; i < len; ++i)
- ReflectApply(listeners[i], this, args);
+ for (var i = 0; i < len; ++i) {
+ const result = ReflectApply(listeners[i], this, args);
+
+ // We check if result is undefined first because that
+ // is the most common case so we do not pay any perf
+ // penalty.
+ // This code is duplicated because extracting it away
+ // would make it non-inlineable.
+ if (result !== undefined && result !== null) {
+ addCatch(this, result, type, args);
+ }
+ }
}
return true;