'use strict'; const path = require('path'); const { codes: { ERR_INVALID_ARG_TYPE, ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET } } = require('internal/errors'); const { executionAsyncId, clearDefaultTriggerAsyncId, clearAsyncIdStack, hasAsyncIdStack, afterHooksExist, emitAfter } = require('internal/async_hooks'); // shouldAbortOnUncaughtToggle is a typed array for faster // communication with JS. const { shouldAbortOnUncaughtToggle } = internalBinding('util'); function tryGetCwd() { try { return process.cwd(); } catch { // getcwd(3) can fail if the current working directory has been deleted. // Fall back to the directory name of the (absolute) executable path. // It's not really correct but what are the alternatives? return path.dirname(process.execPath); } } function evalScript(name, body, breakFristLine) { const CJSModule = require('internal/modules/cjs/loader'); if (breakFristLine) { const fn = `function() {\n\n${body};\n\n}`; body = `process.binding('inspector').callAndPauseOnStart(${fn}, {})`; } const cwd = tryGetCwd(); const module = new CJSModule(name); module.filename = path.join(cwd, name); module.paths = CJSModule._nodeModulePaths(cwd); const script = `global.__filename = ${JSON.stringify(name)};\n` + 'global.exports = exports;\n' + 'global.module = module;\n' + 'global.__dirname = __dirname;\n' + 'global.require = require;\n' + 'return require("vm").runInThisContext(' + `${JSON.stringify(body)}, { filename: ` + `${JSON.stringify(name)}, displayErrors: true });\n`; const result = module._compile(script, `${name}-wrapper`); if (require('internal/options').getOptionValue('--print')) { console.log(result); } // Handle any nextTicks added in the first tick of the program. process._tickCallback(); } const exceptionHandlerState = { captureFn: null }; function setUncaughtExceptionCaptureCallback(fn) { if (fn === null) { exceptionHandlerState.captureFn = fn; shouldAbortOnUncaughtToggle[0] = 1; return; } if (typeof fn !== 'function') { throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'null'], fn); } if (exceptionHandlerState.captureFn !== null) { throw new ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET(); } exceptionHandlerState.captureFn = fn; shouldAbortOnUncaughtToggle[0] = 0; } function hasUncaughtExceptionCaptureCallback() { return exceptionHandlerState.captureFn !== null; } function noop() {} // XXX(joyeecheung): for some reason this cannot be defined at the top-level // and exported to be written to process._fatalException, it has to be // returned as an *anonymous function* wrapped inside a factory function, // otherwise it breaks the test-timers.setInterval async hooks test - // this may indicate that node::FatalException should fix up the callback scope // before calling into process._fatalException, or this function should // take extra care of the async hooks before it schedules a setImmediate. function createFatalException() { return (er) => { // It's possible that defaultTriggerAsyncId was set for a constructor // call that threw and was never cleared. So clear it now. clearDefaultTriggerAsyncId(); if (exceptionHandlerState.captureFn !== null) { exceptionHandlerState.captureFn(er); } else if (!process.emit('uncaughtException', er)) { // If someone handled it, then great. otherwise, die in C++ land // since that means that we'll exit the process, emit the 'exit' event. try { if (!process._exiting) { process._exiting = true; process.exitCode = 1; process.emit('exit', 1); } } catch { // Nothing to be done about it at this point. } try { const { kExpandStackSymbol } = require('internal/util'); if (typeof er[kExpandStackSymbol] === 'function') er[kExpandStackSymbol](); } catch { // Nothing to be done about it at this point. } return false; } // If we handled an error, then make sure any ticks get processed // by ensuring that the next Immediate cycle isn't empty. require('timers').setImmediate(noop); // Emit the after() hooks now that the exception has been handled. if (afterHooksExist()) { do { emitAfter(executionAsyncId()); } while (hasAsyncIdStack()); // Or completely empty the id stack. } else { clearAsyncIdStack(); } return true; }; } module.exports = { tryGetCwd, evalScript, fatalException: createFatalException(), setUncaughtExceptionCaptureCallback, hasUncaughtExceptionCaptureCallback };