| // This file is executed in every realm that is created by Node.js, including |
| // the context of main thread, worker threads, and ShadowRealms. |
| // Only per-realm internal states and bindings should be bootstrapped in this |
| // file and no globals should be exposed to the user code. |
| // |
| // 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 bootstrapped 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_BINDING_CONTEXT_AWARE_CPP() with the flag |
| // NM_F_LINKED. |
| // - internalBinding(): the private internal C++ binding loader, inaccessible |
| // from user land unless through `require('internal/test/binding')`. |
| // These C++ bindings are created using NODE_BINDING_CONTEXT_AWARE_INTERNAL() |
| // and have their nm_flags set to NM_F_INTERNAL. |
| // |
| // Internal JavaScript module loader: |
| // - BuiltinModule: 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.cc, |
| // 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/realm') 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, primordials */ |
| |
| const { |
| ArrayFrom, |
| ArrayPrototypeFilter, |
| ArrayPrototypeIncludes, |
| ArrayPrototypeMap, |
| ArrayPrototypePush, |
| ArrayPrototypePushApply, |
| ArrayPrototypeSlice, |
| Error, |
| ObjectDefineProperty, |
| ObjectKeys, |
| ObjectPrototypeHasOwnProperty, |
| ObjectSetPrototypeOf, |
| ReflectGet, |
| SafeMap, |
| SafeSet, |
| String, |
| StringPrototypeSlice, |
| StringPrototypeStartsWith, |
| TypeError, |
| } = primordials; |
| |
| // Set up process.moduleLoadList. |
| const moduleLoadList = []; |
| ObjectDefineProperty(process, 'moduleLoadList', { |
| __proto__: null, |
| value: moduleLoadList, |
| configurable: true, |
| enumerable: true, |
| writable: false, |
| }); |
| |
| |
| // processBindingAllowList contains the name of bindings that are allowed |
| // for access via process.binding(). This is used to provide a transition |
| // path for modules that are being moved over to internalBinding. |
| // Certain bindings may not actually correspond to an internalBinding any |
| // more, we just implement them as legacy wrappers instead. See the |
| // legacyWrapperList. |
| const processBindingAllowList = new SafeSet([ |
| 'buffer', |
| 'cares_wrap', |
| 'config', |
| 'constants', |
| 'contextify', |
| 'fs', |
| 'fs_event_wrap', |
| 'icu', |
| 'inspector', |
| 'js_stream', |
| 'os', |
| 'pipe_wrap', |
| 'process_wrap', |
| 'spawn_sync', |
| 'stream_wrap', |
| 'tcp_wrap', |
| 'tls_wrap', |
| 'tty_wrap', |
| 'udp_wrap', |
| 'uv', |
| 'zlib', |
| ]); |
| |
| const runtimeDeprecatedList = new SafeSet([ |
| 'async_wrap', |
| 'crypto', |
| 'http_parser', |
| 'signal_wrap', |
| 'url', |
| 'v8', |
| ]); |
| |
| const legacyWrapperList = new SafeSet([ |
| 'natives', |
| 'util', |
| ]); |
| |
| // The code bellow assumes that the two lists must not contain any modules |
| // beginning with "internal/". |
| // Modules that can only be imported via the node: scheme. |
| const schemelessBlockList = new SafeSet([ |
| 'sea', |
| 'sqlite', |
| 'quic', |
| 'test', |
| 'test/reporters', |
| ]); |
| // Modules that will only be enabled at run time. |
| const experimentalModuleList = new SafeSet(['sqlite', 'quic']); |
| |
| // Set up process.binding() and process._linkedBinding(). |
| { |
| const bindingObj = { __proto__: null }; |
| |
| process.binding = function binding(module) { |
| module = String(module); |
| const mod = bindingObj[module]; |
| if (typeof mod === 'object') { |
| return mod; |
| } |
| // Deprecated specific process.binding() modules, but not all, allow |
| // selective fallback to internalBinding for the deprecated ones. |
| if (runtimeDeprecatedList.has(module)) { |
| process.emitWarning( |
| `Access to process.binding('${module}') is deprecated.`, |
| 'DeprecationWarning', |
| 'DEP0111'); |
| return internalBinding(module); |
| } |
| if (legacyWrapperList.has(module)) { |
| return requireBuiltin('internal/legacy/processbinding')[module](); |
| } |
| if (processBindingAllowList.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. |
| * @type {import('typings/globals').internalBinding} |
| */ |
| let internalBinding; |
| { |
| const bindingObj = { __proto__: null }; |
| // eslint-disable-next-line no-global-assign |
| internalBinding = function internalBinding(module) { |
| let mod = bindingObj[module]; |
| if (typeof mod !== 'object') { |
| mod = bindingObj[module] = getInternalBinding(module); |
| ArrayPrototypePush(moduleLoadList, `Internal Binding ${module}`); |
| } |
| return mod; |
| }; |
| } |
| |
| const selfId = 'internal/bootstrap/realm'; |
| const { |
| builtinIds, |
| compileFunction, |
| setInternalLoaders, |
| } = internalBinding('builtins'); |
| |
| const { ModuleWrap } = internalBinding('module_wrap'); |
| ObjectSetPrototypeOf(ModuleWrap.prototype, null); |
| |
| const getOwn = (target, property, receiver) => { |
| return ObjectPrototypeHasOwnProperty(target, property) ? |
| ReflectGet(target, property, receiver) : |
| undefined; |
| }; |
| |
| const publicBuiltinIds = builtinIds |
| .filter((id) => |
| !StringPrototypeStartsWith(id, 'internal/') && |
| !experimentalModuleList.has(id), |
| ); |
| // Do not expose the loaders to user land even with --expose-internals. |
| const internalBuiltinIds = builtinIds |
| .filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId); |
| |
| // When --expose-internals is on we'll add the internal builtin ids to these. |
| let canBeRequiredByUsersList = new SafeSet(publicBuiltinIds); |
| let canBeRequiredByUsersWithoutSchemeList = |
| new SafeSet(publicBuiltinIds.filter((id) => !schemelessBlockList.has(id))); |
| |
| /** |
| * An internal abstraction for the built-in JavaScript modules of Node.js. |
| * Be careful not to expose this to user land unless --expose-internals is |
| * used, in which case there is no compatibility guarantee about this class. |
| */ |
| class BuiltinModule { |
| /** |
| * A map from the module IDs to the module instances. |
| * @type {Map<string, BuiltinModule>} |
| */ |
| static map = new SafeMap( |
| ArrayPrototypeMap(builtinIds, (id) => [id, new BuiltinModule(id)]), |
| ); |
| |
| constructor(id) { |
| this.filename = `${id}.js`; |
| this.id = id; |
| |
| // The CJS exports object of the module. |
| this.exports = {}; |
| // States used to work around circular dependencies. |
| this.loaded = false; |
| this.loading = false; |
| |
| // The following properties are used by the ESM implementation and only |
| // initialized when the built-in module is loaded by users. |
| /** |
| * The C++ ModuleWrap binding used to interface with the ESM implementation. |
| * @type {ModuleWrap|undefined} |
| */ |
| this.module = undefined; |
| /** |
| * Exported names for the ESM imports. |
| * @type {string[]|undefined} |
| */ |
| this.exportKeys = undefined; |
| } |
| |
| static allowRequireByUsers(id) { |
| if (id === selfId) { |
| // No code because this is an assertion against bugs. |
| // eslint-disable-next-line no-restricted-syntax |
| throw new Error(`Should not allow ${id}`); |
| } |
| canBeRequiredByUsersList.add(id); |
| if (!schemelessBlockList.has(id)) { |
| canBeRequiredByUsersWithoutSchemeList.add(id); |
| } |
| } |
| |
| static setRealmAllowRequireByUsers(ids) { |
| canBeRequiredByUsersList = |
| new SafeSet(ArrayPrototypeFilter(ids, (id) => ArrayPrototypeIncludes(publicBuiltinIds, id))); |
| canBeRequiredByUsersWithoutSchemeList = |
| new SafeSet(ArrayPrototypeFilter(ids, (id) => !schemelessBlockList.has(id))); |
| } |
| |
| // To be called during pre-execution when --expose-internals is on. |
| // Enables the user-land module loader to access internal modules. |
| static exposeInternals() { |
| for (let i = 0; i < internalBuiltinIds.length; ++i) { |
| BuiltinModule.allowRequireByUsers(internalBuiltinIds[i]); |
| } |
| } |
| |
| static exists(id) { |
| return BuiltinModule.map.has(id); |
| } |
| |
| static canBeRequiredByUsers(id) { |
| return canBeRequiredByUsersList.has(id); |
| } |
| |
| static canBeRequiredWithoutScheme(id) { |
| return canBeRequiredByUsersWithoutSchemeList.has(id); |
| } |
| |
| static normalizeRequirableId(id) { |
| if (StringPrototypeStartsWith(id, 'node:')) { |
| const normalizedId = StringPrototypeSlice(id, 5); |
| if (BuiltinModule.canBeRequiredByUsers(normalizedId)) { |
| return normalizedId; |
| } |
| } else if (BuiltinModule.canBeRequiredWithoutScheme(id)) { |
| return id; |
| } |
| |
| return undefined; |
| } |
| |
| static isBuiltin(id) { |
| return BuiltinModule.canBeRequiredWithoutScheme(id) || ( |
| typeof id === 'string' && |
| StringPrototypeStartsWith(id, 'node:') && |
| BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(id, 5)) |
| ); |
| } |
| |
| static getSchemeOnlyModuleNames() { |
| return ArrayFrom(schemelessBlockList); |
| } |
| |
| static getAllBuiltinModuleIds() { |
| const allBuiltins = ArrayFrom(canBeRequiredByUsersWithoutSchemeList); |
| ArrayPrototypePushApply(allBuiltins, ArrayFrom(schemelessBlockList, (x) => `node:${x}`)); |
| return allBuiltins; |
| } |
| |
| // Used by user-land module loaders to compile and load builtins. |
| compileForPublicLoader() { |
| if (!BuiltinModule.canBeRequiredByUsers(this.id)) { |
| // No code because this is an assertion against bugs |
| // eslint-disable-next-line no-restricted-syntax |
| throw new Error(`Should not compile ${this.id} for public use`); |
| } |
| this.compileForInternalLoader(); |
| if (!this.exportKeys) { |
| // When using --expose-internals, we do not want to reflect the named |
| // exports from core modules as this can trigger unnecessary getters. |
| const internal = StringPrototypeStartsWith(this.id, 'internal/'); |
| this.exportKeys = internal ? [] : ObjectKeys(this.exports); |
| } |
| return this.exports; |
| } |
| |
| getESMFacade() { |
| if (this.module) return this.module; |
| const url = `node:${this.id}`; |
| const builtin = this; |
| const exportsKeys = ArrayPrototypeSlice(this.exportKeys); |
| if (!ArrayPrototypeIncludes(exportsKeys, 'default')) { |
| ArrayPrototypePush(exportsKeys, 'default'); |
| } |
| this.module = new ModuleWrap( |
| url, undefined, exportsKeys, |
| function() { |
| builtin.syncExports(); |
| this.setExport('default', builtin.exports); |
| }); |
| // Ensure immediate sync execution to capture exports now |
| this.module.instantiate(); |
| this.module.evaluate(-1, false); |
| return this.module; |
| } |
| |
| // 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 updates when this function is |
| // called so that APMs and other behavior are supported. |
| syncExports() { |
| const names = this.exportKeys; |
| if (this.module) { |
| for (let i = 0; i < names.length; i++) { |
| const exportName = names[i]; |
| if (exportName === 'default') continue; |
| this.module.setExport(exportName, |
| getOwn(this.exports, exportName, this.exports)); |
| } |
| } |
| } |
| |
| compileForInternalLoader() { |
| if (this.loaded || this.loading) { |
| return this.exports; |
| } |
| |
| const id = this.id; |
| this.loading = true; |
| |
| try { |
| const requireFn = StringPrototypeStartsWith(this.id, 'internal/deps/') ? |
| requireWithFallbackInDeps : requireBuiltin; |
| |
| const fn = compileFunction(id); |
| // Arguments must match the parameters specified in |
| // BuiltinLoader::LookupAndCompile(). |
| fn(this.exports, requireFn, this, process, internalBinding, primordials); |
| |
| this.loaded = true; |
| } finally { |
| this.loading = false; |
| } |
| |
| // "NativeModule" is a legacy name of "BuiltinModule". We keep it |
| // here to avoid breaking users who parse process.moduleLoadList. |
| ArrayPrototypePush(moduleLoadList, `NativeModule ${id}`); |
| return this.exports; |
| } |
| } |
| |
| // Think of this as module.exports in this file even though it is not |
| // written in CommonJS style. |
| const loaderExports = { |
| internalBinding, |
| BuiltinModule, |
| require: requireBuiltin, |
| }; |
| |
| function requireBuiltin(id) { |
| if (id === selfId) { |
| return loaderExports; |
| } |
| |
| const mod = BuiltinModule.map.get(id); |
| // Can't load the internal errors module from here, have to use a raw error. |
| // eslint-disable-next-line no-restricted-syntax |
| if (!mod) throw new TypeError(`Missing internal module '${id}'`); |
| return mod.compileForInternalLoader(); |
| } |
| |
| // Allow internal modules from dependencies to require |
| // other modules from dependencies by providing fallbacks. |
| function requireWithFallbackInDeps(request) { |
| if (StringPrototypeStartsWith(request, 'node:')) { |
| request = StringPrototypeSlice(request, 5); |
| } else if (!BuiltinModule.map.has(request)) { |
| request = `internal/deps/${request}`; |
| } |
| return requireBuiltin(request); |
| } |
| |
| function setupPrepareStackTrace() { |
| const { |
| setEnhanceStackForFatalException, |
| setPrepareStackTraceCallback, |
| } = internalBinding('errors'); |
| const { |
| prepareStackTraceCallback, |
| ErrorPrepareStackTrace, |
| fatalExceptionStackEnhancers: { |
| beforeInspector, |
| afterInspector, |
| }, |
| } = requireBuiltin('internal/errors'); |
| // Tell our PrepareStackTraceCallback passed to the V8 API |
| // to call prepareStackTrace(). |
| setPrepareStackTraceCallback(prepareStackTraceCallback); |
| // Set the function used to enhance the error stack for printing |
| setEnhanceStackForFatalException(beforeInspector, afterInspector); |
| // Setup the default Error.prepareStackTrace. |
| ObjectDefineProperty(Error, 'prepareStackTrace', { |
| __proto__: null, |
| writable: true, |
| enumerable: false, |
| configurable: true, |
| value: ErrorPrepareStackTrace, |
| }); |
| } |
| |
| // Store the internal loaders in C++. |
| setInternalLoaders(internalBinding, requireBuiltin); |
| |
| // Setup per-realm bindings. |
| setupPrepareStackTrace(); |