1'use strict'; 2 3const { 4 NumberParseInt, 5 ObjectDefineProperties, 6 ObjectDefineProperty, 7 ObjectGetOwnPropertyDescriptor, 8 SafeMap, 9 SafeWeakMap, 10 StringPrototypeStartsWith, 11 Symbol, 12 SymbolDispose, 13 SymbolAsyncDispose, 14 globalThis, 15} = primordials; 16 17const { 18 getOptionValue, 19 getEmbedderOptions, 20 refreshOptions, 21} = require('internal/options'); 22const { reconnectZeroFillToggle } = require('internal/buffer'); 23const { 24 defineOperation, 25 exposeInterface, 26 setupCoverageHooks, 27} = require('internal/util'); 28 29const { 30 ERR_MANIFEST_ASSERT_INTEGRITY, 31} = require('internal/errors').codes; 32const assert = require('internal/assert'); 33 34const { 35 addSerializeCallback, 36 isBuildingSnapshot, 37} = require('v8').startupSnapshot; 38 39function prepareMainThreadExecution(expandArgv1 = false, initializeModules = true) { 40 prepareExecution({ 41 expandArgv1, 42 initializeModules, 43 isMainThread: true, 44 }); 45} 46 47function prepareWorkerThreadExecution() { 48 prepareExecution({ 49 expandArgv1: false, 50 initializeModules: false, // Will need to initialize it after policy setup 51 isMainThread: false, 52 }); 53} 54 55function prepareExecution(options) { 56 const { expandArgv1, initializeModules, isMainThread } = options; 57 58 refreshRuntimeOptions(); 59 reconnectZeroFillToggle(); 60 61 // Patch the process object with legacy properties and normalizations 62 patchProcessObject(expandArgv1); 63 setupTraceCategoryState(); 64 setupPerfHooks(); 65 setupInspectorHooks(); 66 setupWarningHandler(); 67 setupFetch(); 68 setupWebCrypto(); 69 setupCustomEvent(); 70 setupCodeCoverage(); 71 setupDebugEnv(); 72 // Process initial diagnostic reporting configuration, if present. 73 initializeReport(); 74 initializeSourceMapsHandlers(); 75 initializeDeprecations(); 76 require('internal/dns/utils').initializeDns(); 77 78 setupSymbolDisposePolyfill(); 79 80 if (isMainThread) { 81 assert(internalBinding('worker').isMainThread); 82 // Worker threads will get the manifest in the message handler. 83 const policy = readPolicyFromDisk(); 84 if (policy) { 85 require('internal/process/policy') 86 .setup(policy.manifestSrc, policy.manifestURL); 87 } 88 89 // Print stack trace on `SIGINT` if option `--trace-sigint` presents. 90 setupStacktracePrinterOnSigint(); 91 initializeReportSignalHandlers(); // Main-thread-only. 92 initializeHeapSnapshotSignalHandlers(); 93 // If the process is spawned with env NODE_CHANNEL_FD, it's probably 94 // spawned by our child_process module, then initialize IPC. 95 // This attaches some internal event listeners and creates: 96 // process.send(), process.channel, process.connected, 97 // process.disconnect(). 98 setupChildProcessIpcChannel(); 99 // If this is a worker in cluster mode, start up the communication 100 // channel. This needs to be done before any user code gets executed 101 // (including preload modules). 102 initializeClusterIPC(); 103 104 // TODO(joyeecheung): do this for worker threads as well. 105 require('internal/v8/startup_snapshot').runDeserializeCallbacks(); 106 } else { 107 assert(!internalBinding('worker').isMainThread); 108 // The setup should be called in LOAD_SCRIPT message handler. 109 assert(!initializeModules); 110 } 111 112 if (initializeModules) { 113 setupUserModules(); 114 } 115} 116 117function setupSymbolDisposePolyfill() { 118 // TODO(MoLow): Remove this polyfill once Symbol.dispose and Symbol.asyncDispose are available in V8. 119 // eslint-disable-next-line node-core/prefer-primordials 120 Symbol.dispose ??= SymbolDispose; 121 // eslint-disable-next-line node-core/prefer-primordials 122 Symbol.asyncDispose ??= SymbolAsyncDispose; 123} 124 125function setupUserModules() { 126 initializeCJSLoader(); 127 initializeESMLoader(); 128 const CJSLoader = require('internal/modules/cjs/loader'); 129 assert(!CJSLoader.hasLoadedAnyUserCJSModule); 130 loadPreloadModules(); 131 // Need to be done after --require setup. 132 initializeFrozenIntrinsics(); 133} 134 135function refreshRuntimeOptions() { 136 refreshOptions(); 137} 138 139function patchProcessObject(expandArgv1) { 140 const binding = internalBinding('process_methods'); 141 binding.patchProcessObject(process); 142 143 require('internal/process/per_thread').refreshHrtimeBuffer(); 144 145 ObjectDefineProperty(process, 'argv0', { 146 __proto__: null, 147 enumerable: true, 148 // Only set it to true during snapshot building. 149 configurable: getOptionValue('--build-snapshot'), 150 value: process.argv[0], 151 }); 152 153 process.exitCode = undefined; 154 process._exiting = false; 155 process.argv[0] = process.execPath; 156 157 if (expandArgv1 && process.argv[1] && 158 !StringPrototypeStartsWith(process.argv[1], '-')) { 159 // Expand process.argv[1] into a full path. 160 const path = require('path'); 161 try { 162 process.argv[1] = path.resolve(process.argv[1]); 163 } catch { 164 // Continue regardless of error. 165 } 166 } 167 168 // We need to initialize the global console here again with process.stdout 169 // and friends for snapshot deserialization. 170 const globalConsole = require('internal/console/global'); 171 const { initializeGlobalConsole } = require('internal/console/constructor'); 172 initializeGlobalConsole(globalConsole); 173 174 // TODO(joyeecheung): most of these should be deprecated and removed, 175 // except some that we need to be able to mutate during run time. 176 addReadOnlyProcessAlias('_eval', '--eval'); 177 addReadOnlyProcessAlias('_print_eval', '--print'); 178 addReadOnlyProcessAlias('_syntax_check_only', '--check'); 179 addReadOnlyProcessAlias('_forceRepl', '--interactive'); 180 addReadOnlyProcessAlias('_preload_modules', '--require'); 181 addReadOnlyProcessAlias('noDeprecation', '--no-deprecation'); 182 addReadOnlyProcessAlias('noProcessWarnings', '--no-warnings'); 183 addReadOnlyProcessAlias('traceProcessWarnings', '--trace-warnings'); 184 addReadOnlyProcessAlias('throwDeprecation', '--throw-deprecation'); 185 addReadOnlyProcessAlias('profProcess', '--prof-process'); 186 addReadOnlyProcessAlias('traceDeprecation', '--trace-deprecation'); 187 addReadOnlyProcessAlias('_breakFirstLine', '--inspect-brk', false); 188 addReadOnlyProcessAlias('_breakNodeFirstLine', '--inspect-brk-node', false); 189} 190 191function addReadOnlyProcessAlias(name, option, enumerable = true) { 192 const value = getOptionValue(option); 193 if (value) { 194 ObjectDefineProperty(process, name, { 195 __proto__: null, 196 writable: false, 197 configurable: true, 198 enumerable, 199 value, 200 }); 201 } 202} 203 204function setupWarningHandler() { 205 const { 206 onWarning, 207 resetForSerialization, 208 } = require('internal/process/warning'); 209 if (getOptionValue('--warnings') && 210 process.env.NODE_NO_WARNINGS !== '1') { 211 process.on('warning', onWarning); 212 213 // The code above would add the listener back during deserialization, 214 // if applicable. 215 if (isBuildingSnapshot()) { 216 addSerializeCallback(() => { 217 process.removeListener('warning', onWarning); 218 resetForSerialization(); 219 }); 220 } 221 } 222} 223 224// https://fetch.spec.whatwg.org/ 225function setupFetch() { 226 if (process.config.variables.node_no_browser_globals || 227 getOptionValue('--no-experimental-fetch')) { 228 return; 229 } 230 231 let undici; 232 function lazyUndici() { 233 if (undici) { 234 return undici; 235 } 236 237 undici = require('internal/deps/undici/undici'); 238 return undici; 239 } 240 241 async function fetch(input, init = undefined) { 242 return lazyUndici().fetch(input, init); 243 } 244 245 defineOperation(globalThis, 'fetch', fetch); 246 247 function lazyInterface(name) { 248 return { 249 configurable: true, 250 enumerable: false, 251 get() { 252 return lazyUndici()[name]; 253 }, 254 set(value) { 255 exposeInterface(globalThis, name, value); 256 }, 257 }; 258 } 259 260 ObjectDefineProperties(globalThis, { 261 FormData: lazyInterface('FormData'), 262 Headers: lazyInterface('Headers'), 263 Request: lazyInterface('Request'), 264 Response: lazyInterface('Response'), 265 }); 266 267 // The WebAssembly Web API: https://webassembly.github.io/spec/web-api 268 const { wasmStreamingCallback } = require('internal/wasm_web_api'); 269 internalBinding('wasm_web_api').setImplementation(wasmStreamingCallback); 270} 271 272// TODO(aduh95): move this to internal/bootstrap/browser when the CLI flag is 273// removed. 274function setupWebCrypto() { 275 if (process.config.variables.node_no_browser_globals || 276 !getOptionValue('--experimental-global-webcrypto')) { 277 return; 278 } 279 280 let webcrypto; 281 ObjectDefineProperty(globalThis, 'crypto', 282 { __proto__: null, ...ObjectGetOwnPropertyDescriptor({ 283 get crypto() { 284 webcrypto ??= require('internal/crypto/webcrypto'); 285 return webcrypto.crypto; 286 }, 287 }, 'crypto') }); 288 if (internalBinding('config').hasOpenSSL) { 289 webcrypto ??= require('internal/crypto/webcrypto'); 290 exposeInterface(globalThis, 'Crypto', webcrypto.Crypto); 291 exposeInterface(globalThis, 'CryptoKey', webcrypto.CryptoKey); 292 exposeInterface(globalThis, 'SubtleCrypto', webcrypto.SubtleCrypto); 293 } 294} 295 296function setupCodeCoverage() { 297 // Resolve the coverage directory to an absolute path, and 298 // overwrite process.env so that the original path gets passed 299 // to child processes even when they switch cwd. Don't do anything if the 300 // --experimental-test-coverage flag is present, as the test runner will 301 // handle coverage. 302 if (process.env.NODE_V8_COVERAGE && 303 !getOptionValue('--experimental-test-coverage')) { 304 process.env.NODE_V8_COVERAGE = 305 setupCoverageHooks(process.env.NODE_V8_COVERAGE); 306 } 307} 308 309// TODO(daeyeon): move this to internal/bootstrap/browser when the CLI flag is 310// removed. 311function setupCustomEvent() { 312 if (process.config.variables.node_no_browser_globals || 313 !getOptionValue('--experimental-global-customevent')) { 314 return; 315 } 316 const { CustomEvent } = require('internal/event_target'); 317 exposeInterface(globalThis, 'CustomEvent', CustomEvent); 318} 319 320function setupStacktracePrinterOnSigint() { 321 if (!getOptionValue('--trace-sigint')) { 322 return; 323 } 324 const { SigintWatchdog } = require('internal/watchdog'); 325 326 const watchdog = new SigintWatchdog(); 327 watchdog.start(); 328} 329 330function initializeReport() { 331 const { report } = require('internal/process/report'); 332 ObjectDefineProperty(process, 'report', { 333 __proto__: null, 334 enumerable: true, 335 configurable: true, 336 get() { 337 return report; 338 }, 339 }); 340} 341 342function setupDebugEnv() { 343 require('internal/util/debuglog').initializeDebugEnv(process.env.NODE_DEBUG); 344 if (getOptionValue('--expose-internals')) { 345 require('internal/bootstrap/loaders').BuiltinModule.exposeInternals(); 346 } 347} 348 349// This has to be called after initializeReport() is called 350function initializeReportSignalHandlers() { 351 const { addSignalHandler } = require('internal/process/report'); 352 353 addSignalHandler(); 354} 355 356function initializeHeapSnapshotSignalHandlers() { 357 const signal = getOptionValue('--heapsnapshot-signal'); 358 359 if (!signal) 360 return; 361 362 require('internal/validators').validateSignalName(signal); 363 const { writeHeapSnapshot } = require('v8'); 364 365 function doWriteHeapSnapshot() { 366 writeHeapSnapshot(); 367 } 368 process.on(signal, doWriteHeapSnapshot); 369 370 // The code above would add the listener back during deserialization, 371 // if applicable. 372 if (isBuildingSnapshot()) { 373 addSerializeCallback(() => { 374 process.removeListener(signal, doWriteHeapSnapshot); 375 }); 376 } 377} 378 379function setupTraceCategoryState() { 380 const { isTraceCategoryEnabled } = internalBinding('trace_events'); 381 const { toggleTraceCategoryState } = require('internal/process/per_thread'); 382 toggleTraceCategoryState(isTraceCategoryEnabled('node.async_hooks')); 383} 384 385function setupPerfHooks() { 386 require('internal/perf/performance').refreshTimeOrigin(); 387 require('internal/perf/utils').refreshTimeOrigin(); 388} 389 390function setupInspectorHooks() { 391 // If Debugger.setAsyncCallStackDepth is sent during bootstrap, 392 // we cannot immediately call into JS to enable the hooks, which could 393 // interrupt the JS execution of bootstrap. So instead we save the 394 // notification in the inspector agent if it's sent in the middle of 395 // bootstrap, and process the notification later here. 396 if (internalBinding('config').hasInspector) { 397 const { 398 enable, 399 disable, 400 } = require('internal/inspector_async_hook'); 401 internalBinding('inspector').registerAsyncHook(enable, disable); 402 } 403} 404 405// In general deprecations are initialized wherever the APIs are implemented, 406// this is used to deprecate APIs implemented in C++ where the deprecation 407// utilities are not easily accessible. 408function initializeDeprecations() { 409 const { deprecate } = require('internal/util'); 410 const pendingDeprecation = getOptionValue('--pending-deprecation'); 411 412 // DEP0103: access to `process.binding('util').isX` type checkers 413 // TODO(addaleax): Turn into a full runtime deprecation. 414 const utilBinding = internalBinding('util'); 415 const types = require('internal/util/types'); 416 for (const name of [ 417 'isArrayBuffer', 418 'isArrayBufferView', 419 'isAsyncFunction', 420 'isDataView', 421 'isDate', 422 'isExternal', 423 'isMap', 424 'isMapIterator', 425 'isNativeError', 426 'isPromise', 427 'isRegExp', 428 'isSet', 429 'isSetIterator', 430 'isTypedArray', 431 'isUint8Array', 432 'isAnyArrayBuffer', 433 ]) { 434 utilBinding[name] = pendingDeprecation ? 435 deprecate(types[name], 436 'Accessing native typechecking bindings of Node ' + 437 'directly is deprecated. ' + 438 `Please use \`util.types.${name}\` instead.`, 439 'DEP0103') : 440 types[name]; 441 } 442 443 // TODO(joyeecheung): this is a legacy property exposed to process. 444 // Now that we use the config binding to carry this information, remove 445 // it from the process. We may consider exposing it properly in 446 // process.features. 447 const { noBrowserGlobals } = internalBinding('config'); 448 if (noBrowserGlobals) { 449 ObjectDefineProperty(process, '_noBrowserGlobals', { 450 __proto__: null, 451 writable: false, 452 enumerable: true, 453 configurable: true, 454 value: noBrowserGlobals, 455 }); 456 } 457 458 if (pendingDeprecation) { 459 process.binding = deprecate(process.binding, 460 'process.binding() is deprecated. ' + 461 'Please use public APIs instead.', 'DEP0111'); 462 463 process._tickCallback = deprecate(process._tickCallback, 464 'process._tickCallback() is deprecated', 465 'DEP0134'); 466 } 467} 468 469function setupChildProcessIpcChannel() { 470 if (process.env.NODE_CHANNEL_FD) { 471 const assert = require('internal/assert'); 472 473 const fd = NumberParseInt(process.env.NODE_CHANNEL_FD, 10); 474 assert(fd >= 0); 475 476 // Make sure it's not accidentally inherited by child processes. 477 delete process.env.NODE_CHANNEL_FD; 478 479 const serializationMode = 480 process.env.NODE_CHANNEL_SERIALIZATION_MODE || 'json'; 481 delete process.env.NODE_CHANNEL_SERIALIZATION_MODE; 482 483 require('child_process')._forkChild(fd, serializationMode); 484 assert(process.send); 485 } 486} 487 488function initializeClusterIPC() { 489 if (process.argv[1] && process.env.NODE_UNIQUE_ID) { 490 const cluster = require('cluster'); 491 cluster._setupWorker(); 492 // Make sure it's not accidentally inherited by child processes. 493 delete process.env.NODE_UNIQUE_ID; 494 } 495} 496 497function readPolicyFromDisk() { 498 const experimentalPolicy = getOptionValue('--experimental-policy'); 499 if (experimentalPolicy) { 500 process.emitWarning('Policies are experimental.', 501 'ExperimentalWarning'); 502 const { pathToFileURL, URL } = require('internal/url'); 503 // URL here as it is slightly different parsing 504 // no bare specifiers for now 505 let manifestURL; 506 if (require('path').isAbsolute(experimentalPolicy)) { 507 manifestURL = new URL(`file://${experimentalPolicy}`); 508 } else { 509 const cwdURL = pathToFileURL(process.cwd()); 510 cwdURL.pathname += '/'; 511 manifestURL = new URL(experimentalPolicy, cwdURL); 512 } 513 const fs = require('fs'); 514 const src = fs.readFileSync(manifestURL, 'utf8'); 515 const experimentalPolicyIntegrity = getOptionValue('--policy-integrity'); 516 if (experimentalPolicyIntegrity) { 517 const SRI = require('internal/policy/sri'); 518 const { createHash, timingSafeEqual } = require('crypto'); 519 const realIntegrities = new SafeMap(); 520 const integrityEntries = SRI.parse(experimentalPolicyIntegrity); 521 let foundMatch = false; 522 for (let i = 0; i < integrityEntries.length; i++) { 523 const { 524 algorithm, 525 value: expected, 526 } = integrityEntries[i]; 527 const hash = createHash(algorithm); 528 hash.update(src); 529 const digest = hash.digest(); 530 if (digest.length === expected.length && 531 timingSafeEqual(digest, expected)) { 532 foundMatch = true; 533 break; 534 } 535 realIntegrities.set(algorithm, digest.toString('base64')); 536 } 537 if (!foundMatch) { 538 throw new ERR_MANIFEST_ASSERT_INTEGRITY(manifestURL, realIntegrities); 539 } 540 } 541 return { 542 manifestSrc: src, manifestURL: manifestURL.href, 543 }; 544 } 545} 546 547function initializeCJSLoader() { 548 const CJSLoader = require('internal/modules/cjs/loader'); 549 if (!getEmbedderOptions().noGlobalSearchPaths) { 550 CJSLoader.Module._initPaths(); 551 } 552 // TODO(joyeecheung): deprecate this in favor of a proper hook? 553 CJSLoader.Module.runMain = 554 require('internal/modules/run_main').executeUserEntryPoint; 555} 556 557function initializeESMLoader() { 558 // Create this WeakMap in js-land because V8 has no C++ API for WeakMap. 559 internalBinding('module_wrap').callbackMap = new SafeWeakMap(); 560 561 if (getEmbedderOptions().shouldNotRegisterESMLoader) return; 562 563 const { 564 setImportModuleDynamicallyCallback, 565 setInitializeImportMetaObjectCallback, 566 } = internalBinding('module_wrap'); 567 const esm = require('internal/process/esm_loader'); 568 // Setup per-isolate callbacks that locate data or callbacks that we keep 569 // track of for different ESM modules. 570 setInitializeImportMetaObjectCallback(esm.initializeImportMetaObject); 571 setImportModuleDynamicallyCallback(esm.importModuleDynamicallyCallback); 572 573 // Patch the vm module when --experimental-vm-modules is on. 574 // Please update the comments in vm.js when this block changes. 575 if (getOptionValue('--experimental-vm-modules')) { 576 const { 577 Module, SourceTextModule, SyntheticModule, 578 } = require('internal/vm/module'); 579 const vm = require('vm'); 580 vm.Module = Module; 581 vm.SourceTextModule = SourceTextModule; 582 vm.SyntheticModule = SyntheticModule; 583 } 584} 585 586function initializeSourceMapsHandlers() { 587 const { setSourceMapsEnabled, getSourceMapsEnabled } = 588 require('internal/source_map/source_map_cache'); 589 process.setSourceMapsEnabled = setSourceMapsEnabled; 590 // Initialize the environment flag of source maps. 591 getSourceMapsEnabled(); 592} 593 594function initializeFrozenIntrinsics() { 595 if (getOptionValue('--frozen-intrinsics')) { 596 process.emitWarning('The --frozen-intrinsics flag is experimental', 597 'ExperimentalWarning'); 598 require('internal/freeze_intrinsics')(); 599 } 600} 601 602function loadPreloadModules() { 603 // For user code, we preload modules if `-r` is passed 604 const preloadModules = getOptionValue('--require'); 605 if (preloadModules && preloadModules.length > 0) { 606 const { 607 Module: { 608 _preloadModules, 609 }, 610 } = require('internal/modules/cjs/loader'); 611 _preloadModules(preloadModules); 612 } 613} 614 615function markBootstrapComplete() { 616 internalBinding('performance').markBootstrapComplete(); 617} 618 619module.exports = { 620 setupUserModules, 621 prepareMainThreadExecution, 622 prepareWorkerThreadExecution, 623 markBootstrapComplete, 624}; 625