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