• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// This file is executed in every realm that is created by Node.js, including
2// the context of main thread, worker threads, and ShadowRealms.
3// Only per-realm internal states and bindings should be bootstrapped in this
4// file and no globals should be exposed to the user code.
5//
6// This file creates the internal module & binding loaders used by built-in
7// modules. In contrast, user land modules are loaded using
8// lib/internal/modules/cjs/loader.js (CommonJS Modules) or
9// lib/internal/modules/esm/* (ES Modules).
10//
11// This file is compiled and run by node.cc before bootstrap/node.js
12// was called, therefore the loaders are bootstrapped before we start to
13// actually bootstrap Node.js. It creates the following objects:
14//
15// C++ binding loaders:
16// - process.binding(): the legacy C++ binding loader, accessible from user land
17//   because it is an object attached to the global process object.
18//   These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE()
19//   and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees
20//   about the stability of these bindings, but still have to take care of
21//   compatibility issues caused by them from time to time.
22// - process._linkedBinding(): intended to be used by embedders to add
23//   additional C++ bindings in their applications. These C++ bindings
24//   can be created using NODE_BINDING_CONTEXT_AWARE_CPP() with the flag
25//   NM_F_LINKED.
26// - internalBinding(): the private internal C++ binding loader, inaccessible
27//   from user land unless through `require('internal/test/binding')`.
28//   These C++ bindings are created using NODE_BINDING_CONTEXT_AWARE_INTERNAL()
29//   and have their nm_flags set to NM_F_INTERNAL.
30//
31// Internal JavaScript module loader:
32// - BuiltinModule: a minimal module system used to load the JavaScript core
33//   modules found in lib/**/*.js and deps/**/*.js. All core modules are
34//   compiled into the node binary via node_javascript.cc generated by js2c.py,
35//   so they can be loaded faster without the cost of I/O. This class makes the
36//   lib/internal/*, deps/internal/* modules and internalBinding() available by
37//   default to core modules, and lets the core modules require itself via
38//   require('internal/bootstrap/realm') even when this file is not written in
39//   CommonJS style.
40//
41// Other objects:
42// - process.moduleLoadList: an array recording the bindings and the modules
43//   loaded in the process and the order in which they are loaded.
44
45'use strict';
46
47// This file is compiled as if it's wrapped in a function with arguments
48// passed by node::RunBootstrapping()
49/* global process, getLinkedBinding, getInternalBinding, primordials */
50
51const {
52  ArrayFrom,
53  ArrayPrototypeMap,
54  ArrayPrototypePush,
55  ArrayPrototypeSlice,
56  Error,
57  ObjectCreate,
58  ObjectDefineProperty,
59  ObjectKeys,
60  ObjectPrototypeHasOwnProperty,
61  ObjectSetPrototypeOf,
62  ReflectGet,
63  SafeMap,
64  SafeSet,
65  String,
66  StringPrototypeSlice,
67  StringPrototypeStartsWith,
68  TypeError,
69} = primordials;
70
71// Set up process.moduleLoadList.
72const moduleLoadList = [];
73ObjectDefineProperty(process, 'moduleLoadList', {
74  __proto__: null,
75  value: moduleLoadList,
76  configurable: true,
77  enumerable: true,
78  writable: false,
79});
80
81
82// internalBindingAllowlist contains the name of internalBinding modules
83// that are allowed for access via process.binding()... This is used
84// to provide a transition path for modules that are being moved over to
85// internalBinding.
86const internalBindingAllowlist = new SafeSet([
87  'async_wrap',
88  'buffer',
89  'cares_wrap',
90  'config',
91  'constants',
92  'contextify',
93  'crypto',
94  'fs',
95  'fs_event_wrap',
96  'http_parser',
97  'icu',
98  'inspector',
99  'js_stream',
100  'natives',
101  'os',
102  'pipe_wrap',
103  'process_wrap',
104  'signal_wrap',
105  'spawn_sync',
106  'stream_wrap',
107  'tcp_wrap',
108  'tls_wrap',
109  'tty_wrap',
110  'udp_wrap',
111  'url',
112  'util',
113  'uv',
114  'v8',
115  'zlib',
116]);
117
118const runtimeDeprecatedList = new SafeSet([
119  'async_wrap',
120  'crypto',
121  'http_parser',
122  'signal_wrap',
123  'url',
124  'v8',
125]);
126
127const legacyWrapperList = new SafeSet([
128  'util',
129]);
130
131// The code bellow assumes that the two lists must not contain any modules
132// beginning with "internal/".
133// Modules that can only be imported via the node: scheme.
134const schemelessBlockList = new SafeSet([
135  'test',
136  'test/reporters',
137]);
138// Modules that will only be enabled at run time.
139const experimentalModuleList = new SafeSet();
140
141// Set up process.binding() and process._linkedBinding().
142{
143  const bindingObj = ObjectCreate(null);
144
145  process.binding = function binding(module) {
146    module = String(module);
147    // Deprecated specific process.binding() modules, but not all, allow
148    // selective fallback to internalBinding for the deprecated ones.
149    if (internalBindingAllowlist.has(module)) {
150      if (runtimeDeprecatedList.has(module)) {
151        runtimeDeprecatedList.delete(module);
152        process.emitWarning(
153          `Access to process.binding('${module}') is deprecated.`,
154          'DeprecationWarning',
155          'DEP0111');
156      }
157      if (legacyWrapperList.has(module)) {
158        return requireBuiltin('internal/legacy/processbinding')[module]();
159      }
160      return internalBinding(module);
161    }
162    // eslint-disable-next-line no-restricted-syntax
163    throw new Error(`No such module: ${module}`);
164  };
165
166  process._linkedBinding = function _linkedBinding(module) {
167    module = String(module);
168    let mod = bindingObj[module];
169    if (typeof mod !== 'object')
170      mod = bindingObj[module] = getLinkedBinding(module);
171    return mod;
172  };
173}
174
175// Set up internalBinding() in the closure.
176/**
177 * @type {InternalBinding}
178 */
179let internalBinding;
180{
181  const bindingObj = ObjectCreate(null);
182  // eslint-disable-next-line no-global-assign
183  internalBinding = function internalBinding(module) {
184    let mod = bindingObj[module];
185    if (typeof mod !== 'object') {
186      mod = bindingObj[module] = getInternalBinding(module);
187      ArrayPrototypePush(moduleLoadList, `Internal Binding ${module}`);
188    }
189    return mod;
190  };
191}
192
193const selfId = 'internal/bootstrap/realm';
194const {
195  builtinIds,
196  compileFunction,
197  setInternalLoaders,
198} = internalBinding('builtins');
199
200const getOwn = (target, property, receiver) => {
201  return ObjectPrototypeHasOwnProperty(target, property) ?
202    ReflectGet(target, property, receiver) :
203    undefined;
204};
205
206const publicBuiltinIds = builtinIds
207  .filter((id) =>
208    !StringPrototypeStartsWith(id, 'internal/') &&
209      !experimentalModuleList.has(id),
210  );
211// Do not expose the loaders to user land even with --expose-internals.
212const internalBuiltinIds = builtinIds
213  .filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId);
214
215// When --expose-internals is on we'll add the internal builtin ids to these.
216const canBeRequiredByUsersList = new SafeSet(publicBuiltinIds);
217const canBeRequiredByUsersWithoutSchemeList =
218  new SafeSet(publicBuiltinIds.filter((id) => !schemelessBlockList.has(id)));
219
220/**
221 * An internal abstraction for the built-in JavaScript modules of Node.js.
222 * Be careful not to expose this to user land unless --expose-internals is
223 * used, in which case there is no compatibility guarantee about this class.
224 */
225class BuiltinModule {
226  /**
227   * A map from the module IDs to the module instances.
228   * @type {Map<string, BuiltinModule>}
229   */
230  static map = new SafeMap(
231    ArrayPrototypeMap(builtinIds, (id) => [id, new BuiltinModule(id)]),
232  );
233
234  constructor(id) {
235    this.filename = `${id}.js`;
236    this.id = id;
237
238    // The CJS exports object of the module.
239    this.exports = {};
240    // States used to work around circular dependencies.
241    this.loaded = false;
242    this.loading = false;
243
244    // The following properties are used by the ESM implementation and only
245    // initialized when the built-in module is loaded by users.
246    /**
247     * The C++ ModuleWrap binding used to interface with the ESM implementation.
248     * @type {ModuleWrap|undefined}
249     */
250    this.module = undefined;
251    /**
252     * Exported names for the ESM imports.
253     * @type {string[]|undefined}
254     */
255    this.exportKeys = undefined;
256  }
257
258  static allowRequireByUsers(id) {
259    if (id === selfId) {
260      // No code because this is an assertion against bugs.
261      // eslint-disable-next-line no-restricted-syntax
262      throw new Error(`Should not allow ${id}`);
263    }
264    canBeRequiredByUsersList.add(id);
265    if (!schemelessBlockList.has(id)) {
266      canBeRequiredByUsersWithoutSchemeList.add(id);
267    }
268  }
269
270  // To be called during pre-execution when --expose-internals is on.
271  // Enables the user-land module loader to access internal modules.
272  static exposeInternals() {
273    for (let i = 0; i < internalBuiltinIds.length; ++i) {
274      BuiltinModule.allowRequireByUsers(internalBuiltinIds[i]);
275    }
276  }
277
278  static exists(id) {
279    return BuiltinModule.map.has(id);
280  }
281
282  static canBeRequiredByUsers(id) {
283    return canBeRequiredByUsersList.has(id);
284  }
285
286  static canBeRequiredWithoutScheme(id) {
287    return canBeRequiredByUsersWithoutSchemeList.has(id);
288  }
289
290  static isBuiltin(id) {
291    return BuiltinModule.canBeRequiredWithoutScheme(id) || (
292      typeof id === 'string' &&
293        StringPrototypeStartsWith(id, 'node:') &&
294        BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(id, 5))
295    );
296  }
297
298  static getCanBeRequiredByUsersWithoutSchemeList() {
299    return ArrayFrom(canBeRequiredByUsersWithoutSchemeList);
300  }
301
302  static normalizeRequirableId(id) {
303    if (StringPrototypeStartsWith(id, 'node:')) {
304      const normalizedId = StringPrototypeSlice(id, 5);
305      if (BuiltinModule.canBeRequiredByUsers(normalizedId)) {
306        return normalizedId;
307      }
308    } else if (BuiltinModule.canBeRequiredWithoutScheme(id)) {
309      return id;
310    }
311
312    return undefined;
313  }
314
315  static getSchemeOnlyModuleNames() {
316    return ArrayFrom(schemelessBlockList);
317  }
318
319  // Used by user-land module loaders to compile and load builtins.
320  compileForPublicLoader() {
321    if (!BuiltinModule.canBeRequiredByUsers(this.id)) {
322      // No code because this is an assertion against bugs
323      // eslint-disable-next-line no-restricted-syntax
324      throw new Error(`Should not compile ${this.id} for public use`);
325    }
326    this.compileForInternalLoader();
327    if (!this.exportKeys) {
328      // When using --expose-internals, we do not want to reflect the named
329      // exports from core modules as this can trigger unnecessary getters.
330      const internal = StringPrototypeStartsWith(this.id, 'internal/');
331      this.exportKeys = internal ? [] : ObjectKeys(this.exports);
332    }
333    this.getESMFacade();
334    this.syncExports();
335    return this.exports;
336  }
337
338  getESMFacade() {
339    if (this.module) return this.module;
340    const { ModuleWrap } = internalBinding('module_wrap');
341    // TODO(aduh95): move this to C++, alongside the initialization of the class.
342    ObjectSetPrototypeOf(ModuleWrap.prototype, null);
343    const url = `node:${this.id}`;
344    const builtin = this;
345    const exportsKeys = ArrayPrototypeSlice(this.exportKeys);
346    ArrayPrototypePush(exportsKeys, 'default');
347    this.module = new ModuleWrap(
348      url, undefined, exportsKeys,
349      function() {
350        builtin.syncExports();
351        this.setExport('default', builtin.exports);
352      });
353    // Ensure immediate sync execution to capture exports now
354    this.module.instantiate();
355    this.module.evaluate(-1, false);
356    return this.module;
357  }
358
359  // Provide named exports for all builtin libraries so that the libraries
360  // may be imported in a nicer way for ESM users. The default export is left
361  // as the entire namespace (module.exports) and updates when this function is
362  // called so that APMs and other behavior are supported.
363  syncExports() {
364    const names = this.exportKeys;
365    if (this.module) {
366      for (let i = 0; i < names.length; i++) {
367        const exportName = names[i];
368        if (exportName === 'default') continue;
369        this.module.setExport(exportName,
370                              getOwn(this.exports, exportName, this.exports));
371      }
372    }
373  }
374
375  compileForInternalLoader() {
376    if (this.loaded || this.loading) {
377      return this.exports;
378    }
379
380    const id = this.id;
381    this.loading = true;
382
383    try {
384      const requireFn = StringPrototypeStartsWith(this.id, 'internal/deps/') ?
385        requireWithFallbackInDeps : requireBuiltin;
386
387      const fn = compileFunction(id);
388      // Arguments must match the parameters specified in
389      // BuiltinLoader::LookupAndCompile().
390      fn(this.exports, requireFn, this, process, internalBinding, primordials);
391
392      this.loaded = true;
393    } finally {
394      this.loading = false;
395    }
396
397    // "NativeModule" is a legacy name of "BuiltinModule". We keep it
398    // here to avoid breaking users who parse process.moduleLoadList.
399    ArrayPrototypePush(moduleLoadList, `NativeModule ${id}`);
400    return this.exports;
401  }
402}
403
404// Think of this as module.exports in this file even though it is not
405// written in CommonJS style.
406const loaderExports = {
407  internalBinding,
408  BuiltinModule,
409  require: requireBuiltin,
410};
411
412function requireBuiltin(id) {
413  if (id === selfId) {
414    return loaderExports;
415  }
416
417  const mod = BuiltinModule.map.get(id);
418  // Can't load the internal errors module from here, have to use a raw error.
419  // eslint-disable-next-line no-restricted-syntax
420  if (!mod) throw new TypeError(`Missing internal module '${id}'`);
421  return mod.compileForInternalLoader();
422}
423
424// Allow internal modules from dependencies to require
425// other modules from dependencies by providing fallbacks.
426function requireWithFallbackInDeps(request) {
427  if (StringPrototypeStartsWith(request, 'node:')) {
428    request = StringPrototypeSlice(request, 5);
429  } else if (!BuiltinModule.map.has(request)) {
430    request = `internal/deps/${request}`;
431  }
432  return requireBuiltin(request);
433}
434
435function setupPrepareStackTrace() {
436  const {
437    setEnhanceStackForFatalException,
438    setPrepareStackTraceCallback,
439  } = internalBinding('errors');
440  const {
441    prepareStackTrace,
442    fatalExceptionStackEnhancers: {
443      beforeInspector,
444      afterInspector,
445    },
446  } = requireBuiltin('internal/errors');
447  // Tell our PrepareStackTraceCallback passed to the V8 API
448  // to call prepareStackTrace().
449  setPrepareStackTraceCallback(prepareStackTrace);
450  // Set the function used to enhance the error stack for printing
451  setEnhanceStackForFatalException(beforeInspector, afterInspector);
452}
453
454// Store the internal loaders in C++.
455setInternalLoaders(internalBinding, requireBuiltin);
456
457// Setup per-realm bindings.
458setupPrepareStackTrace();
459