| // Copyright Joyent, Inc. and other Node contributors. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a |
| // copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to permit |
| // persons to whom the Software is furnished to do so, subject to the |
| // following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| // USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| // Modified by the vm2 team to make this a standalone module to be loaded into the sandbox. |
| |
| 'use strict'; |
| |
| const host = fromhost; |
| |
| const { |
| Boolean, |
| Error, |
| String, |
| Symbol |
| } = globalThis; |
| |
| const ReflectApply = Reflect.apply; |
| const ReflectOwnKeys = Reflect.ownKeys; |
| |
| const ErrorCaptureStackTrace = Error.captureStackTrace; |
| |
| const NumberIsNaN = Number.isNaN; |
| |
| const ObjectCreate = Object.create; |
| const ObjectDefineProperty = Object.defineProperty; |
| const ObjectDefineProperties = Object.defineProperties; |
| const ObjectGetPrototypeOf = Object.getPrototypeOf; |
| |
| const SymbolFor = Symbol.for; |
| |
| function uncurryThis(func) { |
| return (thiz, ...args) => ReflectApply(func, thiz, args); |
| } |
| |
| const ArrayPrototypeIndexOf = uncurryThis(Array.prototype.indexOf); |
| const ArrayPrototypeJoin = uncurryThis(Array.prototype.join); |
| const ArrayPrototypeSlice = uncurryThis(Array.prototype.slice); |
| const ArrayPrototypeSplice = uncurryThis(Array.prototype.splice); |
| const ArrayPrototypeUnshift = uncurryThis(Array.prototype.unshift); |
| |
| const kRejection = SymbolFor('nodejs.rejection'); |
| |
| function inspect(obj) { |
| return typeof obj === 'symbol' ? obj.toString() : `${obj}`; |
| } |
| |
| function spliceOne(list, index) { |
| for (; index + 1 < list.length; index++) |
| list[index] = list[index + 1]; |
| list.pop(); |
| } |
| |
| function assert(what, message) { |
| if (!what) throw new Error(message); |
| } |
| |
| function E(key, msg, Base) { |
| return function NodeError(...args) { |
| const error = new Base(); |
| const message = ReflectApply(msg, error, args); |
| ObjectDefineProperties(error, { |
| message: { |
| value: message, |
| enumerable: false, |
| writable: true, |
| configurable: true, |
| }, |
| toString: { |
| value() { |
| return `${this.name} [${key}]: ${this.message}`; |
| }, |
| enumerable: false, |
| writable: true, |
| configurable: true, |
| }, |
| }); |
| error.code = key; |
| return error; |
| }; |
| } |
| |
| |
| const ERR_INVALID_ARG_TYPE = E('ERR_INVALID_ARG_TYPE', |
| (name, expected, actual) => { |
| assert(typeof name === 'string', "'name' must be a string"); |
| if (!ArrayIsArray(expected)) { |
| expected = [expected]; |
| } |
| |
| let msg = 'The '; |
| if (StringPrototypeEndsWith(name, ' argument')) { |
| // For cases like 'first argument' |
| msg += `${name} `; |
| } else { |
| const type = StringPrototypeIncludes(name, '.') ? 'property' : 'argument'; |
| msg += `"${name}" ${type} `; |
| } |
| msg += 'must be '; |
| |
| const types = []; |
| const instances = []; |
| const other = []; |
| |
| for (const value of expected) { |
| assert(typeof value === 'string', |
| 'All expected entries have to be of type string'); |
| if (ArrayPrototypeIncludes(kTypes, value)) { |
| ArrayPrototypePush(types, StringPrototypeToLowerCase(value)); |
| } else if (RegExpPrototypeTest(classRegExp, value)) { |
| ArrayPrototypePush(instances, value); |
| } else { |
| assert(value !== 'object', |
| 'The value "object" should be written as "Object"'); |
| ArrayPrototypePush(other, value); |
| } |
| } |
| |
| // Special handle `object` in case other instances are allowed to outline |
| // the differences between each other. |
| if (instances.length > 0) { |
| const pos = ArrayPrototypeIndexOf(types, 'object'); |
| if (pos !== -1) { |
| ArrayPrototypeSplice(types, pos, 1); |
| ArrayPrototypePush(instances, 'Object'); |
| } |
| } |
| |
| if (types.length > 0) { |
| if (types.length > 2) { |
| const last = ArrayPrototypePop(types); |
| msg += `one of type ${ArrayPrototypeJoin(types, ', ')}, or ${last}`; |
| } else if (types.length === 2) { |
| msg += `one of type ${types[0]} or ${types[1]}`; |
| } else { |
| msg += `of type ${types[0]}`; |
| } |
| if (instances.length > 0 || other.length > 0) |
| msg += ' or '; |
| } |
| |
| if (instances.length > 0) { |
| if (instances.length > 2) { |
| const last = ArrayPrototypePop(instances); |
| msg += |
| `an instance of ${ArrayPrototypeJoin(instances, ', ')}, or ${last}`; |
| } else { |
| msg += `an instance of ${instances[0]}`; |
| if (instances.length === 2) { |
| msg += ` or ${instances[1]}`; |
| } |
| } |
| if (other.length > 0) |
| msg += ' or '; |
| } |
| |
| if (other.length > 0) { |
| if (other.length > 2) { |
| const last = ArrayPrototypePop(other); |
| msg += `one of ${ArrayPrototypeJoin(other, ', ')}, or ${last}`; |
| } else if (other.length === 2) { |
| msg += `one of ${other[0]} or ${other[1]}`; |
| } else { |
| if (StringPrototypeToLowerCase(other[0]) !== other[0]) |
| msg += 'an '; |
| msg += `${other[0]}`; |
| } |
| } |
| |
| if (actual == null) { |
| msg += `. Received ${actual}`; |
| } else if (typeof actual === 'function' && actual.name) { |
| msg += `. Received function ${actual.name}`; |
| } else if (typeof actual === 'object') { |
| if (actual.constructor && actual.constructor.name) { |
| msg += `. Received an instance of ${actual.constructor.name}`; |
| } else { |
| const inspected = inspect(actual, { depth: -1 }); |
| msg += `. Received ${inspected}`; |
| } |
| } else { |
| let inspected = inspect(actual, { colors: false }); |
| if (inspected.length > 25) |
| inspected = `${StringPrototypeSlice(inspected, 0, 25)}...`; |
| msg += `. Received type ${typeof actual} (${inspected})`; |
| } |
| return msg; |
| }, TypeError); |
| |
| const ERR_INVALID_THIS = E('ERR_INVALID_THIS', s => `Value of "this" must be of type ${s}`, TypeError); |
| |
| const ERR_OUT_OF_RANGE = E('ERR_OUT_OF_RANGE', |
| (str, range, input, replaceDefaultBoolean = false) => { |
| assert(range, 'Missing "range" argument'); |
| let msg = replaceDefaultBoolean ? str : |
| `The value of "${str}" is out of range.`; |
| const received = inspect(input); |
| msg += ` It must be ${range}. Received ${received}`; |
| return msg; |
| }, RangeError); |
| |
| const ERR_UNHANDLED_ERROR = E('ERR_UNHANDLED_ERROR', |
| err => { |
| const msg = 'Unhandled error.'; |
| if (err === undefined) return msg; |
| return `${msg} (${err})`; |
| }, Error); |
| |
| function validateBoolean(value, name) { |
| if (typeof value !== 'boolean') |
| throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value); |
| } |
| |
| function validateFunction(value, name) { |
| if (typeof value !== 'function') |
| throw new ERR_INVALID_ARG_TYPE(name, 'Function', value); |
| } |
| |
| function validateString(value, name) { |
| if (typeof value !== 'string') |
| throw new ERR_INVALID_ARG_TYPE(name, 'string', value); |
| } |
| |
| function nc(cond, e) { |
| return cond === undefined || cond === null ? e : cond; |
| } |
| |
| function oc(base, key) { |
| return base === undefined || base === null ? undefined : base[key]; |
| } |
| |
| const kCapture = Symbol('kCapture'); |
| const kErrorMonitor = host.kErrorMonitor || Symbol('events.errorMonitor'); |
| const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners'); |
| const kMaxEventTargetListenersWarned = |
| Symbol('events.maxEventTargetListenersWarned'); |
| |
| const kIsEventTarget = SymbolFor('nodejs.event_target'); |
| |
| function isEventTarget(obj) { |
| return oc(oc(obj, 'constructor'), kIsEventTarget); |
| } |
| |
| /** |
| * Creates a new `EventEmitter` instance. |
| * @param {{ captureRejections?: boolean; }} [opts] |
| * @constructs {EventEmitter} |
| */ |
| function EventEmitter(opts) { |
| EventEmitter.init.call(this, opts); |
| } |
| module.exports = EventEmitter; |
| if (host.once) module.exports.once = host.once; |
| if (host.on) module.exports.on = host.on; |
| if (host.getEventListeners) module.exports.getEventListeners = host.getEventListeners; |
| // Backwards-compat with node 0.10.x |
| EventEmitter.EventEmitter = EventEmitter; |
| |
| EventEmitter.usingDomains = false; |
| |
| EventEmitter.captureRejectionSymbol = kRejection; |
| ObjectDefineProperty(EventEmitter, 'captureRejections', { |
| get() { |
| return EventEmitter.prototype[kCapture]; |
| }, |
| set(value) { |
| validateBoolean(value, 'EventEmitter.captureRejections'); |
| |
| EventEmitter.prototype[kCapture] = value; |
| }, |
| enumerable: true |
| }); |
| |
| if (host.EventEmitterReferencingAsyncResource) { |
| const kAsyncResource = Symbol('kAsyncResource'); |
| const EventEmitterReferencingAsyncResource = host.EventEmitterReferencingAsyncResource; |
| |
| class EventEmitterAsyncResource extends EventEmitter { |
| /** |
| * @param {{ |
| * name?: string, |
| * triggerAsyncId?: number, |
| * requireManualDestroy?: boolean, |
| * }} [options] |
| */ |
| constructor(options = undefined) { |
| let name; |
| if (typeof options === 'string') { |
| name = options; |
| options = undefined; |
| } else { |
| if (new.target === EventEmitterAsyncResource) { |
| validateString(oc(options, 'name'), 'options.name'); |
| } |
| name = oc(options, 'name') || new.target.name; |
| } |
| super(options); |
| |
| this[kAsyncResource] = |
| new EventEmitterReferencingAsyncResource(this, name, options); |
| } |
| |
| /** |
| * @param {symbol,string} event |
| * @param {...any} args |
| * @returns {boolean} |
| */ |
| emit(event, ...args) { |
| if (this[kAsyncResource] === undefined) |
| throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); |
| const { asyncResource } = this; |
| ArrayPrototypeUnshift(args, super.emit, this, event); |
| return ReflectApply(asyncResource.runInAsyncScope, asyncResource, |
| args); |
| } |
| |
| /** |
| * @returns {void} |
| */ |
| emitDestroy() { |
| if (this[kAsyncResource] === undefined) |
| throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); |
| this.asyncResource.emitDestroy(); |
| } |
| |
| /** |
| * @type {number} |
| */ |
| get asyncId() { |
| if (this[kAsyncResource] === undefined) |
| throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); |
| return this.asyncResource.asyncId(); |
| } |
| |
| /** |
| * @type {number} |
| */ |
| get triggerAsyncId() { |
| if (this[kAsyncResource] === undefined) |
| throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); |
| return this.asyncResource.triggerAsyncId(); |
| } |
| |
| /** |
| * @type {EventEmitterReferencingAsyncResource} |
| */ |
| get asyncResource() { |
| if (this[kAsyncResource] === undefined) |
| throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); |
| return this[kAsyncResource]; |
| } |
| } |
| EventEmitter.EventEmitterAsyncResource = EventEmitterAsyncResource; |
| } |
| |
| EventEmitter.errorMonitor = kErrorMonitor; |
| |
| // 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; |
| |
| // By default EventEmitters will print a warning if more than 10 listeners are |
| // added to it. This is a useful default which helps finding memory leaks. |
| let defaultMaxListeners = 10; |
| |
| function checkListener(listener) { |
| validateFunction(listener, 'listener'); |
| } |
| |
| ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', { |
| enumerable: true, |
| get: function() { |
| return defaultMaxListeners; |
| }, |
| set: function(arg) { |
| if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { |
| throw new ERR_OUT_OF_RANGE('defaultMaxListeners', |
| 'a non-negative number', |
| arg); |
| } |
| defaultMaxListeners = arg; |
| } |
| }); |
| |
| ObjectDefineProperties(EventEmitter, { |
| kMaxEventTargetListeners: { |
| value: kMaxEventTargetListeners, |
| enumerable: false, |
| configurable: false, |
| writable: false, |
| }, |
| kMaxEventTargetListenersWarned: { |
| value: kMaxEventTargetListenersWarned, |
| enumerable: false, |
| configurable: false, |
| writable: false, |
| } |
| }); |
| |
| /** |
| * Sets the max listeners. |
| * @param {number} n |
| * @param {EventTarget[] | EventEmitter[]} [eventTargets] |
| * @returns {void} |
| */ |
| EventEmitter.setMaxListeners = |
| function(n = defaultMaxListeners, ...eventTargets) { |
| if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) |
| throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n); |
| if (eventTargets.length === 0) { |
| defaultMaxListeners = n; |
| } else { |
| for (let i = 0; i < eventTargets.length; i++) { |
| const target = eventTargets[i]; |
| if (isEventTarget(target)) { |
| target[kMaxEventTargetListeners] = n; |
| target[kMaxEventTargetListenersWarned] = false; |
| } else if (typeof target.setMaxListeners === 'function') { |
| target.setMaxListeners(n); |
| } else { |
| throw new ERR_INVALID_ARG_TYPE( |
| 'eventTargets', |
| ['EventEmitter', 'EventTarget'], |
| target); |
| } |
| } |
| } |
| }; |
| |
| // If you're updating this function definition, please also update any |
| // re-definitions, such as the one in the Domain module (lib/domain.js). |
| EventEmitter.init = function(opts) { |
| |
| if (this._events === undefined || |
| this._events === ObjectGetPrototypeOf(this)._events) { |
| this._events = ObjectCreate(null); |
| this._eventsCount = 0; |
| } |
| |
| this._maxListeners = this._maxListeners || undefined; |
| |
| |
| if (oc(opts, 'captureRejections')) { |
| validateBoolean(opts.captureRejections, 'options.captureRejections'); |
| this[kCapture] = Boolean(opts.captureRejections); |
| } else { |
| // Assigning the kCapture property directly saves an expensive |
| // prototype lookup 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 catchable 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; |
| } |
| } |
| } |
| |
| /** |
| * Increases the max listeners of the event emitter. |
| * @param {number} n |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { |
| if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { |
| throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n); |
| } |
| this._maxListeners = n; |
| return this; |
| }; |
| |
| function _getMaxListeners(that) { |
| if (that._maxListeners === undefined) |
| return EventEmitter.defaultMaxListeners; |
| return that._maxListeners; |
| } |
| |
| /** |
| * Returns the current max listener value for the event emitter. |
| * @returns {number} |
| */ |
| EventEmitter.prototype.getMaxListeners = function getMaxListeners() { |
| return _getMaxListeners(this); |
| }; |
| |
| /** |
| * Synchronously calls each of the listeners registered |
| * for the event. |
| * @param {string | symbol} type |
| * @param {...any} [args] |
| * @returns {boolean} |
| */ |
| EventEmitter.prototype.emit = function emit(type, ...args) { |
| let doError = (type === 'error'); |
| |
| const events = this._events; |
| if (events !== undefined) { |
| if (doError && events[kErrorMonitor] !== undefined) |
| this.emit(kErrorMonitor, ...args); |
| doError = (doError && events.error === undefined); |
| } else if (!doError) |
| return false; |
| |
| // If there is no 'error' event listener then throw. |
| if (doError) { |
| let er; |
| if (args.length > 0) |
| er = args[0]; |
| if (er instanceof Error) { |
| try { |
| const capture = {}; |
| ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit); |
| } catch (e) {} |
| |
| // Note: The comments on the `throw` lines are intentional, they show |
| // up in Node's output if this results in an unhandled exception. |
| throw er; // Unhandled 'error' event |
| } |
| |
| let stringifiedEr; |
| try { |
| stringifiedEr = inspect(er); |
| } catch (e) { |
| stringifiedEr = er; |
| } |
| |
| // At least give some kind of context to the user |
| const err = new ERR_UNHANDLED_ERROR(stringifiedEr); |
| err.context = er; |
| throw err; // Unhandled 'error' event |
| } |
| |
| const handler = events[type]; |
| |
| if (handler === undefined) |
| return false; |
| |
| if (typeof handler === 'function') { |
| const result = handler.apply(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); |
| for (let i = 0; i < len; ++i) { |
| const result = listeners[i].apply(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; |
| }; |
| |
| function _addListener(target, type, listener, prepend) { |
| let m; |
| let events; |
| let existing; |
| |
| checkListener(listener); |
| |
| events = target._events; |
| if (events === undefined) { |
| events = target._events = ObjectCreate(null); |
| target._eventsCount = 0; |
| } else { |
| // To avoid recursion in the case that type === "newListener"! Before |
| // adding it to the listeners, first emit "newListener". |
| if (events.newListener !== undefined) { |
| target.emit('newListener', type, |
| nc(listener.listener, listener)); |
| |
| // Re-assign `events` because a newListener handler could have caused the |
| // this._events to be assigned to a new object |
| events = target._events; |
| } |
| existing = events[type]; |
| } |
| |
| if (existing === undefined) { |
| // Optimize the case of one listener. Don't need the extra array object. |
| events[type] = listener; |
| ++target._eventsCount; |
| } else { |
| if (typeof existing === 'function') { |
| // Adding the second element, need to change to array. |
| existing = events[type] = |
| prepend ? [listener, existing] : [existing, listener]; |
| // If we've already got an array, just append. |
| } else if (prepend) { |
| existing.unshift(listener); |
| } else { |
| existing.push(listener); |
| } |
| |
| // Check for listener leak |
| m = _getMaxListeners(target); |
| if (m > 0 && existing.length > m && !existing.warned) { |
| existing.warned = true; |
| // No error code for this since it is a Warning |
| // eslint-disable-next-line no-restricted-syntax |
| const w = new Error('Possible EventEmitter memory leak detected. ' + |
| `${existing.length} ${String(type)} listeners ` + |
| `added to ${inspect(target, { depth: -1 })}. Use ` + |
| 'emitter.setMaxListeners() to increase limit'); |
| w.name = 'MaxListenersExceededWarning'; |
| w.emitter = target; |
| w.type = type; |
| w.count = existing.length; |
| process.emitWarning(w); |
| } |
| } |
| |
| return target; |
| } |
| |
| /** |
| * Adds a listener to the event emitter. |
| * @param {string | symbol} type |
| * @param {Function} listener |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.addListener = function addListener(type, listener) { |
| return _addListener(this, type, listener, false); |
| }; |
| |
| EventEmitter.prototype.on = EventEmitter.prototype.addListener; |
| |
| /** |
| * Adds the `listener` function to the beginning of |
| * the listeners array. |
| * @param {string | symbol} type |
| * @param {Function} listener |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.prependListener = |
| function prependListener(type, listener) { |
| return _addListener(this, type, listener, true); |
| }; |
| |
| function onceWrapper() { |
| if (!this.fired) { |
| this.target.removeListener(this.type, this.wrapFn); |
| this.fired = true; |
| if (arguments.length === 0) |
| return this.listener.call(this.target); |
| return this.listener.apply(this.target, arguments); |
| } |
| } |
| |
| function _onceWrap(target, type, listener) { |
| const state = { fired: false, wrapFn: undefined, target, type, listener }; |
| const wrapped = onceWrapper.bind(state); |
| wrapped.listener = listener; |
| state.wrapFn = wrapped; |
| return wrapped; |
| } |
| |
| /** |
| * Adds a one-time `listener` function to the event emitter. |
| * @param {string | symbol} type |
| * @param {Function} listener |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.once = function once(type, listener) { |
| checkListener(listener); |
| |
| this.on(type, _onceWrap(this, type, listener)); |
| return this; |
| }; |
| |
| /** |
| * Adds a one-time `listener` function to the beginning of |
| * the listeners array. |
| * @param {string | symbol} type |
| * @param {Function} listener |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.prependOnceListener = |
| function prependOnceListener(type, listener) { |
| checkListener(listener); |
| |
| this.prependListener(type, _onceWrap(this, type, listener)); |
| return this; |
| }; |
| |
| |
| /** |
| * Removes the specified `listener` from the listeners array. |
| * @param {string | symbol} type |
| * @param {Function} listener |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.removeListener = |
| function removeListener(type, listener) { |
| checkListener(listener); |
| |
| const events = this._events; |
| if (events === undefined) |
| return this; |
| |
| const list = events[type]; |
| if (list === undefined) |
| return this; |
| |
| if (list === listener || list.listener === listener) { |
| if (--this._eventsCount === 0) |
| this._events = ObjectCreate(null); |
| else { |
| delete events[type]; |
| if (events.removeListener) |
| this.emit('removeListener', type, list.listener || listener); |
| } |
| } else if (typeof list !== 'function') { |
| let position = -1; |
| |
| for (let i = list.length - 1; i >= 0; i--) { |
| if (list[i] === listener || list[i].listener === listener) { |
| position = i; |
| break; |
| } |
| } |
| |
| if (position < 0) |
| return this; |
| |
| if (position === 0) |
| list.shift(); |
| else { |
| spliceOne(list, position); |
| } |
| |
| if (list.length === 1) |
| events[type] = list[0]; |
| |
| if (events.removeListener !== undefined) |
| this.emit('removeListener', type, listener); |
| } |
| |
| return this; |
| }; |
| |
| EventEmitter.prototype.off = EventEmitter.prototype.removeListener; |
| |
| /** |
| * Removes all listeners from the event emitter. (Only |
| * removes listeners for a specific event name if specified |
| * as `type`). |
| * @param {string | symbol} [type] |
| * @returns {EventEmitter} |
| */ |
| EventEmitter.prototype.removeAllListeners = |
| function removeAllListeners(type) { |
| const events = this._events; |
| if (events === undefined) |
| return this; |
| |
| // Not listening for removeListener, no need to emit |
| if (events.removeListener === undefined) { |
| if (arguments.length === 0) { |
| this._events = ObjectCreate(null); |
| this._eventsCount = 0; |
| } else if (events[type] !== undefined) { |
| if (--this._eventsCount === 0) |
| this._events = ObjectCreate(null); |
| else |
| delete events[type]; |
| } |
| return this; |
| } |
| |
| // Emit removeListener for all listeners on all events |
| if (arguments.length === 0) { |
| for (const key of ReflectOwnKeys(events)) { |
| if (key === 'removeListener') continue; |
| this.removeAllListeners(key); |
| } |
| this.removeAllListeners('removeListener'); |
| this._events = ObjectCreate(null); |
| this._eventsCount = 0; |
| return this; |
| } |
| |
| const listeners = events[type]; |
| |
| if (typeof listeners === 'function') { |
| this.removeListener(type, listeners); |
| } else if (listeners !== undefined) { |
| // LIFO order |
| for (let i = listeners.length - 1; i >= 0; i--) { |
| this.removeListener(type, listeners[i]); |
| } |
| } |
| |
| return this; |
| }; |
| |
| function _listeners(target, type, unwrap) { |
| const events = target._events; |
| |
| if (events === undefined) |
| return []; |
| |
| const evlistener = events[type]; |
| if (evlistener === undefined) |
| return []; |
| |
| if (typeof evlistener === 'function') |
| return unwrap ? [evlistener.listener || evlistener] : [evlistener]; |
| |
| return unwrap ? |
| unwrapListeners(evlistener) : arrayClone(evlistener); |
| } |
| |
| /** |
| * Returns a copy of the array of listeners for the event name |
| * specified as `type`. |
| * @param {string | symbol} type |
| * @returns {Function[]} |
| */ |
| EventEmitter.prototype.listeners = function listeners(type) { |
| return _listeners(this, type, true); |
| }; |
| |
| /** |
| * Returns a copy of the array of listeners and wrappers for |
| * the event name specified as `type`. |
| * @param {string | symbol} type |
| * @returns {Function[]} |
| */ |
| EventEmitter.prototype.rawListeners = function rawListeners(type) { |
| return _listeners(this, type, false); |
| }; |
| |
| /** |
| * Returns the number of listeners listening to the event name |
| * specified as `type`. |
| * @deprecated since v3.2.0 |
| * @param {EventEmitter} emitter |
| * @param {string | symbol} type |
| * @returns {number} |
| */ |
| EventEmitter.listenerCount = function(emitter, type) { |
| if (typeof emitter.listenerCount === 'function') { |
| return emitter.listenerCount(type); |
| } |
| return emitter.listenerCount(type); |
| }; |
| |
| EventEmitter.prototype.listenerCount = listenerCount; |
| |
| /** |
| * Returns the number of listeners listening to event name |
| * specified as `type`. |
| * @param {string | symbol} type |
| * @returns {number} |
| */ |
| function listenerCount(type) { |
| const events = this._events; |
| |
| if (events !== undefined) { |
| const evlistener = events[type]; |
| |
| if (typeof evlistener === 'function') { |
| return 1; |
| } else if (evlistener !== undefined) { |
| return evlistener.length; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Returns an array listing the events for which |
| * the emitter has registered listeners. |
| * @returns {any[]} |
| */ |
| EventEmitter.prototype.eventNames = function eventNames() { |
| return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; |
| }; |
| |
| function arrayClone(arr) { |
| // At least since V8 8.3, this implementation is faster than the previous |
| // which always used a simple for-loop |
| switch (arr.length) { |
| case 2: return [arr[0], arr[1]]; |
| case 3: return [arr[0], arr[1], arr[2]]; |
| case 4: return [arr[0], arr[1], arr[2], arr[3]]; |
| case 5: return [arr[0], arr[1], arr[2], arr[3], arr[4]]; |
| case 6: return [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]]; |
| } |
| return ArrayPrototypeSlice(arr); |
| } |
| |
| function unwrapListeners(arr) { |
| const ret = arrayClone(arr); |
| for (let i = 0; i < ret.length; ++i) { |
| const orig = ret[i].listener; |
| if (typeof orig === 'function') |
| ret[i] = orig; |
| } |
| return ret; |
| } |