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