UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

471 lines (418 loc) 13 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/vm/module.js import * as __hoisted_internal_util_inspect__ from "nstdlib/lib/internal/util/inspect"; import * as assert from "nstdlib/lib/internal/assert"; import { isModuleNamespaceObject } from "nstdlib/lib/internal/util/types"; import { customInspectSymbol, emitExperimentalWarning, getConstructorOf, kEmptyObject, } from "nstdlib/lib/internal/util"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { validateBoolean, validateBuffer, validateFunction, validateInt32, validateObject, validateUint32, validateString, validateInternalField, } from "nstdlib/lib/internal/validators"; import * as binding from "nstdlib/stub/binding/module_wrap"; import { isContext } from "nstdlib/stub/internal/vm"; import * as __hoisted_internal_modules_esm_utils__ from "nstdlib/lib/internal/modules/esm/utils"; const { ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_VM_MODULE_ALREADY_LINKED, ERR_VM_MODULE_DIFFERENT_CONTEXT, ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA, ERR_VM_MODULE_LINK_FAILURE, ERR_VM_MODULE_NOT_MODULE, ERR_VM_MODULE_STATUS, } = __codes__; const { ModuleWrap, kUninstantiated, kInstantiating, kInstantiated, kEvaluating, kEvaluated, kErrored, } = binding; const STATUS_MAP = { [kUninstantiated]: "unlinked", [kInstantiating]: "linking", [kInstantiated]: "linked", [kEvaluating]: "evaluating", [kEvaluated]: "evaluated", [kErrored]: "errored", }; let globalModuleId = 0; const defaultModuleName = "vm:module"; const kWrap = Symbol("kWrap"); const kContext = Symbol("kContext"); const kPerContextModuleId = Symbol("kPerContextModuleId"); const kLink = Symbol("kLink"); function isModule(object) { if ( typeof object !== "object" || object === null || !Object.prototype.hasOwnProperty.call(object, kWrap) ) { return false; } return true; } class Module { constructor(options) { emitExperimentalWarning("VM Modules"); if (new.target === Module) { // eslint-disable-next-line no-restricted-syntax throw new TypeError("Module is not a constructor"); } const { context, sourceText, syntheticExportNames, syntheticEvaluationSteps, } = options; if (context !== undefined) { validateObject(context, "context"); if (!isContext(context)) { throw new ERR_INVALID_ARG_TYPE( "options.context", "vm.Context", context, ); } } let { identifier } = options; if (identifier !== undefined) { validateString(identifier, "options.identifier"); } else if (context === undefined) { identifier = `${defaultModuleName}(${globalModuleId++})`; } else if (context[kPerContextModuleId] !== undefined) { const curId = context[kPerContextModuleId]; identifier = `${defaultModuleName}(${curId})`; context[kPerContextModuleId] += 1; } else { identifier = `${defaultModuleName}(0)`; Object.defineProperty(context, kPerContextModuleId, { __proto__: null, value: 1, writable: true, enumerable: false, configurable: true, }); } let registry = { __proto__: null }; if (sourceText !== undefined) { this[kWrap] = new ModuleWrap( identifier, context, sourceText, options.lineOffset, options.columnOffset, options.cachedData, ); registry = { __proto__: null, initializeImportMeta: options.initializeImportMeta, importModuleDynamically: options.importModuleDynamically ? importModuleDynamicallyWrap(options.importModuleDynamically) : undefined, }; // This will take precedence over the referrer as the object being // passed into the callbacks. registry.callbackReferrer = this; const { registerModule } = __hoisted_internal_modules_esm_utils__; registerModule(this[kWrap], registry); } else { assert(syntheticEvaluationSteps); this[kWrap] = new ModuleWrap( identifier, context, syntheticExportNames, syntheticEvaluationSteps, ); } this[kContext] = context; } get identifier() { validateInternalField(this, kWrap, "Module"); return this[kWrap].url; } get context() { validateInternalField(this, kWrap, "Module"); return this[kContext]; } get namespace() { validateInternalField(this, kWrap, "Module"); if (this[kWrap].getStatus() < kInstantiated) { throw new ERR_VM_MODULE_STATUS("must not be unlinked or linking"); } return this[kWrap].getNamespace(); } get status() { validateInternalField(this, kWrap, "Module"); return STATUS_MAP[this[kWrap].getStatus()]; } get error() { validateInternalField(this, kWrap, "Module"); if (this[kWrap].getStatus() !== kErrored) { throw new ERR_VM_MODULE_STATUS("must be errored"); } return this[kWrap].getError(); } async link(linker) { validateInternalField(this, kWrap, "Module"); validateFunction(linker, "linker"); if (this.status === "linked") { throw new ERR_VM_MODULE_ALREADY_LINKED(); } if (this.status !== "unlinked") { throw new ERR_VM_MODULE_STATUS("must be unlinked"); } await this[kLink](linker); this[kWrap].instantiate(); } async evaluate(options = kEmptyObject) { validateInternalField(this, kWrap, "Module"); validateObject(options, "options"); let timeout = options.timeout; if (timeout === undefined) { timeout = -1; } else { validateUint32(timeout, "options.timeout", true); } const { breakOnSigint = false } = options; validateBoolean(breakOnSigint, "options.breakOnSigint"); const status = this[kWrap].getStatus(); if ( status !== kInstantiated && status !== kEvaluated && status !== kErrored ) { throw new ERR_VM_MODULE_STATUS( "must be one of linked, evaluated, or errored", ); } await this[kWrap].evaluate(timeout, breakOnSigint); } [customInspectSymbol](depth, options) { validateInternalField(this, kWrap, "Module"); if (typeof depth === "number" && depth < 0) return this; const constructor = getConstructorOf(this) || Module; const o = { __proto__: { constructor } }; o.status = this.status; o.identifier = this.identifier; o.context = this.context; Object.setPrototypeOf(o, Object.getPrototypeOf(this)); Object.defineProperty(o, Symbol.toStringTag, { __proto__: null, value: constructor.name, configurable: true, }); // Lazy to avoid circular dependency const { inspect } = __hoisted_internal_util_inspect__; return inspect(o, { ...options, customInspect: false }); } } const kDependencySpecifiers = Symbol("kDependencySpecifiers"); const kNoError = Symbol("kNoError"); class SourceTextModule extends Module { #error = kNoError; #statusOverride; constructor(sourceText, options = kEmptyObject) { validateString(sourceText, "sourceText"); validateObject(options, "options"); const { lineOffset = 0, columnOffset = 0, initializeImportMeta, importModuleDynamically, context, identifier, cachedData, } = options; validateInt32(lineOffset, "options.lineOffset"); validateInt32(columnOffset, "options.columnOffset"); if (initializeImportMeta !== undefined) { validateFunction(initializeImportMeta, "options.initializeImportMeta"); } if (importModuleDynamically !== undefined) { validateFunction( importModuleDynamically, "options.importModuleDynamically", ); } if (cachedData !== undefined) { validateBuffer(cachedData, "options.cachedData"); } super({ sourceText, context, identifier, lineOffset, columnOffset, cachedData, initializeImportMeta, importModuleDynamically, }); this[kDependencySpecifiers] = undefined; } async [kLink](linker) { this.#statusOverride = "linking"; const moduleRequests = this[kWrap].getModuleRequests(); // Iterates the module requests and links with the linker. // Specifiers should be aligned with the moduleRequests array in order. const specifiers = Array(moduleRequests.length); const modulePromises = Array(moduleRequests.length); // Iterates with index to avoid calling into userspace with `Symbol.iterator`. for (let idx = 0; idx < moduleRequests.length; idx++) { const { specifier, attributes } = moduleRequests[idx]; const linkerResult = linker(specifier, this, { attributes, assert: attributes, }); const modulePromise = Promise.prototype.then.call( Promise.resolve(linkerResult), async (module) => { if (!isModule(module)) { throw new ERR_VM_MODULE_NOT_MODULE(); } if (module.context !== this.context) { throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); } if (module.status === "errored") { throw new ERR_VM_MODULE_LINK_FAILURE( `request for '${specifier}' resolved to an errored module`, module.error, ); } if (module.status === "unlinked") { await module[kLink](linker); } return module[kWrap]; }, ); modulePromises[idx] = modulePromise; specifiers[idx] = specifier; } try { const modules = await Promise.all(modulePromises); this[kWrap].link(specifiers, modules); } catch (e) { this.#error = e; throw e; } finally { this.#statusOverride = undefined; } } get dependencySpecifiers() { validateInternalField(this, kDependencySpecifiers, "SourceTextModule"); // TODO(legendecas): add a new getter to expose the import attributes as the value type // of [[RequestedModules]] is changed in https://tc39.es/proposal-import-attributes/#table-cyclic-module-fields. this[kDependencySpecifiers] ??= Object.freeze( Array.prototype.map.call( this[kWrap].getModuleRequests(), (request) => request.specifier, ), ); return this[kDependencySpecifiers]; } get status() { validateInternalField(this, kDependencySpecifiers, "SourceTextModule"); if (this.#error !== kNoError) { return "errored"; } if (this.#statusOverride) { return this.#statusOverride; } return super.status; } get error() { validateInternalField(this, kDependencySpecifiers, "SourceTextModule"); if (this.#error !== kNoError) { return this.#error; } return super.error; } createCachedData() { const { status } = this; if ( status === "evaluating" || status === "evaluated" || status === "errored" ) { throw new ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA(); } return this[kWrap].createCachedData(); } } class SyntheticModule extends Module { constructor(exportNames, evaluateCallback, options = kEmptyObject) { if ( !Array.isArray(exportNames) || Array.prototype.some.call(exportNames, (e) => typeof e !== "string") ) { throw new ERR_INVALID_ARG_TYPE( "exportNames", "Array of unique strings", exportNames, ); } else { Array.prototype.forEach.call(exportNames, (name, i) => { if (Array.prototype.indexOf.call(exportNames, name, i + 1) !== -1) { throw new ERR_INVALID_ARG_VALUE( `exportNames.${name}`, name, "is duplicated", ); } }); } validateFunction(evaluateCallback, "evaluateCallback"); validateObject(options, "options"); const { context, identifier } = options; super({ syntheticExportNames: exportNames, syntheticEvaluationSteps: evaluateCallback, context, identifier, }); } [kLink]() { /** nothing to do for synthetic modules */ } setExport(name, value) { validateInternalField(this, kWrap, "SyntheticModule"); validateString(name, "name"); if (this[kWrap].getStatus() < kInstantiated) { throw new ERR_VM_MODULE_STATUS("must be linked"); } this[kWrap].setExport(name, value); } } function importModuleDynamicallyWrap(importModuleDynamically) { const importModuleDynamicallyWrapper = async (...args) => { const m = await ReflectApply(importModuleDynamically, this, args); if (isModuleNamespaceObject(m)) { return m; } if (!isModule(m)) { throw new ERR_VM_MODULE_NOT_MODULE(); } if (m.status === "errored") { throw m.error; } return m.namespace; }; return importModuleDynamicallyWrapper; } export { Module }; export { SourceTextModule }; export { SyntheticModule }; export { importModuleDynamicallyWrap };