• 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 bootstrapped 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  ArrayPrototypeMap,
48  ArrayPrototypePush,
49  ArrayPrototypeSlice,
50  Error,
51  ObjectCreate,
52  ObjectDefineProperty,
53  ObjectKeys,
54  ObjectPrototypeHasOwnProperty,
55  ReflectGet,
56  SafeMap,
57  SafeSet,
58  String,
59  StringPrototypeStartsWith,
60  TypeError,
61} = primordials;
62
63// Set up process.moduleLoadList.
64const moduleLoadList = [];
65ObjectDefineProperty(process, 'moduleLoadList', {
66  value: moduleLoadList,
67  configurable: true,
68  enumerable: true,
69  writable: false
70});
71
72
73// internalBindingAllowlist contains the name of internalBinding modules
74// that are allowed for access via process.binding()... This is used
75// to provide a transition path for modules that are being moved over to
76// internalBinding.
77const internalBindingAllowlist = new SafeSet([
78  'async_wrap',
79  'buffer',
80  'cares_wrap',
81  'config',
82  'constants',
83  'contextify',
84  'crypto',
85  'fs',
86  'fs_event_wrap',
87  'http_parser',
88  'icu',
89  'inspector',
90  'js_stream',
91  'natives',
92  'os',
93  'pipe_wrap',
94  'process_wrap',
95  'signal_wrap',
96  'spawn_sync',
97  'stream_wrap',
98  'tcp_wrap',
99  'tls_wrap',
100  'tty_wrap',
101  'udp_wrap',
102  'url',
103  'util',
104  'uv',
105  'v8',
106  'zlib',
107]);
108
109// Set up process.binding() and process._linkedBinding().
110{
111  const bindingObj = ObjectCreate(null);
112
113  process.binding = function binding(module) {
114    module = String(module);
115    // Deprecated specific process.binding() modules, but not all, allow
116    // selective fallback to internalBinding for the deprecated ones.
117    if (internalBindingAllowlist.has(module)) {
118      return internalBinding(module);
119    }
120    // eslint-disable-next-line no-restricted-syntax
121    throw new Error(`No such module: ${module}`);
122  };
123
124  process._linkedBinding = function _linkedBinding(module) {
125    module = String(module);
126    let mod = bindingObj[module];
127    if (typeof mod !== 'object')
128      mod = bindingObj[module] = getLinkedBinding(module);
129    return mod;
130  };
131}
132
133// Set up internalBinding() in the closure.
134let internalBinding;
135{
136  const bindingObj = ObjectCreate(null);
137  // eslint-disable-next-line no-global-assign
138  internalBinding = function internalBinding(module) {
139    let mod = bindingObj[module];
140    if (typeof mod !== 'object') {
141      mod = bindingObj[module] = getInternalBinding(module);
142      ArrayPrototypePush(moduleLoadList, `Internal Binding ${module}`);
143    }
144    return mod;
145  };
146}
147
148const loaderId = 'internal/bootstrap/loaders';
149const {
150  moduleIds,
151  compileFunction
152} = internalBinding('native_module');
153
154const getOwn = (target, property, receiver) => {
155  return ObjectPrototypeHasOwnProperty(target, property) ?
156    ReflectGet(target, property, receiver) :
157    undefined;
158};
159
160/**
161 * An internal abstraction for the built-in JavaScript modules of Node.js.
162 * Be careful not to expose this to user land unless --expose-internals is
163 * used, in which case there is no compatibility guarantee about this class.
164 */
165class NativeModule {
166  /**
167   * A map from the module IDs to the module instances.
168   * @type {Map<string, NativeModule>}
169  */
170  static map = new SafeMap(
171    ArrayPrototypeMap(moduleIds, (id) => [id, new NativeModule(id)])
172  );
173
174  constructor(id) {
175    this.filename = `${id}.js`;
176    this.id = id;
177    this.canBeRequiredByUsers = !StringPrototypeStartsWith(id, 'internal/');
178
179    // The CJS exports object of the module.
180    this.exports = {};
181    // States used to work around circular dependencies.
182    this.loaded = false;
183    this.loading = false;
184
185    // The following properties are used by the ESM implementation and only
186    // initialized when the native module is loaded by users.
187    /**
188     * The C++ ModuleWrap binding used to interface with the ESM implementation.
189     * @type {ModuleWrap|undefined}
190     */
191    this.module = undefined;
192    /**
193     * Exported names for the ESM imports.
194     * @type {string[]|undefined}
195     */
196    this.exportKeys = undefined;
197  }
198
199  // To be called during pre-execution when --expose-internals is on.
200  // Enables the user-land module loader to access internal modules.
201  static exposeInternals() {
202    for (const { 0: id, 1: mod } of NativeModule.map) {
203      // Do not expose this to user land even with --expose-internals.
204      if (id !== loaderId) {
205        mod.canBeRequiredByUsers = true;
206      }
207    }
208  }
209
210  static exists(id) {
211    return NativeModule.map.has(id);
212  }
213
214  static canBeRequiredByUsers(id) {
215    const mod = NativeModule.map.get(id);
216    return mod && mod.canBeRequiredByUsers;
217  }
218
219  // Used by user-land module loaders to compile and load builtins.
220  compileForPublicLoader() {
221    if (!this.canBeRequiredByUsers) {
222      // No code because this is an assertion against bugs
223      // eslint-disable-next-line no-restricted-syntax
224      throw new Error(`Should not compile ${this.id} for public use`);
225    }
226    this.compileForInternalLoader();
227    if (!this.exportKeys) {
228      // When using --expose-internals, we do not want to reflect the named
229      // exports from core modules as this can trigger unnecessary getters.
230      const internal = StringPrototypeStartsWith(this.id, 'internal/');
231      this.exportKeys = internal ? [] : ObjectKeys(this.exports);
232    }
233    this.getESMFacade();
234    this.syncExports();
235    return this.exports;
236  }
237
238  getESMFacade() {
239    if (this.module) return this.module;
240    const { ModuleWrap } = internalBinding('module_wrap');
241    const url = `node:${this.id}`;
242    const nativeModule = this;
243    const exportsKeys = ArrayPrototypeSlice(this.exportKeys);
244    ArrayPrototypePush(exportsKeys, 'default');
245    this.module = new ModuleWrap(
246      url, undefined, exportsKeys,
247      function() {
248        nativeModule.syncExports();
249        this.setExport('default', nativeModule.exports);
250      });
251    // Ensure immediate sync execution to capture exports now
252    this.module.instantiate();
253    this.module.evaluate(-1, false);
254    return this.module;
255  }
256
257  // Provide named exports for all builtin libraries so that the libraries
258  // may be imported in a nicer way for ESM users. The default export is left
259  // as the entire namespace (module.exports) and updates when this function is
260  // called so that APMs and other behavior are supported.
261  syncExports() {
262    const names = this.exportKeys;
263    if (this.module) {
264      for (let i = 0; i < names.length; i++) {
265        const exportName = names[i];
266        if (exportName === 'default') continue;
267        this.module.setExport(exportName,
268                              getOwn(this.exports, exportName, this.exports));
269      }
270    }
271  }
272
273  compileForInternalLoader() {
274    if (this.loaded || this.loading) {
275      return this.exports;
276    }
277
278    const id = this.id;
279    this.loading = true;
280
281    try {
282      const requireFn = StringPrototypeStartsWith(this.id, 'internal/deps/') ?
283        requireWithFallbackInDeps : nativeModuleRequire;
284
285      const fn = compileFunction(id);
286      fn(this.exports, requireFn, this, process, internalBinding, primordials);
287
288      this.loaded = true;
289    } finally {
290      this.loading = false;
291    }
292
293    ArrayPrototypePush(moduleLoadList, `NativeModule ${id}`);
294    return this.exports;
295  }
296}
297
298// Think of this as module.exports in this file even though it is not
299// written in CommonJS style.
300const loaderExports = {
301  internalBinding,
302  NativeModule,
303  require: nativeModuleRequire
304};
305
306function nativeModuleRequire(id) {
307  if (id === loaderId) {
308    return loaderExports;
309  }
310
311  const mod = NativeModule.map.get(id);
312  // Can't load the internal errors module from here, have to use a raw error.
313  // eslint-disable-next-line no-restricted-syntax
314  if (!mod) throw new TypeError(`Missing internal module '${id}'`);
315  return mod.compileForInternalLoader();
316}
317
318// Allow internal modules from dependencies to require
319// other modules from dependencies by providing fallbacks.
320function requireWithFallbackInDeps(request) {
321  if (!NativeModule.map.has(request)) {
322    request = `internal/deps/${request}`;
323  }
324  return nativeModuleRequire(request);
325}
326
327// Pass the exports back to C++ land for C++ internals to use.
328return loaderExports;
329