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