| // This file creates the internal module & binding loaders used by built-in |
| // modules. In contrast, user land modules are loaded using |
| // lib/internal/modules/cjs/loader.js (CommonJS Modules) or |
| // lib/internal/modules/esm/* (ES Modules). |
| // |
| // This file is compiled and run by node.cc before bootstrap/node.js |
| // was called, therefore the loaders are bootstraped before we start to |
| // actually bootstrap Node.js. It creates the following objects: |
| // |
| // C++ binding loaders: |
| // - process.binding(): the legacy C++ binding loader, accessible from user land |
| // because it is an object attached to the global process object. |
| // These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE() |
| // and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees |
| // about the stability of these bindings, but still have to take care of |
| // compatibility issues caused by them from time to time. |
| // - process._linkedBinding(): intended to be used by embedders to add |
| // additional C++ bindings in their applications. These C++ bindings |
| // can be created using NODE_MODULE_CONTEXT_AWARE_CPP() with the flag |
| // NM_F_LINKED. |
| // - internalBinding(): the private internal C++ binding loader, inaccessible |
| // from user land because they are only available from NativeModule.require(). |
| // These C++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL() |
| // and have their nm_flags set to NM_F_INTERNAL. |
| // |
| // Internal JavaScript module loader: |
| // - NativeModule: a minimal module system used to load the JavaScript core |
| // modules found in lib/**/*.js and deps/**/*.js. All core modules are |
| // compiled into the node binary via node_javascript.cc generated by js2c.py, |
| // so they can be loaded faster without the cost of I/O. This class makes the |
| // lib/internal/*, deps/internal/* modules and internalBinding() available by |
| // default to core modules, and lets the core modules require itself via |
| // require('internal/bootstrap/loaders') even when this file is not written in |
| // CommonJS style. |
| // |
| // Other objects: |
| // - process.moduleLoadList: an array recording the bindings and the modules |
| // loaded in the process and the order in which they are loaded. |
| |
| 'use strict'; |
| |
| // This file is compiled as if it's wrapped in a function with arguments |
| // passed by node::RunBootstrapping() |
| /* global process, getLinkedBinding, getInternalBinding */ |
| /* global experimentalModules, exposeInternals, primordials */ |
| |
| const { |
| Reflect, |
| Object, |
| ObjectPrototype, |
| SafeSet |
| } = primordials; |
| |
| // Set up process.moduleLoadList. |
| const moduleLoadList = []; |
| Object.defineProperty(process, 'moduleLoadList', { |
| value: moduleLoadList, |
| configurable: true, |
| enumerable: true, |
| writable: false |
| }); |
| |
| |
| // internalBindingWhitelist contains the name of internalBinding modules |
| // that are whitelisted for access via process.binding()... This is used |
| // to provide a transition path for modules that are being moved over to |
| // internalBinding. |
| const internalBindingWhitelist = new SafeSet([ |
| 'async_wrap', |
| 'buffer', |
| 'cares_wrap', |
| 'config', |
| 'constants', |
| 'contextify', |
| 'crypto', |
| 'fs', |
| 'fs_event_wrap', |
| 'http_parser', |
| 'icu', |
| 'inspector', |
| 'js_stream', |
| 'natives', |
| 'os', |
| 'pipe_wrap', |
| 'process_wrap', |
| 'signal_wrap', |
| 'spawn_sync', |
| 'stream_wrap', |
| 'tcp_wrap', |
| 'tls_wrap', |
| 'tty_wrap', |
| 'udp_wrap', |
| 'url', |
| 'util', |
| 'uv', |
| 'v8', |
| 'zlib' |
| ]); |
| |
| // Set up process.binding() and process._linkedBinding(). |
| { |
| const bindingObj = Object.create(null); |
| |
| process.binding = function binding(module) { |
| module = String(module); |
| // Deprecated specific process.binding() modules, but not all, allow |
| // selective fallback to internalBinding for the deprecated ones. |
| if (internalBindingWhitelist.has(module)) { |
| return internalBinding(module); |
| } |
| // eslint-disable-next-line no-restricted-syntax |
| throw new Error(`No such module: ${module}`); |
| }; |
| |
| process._linkedBinding = function _linkedBinding(module) { |
| module = String(module); |
| let mod = bindingObj[module]; |
| if (typeof mod !== 'object') |
| mod = bindingObj[module] = getLinkedBinding(module); |
| return mod; |
| }; |
| } |
| |
| // Set up internalBinding() in the closure. |
| let internalBinding; |
| { |
| const bindingObj = Object.create(null); |
| internalBinding = function internalBinding(module) { |
| let mod = bindingObj[module]; |
| if (typeof mod !== 'object') { |
| mod = bindingObj[module] = getInternalBinding(module); |
| moduleLoadList.push(`Internal Binding ${module}`); |
| } |
| return mod; |
| }; |
| } |
| |
| // Create this WeakMap in js-land because V8 has no C++ API for WeakMap. |
| internalBinding('module_wrap').callbackMap = new WeakMap(); |
| |
| // Think of this as module.exports in this file even though it is not |
| // written in CommonJS style. |
| const loaderExports = { |
| internalBinding, |
| NativeModule, |
| require: nativeModuleRequire |
| }; |
| |
| const loaderId = 'internal/bootstrap/loaders'; |
| |
| // Set up NativeModule. |
| function NativeModule(id) { |
| this.filename = `${id}.js`; |
| this.id = id; |
| this.exports = {}; |
| this.reflect = undefined; |
| this.exportKeys = undefined; |
| this.loaded = false; |
| this.loading = false; |
| if (id === loaderId) { |
| // Do not expose this to user land even with --expose-internals. |
| this.canBeRequiredByUsers = false; |
| } else if (id.startsWith('internal/')) { |
| this.canBeRequiredByUsers = exposeInternals; |
| } else { |
| this.canBeRequiredByUsers = true; |
| } |
| } |
| |
| const { |
| moduleIds, |
| compileFunction |
| } = internalBinding('native_module'); |
| |
| NativeModule.map = new Map(); |
| for (var i = 0; i < moduleIds.length; ++i) { |
| const id = moduleIds[i]; |
| const mod = new NativeModule(id); |
| NativeModule.map.set(id, mod); |
| } |
| |
| function nativeModuleRequire(id) { |
| if (id === loaderId) { |
| return loaderExports; |
| } |
| |
| const mod = NativeModule.map.get(id); |
| if (!mod) { |
| // Model the error off the internal/errors.js model, but |
| // do not use that module given that it could actually be |
| // the one causing the error if there's a bug in Node.js. |
| // eslint-disable-next-line no-restricted-syntax |
| const err = new Error(`No such built-in module: ${id}`); |
| err.code = 'ERR_UNKNOWN_BUILTIN_MODULE'; |
| err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]'; |
| throw err; |
| } |
| |
| if (mod.loaded || mod.loading) { |
| return mod.exports; |
| } |
| |
| moduleLoadList.push(`NativeModule ${id}`); |
| mod.compile(); |
| return mod.exports; |
| } |
| |
| NativeModule.require = nativeModuleRequire; |
| NativeModule.exists = function(id) { |
| return NativeModule.map.has(id); |
| }; |
| |
| NativeModule.canBeRequiredByUsers = function(id) { |
| const mod = NativeModule.map.get(id); |
| return mod && mod.canBeRequiredByUsers; |
| }; |
| |
| // Allow internal modules from dependencies to require |
| // other modules from dependencies by providing fallbacks. |
| NativeModule.requireWithFallbackInDeps = function(request) { |
| if (!NativeModule.map.has(request)) { |
| request = `internal/deps/${request}`; |
| } |
| return NativeModule.require(request); |
| }; |
| |
| const getOwn = (target, property, receiver) => { |
| return Reflect.apply(ObjectPrototype.hasOwnProperty, target, [property]) ? |
| Reflect.get(target, property, receiver) : |
| undefined; |
| }; |
| |
| // Provide named exports for all builtin libraries so that the libraries |
| // may be imported in a nicer way for ESM users. The default export is left |
| // as the entire namespace (module.exports) and wrapped in a proxy such |
| // that APMs and other behavior are still left intact. |
| NativeModule.prototype.proxifyExports = function() { |
| this.exportKeys = Object.keys(this.exports); |
| |
| const update = (property, value) => { |
| if (this.reflect !== undefined && |
| Reflect.apply(ObjectPrototype.hasOwnProperty, |
| this.reflect.exports, [property])) |
| this.reflect.exports[property].set(value); |
| }; |
| |
| const handler = { |
| __proto__: null, |
| defineProperty: (target, prop, descriptor) => { |
| // Use `Object.defineProperty` instead of `Reflect.defineProperty` |
| // to throw the appropriate error if something goes wrong. |
| Object.defineProperty(target, prop, descriptor); |
| if (typeof descriptor.get === 'function' && |
| !Reflect.has(handler, 'get')) { |
| handler.get = (target, prop, receiver) => { |
| const value = Reflect.get(target, prop, receiver); |
| if (Reflect.apply(ObjectPrototype.hasOwnProperty, target, [prop])) |
| update(prop, value); |
| return value; |
| }; |
| } |
| update(prop, getOwn(target, prop)); |
| return true; |
| }, |
| deleteProperty: (target, prop) => { |
| if (Reflect.deleteProperty(target, prop)) { |
| update(prop, undefined); |
| return true; |
| } |
| return false; |
| }, |
| set: (target, prop, value, receiver) => { |
| const descriptor = Reflect.getOwnPropertyDescriptor(target, prop); |
| if (Reflect.set(target, prop, value, receiver)) { |
| if (descriptor && typeof descriptor.set === 'function') { |
| for (const key of this.exportKeys) { |
| update(key, getOwn(target, key, receiver)); |
| } |
| } else { |
| update(prop, getOwn(target, prop, receiver)); |
| } |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| this.exports = new Proxy(this.exports, handler); |
| }; |
| |
| NativeModule.prototype.compile = function() { |
| const id = this.id; |
| |
| this.loading = true; |
| |
| try { |
| const requireFn = this.id.startsWith('internal/deps/') ? |
| NativeModule.requireWithFallbackInDeps : |
| NativeModule.require; |
| |
| const fn = compileFunction(id); |
| fn(this.exports, requireFn, this, process, internalBinding, primordials); |
| |
| if (experimentalModules && this.canBeRequiredByUsers) { |
| this.proxifyExports(); |
| } |
| |
| this.loaded = true; |
| } finally { |
| this.loading = false; |
| } |
| }; |
| |
| // This will be passed to internal/bootstrap/node.js. |
| return loaderExports; |