• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Hello, and welcome to hacking node.js!
2//
3// This file is invoked by `node::RunBootstrapping()` in `src/node.cc`, and is
4// responsible for setting up node.js core before executing main scripts
5// under `lib/internal/main/`.
6//
7// This file is expected not to perform any asynchronous operations itself
8// when being executed - those should be done in either
9// `lib/internal/bootstrap/pre_execution.js` or in main scripts. The majority
10// of the code here focuses on setting up the global proxy and the process
11// object in a synchronous manner.
12// As special caution is given to the performance of the startup process,
13// many dependencies are invoked lazily.
14//
15// Scripts run before this file:
16// - `lib/internal/per_context/primordials.js`: to save copies of JavaScript
17//   builtins that won't be affected by user land monkey-patching for internal
18//   modules to use.
19// - `lib/internal/bootstrap/loaders.js`: to setup internal binding and
20//   module loaders, including `process.binding()`, `process._linkedBinding()`,
21//   `internalBinding()` and `NativeModule`.
22//
23// This file is run to bootstrap both the main thread and the worker threads.
24// After this file is run, certain properties are setup according to the
25// configuration of the Node.js instance using the files in
26// `lib/internal/bootstrap/switches/`.
27//
28// Then, depending on how the Node.js instance is launched, one of the main
29// scripts in `lib/internal/main` will be selected by C++ to start the actual
30// execution. They may run additional setups exported by
31// `lib/internal/bootstrap/pre_execution.js` depending on the runtime states.
32
33'use strict';
34
35// This file is compiled as if it's wrapped in a function with arguments
36// passed by node::RunBootstrapping()
37/* global process, require, internalBinding, primordials */
38
39setupPrepareStackTrace();
40
41const {
42  FunctionPrototypeCall,
43  JSONParse,
44  ObjectDefineProperty,
45  ObjectGetPrototypeOf,
46  ObjectSetPrototypeOf,
47  SymbolToStringTag,
48  globalThis,
49} = primordials;
50const config = internalBinding('config');
51const { deprecate } = require('internal/util');
52
53setupProcessObject();
54
55setupGlobalProxy();
56setupBuffer();
57
58process.domain = null;
59process._exiting = false;
60
61// process.config is serialized config.gypi
62process.config = JSONParse(internalBinding('native_module').config);
63require('internal/worker/js_transferable').setup();
64
65// Bootstrappers for all threads, including worker threads and main thread
66const perThreadSetup = require('internal/process/per_thread');
67const rawMethods = internalBinding('process_methods');
68
69// Set up methods on the process object for all threads
70{
71  process.dlopen = rawMethods.dlopen;
72  process.uptime = rawMethods.uptime;
73
74  // TODO(joyeecheung): either remove them or make them public
75  process._getActiveRequests = rawMethods._getActiveRequests;
76  process._getActiveHandles = rawMethods._getActiveHandles;
77
78  // TODO(joyeecheung): remove these
79  process.reallyExit = rawMethods.reallyExit;
80  process._kill = rawMethods._kill;
81
82  const wrapped = perThreadSetup.wrapProcessMethods(rawMethods);
83  process._rawDebug = wrapped._rawDebug;
84  process.hrtime = wrapped.hrtime;
85  process.hrtime.bigint = wrapped.hrtimeBigInt;
86  process.cpuUsage = wrapped.cpuUsage;
87  process.resourceUsage = wrapped.resourceUsage;
88  process.memoryUsage = wrapped.memoryUsage;
89  process.kill = wrapped.kill;
90  process.exit = wrapped.exit;
91
92  process.openStdin = function() {
93    process.stdin.resume();
94    return process.stdin;
95  };
96}
97
98const credentials = internalBinding('credentials');
99if (credentials.implementsPosixCredentials) {
100  process.getuid = credentials.getuid;
101  process.geteuid = credentials.geteuid;
102  process.getgid = credentials.getgid;
103  process.getegid = credentials.getegid;
104  process.getgroups = credentials.getgroups;
105}
106
107// Setup the callbacks that node::AsyncWrap will call when there are hooks to
108// process. They use the same functions as the JS embedder API. These callbacks
109// are setup immediately to prevent async_wrap.setupHooks() from being hijacked
110// and the cost of doing so is negligible.
111const { nativeHooks } = require('internal/async_hooks');
112internalBinding('async_wrap').setupHooks(nativeHooks);
113
114const {
115  setupTaskQueue,
116  queueMicrotask
117} = require('internal/process/task_queues');
118
119if (!config.noBrowserGlobals) {
120  // Override global console from the one provided by the VM
121  // to the one implemented by Node.js
122  // https://console.spec.whatwg.org/#console-namespace
123  exposeNamespace(globalThis, 'console',
124                  createGlobalConsole(globalThis.console));
125
126  const { URL, URLSearchParams } = require('internal/url');
127  // https://url.spec.whatwg.org/#url
128  exposeInterface(globalThis, 'URL', URL);
129  // https://url.spec.whatwg.org/#urlsearchparams
130  exposeInterface(globalThis, 'URLSearchParams', URLSearchParams);
131
132  const {
133    TextEncoder, TextDecoder
134  } = require('internal/encoding');
135  // https://encoding.spec.whatwg.org/#textencoder
136  exposeInterface(globalThis, 'TextEncoder', TextEncoder);
137  // https://encoding.spec.whatwg.org/#textdecoder
138  exposeInterface(globalThis, 'TextDecoder', TextDecoder);
139
140  // https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope
141  const timers = require('timers');
142  defineOperation(globalThis, 'clearInterval', timers.clearInterval);
143  defineOperation(globalThis, 'clearTimeout', timers.clearTimeout);
144  defineOperation(globalThis, 'setInterval', timers.setInterval);
145  defineOperation(globalThis, 'setTimeout', timers.setTimeout);
146
147  defineOperation(globalThis, 'queueMicrotask', queueMicrotask);
148
149  // Non-standard extensions:
150  defineOperation(globalThis, 'clearImmediate', timers.clearImmediate);
151  defineOperation(globalThis, 'setImmediate', timers.setImmediate);
152}
153
154// Set the per-Environment callback that will be called
155// when the TrackingTraceStateObserver updates trace state.
156// Note that when NODE_USE_V8_PLATFORM is true, the observer is
157// attached to the per-process TracingController.
158const { setTraceCategoryStateUpdateHandler } = internalBinding('trace_events');
159setTraceCategoryStateUpdateHandler(perThreadSetup.toggleTraceCategoryState);
160
161// process.allowedNodeEnvironmentFlags
162ObjectDefineProperty(process, 'allowedNodeEnvironmentFlags', {
163  get() {
164    const flags = perThreadSetup.buildAllowedFlags();
165    process.allowedNodeEnvironmentFlags = flags;
166    return process.allowedNodeEnvironmentFlags;
167  },
168  // If the user tries to set this to another value, override
169  // this completely to that value.
170  set(value) {
171    ObjectDefineProperty(this, 'allowedNodeEnvironmentFlags', {
172      value,
173      configurable: true,
174      enumerable: true,
175      writable: true
176    });
177  },
178  enumerable: true,
179  configurable: true
180});
181
182// process.assert
183process.assert = deprecate(
184  perThreadSetup.assert,
185  'process.assert() is deprecated. Please use the `assert` module instead.',
186  'DEP0100');
187
188// TODO(joyeecheung): this property has not been well-maintained, should we
189// deprecate it in favor of a better API?
190const { isDebugBuild, hasOpenSSL, hasInspector } = config;
191ObjectDefineProperty(process, 'features', {
192  enumerable: true,
193  writable: false,
194  configurable: false,
195  value: {
196    inspector: hasInspector,
197    debug: isDebugBuild,
198    uv: true,
199    ipv6: true,  // TODO(bnoordhuis) ping libuv
200    tls_alpn: hasOpenSSL,
201    tls_sni: hasOpenSSL,
202    tls_ocsp: hasOpenSSL,
203    tls: hasOpenSSL,
204    cached_builtins: config.hasCachedBuiltins,
205  }
206});
207
208{
209  const {
210    onGlobalUncaughtException,
211    setUncaughtExceptionCaptureCallback,
212    hasUncaughtExceptionCaptureCallback
213  } = require('internal/process/execution');
214
215  // For legacy reasons this is still called `_fatalException`, even
216  // though it is now a global uncaught exception handler.
217  // The C++ land node::errors::TriggerUncaughtException grabs it
218  // from the process object because it has been monkey-patchable.
219  // TODO(joyeecheung): investigate whether process._fatalException
220  // can be deprecated.
221  process._fatalException = onGlobalUncaughtException;
222  process.setUncaughtExceptionCaptureCallback =
223    setUncaughtExceptionCaptureCallback;
224  process.hasUncaughtExceptionCaptureCallback =
225    hasUncaughtExceptionCaptureCallback;
226}
227
228const { emitWarning } = require('internal/process/warning');
229process.emitWarning = emitWarning;
230
231// We initialize the tick callbacks and the timer callbacks last during
232// bootstrap to make sure that any operation done before this are synchronous.
233// If any ticks or timers are scheduled before this they are unlikely to work.
234{
235  const { nextTick, runNextTicks } = setupTaskQueue();
236  process.nextTick = nextTick;
237  // Used to emulate a tick manually in the JS land.
238  // A better name for this function would be `runNextTicks` but
239  // it has been exposed to the process object so we keep this legacy name
240  // TODO(joyeecheung): either remove it or make it public
241  process._tickCallback = runNextTicks;
242
243  const { getTimerCallbacks } = require('internal/timers');
244  const { setupTimers } = internalBinding('timers');
245  const { processImmediate, processTimers } = getTimerCallbacks(runNextTicks);
246  // Sets two per-Environment callbacks that will be run from libuv:
247  // - processImmediate will be run in the callback of the per-Environment
248  //   check handle.
249  // - processTimers will be run in the callback of the per-Environment timer.
250  setupTimers(processImmediate, processTimers);
251  // Note: only after this point are the timers effective
252}
253
254function setupPrepareStackTrace() {
255  const {
256    setEnhanceStackForFatalException,
257    setPrepareStackTraceCallback
258  } = internalBinding('errors');
259  const {
260    prepareStackTrace,
261    fatalExceptionStackEnhancers: {
262      beforeInspector,
263      afterInspector
264    }
265  } = require('internal/errors');
266  // Tell our PrepareStackTraceCallback passed to the V8 API
267  // to call prepareStackTrace().
268  setPrepareStackTraceCallback(prepareStackTrace);
269  // Set the function used to enhance the error stack for printing
270  setEnhanceStackForFatalException(beforeInspector, afterInspector);
271}
272
273function setupProcessObject() {
274  const EventEmitter = require('events');
275  const origProcProto = ObjectGetPrototypeOf(process);
276  ObjectSetPrototypeOf(origProcProto, EventEmitter.prototype);
277  FunctionPrototypeCall(EventEmitter, process);
278  ObjectDefineProperty(process, SymbolToStringTag, {
279    enumerable: false,
280    writable: true,
281    configurable: false,
282    value: 'process'
283  });
284  // Make process globally available to users by putting it on the global proxy
285  ObjectDefineProperty(globalThis, 'process', {
286    value: process,
287    enumerable: false,
288    writable: true,
289    configurable: true
290  });
291}
292
293function setupGlobalProxy() {
294  ObjectDefineProperty(globalThis, SymbolToStringTag, {
295    value: 'global',
296    writable: false,
297    enumerable: false,
298    configurable: true
299  });
300}
301
302function setupBuffer() {
303  const { Buffer } = require('buffer');
304  const bufferBinding = internalBinding('buffer');
305
306  // Only after this point can C++ use Buffer::New()
307  bufferBinding.setBufferPrototype(Buffer.prototype);
308  delete bufferBinding.setBufferPrototype;
309  delete bufferBinding.zeroFill;
310
311  ObjectDefineProperty(globalThis, 'Buffer', {
312    value: Buffer,
313    enumerable: false,
314    writable: true,
315    configurable: true
316  });
317}
318
319function createGlobalConsole(consoleFromVM) {
320  const consoleFromNode =
321    require('internal/console/global');
322  if (config.hasInspector) {
323    const inspector = require('internal/util/inspector');
324    // This will be exposed by `require('inspector').console` later.
325    inspector.consoleFromVM = consoleFromVM;
326    // TODO(joyeecheung): postpone this until the first time inspector
327    // is activated.
328    inspector.wrapConsole(consoleFromNode, consoleFromVM);
329    const { setConsoleExtensionInstaller } = internalBinding('inspector');
330    // Setup inspector command line API.
331    setConsoleExtensionInstaller(inspector.installConsoleExtensions);
332  }
333  return consoleFromNode;
334}
335
336// https://heycam.github.io/webidl/#es-namespaces
337function exposeNamespace(target, name, namespaceObject) {
338  ObjectDefineProperty(target, name, {
339    writable: true,
340    enumerable: false,
341    configurable: true,
342    value: namespaceObject
343  });
344}
345
346// https://heycam.github.io/webidl/#es-interfaces
347function exposeInterface(target, name, interfaceObject) {
348  ObjectDefineProperty(target, name, {
349    writable: true,
350    enumerable: false,
351    configurable: true,
352    value: interfaceObject
353  });
354}
355
356// https://heycam.github.io/webidl/#define-the-operations
357function defineOperation(target, name, method) {
358  ObjectDefineProperty(target, name, {
359    writable: true,
360    enumerable: true,
361    configurable: true,
362    value: method
363  });
364}
365