• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// This file creates the internal module & binding loaders used by built-in
2// modules. In contrast, user land modules are loaded using
3// lib/internal/modules/cjs/loader.js (CommonJS Modules) or
4// lib/internal/modules/esm/* (ES Modules).
5//
6// This file is compiled and run by node.cc before bootstrap/node.js
7// was called, therefore the loaders are bootstraped before we start to
8// actually bootstrap Node.js. It creates the following objects:
9//
10// C++ binding loaders:
11// - process.binding(): the legacy C++ binding loader, accessible from user land
12//   because it is an object attached to the global process object.
13//   These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE()
14//   and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees
15//   about the stability of these bindings, but still have to take care of
16//   compatibility issues caused by them from time to time.
17// - process._linkedBinding(): intended to be used by embedders to add
18//   additional C++ bindings in their applications. These C++ bindings
19//   can be created using NODE_MODULE_CONTEXT_AWARE_CPP() with the flag
20//   NM_F_LINKED.
21// - internalBinding(): the private internal C++ binding loader, inaccessible
22//   from user land unless through `require('internal/test/binding')`.
23//   These C++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL()
24//   and have their nm_flags set to NM_F_INTERNAL.
25//
26// Internal JavaScript module loader:
27// - NativeModule: a minimal module system used to load the JavaScript core
28//   modules found in lib/**/*.js and deps/**/*.js. All core modules are
29//   compiled into the node binary via node_javascript.cc generated by js2c.py,
30//   so they can be loaded faster without the cost of I/O. This class makes the
31//   lib/internal/*, deps/internal/* modules and internalBinding() available by
32//   default to core modules, and lets the core modules require itself via
33//   require('internal/bootstrap/loaders') even when this file is not written in
34//   CommonJS style.
35//
36// Other objects:
37// - process.moduleLoadList: an array recording the bindings and the modules
38//   loaded in the process and the order in which they are loaded.
39
40'use strict';
41
42// This file is compiled as if it's wrapped in a function with arguments
43// passed by node::RunBootstrapping()
44/* global process, getLinkedBinding, getInternalBinding, primordials */
45
46const {
47  Error,
48  Map,
49  ObjectCreate,
50  ObjectDefineProperty,
51  ObjectKeys,
52  ObjectPrototypeHasOwnProperty,
53  ReflectGet,
54  SafeSet,
55} = primordials;
56
57// Set up process.moduleLoadList.
58const moduleLoadList = [];
59ObjectDefineProperty(process, 'moduleLoadList', {
60  value: moduleLoadList,
61  configurable: true,
62  enumerable: true,
63  writable: false
64});
65
66
67// internalBindingWhitelist contains the name of internalBinding modules
68// that are whitelisted for access via process.binding()... This is used
69// to provide a transition path for modules that are being moved over to
70// internalBinding.
71const internalBindingWhitelist = new SafeSet([
72  'async_wrap',
73  'buffer',
74  'cares_wrap',
75  'config',
76  'constants',
77  'contextify',
78  'crypto',
79  'fs',
80  'fs_event_wrap',
81  'http_parser',
82  'http_parser_llhttp',
83  'icu',
84  'inspector',
85  'js_stream',
86  'natives',
87  'os',
88  'pipe_wrap',
89  'process_wrap',
90  'signal_wrap',
91  'spawn_sync',
92  'stream_wrap',
93  'tcp_wrap',
94  'tls_wrap',
95  'tty_wrap',
96  'udp_wrap',
97  'url',
98  'util',
99  'uv',
100  'v8',
101  'zlib'
102]);
103
104// Set up process.binding() and process._linkedBinding().
105{
106  const bindingObj = ObjectCreate(null);
107
108  process.binding = function binding(module) {
109    module = String(module);
110    // Deprecated specific process.binding() modules, but not all, allow
111    // selective fallback to internalBinding for the deprecated ones.
112    if (internalBindingWhitelist.has(module)) {
113      return internalBinding(module);
114    }
115    // eslint-disable-next-line no-restricted-syntax
116    throw new Error(`No such module: ${module}`);
117  };
118
119  process._linkedBinding = function _linkedBinding(module) {
120    module = String(module);
121    let mod = bindingObj[module];
122    if (typeof mod !== 'object')
123      mod = bindingObj[module] = getLinkedBinding(module);
124    return mod;
125  };
126}
127
128// Set up internalBinding() in the closure.
129let internalBinding;
130{
131  const bindingObj = ObjectCreate(null);
132  // eslint-disable-next-line no-global-assign
133  internalBinding = function internalBinding(module) {
134    let mod = bindingObj[module];
135    if (typeof mod !== 'object') {
136      mod = bindingObj[module] = getInternalBinding(module);
137      moduleLoadList.push(`Internal Binding ${module}`);
138    }
139    return mod;
140  };
141}
142
143const loaderId = 'internal/bootstrap/loaders';
144const {
145  moduleIds,
146  compileFunction
147} = internalBinding('native_module');
148
149const getOwn = (target, property, receiver) => {
150  return ObjectPrototypeHasOwnProperty(target, property) ?
151    ReflectGet(target, property, receiver) :
152    undefined;
153};
154
155/**
156 * An internal abstraction for the built-in JavaScript modules of Node.js.
157 * Be careful not to expose this to user land unless --expose-internals is
158 * used, in which case there is no compatibility guarantee about this class.
159 */
160class NativeModule {
161  /**
162   * A map from the module IDs to the module instances.
163   * @type {Map<string, NativeModule>}
164  */
165  static map = new Map(moduleIds.map((id) => [id, new NativeModule(id)]));
166
167  constructor(id) {
168    this.filename = `${id}.js`;
169    this.id = id;
170    this.canBeRequiredByUsers = !id.startsWith('internal/');
171
172    // The CJS exports object of the module.
173    this.exports = {};
174    // States used to work around circular dependencies.
175    this.loaded = false;
176    this.loading = false;
177
178    // The following properties are used by the ESM implementation and only
179    // initialized when the native module is loaded by users.
180    /**
181     * The C++ ModuleWrap binding used to interface with the ESM implementation.
182     * @type {ModuleWrap|undefined}
183     */
184    this.module = undefined;
185    /**
186     * Exported names for the ESM imports.
187     * @type {string[]|undefined}
188     */
189    this.exportKeys = undefined;
190  }
191
192  // To be called during pre-execution when --expose-internals is on.
193  // Enables the user-land module loader to access internal modules.
194  static exposeInternals() {
195    for (const [id, mod] of NativeModule.map) {
196      // Do not expose this to user land even with --expose-internals.
197      if (id !== loaderId) {
198        mod.canBeRequiredByUsers = true;
199      }
200    }
201  }
202
203  static exists(id) {
204    return NativeModule.map.has(id);
205  }
206
207  static canBeRequiredByUsers(id) {
208    const mod = NativeModule.map.get(id);
209    return mod && mod.canBeRequiredByUsers;
210  }
211
212  // Used by user-land module loaders to compile and load builtins.
213  compileForPublicLoader() {
214    if (!this.canBeRequiredByUsers) {
215      // No code because this is an assertion against bugs
216      // eslint-disable-next-line no-restricted-syntax
217      throw new Error(`Should not compile ${this.id} for public use`);
218    }
219    this.compileForInternalLoader();
220    if (!this.exportKeys) {
221      // When using --expose-internals, we do not want to reflect the named
222      // exports from core modules as this can trigger unnecessary getters.
223      const internal = this.id.startsWith('internal/');
224      this.exportKeys = internal ? [] : ObjectKeys(this.exports);
225    }
226    this.getESMFacade();
227    this.syncExports();
228    return this.exports;
229  }
230
231  getESMFacade() {
232    if (this.module) return this.module;
233    const { ModuleWrap } = internalBinding('module_wrap');
234    const url = `node:${this.id}`;
235    const nativeModule = this;
236    this.module = new ModuleWrap(
237      url, undefined, [...this.exportKeys, 'default'],
238      function() {
239        nativeModule.syncExports();
240        this.setExport('default', nativeModule.exports);
241      });
242    // Ensure immediate sync execution to capture exports now
243    this.module.instantiate();
244    this.module.evaluate(-1, false);
245    return this.module;
246  }
247
248  // Provide named exports for all builtin libraries so that the libraries
249  // may be imported in a nicer way for ESM users. The default export is left
250  // as the entire namespace (module.exports) and updates when this function is
251  // called so that APMs and other behavior are supported.
252  syncExports() {
253    const names = this.exportKeys;
254    if (this.module) {
255      for (let i = 0; i < names.length; i++) {
256        const exportName = names[i];
257        if (exportName === 'default') continue;
258        this.module.setExport(exportName,
259                              getOwn(this.exports, exportName, this.exports));
260      }
261    }
262  }
263
264  compileForInternalLoader() {
265    if (this.loaded || this.loading) {
266      return this.exports;
267    }
268
269    const id = this.id;
270    this.loading = true;
271
272    try {
273      const requireFn = this.id.startsWith('internal/deps/') ?
274        requireWithFallbackInDeps : nativeModuleRequire;
275
276      const fn = compileFunction(id);
277      fn(this.exports, requireFn, this, process, internalBinding, primordials);
278
279      this.loaded = true;
280    } finally {
281      this.loading = false;
282    }
283
284    moduleLoadList.push(`NativeModule ${id}`);
285    return this.exports;
286  }
287}
288
289// Think of this as module.exports in this file even though it is not
290// written in CommonJS style.
291const loaderExports = {
292  internalBinding,
293  NativeModule,
294  require: nativeModuleRequire
295};
296
297function nativeModuleRequire(id) {
298  if (id === loaderId) {
299    return loaderExports;
300  }
301
302  const mod = NativeModule.map.get(id);
303  // Can't load the internal errors module from here, have to use a raw error.
304  // eslint-disable-next-line no-restricted-syntax
305  if (!mod) throw new TypeError(`Missing internal module '${id}'`);
306  return mod.compileForInternalLoader();
307}
308
309// Allow internal modules from dependencies to require
310// other modules from dependencies by providing fallbacks.
311function requireWithFallbackInDeps(request) {
312  if (!NativeModule.map.has(request)) {
313    request = `internal/deps/${request}`;
314  }
315  return nativeModuleRequire(request);
316}
317
318// Pass the exports back to C++ land for C++ internals to use.
319return loaderExports;
320