1// Hello, and welcome to hacking node.js! 2// 3// This file is invoked by `Realm::BootstrapRealm()` in `src/node_realm.cc`, 4// and is responsible for setting up Node.js core before main scripts 5// under `lib/internal/main/` are executed. 6// 7// By default, Node.js binaries come with an embedded V8 startup snapshot 8// that is generated at build-time with a `node_mksnapshot` executable. 9// The snapshot generation code can be found in `SnapshotBuilder::Generate()` 10// from `src/node_snapshotable.cc`. 11// This snapshot captures the V8 heap initialized by scripts under 12// `lib/internal/bootstrap/`, including this file. When initializing the main 13// thread, Node.js deserializes the heap from the snapshot, instead of actually 14// running this script and others in `lib/internal/bootstrap/`. To disable this 15// behavior, pass `--no-node-snapshot` when starting the process so that 16// Node.js actually runs this script to initialize the heap. 17// 18// This script is expected not to perform any asynchronous operations itself 19// when being executed - those should be done in either 20// `lib/internal/process/pre_execution.js` or in main scripts. It should not 21// query any run-time states (e.g. command line arguments, environment 22// variables) when being executed - functions in this script that are invoked 23// at a later time can, however, query those states lazily. 24// The majority of the code here focuses on setting up the global object and 25// the process object in a synchronous, environment-independent manner. 26// 27// Scripts run before this file: 28// - `lib/internal/per_context/primordials.js`: this saves copies of JavaScript 29// builtins that won't be affected by user land monkey-patching for internal 30// modules to use. 31// - `lib/internal/per_context/domexception.js`: implementation of the 32// `DOMException` class. 33// - `lib/internal/per_context/messageport.js`: JS-side components of the 34// `MessagePort` implementation. 35// - `lib/internal/bootstrap/realm.js`: this sets up internal binding and 36// module loaders, including `process.binding()`, `process._linkedBinding()`, 37// `internalBinding()` and `BuiltinModule`, and per-realm internal states 38// and bindings, including `prepare_stack_trace_callback`. 39// 40// The initialization done in this script is included in both the main thread 41// and the worker threads. After this, further initialization is done based 42// on the configuration of the Node.js instance by executing the scripts in 43// `lib/internal/bootstrap/switches/`. 44// 45// Then, depending on how the Node.js instance is launched, one of the main 46// scripts in `lib/internal/main` will be selected by C++ to start the actual 47// execution. They may run additional setups exported by 48// `lib/internal/process/pre_execution.js` depending on the run-time states. 49 50'use strict'; 51 52// This file is compiled as if it's wrapped in a function with arguments 53// passed by `BuiltinLoader::CompileAndCall()`. 54/* global process, require, internalBinding, primordials */ 55 56const { 57 FunctionPrototypeCall, 58 JSONParse, 59 ObjectDefineProperty, 60 ObjectGetPrototypeOf, 61 ObjectPreventExtensions, 62 ObjectSetPrototypeOf, 63 ReflectGet, 64 ReflectSet, 65 SymbolToStringTag, 66 globalThis, 67} = primordials; 68const config = internalBinding('config'); 69const internalTimers = require('internal/timers'); 70const { 71 defineOperation, 72 deprecate, 73 defineLazyProperties, 74} = require('internal/util'); 75const { 76 privateSymbols: { 77 exiting_aliased_Uint32Array, 78 }, 79} = internalBinding('util'); 80 81setupProcessObject(); 82 83setupGlobalProxy(); 84setupBuffer(); 85 86process.domain = null; 87{ 88 const exitingAliasedUint32Array = process[exiting_aliased_Uint32Array]; 89 ObjectDefineProperty(process, '_exiting', { 90 __proto__: null, 91 get() { 92 return exitingAliasedUint32Array[0] === 1; 93 }, 94 set(value) { 95 exitingAliasedUint32Array[0] = value ? 1 : 0; 96 }, 97 enumerable: true, 98 configurable: true, 99 }); 100} 101process._exiting = false; 102 103// TODO(@jasnell): Once this has gone through one full major 104// release cycle, remove the Proxy and setter and update the 105// getter to either return a read-only object or always return 106// a freshly parsed version of nativeModule.config. 107 108const deprecationHandler = { 109 warned: false, 110 message: 'Setting process.config is deprecated. ' + 111 'In the future the property will be read-only.', 112 code: 'DEP0150', 113 maybeWarn() { 114 if (!this.warned) { 115 process.emitWarning(this.message, { 116 type: 'DeprecationWarning', 117 code: this.code, 118 }); 119 this.warned = true; 120 } 121 }, 122 123 defineProperty(target, key, descriptor) { 124 this.maybeWarn(); 125 return ObjectDefineProperty(target, key, descriptor); 126 }, 127 128 deleteProperty(target, key) { 129 this.maybeWarn(); 130 delete target[key]; 131 }, 132 133 preventExtensions(target) { 134 this.maybeWarn(); 135 return ObjectPreventExtensions(target); 136 }, 137 138 set(target, key, value) { 139 this.maybeWarn(); 140 return ReflectSet(target, key, value); 141 }, 142 143 get(target, key, receiver) { 144 const val = ReflectGet(target, key, receiver); 145 if (val != null && typeof val === 'object') { 146 // eslint-disable-next-line node-core/prefer-primordials 147 return new Proxy(val, deprecationHandler); 148 } 149 return val; 150 }, 151 152 setPrototypeOf(target, proto) { 153 this.maybeWarn(); 154 return ObjectSetPrototypeOf(target, proto); 155 }, 156}; 157 158// process.config is serialized config.gypi 159const binding = internalBinding('builtins'); 160 161// eslint-disable-next-line node-core/prefer-primordials 162let processConfig = new Proxy( 163 JSONParse(binding.config), 164 deprecationHandler); 165 166ObjectDefineProperty(process, 'config', { 167 __proto__: null, 168 enumerable: true, 169 configurable: true, 170 get() { return processConfig; }, 171 set(value) { 172 deprecationHandler.maybeWarn(); 173 processConfig = value; 174 }, 175}); 176 177require('internal/worker/js_transferable').setup(); 178 179// Bootstrappers for all threads, including worker threads and main thread 180const perThreadSetup = require('internal/process/per_thread'); 181const rawMethods = internalBinding('process_methods'); 182 183// Set up methods on the process object for all threads 184{ 185 process.dlopen = rawMethods.dlopen; 186 process.uptime = rawMethods.uptime; 187 188 // TODO(joyeecheung): either remove them or make them public 189 process._getActiveRequests = rawMethods._getActiveRequests; 190 process._getActiveHandles = rawMethods._getActiveHandles; 191 process.getActiveResourcesInfo = rawMethods.getActiveResourcesInfo; 192 193 // TODO(joyeecheung): remove these 194 process.reallyExit = rawMethods.reallyExit; 195 process._kill = rawMethods._kill; 196 197 const wrapped = perThreadSetup.wrapProcessMethods(rawMethods); 198 process._rawDebug = wrapped._rawDebug; 199 process.cpuUsage = wrapped.cpuUsage; 200 process.resourceUsage = wrapped.resourceUsage; 201 process.memoryUsage = wrapped.memoryUsage; 202 process.constrainedMemory = rawMethods.constrainedMemory; 203 process.kill = wrapped.kill; 204 process.exit = wrapped.exit; 205 206 process.hrtime = perThreadSetup.hrtime; 207 process.hrtime.bigint = perThreadSetup.hrtimeBigInt; 208 209 process.openStdin = function() { 210 process.stdin.resume(); 211 return process.stdin; 212 }; 213} 214 215const credentials = internalBinding('credentials'); 216if (credentials.implementsPosixCredentials) { 217 process.getuid = credentials.getuid; 218 process.geteuid = credentials.geteuid; 219 process.getgid = credentials.getgid; 220 process.getegid = credentials.getegid; 221 process.getgroups = credentials.getgroups; 222} 223 224// Setup the callbacks that node::AsyncWrap will call when there are hooks to 225// process. They use the same functions as the JS embedder API. These callbacks 226// are setup immediately to prevent async_wrap.setupHooks() from being hijacked 227// and the cost of doing so is negligible. 228const { nativeHooks } = require('internal/async_hooks'); 229internalBinding('async_wrap').setupHooks(nativeHooks); 230 231const { 232 setupTaskQueue, 233 queueMicrotask, 234} = require('internal/process/task_queues'); 235 236// Non-standard extensions: 237defineOperation(globalThis, 'queueMicrotask', queueMicrotask); 238 239const timers = require('timers'); 240defineOperation(globalThis, 'clearImmediate', timers.clearImmediate); 241defineOperation(globalThis, 'setImmediate', timers.setImmediate); 242 243defineLazyProperties( 244 globalThis, 245 'internal/structured_clone', 246 ['structuredClone'], 247); 248 249// Set the per-Environment callback that will be called 250// when the TrackingTraceStateObserver updates trace state. 251// Note that when NODE_USE_V8_PLATFORM is true, the observer is 252// attached to the per-process TracingController. 253const { setTraceCategoryStateUpdateHandler } = internalBinding('trace_events'); 254setTraceCategoryStateUpdateHandler(perThreadSetup.toggleTraceCategoryState); 255 256// process.allowedNodeEnvironmentFlags 257ObjectDefineProperty(process, 'allowedNodeEnvironmentFlags', { 258 __proto__: null, 259 get() { 260 const flags = perThreadSetup.buildAllowedFlags(); 261 process.allowedNodeEnvironmentFlags = flags; 262 return process.allowedNodeEnvironmentFlags; 263 }, 264 // If the user tries to set this to another value, override 265 // this completely to that value. 266 set(value) { 267 ObjectDefineProperty(this, 'allowedNodeEnvironmentFlags', { 268 __proto__: null, 269 value, 270 configurable: true, 271 enumerable: true, 272 writable: true, 273 }); 274 }, 275 enumerable: true, 276 configurable: true, 277}); 278 279// process.assert 280process.assert = deprecate( 281 perThreadSetup.assert, 282 'process.assert() is deprecated. Please use the `assert` module instead.', 283 'DEP0100'); 284 285// TODO(joyeecheung): this property has not been well-maintained, should we 286// deprecate it in favor of a better API? 287const { isDebugBuild, hasOpenSSL, hasInspector } = config; 288const features = { 289 inspector: hasInspector, 290 debug: isDebugBuild, 291 uv: true, 292 ipv6: true, // TODO(bnoordhuis) ping libuv 293 tls_alpn: hasOpenSSL, 294 tls_sni: hasOpenSSL, 295 tls_ocsp: hasOpenSSL, 296 tls: hasOpenSSL, 297 // This needs to be dynamic because --no-node-snapshot disables the 298 // code cache even if the binary is built with embedded code cache. 299 get cached_builtins() { 300 return binding.hasCachedBuiltins(); 301 }, 302}; 303 304ObjectDefineProperty(process, 'features', { 305 __proto__: null, 306 enumerable: true, 307 writable: false, 308 configurable: false, 309 value: features, 310}); 311 312{ 313 const { 314 onGlobalUncaughtException, 315 setUncaughtExceptionCaptureCallback, 316 hasUncaughtExceptionCaptureCallback, 317 } = require('internal/process/execution'); 318 319 // For legacy reasons this is still called `_fatalException`, even 320 // though it is now a global uncaught exception handler. 321 // The C++ land node::errors::TriggerUncaughtException grabs it 322 // from the process object because it has been monkey-patchable. 323 // TODO(joyeecheung): investigate whether process._fatalException 324 // can be deprecated. 325 process._fatalException = onGlobalUncaughtException; 326 process.setUncaughtExceptionCaptureCallback = 327 setUncaughtExceptionCaptureCallback; 328 process.hasUncaughtExceptionCaptureCallback = 329 hasUncaughtExceptionCaptureCallback; 330} 331 332const { emitWarning } = require('internal/process/warning'); 333process.emitWarning = emitWarning; 334 335// We initialize the tick callbacks and the timer callbacks last during 336// bootstrap to make sure that any operation done before this are synchronous. 337// If any ticks or timers are scheduled before this they are unlikely to work. 338{ 339 const { nextTick, runNextTicks } = setupTaskQueue(); 340 process.nextTick = nextTick; 341 // Used to emulate a tick manually in the JS land. 342 // A better name for this function would be `runNextTicks` but 343 // it has been exposed to the process object so we keep this legacy name 344 // TODO(joyeecheung): either remove it or make it public 345 process._tickCallback = runNextTicks; 346 347 const { setupTimers } = internalBinding('timers'); 348 const { 349 processImmediate, 350 processTimers, 351 } = internalTimers.getTimerCallbacks(runNextTicks); 352 // Sets two per-Environment callbacks that will be run from libuv: 353 // - processImmediate will be run in the callback of the per-Environment 354 // check handle. 355 // - processTimers will be run in the callback of the per-Environment timer. 356 setupTimers(processImmediate, processTimers); 357 // Note: only after this point are the timers effective 358} 359 360{ 361 const { 362 getSourceMapsEnabled, 363 setSourceMapsEnabled, 364 maybeCacheGeneratedSourceMap, 365 } = require('internal/source_map/source_map_cache'); 366 const { 367 setMaybeCacheGeneratedSourceMap, 368 } = internalBinding('errors'); 369 370 ObjectDefineProperty(process, 'sourceMapsEnabled', { 371 __proto__: null, 372 enumerable: true, 373 configurable: true, 374 get() { 375 return getSourceMapsEnabled(); 376 }, 377 }); 378 process.setSourceMapsEnabled = setSourceMapsEnabled; 379 // The C++ land calls back to maybeCacheGeneratedSourceMap() 380 // when code is generated by user with eval() or new Function() 381 // to cache the source maps from the evaluated code, if any. 382 setMaybeCacheGeneratedSourceMap(maybeCacheGeneratedSourceMap); 383} 384 385function setupProcessObject() { 386 const EventEmitter = require('events'); 387 const origProcProto = ObjectGetPrototypeOf(process); 388 ObjectSetPrototypeOf(origProcProto, EventEmitter.prototype); 389 FunctionPrototypeCall(EventEmitter, process); 390 ObjectDefineProperty(process, SymbolToStringTag, { 391 __proto__: null, 392 enumerable: false, 393 writable: true, 394 configurable: false, 395 value: 'process', 396 }); 397 398 // Create global.process as getters so that we have a 399 // deprecation path for these in ES Modules. 400 // See https://github.com/nodejs/node/pull/26334. 401 let _process = process; 402 ObjectDefineProperty(globalThis, 'process', { 403 __proto__: null, 404 get() { 405 return _process; 406 }, 407 set(value) { 408 _process = value; 409 }, 410 enumerable: false, 411 configurable: true, 412 }); 413} 414 415function setupGlobalProxy() { 416 ObjectDefineProperty(globalThis, SymbolToStringTag, { 417 __proto__: null, 418 value: 'global', 419 writable: false, 420 enumerable: false, 421 configurable: true, 422 }); 423 globalThis.global = globalThis; 424} 425 426function setupBuffer() { 427 const { 428 Buffer, 429 } = require('buffer'); 430 const bufferBinding = internalBinding('buffer'); 431 432 // Only after this point can C++ use Buffer::New() 433 bufferBinding.setBufferPrototype(Buffer.prototype); 434 delete bufferBinding.setBufferPrototype; 435 delete bufferBinding.zeroFill; 436 437 // Create global.Buffer as getters so that we have a 438 // deprecation path for these in ES Modules. 439 // See https://github.com/nodejs/node/pull/26334. 440 let _Buffer = Buffer; 441 ObjectDefineProperty(globalThis, 'Buffer', { 442 __proto__: null, 443 get() { 444 return _Buffer; 445 }, 446 set(value) { 447 _Buffer = value; 448 }, 449 enumerable: false, 450 configurable: true, 451 }); 452} 453