diff options
-rw-r--r-- | doc/api/errors.md | 10 | ||||
-rw-r--r-- | doc/api/vm.md | 103 | ||||
-rw-r--r-- | lib/internal/errors.js | 2 | ||||
-rw-r--r-- | lib/internal/modules/esm/loader.js | 4 | ||||
-rw-r--r-- | lib/internal/vm/source_text_module.js | 284 | ||||
-rw-r--r-- | lib/vm.js | 20 | ||||
-rw-r--r-- | src/module_wrap.cc | 12 | ||||
-rw-r--r-- | src/module_wrap.h | 2 | ||||
-rw-r--r-- | test/parallel/test-internal-module-wrap.js | 2 | ||||
-rw-r--r-- | test/parallel/test-util-inspect-namespace.js | 1 | ||||
-rw-r--r-- | test/parallel/test-util-types.js | 1 | ||||
-rw-r--r-- | test/parallel/test-vm-module-basic.js | 29 | ||||
-rw-r--r-- | test/parallel/test-vm-module-dynamic-import.js | 4 | ||||
-rw-r--r-- | test/parallel/test-vm-module-dynamic-namespace.js | 17 | ||||
-rw-r--r-- | test/parallel/test-vm-module-errors.js | 75 | ||||
-rw-r--r-- | test/parallel/test-vm-module-import-meta.js | 1 | ||||
-rw-r--r-- | test/parallel/test-vm-module-link.js | 35 | ||||
-rw-r--r-- | test/parallel/test-vm-module-reevaluate.js | 2 |
18 files changed, 269 insertions, 335 deletions
diff --git a/doc/api/errors.md b/doc/api/errors.md index 34412648b3..4e76673723 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1984,11 +1984,6 @@ than the parent module. Linked modules must share the same context. The linker function returned a module for which linking has failed. -<a id="ERR_VM_MODULE_NOT_LINKED"></a> -### ERR_VM_MODULE_NOT_LINKED - -The module must be successfully linked before instantiation. - <a id="ERR_VM_MODULE_NOT_MODULE"></a> ### ERR_VM_MODULE_NOT_MODULE @@ -2275,6 +2270,11 @@ removed: v10.0.0 Used when a given value is out of the accepted range. +<a id="ERR_VM_MODULE_NOT_LINKED"></a> +### ERR_VM_MODULE_NOT_LINKED + +The module must be successfully linked before instantiation. + <a id="ERR_ZLIB_BINDING_CLOSED"></a> ### ERR_ZLIB_BINDING_CLOSED <!-- YAML diff --git a/doc/api/vm.md b/doc/api/vm.md index 18ebd10a63..601b8c3052 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -322,9 +322,9 @@ intrinsically asynchronous, in contrast with the synchronous nature of `vm.Script` objects. With the help of async functions, however, manipulating `vm.SourceTextModule` objects is fairly straightforward. -Using a `vm.SourceTextModule` object requires four distinct steps: -creation/parsing, linking, instantiation, and evaluation. These four steps are -illustrated in the following example. +Using a `vm.SourceTextModule` object requires three distinct steps: +creation/parsing, linking, and evaluation. These three steps are illustrated in +the following example. This implementation lies at a lower level than the [ECMAScript Module loader][]. There is also currently no way to interact with the Loader, though @@ -391,15 +391,6 @@ const contextifiedSandbox = vm.createContext({ secret: 42 }); // Step 3 // - // Instantiate the top-level Module. - // - // Only the top-level Module needs to be explicitly instantiated; its - // dependencies will be recursively instantiated by instantiate(). - - bar.instantiate(); - - // Step 4 - // // Evaluate the Module. The evaluate() method returns a Promise with a single // property "result" that contains the result of the very last statement // executed in the Module. In the case of `bar`, it is `s;`, which refers to @@ -417,8 +408,9 @@ const contextifiedSandbox = vm.createContext({ secret: 42 }); * `code` {string} JavaScript Module code to parse * `options` - * `url` {string} URL used in module resolution and stack traces. **Default:** - `'vm:module(i)'` where `i` is a context-specific ascending index. + * `identifier` {string} String used in stack traces. + **Default:** `'vm:module(i)'` where `i` is a context-specific ascending + index. * `context` {Object} The [contextified][] object as returned by the `vm.createContext()` method, to compile and evaluate this `Module` in. * `lineOffset` {integer} Specifies the line number offset that is displayed @@ -465,7 +457,6 @@ const contextifiedSandbox = vm.createContext({ secret: 42 }); }); // Since module has no dependencies, the linker function will never be called. await module.link(() => {}); - module.instantiate(); await module.evaluate(); // Now, Object.prototype.secret will be equal to 42. @@ -516,7 +507,7 @@ in the ECMAScript specification. Evaluate the module. -This must be called after the module has been instantiated; otherwise it will +This must be called after the module has been linked; otherwise it will throw an error. It could be called also when the module has already been evaluated, in which case it will do one of the following two things: @@ -531,23 +522,6 @@ This method cannot be called while the module is being evaluated Corresponds to the [Evaluate() concrete method][] field of [Source Text Module Record][]s in the ECMAScript specification. -### module.instantiate() - -Instantiate the module. This must be called after linking has completed -(`linkingStatus` is `'linked'`); otherwise it will throw an error. It may also -throw an exception if one of the dependencies does not provide an export the -parent module requires. - -However, if this function succeeded, further calls to this function after the -initial instantiation will be no-ops, to be consistent with the ECMAScript -specification. - -Unlike other methods operating on `Module`, this function completes -synchronously and returns nothing. - -Corresponds to the [Instantiate() concrete method][] field of [Source Text -Module Record][]s in the ECMAScript specification. - ### module.link(linker) * `linker` {Function} @@ -563,7 +537,7 @@ Module Record][]s in the ECMAScript specification. * Returns: {vm.SourceTextModule|Promise} * Returns: {Promise} -Link module dependencies. This method must be called before instantiation, and +Link module dependencies. This method must be called before evaluation, and can only be called once per module. The function is expected to return a `Module` object or a `Promise` that @@ -571,9 +545,9 @@ eventually resolves to a `Module` object. The returned `Module` must satisfy the following two invariants: * It must belong to the same context as the parent `Module`. -* Its `linkingStatus` must not be `'errored'`. +* Its `status` must not be `'errored'`. -If the returned `Module`'s `linkingStatus` is `'unlinked'`, this method will be +If the returned `Module`'s `status` is `'unlinked'`, this method will be recursively called on the returned `Module` with the same provided `linker` function. @@ -587,37 +561,22 @@ specification, with a few key differences: * The linker function is allowed to be asynchronous while [HostResolveImportedModule][] is synchronous. -* The linker function is executed during linking, a Node.js-specific stage - before instantiation, while [HostResolveImportedModule][] is called during - instantiation. The actual [HostResolveImportedModule][] implementation used during module -instantiation is one that returns the modules linked during linking. Since at +linking is one that returns the modules linked during linking. Since at that point all modules would have been fully linked already, the [HostResolveImportedModule][] implementation is fully synchronous per specification. -### module.linkingStatus - -* {string} - -The current linking status of `module`. It will be one of the following values: - -* `'unlinked'`: `module.link()` has not yet been called. -* `'linking'`: `module.link()` has been called, but not all Promises returned by - the linker function have been resolved yet. -* `'linked'`: `module.link()` has been called, and all its dependencies have - been successfully linked. -* `'errored'`: `module.link()` has been called, but at least one of its - dependencies failed to link, either because the callback returned a `Promise` - that is rejected, or because the `Module` the callback returned is invalid. +Corresponds to the [Link() concrete method][] field of [Source Text Module +Record][]s in the ECMAScript specification. ### module.namespace * {Object} -The namespace object of the module. This is only available after instantiation -(`module.instantiate()`) has completed. +The namespace object of the module. This is only available after linking +(`module.link()`) has completed. Corresponds to the [GetModuleNamespace][] abstract operation in the ECMAScript specification. @@ -628,21 +587,13 @@ specification. The current status of the module. Will be one of: -* `'uninstantiated'`: The module is not instantiated. It may because of any of - the following reasons: - - * The module was just created. - * `module.instantiate()` has been called on this module, but it failed for - some reason. - - This status does not convey any information regarding if `module.link()` has - been called. See `module.linkingStatus` for that. +* `'unlinked'`: `module.link()` has not yet been called. -* `'instantiating'`: The module is currently being instantiated through a - `module.instantiate()` call on itself or a parent module. +* `'linking'`: `module.link()` has been called, but not all Promises returned + by the linker function have been resolved yet. -* `'instantiated'`: The module has been instantiated successfully, but - `module.evaluate()` has not yet been called. +* `'linked'`: The module has been linked successfully, and all of its + dependencies are linked, but `module.evaluate()` has not yet been called. * `'evaluating'`: The module is being evaluated through a `module.evaluate()` on itself or a parent module. @@ -656,11 +607,11 @@ Other than `'errored'`, this status string corresponds to the specification's `'evaluated'` in the specification, but with `[[EvaluationError]]` set to a value that is not `undefined`. -### module.url +### module.identifier * {string} -The URL of the current module, as set in the constructor. +The identifier of the current module, as set in the constructor. ## vm.compileFunction(code[, params[, options]]) <!-- YAML @@ -1127,11 +1078,11 @@ queues. [`vm.runInContext()`]: #vm_vm_runincontext_code_contextifiedsandbox_options [`vm.runInThisContext()`]: #vm_vm_runinthiscontext_code_options [ECMAScript Module Loader]: esm.html#esm_ecmascript_modules -[Evaluate() concrete method]: https://tc39.github.io/ecma262/#sec-moduleevaluation -[GetModuleNamespace]: https://tc39.github.io/ecma262/#sec-getmodulenamespace -[HostResolveImportedModule]: https://tc39.github.io/ecma262/#sec-hostresolveimportedmodule -[Instantiate() concrete method]: https://tc39.github.io/ecma262/#sec-moduledeclarationinstantiation -[Source Text Module Record]: https://tc39.github.io/ecma262/#sec-source-text-module-records +[Evaluate() concrete method]: https://tc39.es/ecma262/#sec-moduleevaluation +[GetModuleNamespace]: https://tc39.es/ecma262/#sec-getmodulenamespace +[HostResolveImportedModule]: https://tc39.es/ecma262/#sec-hostresolveimportedmodule +[Link() concrete method]: https://tc39.es/ecma262/#sec-moduledeclarationlinking +[Source Text Module Record]: https://tc39.es/ecma262/#sec-source-text-module-records [V8 Embedder's Guide]: https://v8.dev/docs/embed#contexts [contextified]: #vm_what_does_it_mean_to_contextify_an_object [global object]: https://es5.github.io/#x15.1 diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 53f448cf2f..ab874cd533 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1211,8 +1211,6 @@ E('ERR_VM_MODULE_DIFFERENT_CONTEXT', 'Linked modules must use the same context', Error); E('ERR_VM_MODULE_LINKING_ERRORED', 'Linking has already failed for the provided module', Error); -E('ERR_VM_MODULE_NOT_LINKED', - 'Module must be linked before it can be instantiated', Error); E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module', Error); E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 09109d3c71..9800e8a550 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -127,7 +127,7 @@ class Loader { this.moduleMap.set(url, job); const { module, result } = await job.run(); return { - namespace: module.namespace(), + namespace: module.getNamespace(), result }; } @@ -135,7 +135,7 @@ class Loader { async import(specifier, parent) { const job = await this.getModuleJob(specifier, parent); const { module } = await job.run(); - return module.namespace(); + return module.getNamespace(); } hook({ resolve, dynamicInstantiate }) { diff --git a/lib/internal/vm/source_text_module.js b/lib/internal/vm/source_text_module.js index 881c298ce5..61af2e7a29 100644 --- a/lib/internal/vm/source_text_module.js +++ b/lib/internal/vm/source_text_module.js @@ -3,14 +3,12 @@ const { Object, SafePromise } = primordials; const { isModuleNamespaceObject } = require('internal/util/types'); -const { URL } = require('internal/url'); const { isContext } = internalBinding('contextify'); const { ERR_INVALID_ARG_TYPE, ERR_VM_MODULE_ALREADY_LINKED, ERR_VM_MODULE_DIFFERENT_CONTEXT, ERR_VM_MODULE_LINKING_ERRORED, - ERR_VM_MODULE_NOT_LINKED, ERR_VM_MODULE_NOT_MODULE, ERR_VM_MODULE_STATUS, } = require('internal/errors').codes; @@ -22,7 +20,7 @@ const { const { validateInt32, validateUint32, - validateString + validateString, } = require('internal/validators'); const binding = internalBinding('module_wrap'); @@ -37,29 +35,33 @@ const { } = binding; const STATUS_MAP = { - [kUninstantiated]: 'uninstantiated', - [kInstantiating]: 'instantiating', - [kInstantiated]: 'instantiated', + [kUninstantiated]: 'unlinked', + [kInstantiating]: 'linking', + [kInstantiated]: 'linked', [kEvaluating]: 'evaluating', [kEvaluated]: 'evaluated', [kErrored]: 'errored', }; let globalModuleId = 0; +const defaultModuleName = 'vm:module'; const perContextModuleId = new WeakMap(); -const wrapMap = new WeakMap(); -const dependencyCacheMap = new WeakMap(); -const linkingStatusMap = new WeakMap(); -// ModuleWrap -> vm.SourceTextModule const wrapToModuleMap = new WeakMap(); -const defaultModuleName = 'vm:module'; -// TODO(devsnek): figure out AbstractModule class or protocol +const kNoError = Symbol('kNoError'); + class SourceTextModule { - constructor(src, options = {}) { + #wrap; + #identifier; + #context; + #dependencySpecifiers; + #statusOverride; + #error = kNoError; + + constructor(source, options = {}) { emitExperimentalWarning('vm.SourceTextModule'); - validateString(src, 'src'); + validateString(source, 'source'); if (typeof options !== 'object' || options === null) throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); @@ -81,21 +83,6 @@ class SourceTextModule { } } - let { url } = options; - if (url !== undefined) { - validateString(url, 'options.url'); - url = new URL(url).href; - } else if (context === undefined) { - url = `${defaultModuleName}(${globalModuleId++})`; - } else if (perContextModuleId.has(context)) { - const curId = perContextModuleId.get(context); - url = `${defaultModuleName}(${curId})`; - perContextModuleId.set(context, curId + 1); - } else { - url = `${defaultModuleName}(0)`; - perContextModuleId.set(context, 1); - } - validateInt32(lineOffset, 'options.lineOffset'); validateInt32(columnOffset, 'options.columnOffset'); @@ -111,114 +98,167 @@ class SourceTextModule { 'options.importModuleDynamically', 'function', importModuleDynamically); } - const wrap = new ModuleWrap(src, url, context, lineOffset, columnOffset); - wrapMap.set(this, wrap); - linkingStatusMap.set(this, 'unlinked'); - wrapToModuleMap.set(wrap, this); + let { identifier } = options; + if (identifier !== undefined) { + validateString(identifier, 'options.identifier'); + } else if (context === undefined) { + identifier = `${defaultModuleName}(${globalModuleId++})`; + } else if (perContextModuleId.has(context)) { + const curId = perContextModuleId.get(context); + identifier = `${defaultModuleName}(${curId})`; + perContextModuleId.set(context, curId + 1); + } else { + identifier = `${defaultModuleName}(0)`; + perContextModuleId.set(context, 1); + } - binding.callbackMap.set(wrap, { + this.#wrap = new ModuleWrap( + source, identifier, context, + lineOffset, columnOffset, + ); + wrapToModuleMap.set(this.#wrap, this); + this.#identifier = identifier; + this.#context = context; + + binding.callbackMap.set(this.#wrap, { initializeImportMeta, - importModuleDynamically: importModuleDynamically ? async (...args) => { - const m = await importModuleDynamically(...args); - if (isModuleNamespaceObject(m)) { - return m; - } - if (!m || !wrapMap.has(m)) - throw new ERR_VM_MODULE_NOT_MODULE(); - const childLinkingStatus = linkingStatusMap.get(m); - if (childLinkingStatus === 'errored') - throw m.error; - return m.namespace; - } : undefined, + importModuleDynamically: importModuleDynamically ? + importModuleDynamicallyWrap(importModuleDynamically) : + undefined, }); + } - Object.defineProperties(this, { - url: { value: url, enumerable: true }, - context: { value: context, enumerable: true }, - }); + get status() { + try { + this.#error; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + if (this.#error !== kNoError) { + return 'errored'; + } + if (this.#statusOverride) { + return this.#statusOverride; + } + return STATUS_MAP[this.#wrap.getStatus()]; } - get linkingStatus() { - return linkingStatusMap.get(this); + get identifier() { + try { + return this.#identifier; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } } - get status() { - return STATUS_MAP[wrapMap.get(this).getStatus()]; + get context() { + try { + return this.#context; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } } get namespace() { - const wrap = wrapMap.get(this); - if (wrap.getStatus() < kInstantiated) - throw new ERR_VM_MODULE_STATUS( - 'must not be uninstantiated or instantiating' - ); - return wrap.namespace(); + try { + this.#wrap; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + if (this.#wrap.getStatus() < kInstantiated) { + throw new ERR_VM_MODULE_STATUS('must not be unlinked or linking'); + } + return this.#wrap.getNamespace(); } get dependencySpecifiers() { - let deps = dependencyCacheMap.get(this); - if (deps !== undefined) - return deps; - - deps = wrapMap.get(this).getStaticDependencySpecifiers(); - Object.freeze(deps); - dependencyCacheMap.set(this, deps); - return deps; + try { + this.#dependencySpecifiers; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + if (this.#dependencySpecifiers === undefined) { + this.#dependencySpecifiers = this.#wrap.getStaticDependencySpecifiers(); + } + return this.#dependencySpecifiers; } get error() { - const wrap = wrapMap.get(this); - if (wrap.getStatus() !== kErrored) + try { + this.#error; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + if (this.#error !== kNoError) { + return this.#error; + } + if (this.#wrap.getStatus() !== kErrored) { throw new ERR_VM_MODULE_STATUS('must be errored'); - return wrap.getError(); + } + return this.#wrap.getError(); } async link(linker) { - if (typeof linker !== 'function') + try { + this.#link; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + + if (typeof linker !== 'function') { throw new ERR_INVALID_ARG_TYPE('linker', 'function', linker); - if (linkingStatusMap.get(this) !== 'unlinked') + } + if (this.status !== 'unlinked') { throw new ERR_VM_MODULE_ALREADY_LINKED(); - const wrap = wrapMap.get(this); - if (wrap.getStatus() !== kUninstantiated) - throw new ERR_VM_MODULE_STATUS('must be uninstantiated'); + } - linkingStatusMap.set(this, 'linking'); + await this.#link(linker); - const promises = wrap.link(async (specifier) => { - const m = await linker(specifier, this); - if (!m || !wrapMap.has(m)) + this.#wrap.instantiate(); + } + + #link = async function(linker) { + this.#statusOverride = 'linking'; + + const promises = this.#wrap.link(async (identifier) => { + const module = await linker(identifier, this); + try { + module.#wrap; + } catch { throw new ERR_VM_MODULE_NOT_MODULE(); - if (m.context !== this.context) + } + if (module.context !== this.context) { throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); - const childLinkingStatus = linkingStatusMap.get(m); - if (childLinkingStatus === 'errored') + } + if (module.status === 'errored') { throw new ERR_VM_MODULE_LINKING_ERRORED(); - if (childLinkingStatus === 'unlinked') - await m.link(linker); - return wrapMap.get(m); + } + if (module.status === 'unlinked') { + await module.#link(linker); + } + return module.#wrap; }); try { - if (promises !== undefined) + if (promises !== undefined) { await SafePromise.all(promises); - linkingStatusMap.set(this, 'linked'); - } catch (err) { - linkingStatusMap.set(this, 'errored'); - throw err; + } + } catch (e) { + this.#error = e; + throw e; + } finally { + this.#statusOverride = undefined; } - } + }; - instantiate() { - const wrap = wrapMap.get(this); - const status = wrap.getStatus(); - if (status === kInstantiating || status === kEvaluating) - throw new ERR_VM_MODULE_STATUS('must not be instantiating or evaluating'); - if (linkingStatusMap.get(this) !== 'linked') - throw new ERR_VM_MODULE_NOT_LINKED(); - wrap.instantiate(); - } async evaluate(options = {}) { + try { + this.#wrap; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + if (typeof options !== 'object' || options === null) { throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); } @@ -229,24 +269,41 @@ class SourceTextModule { } else { validateUint32(timeout, 'options.timeout', true); } - const { breakOnSigint = false } = options; if (typeof breakOnSigint !== 'boolean') { throw new ERR_INVALID_ARG_TYPE('options.breakOnSigint', 'boolean', breakOnSigint); } - - const wrap = wrapMap.get(this); - const status = wrap.getStatus(); + const status = this.#wrap.getStatus(); if (status !== kInstantiated && status !== kEvaluated && status !== kErrored) { throw new ERR_VM_MODULE_STATUS( - 'must be one of instantiated, evaluated, and errored' + 'must be one of linked, evaluated, or errored' ); } - const result = wrap.evaluate(timeout, breakOnSigint); - return { result, __proto__: null }; + const result = this.#wrap.evaluate(timeout, breakOnSigint); + return { __proto__: null, result }; + } + + static importModuleDynamicallyWrap(importModuleDynamically) { + // Named declaration for function name + const importModuleDynamicallyWrapper = async (...args) => { + const m = await importModuleDynamically(...args); + if (isModuleNamespaceObject(m)) { + return m; + } + try { + m.#wrap; + } catch { + throw new ERR_VM_MODULE_NOT_MODULE(); + } + if (m.status === 'errored') { + throw m.error; + } + return m.namespace; + }; + return importModuleDynamicallyWrapper; } [customInspectSymbol](depth, options) { @@ -258,16 +315,19 @@ class SourceTextModule { const o = Object.create({ constructor: ctor }); o.status = this.status; - o.linkingStatus = this.linkingStatus; - o.url = this.url; + o.identifier = this.identifier; o.context = this.context; return require('internal/util/inspect').inspect(o, options); } } +// Declared as static to allow access to #wrap +const importModuleDynamicallyWrap = + SourceTextModule.importModuleDynamicallyWrap; +delete SourceTextModule.importModuleDynamicallyWrap; + module.exports = { SourceTextModule, wrapToModuleMap, - wrapMap, - linkingStatusMap, + importModuleDynamicallyWrap, }; @@ -31,10 +31,8 @@ const { } = internalBinding('contextify'); const { ERR_INVALID_ARG_TYPE, - ERR_VM_MODULE_NOT_MODULE, } = require('internal/errors').codes; const { - isModuleNamespaceObject, isArrayBufferView, } = require('internal/util/types'); const { @@ -100,21 +98,13 @@ class Script extends ContextifyScript { 'function', importModuleDynamically); } - const { wrapMap, linkingStatusMap } = + const { importModuleDynamicallyWrap } = require('internal/vm/source_text_module'); const { callbackMap } = internalBinding('module_wrap'); - callbackMap.set(this, { importModuleDynamically: async (...args) => { - const m = await importModuleDynamically(...args); - if (isModuleNamespaceObject(m)) { - return m; - } - if (!m || !wrapMap.has(m)) - throw new ERR_VM_MODULE_NOT_MODULE(); - const childLinkingStatus = linkingStatusMap.get(m); - if (childLinkingStatus === 'errored') - throw m.error; - return m.namespace; - } }); + callbackMap.set(this, { + importModuleDynamically: + importModuleDynamicallyWrap(importModuleDynamically), + }); } } diff --git a/src/module_wrap.cc b/src/module_wrap.cc index aeb44675f6..4a572c00e1 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -331,7 +331,7 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) { args.GetReturnValue().Set(result.ToLocalChecked()); } -void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) { +void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = args.GetIsolate(); ModuleWrap* obj; @@ -1328,8 +1328,12 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback( Local<Function> callback = env->host_initialize_import_meta_object_callback(); Local<Value> args[] = { wrap, meta }; - callback->Call(context, Undefined(env->isolate()), arraysize(args), args) - .ToLocalChecked(); + TryCatchScope try_catch(env); + USE(callback->Call( + context, Undefined(env->isolate()), arraysize(args), args)); + if (try_catch.HasCaught() && !try_catch.HasTerminated()) { + try_catch.ReThrow(); + } } void ModuleWrap::SetInitializeImportMetaObjectCallback( @@ -1360,7 +1364,7 @@ void ModuleWrap::Initialize(Local<Object> target, env->SetProtoMethod(tpl, "link", Link); env->SetProtoMethod(tpl, "instantiate", Instantiate); env->SetProtoMethod(tpl, "evaluate", Evaluate); - env->SetProtoMethodNoSideEffect(tpl, "namespace", Namespace); + env->SetProtoMethodNoSideEffect(tpl, "getNamespace", GetNamespace); env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus); env->SetProtoMethodNoSideEffect(tpl, "getError", GetError); env->SetProtoMethodNoSideEffect(tpl, "getStaticDependencySpecifiers", diff --git a/src/module_wrap.h b/src/module_wrap.h index 6c79781a9d..9e2aa3b948 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -57,7 +57,7 @@ class ModuleWrap : public BaseObject { static void Link(const v8::FunctionCallbackInfo<v8::Value>& args); static void Instantiate(const v8::FunctionCallbackInfo<v8::Value>& args); static void Evaluate(const v8::FunctionCallbackInfo<v8::Value>& args); - static void Namespace(const v8::FunctionCallbackInfo<v8::Value>& args); + static void GetNamespace(const v8::FunctionCallbackInfo<v8::Value>& args); static void GetStatus(const v8::FunctionCallbackInfo<v8::Value>& args); static void GetError(const v8::FunctionCallbackInfo<v8::Value>& args); static void GetStaticDependencySpecifiers( diff --git a/test/parallel/test-internal-module-wrap.js b/test/parallel/test-internal-module-wrap.js index ee0b722d45..116a300543 100644 --- a/test/parallel/test-internal-module-wrap.js +++ b/test/parallel/test-internal-module-wrap.js @@ -25,5 +25,5 @@ const bar = new ModuleWrap('export const five = 5', 'bar'); foo.instantiate(); assert.strictEqual(await foo.evaluate(-1, false), 6); - assert.strictEqual(foo.namespace().five, 5); + assert.strictEqual(foo.getNamespace().five, 5); })(); diff --git a/test/parallel/test-util-inspect-namespace.js b/test/parallel/test-util-inspect-namespace.js index f2b6e57178..89b26fcdbc 100644 --- a/test/parallel/test-util-inspect-namespace.js +++ b/test/parallel/test-util-inspect-namespace.js @@ -11,7 +11,6 @@ const { inspect } = require('util'); (async () => { const m = new SourceTextModule('export const a = 1; export var b = 2'); await m.link(() => 0); - m.instantiate(); assert.strictEqual( inspect(m.namespace), '[Module] { a: <uninitialized>, b: undefined }'); diff --git a/test/parallel/test-util-types.js b/test/parallel/test-util-types.js index 69ebaaba12..6a9bad0169 100644 --- a/test/parallel/test-util-types.js +++ b/test/parallel/test-util-types.js @@ -281,7 +281,6 @@ for (const [ value, _method ] of [ (async () => { const m = new vm.SourceTextModule(''); await m.link(() => 0); - m.instantiate(); await m.evaluate(); assert.ok(types.isModuleNamespaceObject(m.namespace)); })(); diff --git a/test/parallel/test-vm-module-basic.js b/test/parallel/test-vm-module-basic.js index 76a8e8a3d2..cbe5e29d3c 100644 --- a/test/parallel/test-vm-module-basic.js +++ b/test/parallel/test-vm-module-basic.js @@ -17,10 +17,9 @@ const util = require('util'); 'baz = foo; typeofProcess = typeof process; typeof Object;', { context } ); - assert.strictEqual(m.status, 'uninstantiated'); + assert.strictEqual(m.status, 'unlinked'); await m.link(common.mustNotCall()); - m.instantiate(); - assert.strictEqual(m.status, 'instantiated'); + assert.strictEqual(m.status, 'linked'); const result = await m.evaluate(); assert.strictEqual(m.status, 'evaluated'); assert.strictEqual(Object.getPrototypeOf(result), null); @@ -37,7 +36,6 @@ const util = require('util'); 'global.vmResult = "foo"; Object.prototype.toString.call(process);' ); await m.link(common.mustNotCall()); - m.instantiate(); const { result } = await m.evaluate(); assert.strictEqual(global.vmResult, 'foo'); assert.strictEqual(result, '[object process]'); @@ -47,22 +45,21 @@ const util = require('util'); (async () => { const m = new SourceTextModule('while (true) {}'); await m.link(common.mustNotCall()); - m.instantiate(); await m.evaluate({ timeout: 500 }) .then(() => assert(false), () => {}); })(); -// Check the generated url for each module +// Check the generated identifier for each module (async () => { const context1 = createContext({ }); const context2 = createContext({ }); const m1 = new SourceTextModule('1', { context: context1 }); - assert.strictEqual(m1.url, 'vm:module(0)'); + assert.strictEqual(m1.identifier, 'vm:module(0)'); const m2 = new SourceTextModule('2', { context: context1 }); - assert.strictEqual(m2.url, 'vm:module(1)'); + assert.strictEqual(m2.identifier, 'vm:module(1)'); const m3 = new SourceTextModule('3', { context: context2 }); - assert.strictEqual(m3.url, 'vm:module(0)'); + assert.strictEqual(m3.identifier, 'vm:module(0)'); })(); // Check inspection of the instance @@ -72,13 +69,19 @@ const util = require('util'); assert.strictEqual( util.inspect(m), - "SourceTextModule {\n status: 'uninstantiated',\n linkingStatus:" + - " 'unlinked',\n url: 'vm:module(0)',\n context: { foo: 'bar' }\n}" + `SourceTextModule { + status: 'unlinked', + identifier: 'vm:module(0)', + context: { foo: 'bar' } +}` ); assert.strictEqual( m[util.inspect.custom].call(Object.create(null)), - 'SourceTextModule {\n status: undefined,\n linkingStatus: undefined,' + - '\n url: undefined,\n context: undefined\n}' + `SourceTextModule { + status: undefined, + identifier: undefined, + context: undefined +}`, ); assert.strictEqual(util.inspect(m, { depth: -1 }), '[SourceTextModule]'); } diff --git a/test/parallel/test-vm-module-dynamic-import.js b/test/parallel/test-vm-module-dynamic-import.js index 600f455e93..897d9f27d7 100644 --- a/test/parallel/test-vm-module-dynamic-import.js +++ b/test/parallel/test-vm-module-dynamic-import.js @@ -10,7 +10,6 @@ const { Script, SourceTextModule, createContext } = require('vm'); async function testNoCallback() { const m = new SourceTextModule('import("foo")', { context: createContext() }); await m.link(common.mustNotCall()); - m.instantiate(); const { result } = await m.evaluate(); let threw = false; try { @@ -25,7 +24,6 @@ async function testNoCallback() { async function test() { const foo = new SourceTextModule('export const a = 1;'); await foo.link(common.mustNotCall()); - foo.instantiate(); await foo.evaluate(); { @@ -50,7 +48,6 @@ async function test() { }), }); await m.link(common.mustNotCall()); - m.instantiate(); const { result } = await m.evaluate(); assert.strictEqual(foo.namespace, await result); } @@ -63,7 +60,6 @@ async function testInvalid() { }), }); await m.link(common.mustNotCall()); - m.instantiate(); const { result } = await m.evaluate(); await result.catch(common.mustCall((e) => { assert.strictEqual(e.code, 'ERR_VM_MODULE_NOT_MODULE'); diff --git a/test/parallel/test-vm-module-dynamic-namespace.js b/test/parallel/test-vm-module-dynamic-namespace.js index bcd91daebd..987b21f2ec 100644 --- a/test/parallel/test-vm-module-dynamic-namespace.js +++ b/test/parallel/test-vm-module-dynamic-namespace.js @@ -1,35 +1,30 @@ 'use strict'; -// Flags: --experimental-vm-modules --expose-internals -// +// Flags: --experimental-vm-modules + const common = require('../common'); const assert = require('assert'); const { types } = require('util'); -const { SourceTextModule, wrapMap } = require('internal/vm/source_text_module'); - -const { importModuleDynamicallyCallback } = - require('internal/process/esm_loader'); +const { SourceTextModule } = require('vm'); async function getNamespace() { const m = new SourceTextModule(''); await m.link(() => 0); - m.instantiate(); await m.evaluate(); return m.namespace; } (async () => { const namespace = await getNamespace(); - const m = new SourceTextModule('export const A = "A";', { + const m = new SourceTextModule('export const A = "A"; import("");', { importModuleDynamically: common.mustCall((specifier, wrap) => { return namespace; }) }); await m.link(() => 0); - m.instantiate(); - await m.evaluate(); - const ns = await importModuleDynamicallyCallback(wrapMap.get(m)); + const { result } = await m.evaluate(); + const ns = await result; assert.ok(types.isModuleNamespaceObject(ns)); })().then(common.mustCall()); diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js index a92863b125..e76bbbb0e1 100644 --- a/test/parallel/test-vm-module-errors.js +++ b/test/parallel/test-vm-module-errors.js @@ -23,7 +23,7 @@ async function checkArgType() { }); for (const invalidOptions of [ - 0, 1, null, true, 'str', () => {}, { url: 0 }, Symbol.iterator, + 0, 1, null, true, 'str', () => {}, { identifier: 0 }, Symbol.iterator, { context: null }, { context: 'hucairz' }, { context: {} } ]) { common.expectsError(() => { @@ -52,7 +52,7 @@ async function checkModuleState() { await assert.rejects(async () => { const m = new SourceTextModule(''); await m.link(common.mustNotCall()); - assert.strictEqual(m.linkingStatus, 'linked'); + assert.strictEqual(m.status, 'linked'); await m.link(common.mustNotCall()); }, { code: 'ERR_VM_MODULE_ALREADY_LINKED' @@ -61,54 +61,18 @@ async function checkModuleState() { await assert.rejects(async () => { const m = new SourceTextModule(''); m.link(common.mustNotCall()); - assert.strictEqual(m.linkingStatus, 'linking'); + assert.strictEqual(m.status, 'linking'); await m.link(common.mustNotCall()); }, { code: 'ERR_VM_MODULE_ALREADY_LINKED' }); - common.expectsError(() => { - const m = new SourceTextModule(''); - m.instantiate(); - }, { - code: 'ERR_VM_MODULE_NOT_LINKED' - }); - - await assert.rejects(async () => { - const m = new SourceTextModule('import "foo";'); - try { - await m.link(common.mustCall(() => ({}))); - } catch { - assert.strictEqual(m.linkingStatus, 'errored'); - m.instantiate(); - } - }, { - code: 'ERR_VM_MODULE_NOT_LINKED' - }); - - { - const m = new SourceTextModule('import "foo";'); - await m.link(common.mustCall(async (specifier, module) => { - assert.strictEqual(module, m); - assert.strictEqual(specifier, 'foo'); - assert.strictEqual(m.linkingStatus, 'linking'); - common.expectsError(() => { - m.instantiate(); - }, { - code: 'ERR_VM_MODULE_NOT_LINKED' - }); - return new SourceTextModule(''); - })); - m.instantiate(); - await m.evaluate(); - } - await assert.rejects(async () => { const m = new SourceTextModule(''); await m.evaluate(); }, { code: 'ERR_VM_MODULE_STATUS', - message: 'Module status must be one of instantiated, evaluated, and errored' + message: 'Module status must be one of linked, evaluated, or errored' }); await assert.rejects(async () => { @@ -120,14 +84,6 @@ async function checkModuleState() { 'Received type boolean' }); - await assert.rejects(async () => { - const m = await createEmptyLinkedModule(); - await m.evaluate(); - }, { - code: 'ERR_VM_MODULE_STATUS', - message: 'Module status must be one of instantiated, evaluated, and errored' - }); - common.expectsError(() => { const m = new SourceTextModule(''); m.error; @@ -138,7 +94,6 @@ async function checkModuleState() { await assert.rejects(async () => { const m = await createEmptyLinkedModule(); - m.instantiate(); await m.evaluate(); m.error; }, { @@ -151,15 +106,7 @@ async function checkModuleState() { m.namespace; }, { code: 'ERR_VM_MODULE_STATUS', - message: 'Module status must not be uninstantiated or instantiating' - }); - - await assert.rejects(async () => { - const m = await createEmptyLinkedModule(); - m.namespace; - }, { - code: 'ERR_VM_MODULE_STATUS', - message: 'Module status must not be uninstantiated or instantiating' + message: 'Module status must not be unlinked or linking' }); } @@ -170,7 +117,7 @@ async function checkLinking() { try { await m.link(common.mustCall(() => ({}))); } catch (err) { - assert.strictEqual(m.linkingStatus, 'errored'); + assert.strictEqual(m.status, 'errored'); throw err; } }, { @@ -185,7 +132,7 @@ async function checkLinking() { try { await bar.link(common.mustCall(() => foo)); } catch (err) { - assert.strictEqual(bar.linkingStatus, 'errored'); + assert.strictEqual(bar.status, 'errored'); throw err; } }, { @@ -199,7 +146,7 @@ async function checkLinking() { } catch { // ignored } finally { - assert.strictEqual(erroredModule.linkingStatus, 'errored'); + assert.strictEqual(erroredModule.status, 'errored'); } const rootModule = new SourceTextModule('import "errored";'); @@ -224,19 +171,17 @@ common.expectsError(() => { async function checkExecution() { await (async () => { const m = new SourceTextModule('import { nonexistent } from "module";'); - await m.link(common.mustCall(() => new SourceTextModule(''))); // There is no code for this exception since it is thrown by the JavaScript // engine. - assert.throws(() => { - m.instantiate(); + await assert.rejects(() => { + return m.link(common.mustCall(() => new SourceTextModule(''))); }, SyntaxError); })(); await (async () => { const m = new SourceTextModule('throw new Error();'); await m.link(common.mustNotCall()); - m.instantiate(); const evaluatePromise = m.evaluate(); await evaluatePromise.catch(() => {}); assert.strictEqual(m.status, 'errored'); diff --git a/test/parallel/test-vm-module-import-meta.js b/test/parallel/test-vm-module-import-meta.js index 4886464f34..1db4467b08 100644 --- a/test/parallel/test-vm-module-import-meta.js +++ b/test/parallel/test-vm-module-import-meta.js @@ -14,7 +14,6 @@ async function testBasic() { }) }); await m.link(common.mustNotCall()); - m.instantiate(); const { result } = await m.evaluate(); assert.strictEqual(typeof result, 'object'); assert.strictEqual(Object.getPrototypeOf(result), null); diff --git a/test/parallel/test-vm-module-link.js b/test/parallel/test-vm-module-link.js index 20518c4054..9678d13737 100644 --- a/test/parallel/test-vm-module-link.js +++ b/test/parallel/test-vm-module-link.js @@ -23,8 +23,6 @@ async function simple() { return foo; })); - bar.instantiate(); - assert.strictEqual((await bar.evaluate()).result, 5); } @@ -49,7 +47,6 @@ async function depth() { const baz = await getProxy('bar', bar); const barz = await getProxy('baz', baz); - barz.instantiate(); await barz.evaluate(); assert.strictEqual(barz.namespace.default, 5); @@ -67,20 +64,19 @@ async function circular() { return foo; } `); - await foo.link(common.mustCall(async (fooSpecifier, fooModule) => { - assert.strictEqual(fooModule, foo); - assert.strictEqual(fooSpecifier, 'bar'); - await bar.link(common.mustCall((barSpecifier, barModule) => { - assert.strictEqual(barModule, bar); - assert.strictEqual(barSpecifier, 'foo'); - assert.strictEqual(foo.linkingStatus, 'linking'); - return foo; - })); - assert.strictEqual(bar.linkingStatus, 'linked'); - return bar; - })); + await foo.link(common.mustCall(async (specifier, module) => { + if (specifier === 'bar') { + assert.strictEqual(module, foo); + return bar; + } + assert.strictEqual(specifier, 'foo'); + assert.strictEqual(module, bar); + assert.strictEqual(foo.status, 'linking'); + return foo; + }, 2)); + + assert.strictEqual(bar.status, 'linked'); - foo.instantiate(); await foo.evaluate(); assert.strictEqual(foo.namespace.default, 42); } @@ -109,19 +105,20 @@ async function circular2() { ` }; const moduleMap = new Map(); - const rootModule = new SourceTextModule(sourceMap.root, { url: 'vm:root' }); + const rootModule = new SourceTextModule(sourceMap.root, { + identifier: 'vm:root', + }); async function link(specifier, referencingModule) { if (moduleMap.has(specifier)) { return moduleMap.get(specifier); } const mod = new SourceTextModule(sourceMap[specifier], { - url: new URL(specifier, 'file:///').href, + identifier: new URL(specifier, 'file:///').href, }); moduleMap.set(specifier, mod); return mod; } await rootModule.link(link); - rootModule.instantiate(); await rootModule.evaluate(); } diff --git a/test/parallel/test-vm-module-reevaluate.js b/test/parallel/test-vm-module-reevaluate.js index c3914f362f..4767541dd2 100644 --- a/test/parallel/test-vm-module-reevaluate.js +++ b/test/parallel/test-vm-module-reevaluate.js @@ -14,7 +14,6 @@ const finished = common.mustCall(); { const m = new SourceTextModule('1'); await m.link(common.mustNotCall()); - m.instantiate(); assert.strictEqual((await m.evaluate()).result, 1); assert.strictEqual((await m.evaluate()).result, undefined); assert.strictEqual((await m.evaluate()).result, undefined); @@ -23,7 +22,6 @@ const finished = common.mustCall(); { const m = new SourceTextModule('throw new Error()'); await m.link(common.mustNotCall()); - m.instantiate(); let threw = false; try { |