'use strict'; const common = require('../common'); const assert = require('assert'); const { AsyncLocalStorage } = require('async_hooks'); const vm = require('vm'); // err1 is emitted sync as a control - no events // err2 is emitted after a timeout - uncaughtExceptionMonitor // + uncaughtException // err3 is emitted after some awaits - unhandledRejection // err4 is emitted during handling err3 - uncaughtExceptionMonitor // err5 is emitted after err4 from a VM lacking hooks - unhandledRejection // + uncaughtException const asyncLocalStorage = new AsyncLocalStorage(); const callbackToken = { callbackToken: true }; const awaitToken = { awaitToken: true }; let i = 0; // Redefining the uncaughtExceptionHandler is a bit odd, so we just do this // so we can track total invocations let underlyingExceptionHandler; const exceptionHandler = common.mustCall(function(...args) { return underlyingExceptionHandler.call(this, ...args); }, 2); process.setUncaughtExceptionCaptureCallback(exceptionHandler); const exceptionMonitor = common.mustCall((err, origin) => { if (err.message === 'err2') { assert.strictEqual(origin, 'uncaughtException'); assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); } else if (err.message === 'err4') { assert.strictEqual(origin, 'unhandledRejection'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); } else { assert.fail('unknown error ' + err); } }, 2); process.on('uncaughtExceptionMonitor', exceptionMonitor); function fireErr1() { underlyingExceptionHandler = common.mustCall(function(err) { ++i; assert.strictEqual(err.message, 'err2'); assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); }, 1); try { asyncLocalStorage.run(callbackToken, () => { setTimeout(fireErr2, 0); throw new Error('err1'); }); } catch (e) { assert.strictEqual(e.message, 'err1'); assert.strictEqual(asyncLocalStorage.getStore(), undefined); } } function fireErr2() { process.nextTick(() => { assert.strictEqual(i, 1); fireErr3(); }); throw new Error('err2'); } function fireErr3() { assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); const rejectionHandler3 = common.mustCall((err) => { assert.strictEqual(err.message, 'err3'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); process.off('unhandledRejection', rejectionHandler3); fireErr4(); }, 1); process.on('unhandledRejection', rejectionHandler3); async function awaitTest() { await null; throw new Error('err3'); } asyncLocalStorage.run(awaitToken, awaitTest); } const uncaughtExceptionHandler4 = common.mustCall( function(err) { assert.strictEqual(err.message, 'err4'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); fireErr5(); }, 1); function fireErr4() { assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); underlyingExceptionHandler = uncaughtExceptionHandler4; // re-entrant check Promise.reject(new Error('err4')); } function fireErr5() { assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); underlyingExceptionHandler = () => {}; const rejectionHandler5 = common.mustCall((err) => { assert.strictEqual(err.message, 'err5'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); process.off('unhandledRejection', rejectionHandler5); }, 1); process.on('unhandledRejection', rejectionHandler5); const makeOrphan = vm.compileFunction(`(${String(() => { async function main() { await null; Promise.resolve().then(() => { throw new Error('err5'); }); } main(); })})()`); makeOrphan(); } fireErr1();