Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2019-12-08 19:28:59 +0300
committerRich Trott <rtrott@gmail.com>2019-12-14 04:53:29 +0300
commite4e5a835b8ced02ca6d08d7f44826e7617ec5e9e (patch)
treefc25cf3af67a6d40e726df42ccf2c6ca1e01c894 /lib/internal/bootstrap
parent1807c3eadffb6fb42e4d4c482d265dcef1761535 (diff)
lib: refactor NativeModule
Refactor the internal NativeModule class to a JS class and add more documentation about its properties. PR-URL: https://github.com/nodejs/node/pull/30856 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'lib/internal/bootstrap')
-rw-r--r--lib/internal/bootstrap/loaders.js278
1 files changed, 146 insertions, 132 deletions
diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js
index cfefc56bd81..1029d52de9f 100644
--- a/lib/internal/bootstrap/loaders.js
+++ b/lib/internal/bootstrap/loaders.js
@@ -137,166 +137,180 @@ let internalBinding;
};
}
-// Think of this as module.exports in this file even though it is not
-// written in CommonJS style.
-const loaderExports = {
- internalBinding,
- NativeModule,
- require: nativeModuleRequire
-};
-
const loaderId = 'internal/bootstrap/loaders';
-
-// Set up NativeModule.
-function NativeModule(id) {
- this.filename = `${id}.js`;
- this.id = id;
- this.exports = {};
- this.module = undefined;
- this.exportKeys = undefined;
- this.loaded = false;
- this.loading = false;
- this.canBeRequiredByUsers = !id.startsWith('internal/');
-}
-
-// To be called during pre-execution when --expose-internals is on.
-// Enables the user-land module loader to access internal modules.
-NativeModule.exposeInternals = function() {
- for (const [id, mod] of NativeModule.map) {
- // Do not expose this to user land even with --expose-internals.
- if (id !== loaderId) {
- mod.canBeRequiredByUsers = true;
- }
- }
-};
-
const {
moduleIds,
compileFunction
} = internalBinding('native_module');
-NativeModule.map = new Map();
-for (let i = 0; i < moduleIds.length; ++i) {
- const id = moduleIds[i];
- const mod = new NativeModule(id);
- NativeModule.map.set(id, mod);
-}
+const getOwn = (target, property, receiver) => {
+ return ObjectPrototypeHasOwnProperty(target, property) ?
+ ReflectGet(target, property, receiver) :
+ undefined;
+};
-function nativeModuleRequire(id) {
- if (id === loaderId) {
- return loaderExports;
+/**
+ * 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/');
+
+ // 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;
}
- 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.compile();
-}
+ // 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;
+ }
+ }
+ }
-NativeModule.exists = function(id) {
- return NativeModule.map.has(id);
-};
+ static exists(id) {
+ return NativeModule.map.has(id);
+ }
-NativeModule.canBeRequiredByUsers = function(id) {
- const mod = NativeModule.map.get(id);
- return mod && mod.canBeRequiredByUsers;
-};
+ static canBeRequiredByUsers(id) {
+ const mod = NativeModule.map.get(id);
+ return mod && mod.canBeRequiredByUsers;
+ }
-// 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}`;
+ // 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;
}
- return nativeModuleRequire(request);
-}
-// This is exposed for public loaders
-NativeModule.prototype.compileForPublicLoader = function() {
- 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`);
+ 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;
}
- this.compile();
- 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);
+
+ // 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));
+ }
+ }
}
- this.getESMFacade();
- this.syncExports();
- return this.exports;
-};
-const getOwn = (target, property, receiver) => {
- return ObjectPrototypeHasOwnProperty(target, property) ?
- ReflectGet(target, property, receiver) :
- undefined;
-};
+ compileForInternalLoader() {
+ if (this.loaded || this.loading) {
+ return this.exports;
+ }
-NativeModule.prototype.getURL = function() {
- return `node:${this.id}`;
-};
+ const id = this.id;
+ this.loading = true;
-NativeModule.prototype.getESMFacade = function() {
- if (this.module) return this.module;
- const { ModuleWrap } = internalBinding('module_wrap');
- const url = this.getURL();
- 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;
-};
+ try {
+ const requireFn = this.id.startsWith('internal/deps/') ?
+ requireWithFallbackInDeps : nativeModuleRequire;
-// 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.
-NativeModule.prototype.syncExports = function() {
- 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));
+ const fn = compileFunction(id);
+ fn(this.exports, requireFn, this, process, internalBinding, primordials);
+
+ this.loaded = true;
+ } finally {
+ this.loading = false;
}
- }
-};
-NativeModule.prototype.compile = function() {
- if (this.loaded || this.loading) {
+ moduleLoadList.push(`NativeModule ${id}`);
return this.exports;
}
+}
- const id = this.id;
- this.loading = true;
+// Think of this as module.exports in this file even though it is not
+// written in CommonJS style.
+const loaderExports = {
+ internalBinding,
+ NativeModule,
+ require: nativeModuleRequire
+};
- try {
- const requireFn = this.id.startsWith('internal/deps/') ?
- requireWithFallbackInDeps : nativeModuleRequire;
+function nativeModuleRequire(id) {
+ if (id === loaderId) {
+ return loaderExports;
+ }
- const fn = compileFunction(id);
- fn(this.exports, requireFn, this, process, internalBinding, primordials);
+ 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();
+}
- this.loaded = true;
- } finally {
- this.loading = false;
+// 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);
+}
- moduleLoadList.push(`NativeModule ${id}`);
- return this.exports;
-};
-
-// This will be passed to internal/bootstrap/node.js.
+// Pass the exports back to C++ land for C++ internals to use.
return loaderExports;