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