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