1// This file is executed in every realm that is created by Node.js, including 2// the context of main thread, worker threads, and ShadowRealms. 3// Only per-realm internal states and bindings should be bootstrapped in this 4// file and no globals should be exposed to the user code. 5// 6// This file creates the internal module & binding loaders used by built-in 7// modules. In contrast, user land modules are loaded using 8// lib/internal/modules/cjs/loader.js (CommonJS Modules) or 9// lib/internal/modules/esm/* (ES Modules). 10// 11// This file is compiled and run by node.cc before bootstrap/node.js 12// was called, therefore the loaders are bootstrapped before we start to 13// actually bootstrap Node.js. It creates the following objects: 14// 15// C++ binding loaders: 16// - process.binding(): the legacy C++ binding loader, accessible from user land 17// because it is an object attached to the global process object. 18// These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE() 19// and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees 20// about the stability of these bindings, but still have to take care of 21// compatibility issues caused by them from time to time. 22// - process._linkedBinding(): intended to be used by embedders to add 23// additional C++ bindings in their applications. These C++ bindings 24// can be created using NODE_BINDING_CONTEXT_AWARE_CPP() with the flag 25// NM_F_LINKED. 26// - internalBinding(): the private internal C++ binding loader, inaccessible 27// from user land unless through `require('internal/test/binding')`. 28// These C++ bindings are created using NODE_BINDING_CONTEXT_AWARE_INTERNAL() 29// and have their nm_flags set to NM_F_INTERNAL. 30// 31// Internal JavaScript module loader: 32// - BuiltinModule: a minimal module system used to load the JavaScript core 33// modules found in lib/**/*.js and deps/**/*.js. All core modules are 34// compiled into the node binary via node_javascript.cc generated by js2c.py, 35// so they can be loaded faster without the cost of I/O. This class makes the 36// lib/internal/*, deps/internal/* modules and internalBinding() available by 37// default to core modules, and lets the core modules require itself via 38// require('internal/bootstrap/realm') even when this file is not written in 39// CommonJS style. 40// 41// Other objects: 42// - process.moduleLoadList: an array recording the bindings and the modules 43// loaded in the process and the order in which they are loaded. 44 45'use strict'; 46 47// This file is compiled as if it's wrapped in a function with arguments 48// passed by node::RunBootstrapping() 49/* global process, getLinkedBinding, getInternalBinding, primordials */ 50 51const { 52 ArrayFrom, 53 ArrayPrototypeMap, 54 ArrayPrototypePush, 55 ArrayPrototypeSlice, 56 Error, 57 ObjectCreate, 58 ObjectDefineProperty, 59 ObjectKeys, 60 ObjectPrototypeHasOwnProperty, 61 ObjectSetPrototypeOf, 62 ReflectGet, 63 SafeMap, 64 SafeSet, 65 String, 66 StringPrototypeSlice, 67 StringPrototypeStartsWith, 68 TypeError, 69} = primordials; 70 71// Set up process.moduleLoadList. 72const moduleLoadList = []; 73ObjectDefineProperty(process, 'moduleLoadList', { 74 __proto__: null, 75 value: moduleLoadList, 76 configurable: true, 77 enumerable: true, 78 writable: false, 79}); 80 81 82// internalBindingAllowlist contains the name of internalBinding modules 83// that are allowed for access via process.binding()... This is used 84// to provide a transition path for modules that are being moved over to 85// internalBinding. 86const internalBindingAllowlist = new SafeSet([ 87 'async_wrap', 88 'buffer', 89 'cares_wrap', 90 'config', 91 'constants', 92 'contextify', 93 'crypto', 94 'fs', 95 'fs_event_wrap', 96 'http_parser', 97 'icu', 98 'inspector', 99 'js_stream', 100 'natives', 101 'os', 102 'pipe_wrap', 103 'process_wrap', 104 'signal_wrap', 105 'spawn_sync', 106 'stream_wrap', 107 'tcp_wrap', 108 'tls_wrap', 109 'tty_wrap', 110 'udp_wrap', 111 'url', 112 'util', 113 'uv', 114 'v8', 115 'zlib', 116]); 117 118const runtimeDeprecatedList = new SafeSet([ 119 'async_wrap', 120 'crypto', 121 'http_parser', 122 'signal_wrap', 123 'url', 124 'v8', 125]); 126 127const legacyWrapperList = new SafeSet([ 128 'util', 129]); 130 131// The code bellow assumes that the two lists must not contain any modules 132// beginning with "internal/". 133// Modules that can only be imported via the node: scheme. 134const schemelessBlockList = new SafeSet([ 135 'test', 136 'test/reporters', 137]); 138// Modules that will only be enabled at run time. 139const experimentalModuleList = new SafeSet(); 140 141// Set up process.binding() and process._linkedBinding(). 142{ 143 const bindingObj = ObjectCreate(null); 144 145 process.binding = function binding(module) { 146 module = String(module); 147 // Deprecated specific process.binding() modules, but not all, allow 148 // selective fallback to internalBinding for the deprecated ones. 149 if (internalBindingAllowlist.has(module)) { 150 if (runtimeDeprecatedList.has(module)) { 151 runtimeDeprecatedList.delete(module); 152 process.emitWarning( 153 `Access to process.binding('${module}') is deprecated.`, 154 'DeprecationWarning', 155 'DEP0111'); 156 } 157 if (legacyWrapperList.has(module)) { 158 return requireBuiltin('internal/legacy/processbinding')[module](); 159 } 160 return internalBinding(module); 161 } 162 // eslint-disable-next-line no-restricted-syntax 163 throw new Error(`No such module: ${module}`); 164 }; 165 166 process._linkedBinding = function _linkedBinding(module) { 167 module = String(module); 168 let mod = bindingObj[module]; 169 if (typeof mod !== 'object') 170 mod = bindingObj[module] = getLinkedBinding(module); 171 return mod; 172 }; 173} 174 175// Set up internalBinding() in the closure. 176/** 177 * @type {InternalBinding} 178 */ 179let internalBinding; 180{ 181 const bindingObj = ObjectCreate(null); 182 // eslint-disable-next-line no-global-assign 183 internalBinding = function internalBinding(module) { 184 let mod = bindingObj[module]; 185 if (typeof mod !== 'object') { 186 mod = bindingObj[module] = getInternalBinding(module); 187 ArrayPrototypePush(moduleLoadList, `Internal Binding ${module}`); 188 } 189 return mod; 190 }; 191} 192 193const selfId = 'internal/bootstrap/realm'; 194const { 195 builtinIds, 196 compileFunction, 197 setInternalLoaders, 198} = internalBinding('builtins'); 199 200const getOwn = (target, property, receiver) => { 201 return ObjectPrototypeHasOwnProperty(target, property) ? 202 ReflectGet(target, property, receiver) : 203 undefined; 204}; 205 206const publicBuiltinIds = builtinIds 207 .filter((id) => 208 !StringPrototypeStartsWith(id, 'internal/') && 209 !experimentalModuleList.has(id), 210 ); 211// Do not expose the loaders to user land even with --expose-internals. 212const internalBuiltinIds = builtinIds 213 .filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId); 214 215// When --expose-internals is on we'll add the internal builtin ids to these. 216const canBeRequiredByUsersList = new SafeSet(publicBuiltinIds); 217const canBeRequiredByUsersWithoutSchemeList = 218 new SafeSet(publicBuiltinIds.filter((id) => !schemelessBlockList.has(id))); 219 220/** 221 * An internal abstraction for the built-in JavaScript modules of Node.js. 222 * Be careful not to expose this to user land unless --expose-internals is 223 * used, in which case there is no compatibility guarantee about this class. 224 */ 225class BuiltinModule { 226 /** 227 * A map from the module IDs to the module instances. 228 * @type {Map<string, BuiltinModule>} 229 */ 230 static map = new SafeMap( 231 ArrayPrototypeMap(builtinIds, (id) => [id, new BuiltinModule(id)]), 232 ); 233 234 constructor(id) { 235 this.filename = `${id}.js`; 236 this.id = id; 237 238 // The CJS exports object of the module. 239 this.exports = {}; 240 // States used to work around circular dependencies. 241 this.loaded = false; 242 this.loading = false; 243 244 // The following properties are used by the ESM implementation and only 245 // initialized when the built-in module is loaded by users. 246 /** 247 * The C++ ModuleWrap binding used to interface with the ESM implementation. 248 * @type {ModuleWrap|undefined} 249 */ 250 this.module = undefined; 251 /** 252 * Exported names for the ESM imports. 253 * @type {string[]|undefined} 254 */ 255 this.exportKeys = undefined; 256 } 257 258 static allowRequireByUsers(id) { 259 if (id === selfId) { 260 // No code because this is an assertion against bugs. 261 // eslint-disable-next-line no-restricted-syntax 262 throw new Error(`Should not allow ${id}`); 263 } 264 canBeRequiredByUsersList.add(id); 265 if (!schemelessBlockList.has(id)) { 266 canBeRequiredByUsersWithoutSchemeList.add(id); 267 } 268 } 269 270 // To be called during pre-execution when --expose-internals is on. 271 // Enables the user-land module loader to access internal modules. 272 static exposeInternals() { 273 for (let i = 0; i < internalBuiltinIds.length; ++i) { 274 BuiltinModule.allowRequireByUsers(internalBuiltinIds[i]); 275 } 276 } 277 278 static exists(id) { 279 return BuiltinModule.map.has(id); 280 } 281 282 static canBeRequiredByUsers(id) { 283 return canBeRequiredByUsersList.has(id); 284 } 285 286 static canBeRequiredWithoutScheme(id) { 287 return canBeRequiredByUsersWithoutSchemeList.has(id); 288 } 289 290 static isBuiltin(id) { 291 return BuiltinModule.canBeRequiredWithoutScheme(id) || ( 292 typeof id === 'string' && 293 StringPrototypeStartsWith(id, 'node:') && 294 BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(id, 5)) 295 ); 296 } 297 298 static getCanBeRequiredByUsersWithoutSchemeList() { 299 return ArrayFrom(canBeRequiredByUsersWithoutSchemeList); 300 } 301 302 static normalizeRequirableId(id) { 303 if (StringPrototypeStartsWith(id, 'node:')) { 304 const normalizedId = StringPrototypeSlice(id, 5); 305 if (BuiltinModule.canBeRequiredByUsers(normalizedId)) { 306 return normalizedId; 307 } 308 } else if (BuiltinModule.canBeRequiredWithoutScheme(id)) { 309 return id; 310 } 311 312 return undefined; 313 } 314 315 static getSchemeOnlyModuleNames() { 316 return ArrayFrom(schemelessBlockList); 317 } 318 319 // Used by user-land module loaders to compile and load builtins. 320 compileForPublicLoader() { 321 if (!BuiltinModule.canBeRequiredByUsers(this.id)) { 322 // No code because this is an assertion against bugs 323 // eslint-disable-next-line no-restricted-syntax 324 throw new Error(`Should not compile ${this.id} for public use`); 325 } 326 this.compileForInternalLoader(); 327 if (!this.exportKeys) { 328 // When using --expose-internals, we do not want to reflect the named 329 // exports from core modules as this can trigger unnecessary getters. 330 const internal = StringPrototypeStartsWith(this.id, 'internal/'); 331 this.exportKeys = internal ? [] : ObjectKeys(this.exports); 332 } 333 this.getESMFacade(); 334 this.syncExports(); 335 return this.exports; 336 } 337 338 getESMFacade() { 339 if (this.module) return this.module; 340 const { ModuleWrap } = internalBinding('module_wrap'); 341 // TODO(aduh95): move this to C++, alongside the initialization of the class. 342 ObjectSetPrototypeOf(ModuleWrap.prototype, null); 343 const url = `node:${this.id}`; 344 const builtin = this; 345 const exportsKeys = ArrayPrototypeSlice(this.exportKeys); 346 ArrayPrototypePush(exportsKeys, 'default'); 347 this.module = new ModuleWrap( 348 url, undefined, exportsKeys, 349 function() { 350 builtin.syncExports(); 351 this.setExport('default', builtin.exports); 352 }); 353 // Ensure immediate sync execution to capture exports now 354 this.module.instantiate(); 355 this.module.evaluate(-1, false); 356 return this.module; 357 } 358 359 // Provide named exports for all builtin libraries so that the libraries 360 // may be imported in a nicer way for ESM users. The default export is left 361 // as the entire namespace (module.exports) and updates when this function is 362 // called so that APMs and other behavior are supported. 363 syncExports() { 364 const names = this.exportKeys; 365 if (this.module) { 366 for (let i = 0; i < names.length; i++) { 367 const exportName = names[i]; 368 if (exportName === 'default') continue; 369 this.module.setExport(exportName, 370 getOwn(this.exports, exportName, this.exports)); 371 } 372 } 373 } 374 375 compileForInternalLoader() { 376 if (this.loaded || this.loading) { 377 return this.exports; 378 } 379 380 const id = this.id; 381 this.loading = true; 382 383 try { 384 const requireFn = StringPrototypeStartsWith(this.id, 'internal/deps/') ? 385 requireWithFallbackInDeps : requireBuiltin; 386 387 const fn = compileFunction(id); 388 // Arguments must match the parameters specified in 389 // BuiltinLoader::LookupAndCompile(). 390 fn(this.exports, requireFn, this, process, internalBinding, primordials); 391 392 this.loaded = true; 393 } finally { 394 this.loading = false; 395 } 396 397 // "NativeModule" is a legacy name of "BuiltinModule". We keep it 398 // here to avoid breaking users who parse process.moduleLoadList. 399 ArrayPrototypePush(moduleLoadList, `NativeModule ${id}`); 400 return this.exports; 401 } 402} 403 404// Think of this as module.exports in this file even though it is not 405// written in CommonJS style. 406const loaderExports = { 407 internalBinding, 408 BuiltinModule, 409 require: requireBuiltin, 410}; 411 412function requireBuiltin(id) { 413 if (id === selfId) { 414 return loaderExports; 415 } 416 417 const mod = BuiltinModule.map.get(id); 418 // Can't load the internal errors module from here, have to use a raw error. 419 // eslint-disable-next-line no-restricted-syntax 420 if (!mod) throw new TypeError(`Missing internal module '${id}'`); 421 return mod.compileForInternalLoader(); 422} 423 424// Allow internal modules from dependencies to require 425// other modules from dependencies by providing fallbacks. 426function requireWithFallbackInDeps(request) { 427 if (StringPrototypeStartsWith(request, 'node:')) { 428 request = StringPrototypeSlice(request, 5); 429 } else if (!BuiltinModule.map.has(request)) { 430 request = `internal/deps/${request}`; 431 } 432 return requireBuiltin(request); 433} 434 435function setupPrepareStackTrace() { 436 const { 437 setEnhanceStackForFatalException, 438 setPrepareStackTraceCallback, 439 } = internalBinding('errors'); 440 const { 441 prepareStackTrace, 442 fatalExceptionStackEnhancers: { 443 beforeInspector, 444 afterInspector, 445 }, 446 } = requireBuiltin('internal/errors'); 447 // Tell our PrepareStackTraceCallback passed to the V8 API 448 // to call prepareStackTrace(). 449 setPrepareStackTraceCallback(prepareStackTrace); 450 // Set the function used to enhance the error stack for printing 451 setEnhanceStackForFatalException(beforeInspector, afterInspector); 452} 453 454// Store the internal loaders in C++. 455setInternalLoaders(internalBinding, requireBuiltin); 456 457// Setup per-realm bindings. 458setupPrepareStackTrace(); 459