1'use strict'; 2 3const { 4 NumberParseInt, 5 ObjectDefineProperty, 6 ObjectDefineProperties, 7 SafeMap, 8 SafeWeakMap, 9 StringPrototypeStartsWith, 10 globalThis, 11} = primordials; 12 13const { 14 getOptionValue, 15 shouldNotRegisterESMLoader 16} = require('internal/options'); 17const { Buffer } = require('buffer'); 18const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes; 19const assert = require('internal/assert'); 20 21function prepareMainThreadExecution(expandArgv1 = false) { 22 // Patch the process object with legacy properties and normalizations 23 patchProcessObject(expandArgv1); 24 setupTraceCategoryState(); 25 setupInspectorHooks(); 26 setupWarningHandler(); 27 28 // Resolve the coverage directory to an absolute path, and 29 // overwrite process.env so that the original path gets passed 30 // to child processes even when they switch cwd. 31 if (process.env.NODE_V8_COVERAGE) { 32 process.env.NODE_V8_COVERAGE = 33 setupCoverageHooks(process.env.NODE_V8_COVERAGE); 34 } 35 36 37 setupDebugEnv(); 38 39 // Print stack trace on `SIGINT` if option `--trace-sigint` presents. 40 setupStacktracePrinterOnSigint(); 41 42 // Process initial diagnostic reporting configuration, if present. 43 initializeReport(); 44 initializeReportSignalHandlers(); // Main-thread-only. 45 46 initializeHeapSnapshotSignalHandlers(); 47 48 // If the process is spawned with env NODE_CHANNEL_FD, it's probably 49 // spawned by our child_process module, then initialize IPC. 50 // This attaches some internal event listeners and creates: 51 // process.send(), process.channel, process.connected, 52 // process.disconnect(). 53 setupChildProcessIpcChannel(); 54 55 // Load policy from disk and parse it. 56 initializePolicy(); 57 58 // If this is a worker in cluster mode, start up the communication 59 // channel. This needs to be done before any user code gets executed 60 // (including preload modules). 61 initializeClusterIPC(); 62 63 initializeAbortController(); 64 initializeSourceMapsHandlers(); 65 initializeDeprecations(); 66 initializeWASI(); 67 initializeCJSLoader(); 68 initializeESMLoader(); 69 70 const CJSLoader = require('internal/modules/cjs/loader'); 71 assert(!CJSLoader.hasLoadedAnyUserCJSModule); 72 loadPreloadModules(); 73 initializeFrozenIntrinsics(); 74} 75 76function patchProcessObject(expandArgv1) { 77 const { 78 patchProcessObject: patchProcessObjectNative 79 } = internalBinding('process_methods'); 80 81 patchProcessObjectNative(process); 82 83 ObjectDefineProperty(process, 'argv0', { 84 enumerable: true, 85 configurable: false, 86 value: process.argv[0] 87 }); 88 process.argv[0] = process.execPath; 89 90 if (expandArgv1 && process.argv[1] && 91 !StringPrototypeStartsWith(process.argv[1], '-')) { 92 // Expand process.argv[1] into a full path. 93 const path = require('path'); 94 try { 95 process.argv[1] = path.resolve(process.argv[1]); 96 } catch {} 97 } 98 99 // TODO(joyeecheung): most of these should be deprecated and removed, 100 // except some that we need to be able to mutate during run time. 101 addReadOnlyProcessAlias('_eval', '--eval'); 102 addReadOnlyProcessAlias('_print_eval', '--print'); 103 addReadOnlyProcessAlias('_syntax_check_only', '--check'); 104 addReadOnlyProcessAlias('_forceRepl', '--interactive'); 105 addReadOnlyProcessAlias('_preload_modules', '--require'); 106 addReadOnlyProcessAlias('noDeprecation', '--no-deprecation'); 107 addReadOnlyProcessAlias('noProcessWarnings', '--no-warnings'); 108 addReadOnlyProcessAlias('traceProcessWarnings', '--trace-warnings'); 109 addReadOnlyProcessAlias('throwDeprecation', '--throw-deprecation'); 110 addReadOnlyProcessAlias('profProcess', '--prof-process'); 111 addReadOnlyProcessAlias('traceDeprecation', '--trace-deprecation'); 112 addReadOnlyProcessAlias('_breakFirstLine', '--inspect-brk', false); 113 addReadOnlyProcessAlias('_breakNodeFirstLine', '--inspect-brk-node', false); 114} 115 116function addReadOnlyProcessAlias(name, option, enumerable = true) { 117 const value = getOptionValue(option); 118 if (value) { 119 ObjectDefineProperty(process, name, { 120 writable: false, 121 configurable: true, 122 enumerable, 123 value 124 }); 125 } 126} 127 128function setupWarningHandler() { 129 const { 130 onWarning 131 } = require('internal/process/warning'); 132 if (getOptionValue('--warnings') && 133 process.env.NODE_NO_WARNINGS !== '1') { 134 process.on('warning', onWarning); 135 } 136} 137 138// Setup User-facing NODE_V8_COVERAGE environment variable that writes 139// ScriptCoverage to a specified file. 140function setupCoverageHooks(dir) { 141 const cwd = require('internal/process/execution').tryGetCwd(); 142 const { resolve } = require('path'); 143 const coverageDirectory = resolve(cwd, dir); 144 const { sourceMapCacheToObject } = 145 require('internal/source_map/source_map_cache'); 146 147 if (process.features.inspector) { 148 internalBinding('profiler').setCoverageDirectory(coverageDirectory); 149 internalBinding('profiler').setSourceMapCacheGetter(sourceMapCacheToObject); 150 } else { 151 process.emitWarning('The inspector is disabled, ' + 152 'coverage could not be collected', 153 'Warning'); 154 return ''; 155 } 156 return coverageDirectory; 157} 158 159function setupStacktracePrinterOnSigint() { 160 if (!getOptionValue('--trace-sigint')) { 161 return; 162 } 163 const { SigintWatchdog } = require('internal/watchdog'); 164 165 const watchdog = new SigintWatchdog(); 166 watchdog.start(); 167} 168 169function initializeReport() { 170 const { report } = require('internal/process/report'); 171 ObjectDefineProperty(process, 'report', { 172 enumerable: false, 173 configurable: true, 174 get() { 175 return report; 176 } 177 }); 178} 179 180function setupDebugEnv() { 181 require('internal/util/debuglog').initializeDebugEnv(process.env.NODE_DEBUG); 182 if (getOptionValue('--expose-internals')) { 183 require('internal/bootstrap/loaders').NativeModule.exposeInternals(); 184 } 185} 186 187// This has to be called after initializeReport() is called 188function initializeReportSignalHandlers() { 189 const { addSignalHandler } = require('internal/process/report'); 190 191 addSignalHandler(); 192} 193 194function initializeHeapSnapshotSignalHandlers() { 195 const signal = getOptionValue('--heapsnapshot-signal'); 196 197 if (!signal) 198 return; 199 200 require('internal/validators').validateSignalName(signal); 201 const { writeHeapSnapshot } = require('v8'); 202 203 process.on(signal, () => { 204 writeHeapSnapshot(); 205 }); 206} 207 208function setupTraceCategoryState() { 209 const { isTraceCategoryEnabled } = internalBinding('trace_events'); 210 const { toggleTraceCategoryState } = require('internal/process/per_thread'); 211 toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks')); 212} 213 214function setupInspectorHooks() { 215 // If Debugger.setAsyncCallStackDepth is sent during bootstrap, 216 // we cannot immediately call into JS to enable the hooks, which could 217 // interrupt the JS execution of bootstrap. So instead we save the 218 // notification in the inspector agent if it's sent in the middle of 219 // bootstrap, and process the notification later here. 220 if (internalBinding('config').hasInspector) { 221 const { 222 enable, 223 disable 224 } = require('internal/inspector_async_hook'); 225 internalBinding('inspector').registerAsyncHook(enable, disable); 226 } 227} 228 229// In general deprecations are initialized wherever the APIs are implemented, 230// this is used to deprecate APIs implemented in C++ where the deprecation 231// utilities are not easily accessible. 232function initializeDeprecations() { 233 const { deprecate } = require('internal/util'); 234 const pendingDeprecation = getOptionValue('--pending-deprecation'); 235 236 // DEP0103: access to `process.binding('util').isX` type checkers 237 // TODO(addaleax): Turn into a full runtime deprecation. 238 const utilBinding = internalBinding('util'); 239 const types = require('internal/util/types'); 240 for (const name of [ 241 'isArrayBuffer', 242 'isArrayBufferView', 243 'isAsyncFunction', 244 'isDataView', 245 'isDate', 246 'isExternal', 247 'isMap', 248 'isMapIterator', 249 'isNativeError', 250 'isPromise', 251 'isRegExp', 252 'isSet', 253 'isSetIterator', 254 'isTypedArray', 255 'isUint8Array', 256 'isAnyArrayBuffer', 257 ]) { 258 utilBinding[name] = pendingDeprecation ? 259 deprecate(types[name], 260 'Accessing native typechecking bindings of Node ' + 261 'directly is deprecated. ' + 262 `Please use \`util.types.${name}\` instead.`, 263 'DEP0103') : 264 types[name]; 265 } 266 267 // TODO(joyeecheung): this is a legacy property exposed to process. 268 // Now that we use the config binding to carry this information, remove 269 // it from the process. We may consider exposing it properly in 270 // process.features. 271 const { noBrowserGlobals } = internalBinding('config'); 272 if (noBrowserGlobals) { 273 ObjectDefineProperty(process, '_noBrowserGlobals', { 274 writable: false, 275 enumerable: true, 276 configurable: true, 277 value: noBrowserGlobals 278 }); 279 } 280 281 if (pendingDeprecation) { 282 process.binding = deprecate(process.binding, 283 'process.binding() is deprecated. ' + 284 'Please use public APIs instead.', 'DEP0111'); 285 286 process._tickCallback = deprecate(process._tickCallback, 287 'process._tickCallback() is deprecated', 288 'DEP0134'); 289 } 290 291 // Create global.process and global.Buffer as getters so that we have a 292 // deprecation path for these in ES Modules. 293 // See https://github.com/nodejs/node/pull/26334. 294 let _process = process; 295 ObjectDefineProperty(globalThis, 'process', { 296 get() { 297 return _process; 298 }, 299 set(value) { 300 _process = value; 301 }, 302 enumerable: false, 303 configurable: true 304 }); 305 306 let _Buffer = Buffer; 307 ObjectDefineProperty(globalThis, 'Buffer', { 308 get() { 309 return _Buffer; 310 }, 311 set(value) { 312 _Buffer = value; 313 }, 314 enumerable: false, 315 configurable: true 316 }); 317} 318 319function initializeAbortController() { 320 const abortController = getOptionValue('--experimental-abortcontroller'); 321 if (abortController) { 322 const { 323 AbortController, 324 AbortSignal 325 } = require('internal/abort_controller'); 326 ObjectDefineProperties(globalThis, { 327 AbortController: { 328 writable: true, 329 enumerable: false, 330 configurable: true, 331 value: AbortController 332 }, 333 AbortSignal: { 334 writable: true, 335 enumerable: false, 336 configurable: true, 337 value: AbortSignal 338 } 339 }); 340 } 341} 342 343function setupChildProcessIpcChannel() { 344 if (process.env.NODE_CHANNEL_FD) { 345 const assert = require('internal/assert'); 346 347 const fd = NumberParseInt(process.env.NODE_CHANNEL_FD, 10); 348 assert(fd >= 0); 349 350 // Make sure it's not accidentally inherited by child processes. 351 delete process.env.NODE_CHANNEL_FD; 352 353 const serializationMode = 354 process.env.NODE_CHANNEL_SERIALIZATION_MODE || 'json'; 355 delete process.env.NODE_CHANNEL_SERIALIZATION_MODE; 356 357 require('child_process')._forkChild(fd, serializationMode); 358 assert(process.send); 359 } 360} 361 362function initializeClusterIPC() { 363 if (process.argv[1] && process.env.NODE_UNIQUE_ID) { 364 const cluster = require('cluster'); 365 cluster._setupWorker(); 366 // Make sure it's not accidentally inherited by child processes. 367 delete process.env.NODE_UNIQUE_ID; 368 } 369} 370 371function initializePolicy() { 372 const experimentalPolicy = getOptionValue('--experimental-policy'); 373 if (experimentalPolicy) { 374 process.emitWarning('Policies are experimental.', 375 'ExperimentalWarning'); 376 const { pathToFileURL, URL } = require('internal/url'); 377 // URL here as it is slightly different parsing 378 // no bare specifiers for now 379 let manifestURL; 380 if (require('path').isAbsolute(experimentalPolicy)) { 381 manifestURL = new URL(`file:///${experimentalPolicy}`); 382 } else { 383 const cwdURL = pathToFileURL(process.cwd()); 384 cwdURL.pathname += '/'; 385 manifestURL = new URL(experimentalPolicy, cwdURL); 386 } 387 const fs = require('fs'); 388 const src = fs.readFileSync(manifestURL, 'utf8'); 389 const experimentalPolicyIntegrity = getOptionValue('--policy-integrity'); 390 if (experimentalPolicyIntegrity) { 391 const SRI = require('internal/policy/sri'); 392 const { createHash, timingSafeEqual } = require('crypto'); 393 const realIntegrities = new SafeMap(); 394 const integrityEntries = SRI.parse(experimentalPolicyIntegrity); 395 let foundMatch = false; 396 for (let i = 0; i < integrityEntries.length; i++) { 397 const { 398 algorithm, 399 value: expected 400 } = integrityEntries[i]; 401 const hash = createHash(algorithm); 402 hash.update(src); 403 const digest = hash.digest(); 404 if (digest.length === expected.length && 405 timingSafeEqual(digest, expected)) { 406 foundMatch = true; 407 break; 408 } 409 realIntegrities.set(algorithm, digest.toString('base64')); 410 } 411 if (!foundMatch) { 412 throw new ERR_MANIFEST_ASSERT_INTEGRITY(manifestURL, realIntegrities); 413 } 414 } 415 require('internal/process/policy') 416 .setup(src, manifestURL.href); 417 } 418} 419 420function initializeWASI() { 421 const { NativeModule } = require('internal/bootstrap/loaders'); 422 const mod = NativeModule.map.get('wasi'); 423 mod.canBeRequiredByUsers = 424 getOptionValue('--experimental-wasi-unstable-preview1'); 425} 426 427function initializeCJSLoader() { 428 const CJSLoader = require('internal/modules/cjs/loader'); 429 CJSLoader.Module._initPaths(); 430 // TODO(joyeecheung): deprecate this in favor of a proper hook? 431 CJSLoader.Module.runMain = 432 require('internal/modules/run_main').executeUserEntryPoint; 433} 434 435function initializeESMLoader() { 436 // Create this WeakMap in js-land because V8 has no C++ API for WeakMap. 437 internalBinding('module_wrap').callbackMap = new SafeWeakMap(); 438 439 if (shouldNotRegisterESMLoader) return; 440 441 const { 442 setImportModuleDynamicallyCallback, 443 setInitializeImportMetaObjectCallback 444 } = internalBinding('module_wrap'); 445 const esm = require('internal/process/esm_loader'); 446 // Setup per-isolate callbacks that locate data or callbacks that we keep 447 // track of for different ESM modules. 448 setInitializeImportMetaObjectCallback(esm.initializeImportMetaObject); 449 setImportModuleDynamicallyCallback(esm.importModuleDynamicallyCallback); 450} 451 452function initializeSourceMapsHandlers() { 453 const { setSourceMapsEnabled } = 454 require('internal/source_map/source_map_cache'); 455 process.setSourceMapsEnabled = setSourceMapsEnabled; 456} 457 458function initializeFrozenIntrinsics() { 459 if (getOptionValue('--frozen-intrinsics')) { 460 process.emitWarning('The --frozen-intrinsics flag is experimental', 461 'ExperimentalWarning'); 462 require('internal/freeze_intrinsics')(); 463 } 464} 465 466function loadPreloadModules() { 467 // For user code, we preload modules if `-r` is passed 468 const preloadModules = getOptionValue('--require'); 469 if (preloadModules && preloadModules.length > 0) { 470 const { 471 Module: { 472 _preloadModules 473 }, 474 } = require('internal/modules/cjs/loader'); 475 _preloadModules(preloadModules); 476 } 477} 478 479module.exports = { 480 patchProcessObject, 481 setupCoverageHooks, 482 setupWarningHandler, 483 setupDebugEnv, 484 prepareMainThreadExecution, 485 initializeAbortController, 486 initializeDeprecations, 487 initializeESMLoader, 488 initializeFrozenIntrinsics, 489 initializeSourceMapsHandlers, 490 loadPreloadModules, 491 setupTraceCategoryState, 492 setupInspectorHooks, 493 initializeReport, 494 initializeCJSLoader, 495 initializeWASI 496}; 497