diff --git a/lib/domain.js b/lib/domain.js index 20cdd4090d9c1f..4951c0ae21b0bb 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -39,6 +39,7 @@ const { Promise, ReflectApply, SafeMap, + SafeWeakMap, Symbol, } = primordials; @@ -69,6 +70,7 @@ ObjectDefineProperty(process, 'domain', { } }); +const vmPromises = new SafeWeakMap(); const pairing = new SafeMap(); const asyncHook = createHook({ init(asyncId, type, triggerAsyncId, resource) { @@ -85,6 +87,11 @@ const asyncHook = createHook({ value: process.domain, writable: true }); + // Because promises from other contexts don't get a domain field, + // the domain needs to be held alive another way. Stuffing it in a + // weakmap connected to the promise lifetime can fix that. + } else { + vmPromises.set(resource, process.domain); } } }, diff --git a/test/parallel/test-domain-vm-promise-isolation.js b/test/parallel/test-domain-vm-promise-isolation.js new file mode 100644 index 00000000000000..41aed1ee337df4 --- /dev/null +++ b/test/parallel/test-domain-vm-promise-isolation.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const vm = require('vm'); + +// A promise created in a VM should not include a domain field but +// domains should still be able to propagate through them. +// +// See; https://github.com/nodejs/node/issues/40999 + +const context = vm.createContext({}); + +function run(code) { + const d = domain.createDomain(); + d.run(common.mustCall(() => { + const p = vm.runInContext(code, context)(); + assert.strictEqual(p.domain, undefined); + p.then(common.mustCall(() => { + assert.strictEqual(process.domain, d); + })); + })); +} + +for (let i = 0; i < 1000; i++) { + run('async () => null'); +}