1'use strict'; 2 3const { 4 RegExpPrototypeExec, 5 globalThis, 6} = primordials; 7 8const path = require('path'); 9 10const { 11 codes: { 12 ERR_INVALID_ARG_TYPE, 13 ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET, 14 ERR_EVAL_ESM_CANNOT_PRINT, 15 }, 16} = require('internal/errors'); 17 18const { 19 executionAsyncId, 20 clearDefaultTriggerAsyncId, 21 clearAsyncIdStack, 22 hasAsyncIdStack, 23 afterHooksExist, 24 emitAfter, 25 popAsyncContext, 26} = require('internal/async_hooks'); 27 28// shouldAbortOnUncaughtToggle is a typed array for faster 29// communication with JS. 30const { shouldAbortOnUncaughtToggle } = internalBinding('util'); 31 32function tryGetCwd() { 33 try { 34 return process.cwd(); 35 } catch { 36 // getcwd(3) can fail if the current working directory has been deleted. 37 // Fall back to the directory name of the (absolute) executable path. 38 // It's not really correct but what are the alternatives? 39 return path.dirname(process.execPath); 40 } 41} 42 43function evalModule(source, print) { 44 if (print) { 45 throw new ERR_EVAL_ESM_CANNOT_PRINT(); 46 } 47 const { loadESM } = require('internal/process/esm_loader'); 48 const { handleMainPromise } = require('internal/modules/run_main'); 49 RegExpPrototypeExec(/^/, ''); // Necessary to reset RegExp statics before user code runs. 50 return handleMainPromise(loadESM((loader) => loader.eval(source))); 51} 52 53function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) { 54 const CJSModule = require('internal/modules/cjs/loader').Module; 55 const { kVmBreakFirstLineSymbol } = require('internal/util'); 56 const { pathToFileURL } = require('url'); 57 58 const cwd = tryGetCwd(); 59 const origModule = globalThis.module; // Set e.g. when called from the REPL. 60 61 const module = new CJSModule(name); 62 module.filename = path.join(cwd, name); 63 module.paths = CJSModule._nodeModulePaths(cwd); 64 65 const { handleMainPromise } = require('internal/modules/run_main'); 66 const asyncESM = require('internal/process/esm_loader'); 67 const baseUrl = pathToFileURL(module.filename).href; 68 const { loadESM } = asyncESM; 69 70 const runScript = () => { 71 // Create wrapper for cache entry 72 const script = ` 73 globalThis.module = module; 74 globalThis.exports = exports; 75 globalThis.__dirname = __dirname; 76 globalThis.require = require; 77 return (main) => main(); 78 `; 79 globalThis.__filename = name; 80 RegExpPrototypeExec(/^/, ''); // Necessary to reset RegExp statics before user code runs. 81 const result = module._compile(script, `${name}-wrapper`)(() => 82 require('vm').runInThisContext(body, { 83 filename: name, 84 displayErrors: true, 85 [kVmBreakFirstLineSymbol]: !!breakFirstLine, 86 importModuleDynamically(specifier, _, importAssertions) { 87 const loader = asyncESM.esmLoader; 88 return loader.import(specifier, baseUrl, importAssertions); 89 }, 90 })); 91 if (print) { 92 const { log } = require('internal/console/global'); 93 log(result); 94 } 95 96 if (origModule !== undefined) 97 globalThis.module = origModule; 98 }; 99 100 if (shouldLoadESM) { 101 return handleMainPromise(loadESM(runScript)); 102 } 103 return runScript(); 104} 105 106const exceptionHandlerState = { 107 captureFn: null, 108 reportFlag: false, 109}; 110 111function setUncaughtExceptionCaptureCallback(fn) { 112 if (fn === null) { 113 exceptionHandlerState.captureFn = fn; 114 shouldAbortOnUncaughtToggle[0] = 1; 115 process.report.reportOnUncaughtException = exceptionHandlerState.reportFlag; 116 return; 117 } 118 if (typeof fn !== 'function') { 119 throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'null'], fn); 120 } 121 if (exceptionHandlerState.captureFn !== null) { 122 throw new ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET(); 123 } 124 exceptionHandlerState.captureFn = fn; 125 shouldAbortOnUncaughtToggle[0] = 0; 126 exceptionHandlerState.reportFlag = 127 process.report.reportOnUncaughtException === true; 128 process.report.reportOnUncaughtException = false; 129} 130 131function hasUncaughtExceptionCaptureCallback() { 132 return exceptionHandlerState.captureFn !== null; 133} 134 135function noop() {} 136 137// XXX(joyeecheung): for some reason this cannot be defined at the top-level 138// and exported to be written to process._fatalException, it has to be 139// returned as an *anonymous function* wrapped inside a factory function, 140// otherwise it breaks the test-timers.setInterval async hooks test - 141// this may indicate that node::errors::TriggerUncaughtException() should 142// fix up the callback scope before calling into process._fatalException, 143// or this function should take extra care of the async hooks before it 144// schedules a setImmediate. 145function createOnGlobalUncaughtException() { 146 // The C++ land node::errors::TriggerUncaughtException() will 147 // exit the process if it returns false, and continue execution if it 148 // returns true (which indicates that the exception is handled by the user). 149 return (er, fromPromise) => { 150 // It's possible that defaultTriggerAsyncId was set for a constructor 151 // call that threw and was never cleared. So clear it now. 152 clearDefaultTriggerAsyncId(); 153 154 const type = fromPromise ? 'unhandledRejection' : 'uncaughtException'; 155 process.emit('uncaughtExceptionMonitor', er, type); 156 if (exceptionHandlerState.captureFn !== null) { 157 exceptionHandlerState.captureFn(er); 158 } else if (!process.emit('uncaughtException', er, type)) { 159 // If someone handled it, then great. Otherwise, die in C++ land 160 // since that means that we'll exit the process, emit the 'exit' event. 161 try { 162 if (!process._exiting) { 163 process._exiting = true; 164 process.exitCode = 1; 165 process.emit('exit', 1); 166 } 167 } catch { 168 // Nothing to be done about it at this point. 169 } 170 return false; 171 } 172 173 // If we handled an error, then make sure any ticks get processed 174 // by ensuring that the next Immediate cycle isn't empty. 175 require('timers').setImmediate(noop); 176 177 // Emit the after() hooks now that the exception has been handled. 178 if (afterHooksExist()) { 179 do { 180 const asyncId = executionAsyncId(); 181 if (asyncId === 0) 182 popAsyncContext(0); 183 else 184 emitAfter(asyncId); 185 } while (hasAsyncIdStack()); 186 } 187 // And completely empty the id stack, including anything that may be 188 // cached on the native side. 189 clearAsyncIdStack(); 190 191 return true; 192 }; 193} 194 195function readStdin(callback) { 196 process.stdin.setEncoding('utf8'); 197 198 let code = ''; 199 process.stdin.on('data', (d) => { 200 code += d; 201 }); 202 203 process.stdin.on('end', () => { 204 callback(code); 205 }); 206} 207 208module.exports = { 209 readStdin, 210 tryGetCwd, 211 evalModule, 212 evalScript, 213 onGlobalUncaughtException: createOnGlobalUncaughtException(), 214 setUncaughtExceptionCaptureCallback, 215 hasUncaughtExceptionCaptureCallback, 216}; 217