mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
fix(node): use primordials more consistently in _events.mjs
(#29930)
Some checks failed
ci / pre-build (push) Has been cancelled
ci / lint debug windows-x86_64 (push) Has been cancelled
ci / test debug linux-x86_64 (push) Has been cancelled
ci / test release linux-x86_64 (push) Has been cancelled
ci / test debug macos-x86_64 (push) Has been cancelled
ci / test release macos-x86_64 (push) Has been cancelled
ci / test debug windows-x86_64 (push) Has been cancelled
ci / test release windows-x86_64 (push) Has been cancelled
ci / build libs (push) Has been cancelled
ci / test debug linux-aarch64 (push) Has been cancelled
ci / test release linux-aarch64 (push) Has been cancelled
ci / test debug macos-aarch64 (push) Has been cancelled
ci / test release macos-aarch64 (push) Has been cancelled
ci / bench release linux-x86_64 (push) Has been cancelled
ci / lint debug linux-x86_64 (push) Has been cancelled
ci / lint debug macos-x86_64 (push) Has been cancelled
ci / publish canary (push) Has been cancelled
Some checks failed
ci / pre-build (push) Has been cancelled
ci / lint debug windows-x86_64 (push) Has been cancelled
ci / test debug linux-x86_64 (push) Has been cancelled
ci / test release linux-x86_64 (push) Has been cancelled
ci / test debug macos-x86_64 (push) Has been cancelled
ci / test release macos-x86_64 (push) Has been cancelled
ci / test debug windows-x86_64 (push) Has been cancelled
ci / test release windows-x86_64 (push) Has been cancelled
ci / build libs (push) Has been cancelled
ci / test debug linux-aarch64 (push) Has been cancelled
ci / test release linux-aarch64 (push) Has been cancelled
ci / test debug macos-aarch64 (push) Has been cancelled
ci / test release macos-aarch64 (push) Has been cancelled
ci / bench release linux-x86_64 (push) Has been cancelled
ci / lint debug linux-x86_64 (push) Has been cancelled
ci / lint debug macos-x86_64 (push) Has been cancelled
ci / publish canary (push) Has been cancelled
Fixes #29929 --------- Signed-off-by: Nicholas Berlette <11234104+nberlette@users.noreply.github.com> Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
8fcbb0fa43
commit
a70f1cfab4
2 changed files with 121 additions and 37 deletions
|
@ -29,13 +29,27 @@ import { primordials } from "ext:core/mod.js";
|
||||||
const {
|
const {
|
||||||
ArrayPrototypeMap,
|
ArrayPrototypeMap,
|
||||||
ArrayPrototypeFilter,
|
ArrayPrototypeFilter,
|
||||||
|
ArrayPrototypePush,
|
||||||
|
ArrayPrototypeShift,
|
||||||
|
ArrayPrototypeUnshift,
|
||||||
|
Error,
|
||||||
|
ErrorCaptureStackTrace,
|
||||||
|
FunctionPrototypeCall,
|
||||||
|
FunctionPrototypeApply,
|
||||||
|
ObjectCreate,
|
||||||
ObjectDefineProperty,
|
ObjectDefineProperty,
|
||||||
ObjectEntries,
|
ObjectEntries,
|
||||||
|
ObjectGetPrototypeOf,
|
||||||
|
ObjectSetPrototypeOf,
|
||||||
|
ReflectOwnKeys,
|
||||||
SafeMap,
|
SafeMap,
|
||||||
SafeSet,
|
SafeSet,
|
||||||
|
Symbol,
|
||||||
|
SymbolFor,
|
||||||
|
SymbolAsyncIterator,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
const kRejection = Symbol.for("nodejs.rejection");
|
const kRejection = SymbolFor("nodejs.rejection");
|
||||||
export const kEvents = Symbol("kEvents");
|
export const kEvents = Symbol("kEvents");
|
||||||
|
|
||||||
import { inspect } from "ext:deno_node/internal/util/inspect.mjs";
|
import { inspect } from "ext:deno_node/internal/util/inspect.mjs";
|
||||||
|
@ -78,7 +92,7 @@ const kMaxEventTargetListenersWarned = Symbol(
|
||||||
* @returns {EventEmitter}
|
* @returns {EventEmitter}
|
||||||
*/
|
*/
|
||||||
export function EventEmitter(opts) {
|
export function EventEmitter(opts) {
|
||||||
EventEmitter.init.call(this, opts);
|
FunctionPrototypeCall(EventEmitter.init, this, opts);
|
||||||
}
|
}
|
||||||
export default EventEmitter;
|
export default EventEmitter;
|
||||||
EventEmitter.on = on;
|
EventEmitter.on = on;
|
||||||
|
@ -95,7 +109,7 @@ EventEmitter.captureRejectionSymbol = kRejection;
|
||||||
export const captureRejectionSymbol = EventEmitter.captureRejectionSymbol;
|
export const captureRejectionSymbol = EventEmitter.captureRejectionSymbol;
|
||||||
export const errorMonitor = EventEmitter.errorMonitor;
|
export const errorMonitor = EventEmitter.errorMonitor;
|
||||||
|
|
||||||
Object.defineProperty(EventEmitter, "captureRejections", {
|
ObjectDefineProperty(EventEmitter, "captureRejections", {
|
||||||
get() {
|
get() {
|
||||||
return EventEmitter.prototype[kCapture];
|
return EventEmitter.prototype[kCapture];
|
||||||
},
|
},
|
||||||
|
@ -110,7 +124,7 @@ Object.defineProperty(EventEmitter, "captureRejections", {
|
||||||
EventEmitter.errorMonitor = kErrorMonitor;
|
EventEmitter.errorMonitor = kErrorMonitor;
|
||||||
|
|
||||||
// The default for captureRejections is false
|
// The default for captureRejections is false
|
||||||
Object.defineProperty(EventEmitter.prototype, kCapture, {
|
ObjectDefineProperty(EventEmitter.prototype, kCapture, {
|
||||||
value: false,
|
value: false,
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
@ -128,7 +142,7 @@ function checkListener(listener) {
|
||||||
validateFunction(listener, "listener");
|
validateFunction(listener, "listener");
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(EventEmitter, "defaultMaxListeners", {
|
ObjectDefineProperty(EventEmitter, "defaultMaxListeners", {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: function () {
|
get: function () {
|
||||||
return defaultMaxListeners;
|
return defaultMaxListeners;
|
||||||
|
@ -189,9 +203,9 @@ export function setMaxListeners(
|
||||||
EventEmitter.init = function (opts) {
|
EventEmitter.init = function (opts) {
|
||||||
if (
|
if (
|
||||||
this._events === undefined ||
|
this._events === undefined ||
|
||||||
this._events === Object.getPrototypeOf(this)._events
|
this._events === ObjectGetPrototypeOf(this)._events
|
||||||
) {
|
) {
|
||||||
this._events = Object.create(null);
|
this._events = ObjectCreate(null);
|
||||||
this._eventsCount = 0;
|
this._eventsCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +232,7 @@ function addCatch(that, promise, type, args) {
|
||||||
const then = promise.then;
|
const then = promise.then;
|
||||||
|
|
||||||
if (typeof then === "function") {
|
if (typeof then === "function") {
|
||||||
then.call(promise, undefined, function (err) {
|
FunctionPrototypeCall(then, promise, undefined, function (err) {
|
||||||
// The callback is called with nextTick to avoid a follow-up
|
// The callback is called with nextTick to avoid a follow-up
|
||||||
// rejection from this promise.
|
// rejection from this promise.
|
||||||
nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
|
nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
|
||||||
|
@ -385,8 +399,8 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
|
||||||
if (er instanceof Error) {
|
if (er instanceof Error) {
|
||||||
try {
|
try {
|
||||||
const capture = {};
|
const capture = {};
|
||||||
Error.captureStackTrace(capture, EventEmitter.prototype.emit);
|
ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
|
||||||
// Object.defineProperty(er, kEnhanceStackBeforeInspector, {
|
// ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
|
||||||
// value: enhanceStackTrace.bind(this, er, capture),
|
// value: enhanceStackTrace.bind(this, er, capture),
|
||||||
// configurable: true
|
// configurable: true
|
||||||
// });
|
// });
|
||||||
|
@ -419,7 +433,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof handler === "function") {
|
if (typeof handler === "function") {
|
||||||
const result = handler.apply(this, args);
|
const result = FunctionPrototypeApply(handler, this, args);
|
||||||
|
|
||||||
// We check if result is undefined first because that
|
// We check if result is undefined first because that
|
||||||
// is the most common case so we do not pay any perf
|
// is the most common case so we do not pay any perf
|
||||||
|
@ -431,7 +445,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
|
||||||
const len = handler.length;
|
const len = handler.length;
|
||||||
const listeners = arrayClone(handler);
|
const listeners = arrayClone(handler);
|
||||||
for (let i = 0; i < len; ++i) {
|
for (let i = 0; i < len; ++i) {
|
||||||
const result = listeners[i].apply(this, args);
|
const result = FunctionPrototypeApply(listeners[i], this, args);
|
||||||
|
|
||||||
// We check if result is undefined first because that
|
// We check if result is undefined first because that
|
||||||
// is the most common case so we do not pay any perf
|
// is the most common case so we do not pay any perf
|
||||||
|
@ -456,7 +470,7 @@ function _addListener(target, type, listener, prepend) {
|
||||||
|
|
||||||
events = target._events;
|
events = target._events;
|
||||||
if (events === undefined) {
|
if (events === undefined) {
|
||||||
events = target._events = Object.create(null);
|
events = target._events = ObjectCreate(null);
|
||||||
target._eventsCount = 0;
|
target._eventsCount = 0;
|
||||||
} else {
|
} else {
|
||||||
// To avoid recursion in the case that type === "newListener"! Before
|
// To avoid recursion in the case that type === "newListener"! Before
|
||||||
|
@ -483,9 +497,9 @@ function _addListener(target, type, listener, prepend) {
|
||||||
: [existing, listener];
|
: [existing, listener];
|
||||||
// If we've already got an array, just append.
|
// If we've already got an array, just append.
|
||||||
} else if (prepend) {
|
} else if (prepend) {
|
||||||
existing.unshift(listener);
|
ArrayPrototypeUnshift(existing, listener);
|
||||||
} else {
|
} else {
|
||||||
existing.push(listener);
|
ArrayPrototypePush(existing, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for listener leak
|
// Check for listener leak
|
||||||
|
@ -542,9 +556,9 @@ function onceWrapper() {
|
||||||
this.target.removeListener(this.type, this.wrapFn);
|
this.target.removeListener(this.type, this.wrapFn);
|
||||||
this.fired = true;
|
this.fired = true;
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
return this.listener.call(this.target);
|
return FunctionPrototypeCall(this.listener, this.target);
|
||||||
}
|
}
|
||||||
return this.listener.apply(this.target, arguments);
|
return FunctionPrototypeApply(this.listener, this.target, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +624,7 @@ EventEmitter.prototype.removeListener = function removeListener(
|
||||||
|
|
||||||
if (list === listener || list.listener === listener) {
|
if (list === listener || list.listener === listener) {
|
||||||
if (--this._eventsCount === 0) {
|
if (--this._eventsCount === 0) {
|
||||||
this._events = Object.create(null);
|
this._events = ObjectCreate(null);
|
||||||
} else {
|
} else {
|
||||||
delete events[type];
|
delete events[type];
|
||||||
if (events.removeListener) {
|
if (events.removeListener) {
|
||||||
|
@ -632,7 +646,7 @@ EventEmitter.prototype.removeListener = function removeListener(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position === 0) {
|
if (position === 0) {
|
||||||
list.shift();
|
ArrayPrototypeShift(list);
|
||||||
} else {
|
} else {
|
||||||
spliceOne(list, position);
|
spliceOne(list, position);
|
||||||
}
|
}
|
||||||
|
@ -667,11 +681,11 @@ EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
|
||||||
// Not listening for removeListener, no need to emit
|
// Not listening for removeListener, no need to emit
|
||||||
if (events.removeListener === undefined) {
|
if (events.removeListener === undefined) {
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
this._events = Object.create(null);
|
this._events = ObjectCreate(null);
|
||||||
this._eventsCount = 0;
|
this._eventsCount = 0;
|
||||||
} else if (events[type] !== undefined) {
|
} else if (events[type] !== undefined) {
|
||||||
if (--this._eventsCount === 0) {
|
if (--this._eventsCount === 0) {
|
||||||
this._events = Object.create(null);
|
this._events = ObjectCreate(null);
|
||||||
} else {
|
} else {
|
||||||
delete events[type];
|
delete events[type];
|
||||||
}
|
}
|
||||||
|
@ -681,12 +695,12 @@ EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
|
||||||
|
|
||||||
// Emit removeListener for all listeners on all events
|
// Emit removeListener for all listeners on all events
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
for (const key of Reflect.ownKeys(events)) {
|
for (const key of ReflectOwnKeys(events)) {
|
||||||
if (key === "removeListener") continue;
|
if (key === "removeListener") continue;
|
||||||
this.removeAllListeners(key);
|
this.removeAllListeners(key);
|
||||||
}
|
}
|
||||||
this.removeAllListeners("removeListener");
|
this.removeAllListeners("removeListener");
|
||||||
this._events = Object.create(null);
|
this._events = ObjectCreate(null);
|
||||||
this._eventsCount = 0;
|
this._eventsCount = 0;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -799,7 +813,7 @@ export function listenerCount(emitter, type) {
|
||||||
if (typeof emitter.listenerCount === "function") {
|
if (typeof emitter.listenerCount === "function") {
|
||||||
return emitter.listenerCount(type);
|
return emitter.listenerCount(type);
|
||||||
}
|
}
|
||||||
return _listenerCount.call(emitter, type);
|
return FunctionPrototypeCall(_listenerCount, emitter, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -808,7 +822,7 @@ export function listenerCount(emitter, type) {
|
||||||
* @returns {any[]}
|
* @returns {any[]}
|
||||||
*/
|
*/
|
||||||
EventEmitter.prototype.eventNames = function eventNames() {
|
EventEmitter.prototype.eventNames = function eventNames() {
|
||||||
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
|
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
function arrayClone(arr) {
|
function arrayClone(arr) {
|
||||||
|
@ -926,8 +940,8 @@ export async function once(emitter, name, options = kEmptyObject) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const AsyncIteratorPrototype = Object.getPrototypeOf(
|
const AsyncIteratorPrototype = ObjectGetPrototypeOf(
|
||||||
Object.getPrototypeOf(async function* () {}).prototype,
|
ObjectGetPrototypeOf(async function* () {}).prototype,
|
||||||
);
|
);
|
||||||
|
|
||||||
function createIterResult(value, done) {
|
function createIterResult(value, done) {
|
||||||
|
@ -1006,10 +1020,10 @@ export function on(emitter, event, options = kEmptyObject) {
|
||||||
let error = null;
|
let error = null;
|
||||||
let finished = false;
|
let finished = false;
|
||||||
|
|
||||||
const iterator = Object.setPrototypeOf({
|
const iterator = ObjectSetPrototypeOf({
|
||||||
next() {
|
next() {
|
||||||
// First, we consume all unread events
|
// First, we consume all unread events
|
||||||
const value = unconsumedEvents.shift();
|
const value = ArrayPrototypeShift(unconsumedEvents);
|
||||||
if (value) {
|
if (value) {
|
||||||
return Promise.resolve(createIterResult(value, false));
|
return Promise.resolve(createIterResult(value, false));
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1045,7 @@ export function on(emitter, event, options = kEmptyObject) {
|
||||||
|
|
||||||
// Wait until an event happens
|
// Wait until an event happens
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
unconsumedPromises.push({ resolve, reject });
|
ArrayPrototypePush(unconsumedPromises, { resolve, reject });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1077,7 +1091,7 @@ export function on(emitter, event, options = kEmptyObject) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[Symbol.asyncIterator]() {
|
[SymbolAsyncIterator]() {
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
}, AsyncIteratorPrototype);
|
}, AsyncIteratorPrototype);
|
||||||
|
@ -1103,18 +1117,18 @@ export function on(emitter, event, options = kEmptyObject) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventHandler(...args) {
|
function eventHandler(...args) {
|
||||||
const promise = unconsumedPromises.shift();
|
const promise = ArrayPrototypeShift(unconsumedPromises);
|
||||||
if (promise) {
|
if (promise) {
|
||||||
promise.resolve(createIterResult(args, false));
|
promise.resolve(createIterResult(args, false));
|
||||||
} else {
|
} else {
|
||||||
unconsumedEvents.push(args);
|
ArrayPrototypePush(unconsumedEvents, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function errorHandler(err) {
|
function errorHandler(err) {
|
||||||
finished = true;
|
finished = true;
|
||||||
|
|
||||||
const toError = unconsumedPromises.shift();
|
const toError = ArrayPrototypeShift(unconsumedPromises);
|
||||||
|
|
||||||
if (toError) {
|
if (toError) {
|
||||||
toError.reject(err);
|
toError.reject(err);
|
||||||
|
@ -1193,8 +1207,12 @@ export class EventEmitterAsyncResource extends EventEmitter {
|
||||||
throw new ERR_INVALID_THIS("EventEmitterAsyncResource");
|
throw new ERR_INVALID_THIS("EventEmitterAsyncResource");
|
||||||
}
|
}
|
||||||
const { asyncResource } = this;
|
const { asyncResource } = this;
|
||||||
args.unshift(super.emit, this, event);
|
ArrayPrototypeUnshift(args, super.emit, this, event);
|
||||||
return asyncResource.runInAsyncScope.apply(asyncResource, args);
|
return FunctionPrototypeApply(
|
||||||
|
asyncResource.runInAsyncScope,
|
||||||
|
asyncResource,
|
||||||
|
args,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,7 +18,7 @@ Deno.test("regression #20441", async () => {
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
|
|
||||||
ee.on("error", function (_) {
|
ee.on("error", function (_: unknown) {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,3 +44,69 @@ Deno.test("addAbortListener", async () => {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
await promise;
|
await promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("EventEmitter works when Object.create is deleted (#29929)", () => {
|
||||||
|
const ObjectCreate = Object.create;
|
||||||
|
Object.create = undefined!;
|
||||||
|
try {
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
let called = false;
|
||||||
|
emitter.on("foo", () => {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
emitter.emit("foo");
|
||||||
|
if (!called) throw new Error("Listener was not called");
|
||||||
|
} finally {
|
||||||
|
Object.create = ObjectCreate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("EventEmitter works if Array.prototype.unshift is deleted", () => {
|
||||||
|
const ArrayPrototypeUnshift = Array.prototype.unshift;
|
||||||
|
// @ts-ignore -- this is fine for testing purposes
|
||||||
|
delete Array.prototype.unshift;
|
||||||
|
try {
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
let called = false;
|
||||||
|
emitter.on("bar", () => {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
emitter.emit("bar");
|
||||||
|
if (!called) throw new Error("Listener was not called");
|
||||||
|
} finally {
|
||||||
|
Array.prototype.unshift = ArrayPrototypeUnshift;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("EventEmitter works if Array.prototype.push is deleted", () => {
|
||||||
|
const ArrayPrototypePush = Array.prototype.push;
|
||||||
|
// @ts-ignore -- this is fine for testing purposes
|
||||||
|
delete Array.prototype.push;
|
||||||
|
try {
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
let called = false;
|
||||||
|
emitter.on("baz", () => {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
emitter.emit("baz");
|
||||||
|
if (!called) throw new Error("Listener was not called");
|
||||||
|
} finally {
|
||||||
|
Array.prototype.push = ArrayPrototypePush;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("EventEmitter works if Object.setPrototypeOf is deleted", () => {
|
||||||
|
const ObjectSetPrototypeOf = Object.setPrototypeOf;
|
||||||
|
Object.setPrototypeOf = undefined!;
|
||||||
|
try {
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
let called = false;
|
||||||
|
emitter.on("zap", () => {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
emitter.emit("zap");
|
||||||
|
if (!called) throw new Error("Listener was not called");
|
||||||
|
} finally {
|
||||||
|
Object.setPrototypeOf = ObjectSetPrototypeOf;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue