1'use strict'; 2const common = require('../common'); 3const assert = require('assert'); 4const { AsyncLocalStorage } = require('async_hooks'); 5const vm = require('vm'); 6 7// err1 is emitted sync as a control - no events 8// err2 is emitted after a timeout - uncaughtExceptionMonitor 9// + uncaughtException 10// err3 is emitted after some awaits - unhandledRejection 11// err4 is emitted during handling err3 - uncaughtExceptionMonitor 12// err5 is emitted after err4 from a VM lacking hooks - unhandledRejection 13// + uncaughtException 14 15const asyncLocalStorage = new AsyncLocalStorage(); 16const callbackToken = { callbackToken: true }; 17const awaitToken = { awaitToken: true }; 18 19let i = 0; 20 21// Redefining the uncaughtExceptionHandler is a bit odd, so we just do this 22// so we can track total invocations 23let underlyingExceptionHandler; 24const exceptionHandler = common.mustCall(function(...args) { 25 return underlyingExceptionHandler.call(this, ...args); 26}, 2); 27process.setUncaughtExceptionCaptureCallback(exceptionHandler); 28 29const exceptionMonitor = common.mustCall((err, origin) => { 30 if (err.message === 'err2') { 31 assert.strictEqual(origin, 'uncaughtException'); 32 assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); 33 } else if (err.message === 'err4') { 34 assert.strictEqual(origin, 'unhandledRejection'); 35 assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); 36 } else { 37 assert.fail('unknown error ' + err); 38 } 39}, 2); 40process.on('uncaughtExceptionMonitor', exceptionMonitor); 41 42function fireErr1() { 43 underlyingExceptionHandler = common.mustCall(function(err) { 44 ++i; 45 assert.strictEqual(err.message, 'err2'); 46 assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); 47 }, 1); 48 try { 49 asyncLocalStorage.run(callbackToken, () => { 50 setTimeout(fireErr2, 0); 51 throw new Error('err1'); 52 }); 53 } catch (e) { 54 assert.strictEqual(e.message, 'err1'); 55 assert.strictEqual(asyncLocalStorage.getStore(), undefined); 56 } 57} 58 59function fireErr2() { 60 process.nextTick(() => { 61 assert.strictEqual(i, 1); 62 fireErr3(); 63 }); 64 throw new Error('err2'); 65} 66 67function fireErr3() { 68 assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); 69 const rejectionHandler3 = common.mustCall((err) => { 70 assert.strictEqual(err.message, 'err3'); 71 assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); 72 process.off('unhandledRejection', rejectionHandler3); 73 74 fireErr4(); 75 }, 1); 76 process.on('unhandledRejection', rejectionHandler3); 77 async function awaitTest() { 78 await null; 79 throw new Error('err3'); 80 } 81 asyncLocalStorage.run(awaitToken, awaitTest); 82} 83 84const uncaughtExceptionHandler4 = common.mustCall( 85 function(err) { 86 assert.strictEqual(err.message, 'err4'); 87 assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); 88 fireErr5(); 89 }, 1); 90function fireErr4() { 91 assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); 92 underlyingExceptionHandler = uncaughtExceptionHandler4; 93 // re-entrant check 94 Promise.reject(new Error('err4')); 95} 96 97function fireErr5() { 98 assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); 99 underlyingExceptionHandler = () => {}; 100 const rejectionHandler5 = common.mustCall((err) => { 101 assert.strictEqual(err.message, 'err5'); 102 assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); 103 process.off('unhandledRejection', rejectionHandler5); 104 }, 1); 105 process.on('unhandledRejection', rejectionHandler5); 106 const makeOrphan = vm.compileFunction(`(${String(() => { 107 async function main() { 108 await null; 109 Promise.resolve().then(() => { 110 throw new Error('err5'); 111 }); 112 } 113 main(); 114 })})()`); 115 makeOrphan(); 116} 117 118fireErr1(); 119