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