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