--- /dev/null
+// 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 bootstraped 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_MODULE_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_MODULE_CONTEXT_AWARE_INTERNAL()
+// and have their nm_flags set to NM_F_INTERNAL.
+//
+// Internal JavaScript module loader:
+// - NativeModule: 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.py,
+// 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/loaders') 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 {
+ Error,
+ Map,
+ ObjectCreate,
+ ObjectDefineProperty,
+ ObjectKeys,
+ ObjectPrototypeHasOwnProperty,
+ ReflectGet,
+ SafeSet,
+ String,
+ TypeError,
+} = primordials;
+
+// Set up process.moduleLoadList.
+const moduleLoadList = [];
+ObjectDefineProperty(process, 'moduleLoadList', {
+ value: moduleLoadList,
+ configurable: true,
+ enumerable: true,
+ writable: false
+});
+
+
+// internalBindingWhitelist contains the name of internalBinding modules
+// that are whitelisted for access via process.binding()... This is used
+// to provide a transition path for modules that are being moved over to
+// internalBinding.
+const internalBindingWhitelist = new SafeSet([
+ 'async_wrap',
+ 'buffer',
+ 'cares_wrap',
+ 'config',
+ 'constants',
+ 'contextify',
+ 'crypto',
+ 'fs',
+ 'fs_event_wrap',
+ 'http_parser',
+ 'icu',
+ 'inspector',
+ 'js_stream',
+ 'natives',
+ 'os',
+ 'pipe_wrap',
+ 'process_wrap',
+ 'signal_wrap',
+ 'spawn_sync',
+ 'stream_wrap',
+ 'tcp_wrap',
+ 'tls_wrap',
+ 'tty_wrap',
+ 'udp_wrap',
+ 'url',
+ 'util',
+ 'uv',
+ 'v8',
+ 'zlib'
+]);
+
+// @lwnode
+function testCodeGenerationFromStringsAllowed() {
+ // related to: --allow-code-generation-from-strings
+ try {
+ eval();
+ } catch (error) {
+ return false;
+ }
+ return true;
+};
+
+const userModuleBlacklist = [
+ 'v8',
+ ...(testCodeGenerationFromStringsAllowed() ? [] : ['vm']),
+];
+
+// Set up process.binding() and process._linkedBinding().
+{
+ const bindingObj = ObjectCreate(null);
+
+ process.binding = function binding(module) {
+ module = String(module);
+ // Deprecated specific process.binding() modules, but not all, allow
+ // selective fallback to internalBinding for the deprecated ones.
+ if (internalBindingWhitelist.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.
+let internalBinding;
+{
+ const bindingObj = ObjectCreate(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);
+ moduleLoadList.push(`Internal Binding ${module}`);
+ }
+ return mod;
+ };
+}
+
+const loaderId = 'internal/bootstrap/loaders';
+const {
+ moduleIds,
+ compileFunction
+} = internalBinding('native_module');
+
+const getOwn = (target, property, receiver) => {
+ return ObjectPrototypeHasOwnProperty(target, property) ?
+ ReflectGet(target, property, receiver) :
+ undefined;
+};
+
+/**
+ * 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 NativeModule {
+ /**
+ * A map from the module IDs to the module instances.
+ * @type {Map<string, NativeModule>}
+ */
+ static map = new Map(moduleIds.map((id) => [id, new NativeModule(id)]));
+
+ constructor(id) {
+ this.filename = `${id}.js`;
+ this.id = id;
+ // this.canBeRequiredByUsers = !id.startsWith('internal/');
+ // @lwnode
+ this.canBeRequiredByUsers =
+ !id.startsWith("internal/") && !userModuleBlacklist.includes(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 native 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;
+ }
+
+ // To be called during pre-execution when --expose-internals is on.
+ // Enables the user-land module loader to access internal modules.
+ static exposeInternals() {
+ for (const [id, mod] of NativeModule.map) {
+ // Do not expose this to user land even with --expose-internals.
+ if (id !== loaderId) {
+ mod.canBeRequiredByUsers = true;
+ }
+ }
+ }
+
+ static exists(id) {
+ return NativeModule.map.has(id);
+ }
+
+ static canBeRequiredByUsers(id) {
+ const mod = NativeModule.map.get(id);
+ return mod && mod.canBeRequiredByUsers;
+ }
+
+ // Used by user-land module loaders to compile and load builtins.
+ compileForPublicLoader() {
+ if (!this.canBeRequiredByUsers) {
+ // 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 = this.id.startsWith('internal/');
+ this.exportKeys = internal ? [] : ObjectKeys(this.exports);
+ }
+ this.getESMFacade();
+ this.syncExports();
+ return this.exports;
+ }
+
+ getESMFacade() {
+ if (this.module) return this.module;
+ const { ModuleWrap } = internalBinding('module_wrap');
+ const url = `node:${this.id}`;
+ const nativeModule = this;
+ this.module = new ModuleWrap(
+ url, undefined, [...this.exportKeys, 'default'],
+ function() {
+ nativeModule.syncExports();
+ this.setExport('default', nativeModule.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 = this.id.startsWith('internal/deps/') ?
+ requireWithFallbackInDeps : nativeModuleRequire;
+
+ const fn = compileFunction(id);
+ fn(this.exports, requireFn, this, process, internalBinding, primordials);
+
+ this.loaded = true;
+ } finally {
+ this.loading = false;
+ }
+
+ moduleLoadList.push(`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,
+ NativeModule,
+ require: nativeModuleRequire
+};
+
+function nativeModuleRequire(id) {
+ if (id === loaderId) {
+ return loaderExports;
+ }
+
+ const mod = NativeModule.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 (!NativeModule.map.has(request)) {
+ request = `internal/deps/${request}`;
+ }
+ return nativeModuleRequire(request);
+}
+
+// Pass the exports back to C++ land for C++ internals to use.
+return loaderExports;