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