/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/domain.ts
435 строк
11 KB
Nathan Whitaker
perf: lazy-load more modules in the snapshot (#34061)
19 май 2026, 02:37
Не верифицирован
19 май 2026, 02:37
eb4061f
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. // This code has been inspired by https://github.com/bevry/domain-browser/commit/8bce7f4a093966ca850da75b024239ad5d0b33c6 // deno-lint-ignore-file no-process-global (function () { const { core, primordials } = __bootstrap; const { ERR_UNHANDLED_ERROR } = core.loadExtScript( "ext:deno_node/internal/errors.ts", ); const { AsyncHook } = core.loadExtScript( "ext:deno_node/internal/async_hooks.ts", ); const { ArrayPrototypeIndexOf, ArrayPrototypeLastIndexOf, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSplice, FunctionPrototypeCall, FunctionPrototypeApply, ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, ReflectApply, SafeMap, } = primordials; const { EventEmitter } = core.loadExtScript("ext:deno_node/_events.mjs"); function emitError(e) { this.emit("error", e); } let stack = []; let _stack = stack; let active = null; // Map asyncId -> domain for tracking async operations const pairing = new SafeMap(); // Async hook to track domain associations across async operations const asyncHook = new AsyncHook({ init(asyncId, _type, _triggerAsyncId, resource) { if (process.domain !== null && process.domain !== undefined) { // Record which domain this async operation belongs to pairing.set(asyncId, process.domain); // Attach domain to resource if (typeof resource === "object" && resource !== null) { ObjectDefineProperty(resource, "domain", { __proto__: null, configurable: true, enumerable: false, value: process.domain, writable: true, }); } } }, before(asyncId) { const domain = pairing.get(asyncId); if (domain !== undefined) { domain.enter(); } }, after(asyncId) { const domain = pairing.get(asyncId); if (domain !== undefined) { domain.exit(); } }, destroy(asyncId) { pairing.delete(asyncId); }, }); function create() { return new Domain(); } function createDomain() { return new Domain(); } class Domain extends EventEmitter { members = []; constructor() { super(); patchEventEmitter(); asyncHook.enable(); } add(ee) { if (ee.domain === this) { return; } if (ee.domain) { ee.domain.remove(ee); } if (this.domain && (ObjectPrototypeIsPrototypeOf(Domain.prototype, ee))) { for (let d = this.domain; d; d = d.domain) { if (ee === d) return; } } ObjectDefineProperty(ee, "domain", { __proto__: null, configurable: true, enumerable: false, value: this, writable: true, }); ArrayPrototypePush(this.members, ee); } remove(ee) { ee.domain = null; const index = ArrayPrototypeIndexOf(this.members, ee); if (index !== -1) { ArrayPrototypeSplice(this.members, index, 1); } } bind(fn) { // deno-lint-ignore no-this-alias const self = this; return function () { self.enter(); try { const ret = FunctionPrototypeApply( fn, this, ArrayPrototypeSlice(arguments), ); self.exit(); return ret; } catch (e) { self.exit(); if (typeof e === "object" && e !== null) { e.domainBound = fn; e.domainThrown = false; ObjectDefineProperty(e, "domain", { __proto__: null, configurable: true, enumerable: false, value: self, writable: true, }); } FunctionPrototypeCall(emitError, self, e); } }; } intercept(fn) { // deno-lint-ignore no-this-alias const self = this; return function (e) { if (e) { if (typeof e === "object" && e !== null) { e.domainBound = fn; e.domainThrown = false; ObjectDefineProperty(e, "domain", { __proto__: null, configurable: true, enumerable: false, value: self, writable: true, }); } FunctionPrototypeCall(emitError, self, e); } else { self.enter(); try { const ret = FunctionPrototypeApply( fn, this, ArrayPrototypeSlice(arguments, 1), ); self.exit(); return ret; } catch (e) { self.exit(); if (typeof e === "object" && e !== null) { e.domainBound = fn; e.domainThrown = false; ObjectDefineProperty(e, "domain", { __proto__: null, configurable: true, enumerable: false, value: self, writable: true, }); } FunctionPrototypeCall(emitError, self, e); } } }; } run(fn, ...args) { this.enter(); try { const ret = FunctionPrototypeApply(fn, this, args); this.exit(); return ret; } catch (e) { this.exit(); if (typeof e === "object" && e !== null) { e.domainThrown = true; ObjectDefineProperty(e, "domain", { __proto__: null, configurable: true, enumerable: false, value: this, writable: true, }); } FunctionPrototypeCall(emitError, this, e); } } dispose() { this._disposed = true; this.removeAllListeners(); return this; } enter() { active = process.domain = this; ArrayPrototypePush(stack, this); updateExceptionCapture(); return this; } exit() { // Use lastIndexOf (most recent occurrence) and remove everything from that // position onwards. This matches Node.js behavior: exiting a domain also // exits all domains that were entered after its most recent entry. const index = ArrayPrototypeLastIndexOf(stack, this); if (index !== -1) { ArrayPrototypeSplice(stack, index); } active = stack.length === 0 ? null : stack[stack.length - 1]; process.domain = active; updateExceptionCapture(); return this; } } let exceptionCaptureActive = false; function updateExceptionCapture() { if (stack.length > 0 && !exceptionCaptureActive) { exceptionCaptureActive = true; process.setUncaughtExceptionCaptureCallback( domainUncaughtExceptionHandler, ); } else if (stack.length === 0 && exceptionCaptureActive) { exceptionCaptureActive = false; process.setUncaughtExceptionCaptureCallback(null); } } function domainUncaughtExceptionHandler(er) { const curDomain = process.domain; if (!curDomain || curDomain._disposed) { // No active domain or domain has been disposed, re-throw throw er; } if (typeof er === "object" && er !== null) { ObjectDefineProperty(er, "domain", { __proto__: null, configurable: true, enumerable: false, value: curDomain, writable: true, }); er.domainThrown = true; } // Remove the errored domain from the stack. This cleans up the entry // left by emitBefore when the callback threw before emitAfter could run. if (stack.length === 1) { active = process.domain = null; stack.length = 0; } else { const idx = ArrayPrototypeLastIndexOf(stack, curDomain); if (idx !== -1) { ArrayPrototypeSplice(stack, idx, 1); } active = process.domain = stack.length > 0 ? stack[stack.length - 1] : null; } updateExceptionCapture(); curDomain.emit("error", er); } let patched = false; /** Patches EventEmitter method to make it domain-aware. * This happens at top-level of domain module in Node. That works because * Node uses cjs for internal modules. We do this patching at constructor * of Domain class to best approximate that behavior. */ function patchEventEmitter() { if (patched) return; patched = true; EventEmitter.usingDomains = true; const eventInit = EventEmitter.init; EventEmitter.init = function (opts) { ObjectDefineProperty(this, "domain", { __proto__: null, configurable: true, enumerable: false, value: null, writable: true, }); if (active && !ObjectPrototypeIsPrototypeOf(Domain.prototype, this)) { this.domain = active; } return FunctionPrototypeCall(eventInit, this, opts); }; const eventEmit = EventEmitter.prototype.emit; EventEmitter.prototype.emit = function emit(...args) { const domain = this.domain; const type = args[0]; const shouldEmitError = type === "error" && this.listenerCount(type) > 0; // No domain on this emitter or this is process - just call original emit if (domain === null || domain === undefined || this === process) { return ReflectApply(eventEmit, this, args); } // If the emitter has an error handler and a domain, wrap with // domain.enter()/exit() to preserve domain context in the handler. // Only exit on success - on error, the domainUncaughtExceptionHandler // handles cleanup (same pattern as timer async hooks). if (shouldEmitError) { domain.enter(); const ret = ReflectApply(eventEmit, this, args); domain.exit(); return ret; } if (type === "error") { const er = args.length > 1 && args[1] ? args[1] : new ERR_UNHANDLED_ERROR(); if (typeof er === "object") { er.domainEmitter = this; ObjectDefineProperty(er, "domain", { __proto__: null, configurable: true, enumerable: false, value: domain, writable: true, }); er.domainThrown = false; } // Remove the current domain (and its duplicates) from the domains stack and // set the active domain to its parent (if any) so that the domain's error // handler doesn't run in its own context. This prevents any event emitter // created or any exception thrown in that error handler from recursively // executing that error handler. const origDomainsStack = ArrayPrototypeSlice(stack); const origActiveDomain = process.domain; // Travel the domains stack from top to bottom to find the first domain // instance that is not a duplicate of the current active domain. let idx = stack.length - 1; while (idx > -1 && process.domain === stack[idx]) { --idx; } // Change the stack to not contain the current active domain, and only the // domains above it on the stack. if (idx < 0) { stack.length = 0; } else { ArrayPrototypeSplice(stack, idx + 1); } // Change the current active domain if (stack.length > 0) { active = process.domain = stack[stack.length - 1]; } else { active = process.domain = null; } updateExceptionCapture(); domain.emit("error", er); // Now that the domain's error handler has completed, restore the domains // stack and the active domain to their original values. _stack = stack = origDomainsStack; active = process.domain = origActiveDomain; updateExceptionCapture(); return false; } domain.enter(); const ret = ReflectApply(eventEmit, this, args); domain.exit(); return ret; }; } return { default: { _stack, create, active, createDomain, Domain, }, _stack, create, active, createDomain, Domain, }; })();