• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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