// 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 because they are only available from NativeModule.require() // 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'; (function bootstrapInternalLoaders(process, getBinding, getLinkedBinding, getInternalBinding, debugBreak) { if (debugBreak) debugger; // eslint-disable-line no-debugger const { apply: ReflectApply, deleteProperty: ReflectDeleteProperty, get: ReflectGet, getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor, has: ReflectHas, set: ReflectSet, } = Reflect; const { prototype: { hasOwnProperty: ObjectHasOwnProperty, }, create: ObjectCreate, defineProperty: ObjectDefineProperty, keys: ObjectKeys, } = Object; // Set up process.moduleLoadList const moduleLoadList = []; ObjectDefineProperty(process, 'moduleLoadList', { value: moduleLoadList, configurable: true, enumerable: true, writable: false }); // Set up process.binding() and process._linkedBinding() { const bindingObj = ObjectCreate(null); process.binding = function binding(module) { module = String(module); let mod = bindingObj[module]; if (typeof mod !== 'object') { mod = bindingObj[module] = getBinding(module); moduleLoadList.push(`Binding ${module}`); } return mod; }; 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); 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 { ContextifyScript } = process.binding('contextify'); // Set up NativeModule function NativeModule(id) { this.filename = `${id}.js`; this.id = id; this.exports = {}; this.reflect = undefined; this.exportKeys = undefined; this.loaded = false; this.loading = false; this.script = null; // The ContextifyScript of the module } NativeModule._source = getBinding('natives'); NativeModule._cache = {}; const config = getBinding('config'); const codeCache = getInternalBinding('code_cache'); const compiledWithoutCache = NativeModule.compiledWithoutCache = []; const compiledWithCache = NativeModule.compiledWithCache = []; // Think of this as module.exports in this file even though it is not // written in CommonJS style. const loaderExports = { internalBinding, NativeModule }; const loaderId = 'internal/bootstrap/loaders'; NativeModule.require = function(id) { if (id === loaderId) { return loaderExports; } const cached = NativeModule.getCached(id); if (cached && (cached.loaded || cached.loading)) { return cached.exports; } if (!NativeModule.exists(id)) { // Model the error off the internal/errors.js model, but // do not use that module given that it could actually be // the one causing the error if there's a bug in Node.js // eslint-disable-next-line no-restricted-syntax const err = new Error(`No such built-in module: ${id}`); err.code = 'ERR_UNKNOWN_BUILTIN_MODULE'; err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]'; throw err; } moduleLoadList.push(`NativeModule ${id}`); const nativeModule = new NativeModule(id); nativeModule.cache(); nativeModule.compile(); return nativeModule.exports; }; NativeModule.isDepsModule = function(id) { return id.startsWith('node-inspect/') || id.startsWith('v8/'); }; NativeModule.requireForDeps = function(id) { if (!NativeModule.exists(id) || // TODO(TimothyGu): remove when DEP0084 reaches end of life. NativeModule.isDepsModule(id)) { id = `internal/deps/${id}`; } return NativeModule.require(id); }; NativeModule.getCached = function(id) { return NativeModule._cache[id]; }; NativeModule.exists = function(id) { return NativeModule._source.hasOwnProperty(id); }; if (config.exposeInternals) { NativeModule.nonInternalExists = function(id) { // Do not expose this to user land even with --expose-internals if (id === loaderId) { return false; } return NativeModule.exists(id); }; NativeModule.isInternal = function(id) { // Do not expose this to user land even with --expose-internals return id === loaderId; }; } else { NativeModule.nonInternalExists = function(id) { return NativeModule.exists(id) && !NativeModule.isInternal(id); }; NativeModule.isInternal = function(id) { return id.startsWith('internal/') || (id === 'worker_threads' && !process.binding('config').experimentalWorker); }; } NativeModule.getSource = function(id) { return NativeModule._source[id]; }; NativeModule.wrap = function(script) { return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; }; NativeModule.wrapper = [ '(function (exports, require, module, process) {', '\n});' ]; const getOwn = (target, property, receiver) => { return ReflectApply(ObjectHasOwnProperty, target, [property]) ? ReflectGet(target, property, receiver) : undefined; }; NativeModule.prototype.compile = function() { let source = NativeModule.getSource(this.id); source = NativeModule.wrap(source); this.loading = true; try { // (code, filename, lineOffset, columnOffset // cachedData, produceCachedData, parsingContext) const script = new ContextifyScript( source, this.filename, 0, 0, codeCache[this.id], false, undefined ); this.script = script; // One of these conditions may be false when any of the inputs // of the `node_js2c` target in node.gyp is modified. // FIXME(joyeecheung): // 1. Figure out how to resolve the dependency issue. When the // code cache was introduced we were at a point where refactoring // node.gyp may not be worth the effort. // 2. Calculate checksums in both js2c and generate_code_cache.js // and compare them before compiling the native modules since // V8 only checks the length of the source to decide whether to // reject the cache. if (!codeCache[this.id] || script.cachedDataRejected) { compiledWithoutCache.push(this.id); } else { compiledWithCache.push(this.id); } // Arguments: timeout, displayErrors, breakOnSigint const fn = script.runInThisContext(-1, true, false); const requireFn = this.id.startsWith('internal/deps/') ? NativeModule.requireForDeps : NativeModule.require; fn(this.exports, requireFn, this, process); if (config.experimentalModules && !NativeModule.isInternal(this.id)) { this.exportKeys = ObjectKeys(this.exports); const update = (property, value) => { if (this.reflect !== undefined && ReflectApply(ObjectHasOwnProperty, this.reflect.exports, [property])) this.reflect.exports[property].set(value); }; const handler = { __proto__: null, defineProperty: (target, prop, descriptor) => { // Use `Object.defineProperty` instead of `Reflect.defineProperty` // to throw the appropriate error if something goes wrong. ObjectDefineProperty(target, prop, descriptor); if (typeof descriptor.get === 'function' && !ReflectHas(handler, 'get')) { handler.get = (target, prop, receiver) => { const value = ReflectGet(target, prop, receiver); if (ReflectApply(ObjectHasOwnProperty, target, [prop])) update(prop, value); return value; }; } update(prop, getOwn(target, prop)); return true; }, deleteProperty: (target, prop) => { if (ReflectDeleteProperty(target, prop)) { update(prop, undefined); return true; } return false; }, set: (target, prop, value, receiver) => { const descriptor = ReflectGetOwnPropertyDescriptor(target, prop); if (ReflectSet(target, prop, value, receiver)) { if (descriptor && typeof descriptor.set === 'function') { for (const key of this.exportKeys) { update(key, getOwn(target, key, receiver)); } } else { update(prop, getOwn(target, prop, receiver)); } return true; } return false; } }; this.exports = new Proxy(this.exports, handler); } this.loaded = true; } finally { this.loading = false; } }; NativeModule.prototype.cache = function() { NativeModule._cache[this.id] = this; }; // This will be passed to the bootstrapNodeJSCore function in // bootstrap/node.js. return loaderExports; });