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