(original) (raw)
2.2.1 Module Semantics
2.2.1.1 Abstract Module Records
A Module Record encapsulates structural information about the imports and exports of a single module. This information is used to link the imports and exports of sets of connected modules. A Module Record includes four fields that are only used when evaluating a module.
For specification purposes Module Record values are values of the Record specification type and can be thought of as existing in a simple object-oriented hierarchy where Module Record is an abstract class with both abstract and concrete subclasses. This specification defines the abstract subclass named Cyclic Module Record and its concrete subclass named Source Text Module Record. Other specifications and implementations may define additional Module Record subclasses corresponding to alternative module definition facilities that they defined.
Module Record defines the fields listed in Table 1. All Module Definition subclasses include at least those fields. Module Record also defines the abstract method list in Table 2. All Module definition subclasses must provide concrete implementations of these abstract methods.
Table 1: Module Record Fields
Field Name | Value Type | Meaning |
---|---|---|
[[Realm]] | Realm Record | undefined | The Realm within which this module was created. undefined if not yet assigned. |
[[Environment]] | module Environment Record | undefined | The Environment Record containing the top level bindings for this module. This field is set when the module is linked. |
[[Namespace]] | Object | undefined | The Module Namespace Object (26.3) if one has been created for this module. Otherwise undefined. |
[[HostDefined]] | Any, default value is undefined. | Field reserved for use by host environments that need to associate additional information with a module. |
Table 2: Abstract Methods of Module Records
Method | Purpose |
---|---|
GetExportedNames([exportStarSet]) | Return a list of all names that are either directly or indirectly exported from this module. |
ResolveExport(exportName [, resolveSet]) | Return the binding of a name exported by this module. Bindings are represented by a ResolvedBinding Record, of the form { [[Module]]: Module Record, [[BindingName]]: String }. If the export is a Module Namespace Object without a direct binding in any module, [[BindingName]] will be set to "*namespace*". Return null if the name cannot be resolved, or "ambiguous" if multiple bindings were found. Each time this operation is called with a specific exportName, resolveSet pair as arguments it must return the same result if it completes normally. |
Link() | Prepare the module for evaluation by transitively resolving all module dependencies and creating a module Environment Record. |
Evaluate() | If this module has already been evaluated successfully, return undefined; if it has already been evaluated unsuccessfully, throw the exception that was produced. Otherwise, transitively evaluate all module dependencies of this module and then evaluate this module. Link must have completed successfully prior to invoking this method. |
2.2.1.2 Synthetic Module Records
A Synthetic Module Record is used to represent information about a module that is defined by specifications. Its exports are derived from a pair of lists, of string keys and of ECMAScript values. The set of exported names is static, and determined at creation time (as an argument to CreateSyntheticModule), while the set of exported values can be changed over time using SetModuleExport. It has no imports or dependencies.
Note
A Synthetic Module Record could be used for defining a variety of module types: for example, built-in modules, or JSON modules, or CSS modules.
In addition to the fields defined in Table 1, Synthetic Module Records have the additional fields listed in Table 3. Each of these fields is initially set in CreateSyntheticModule.
Table 3: Additional Fields of Synthetic Module Records
Field Name | Value Type | Meaning |
---|---|---|
[[ExportNames]] | List of String | A List of all names that are exported. |
[[IsFrozen]] | Boolean | If true, the module cannot be modified. |
[[EvaluationSteps]] | An abstract operation | An abstract operation that will be performed upon evaluation of the module, taking the Synthetic Module Record as its sole argument. These will usually set up the exported values, by using SetModuleExport. They must not modify [[ExportNames]]. They may return an abrupt completion. |
2.2.1.2.1 CreateSyntheticModule ( exportNames, evaluationSteps, realm, hostDefined )
The abstract operation CreateSyntheticModule creates a Synthetic Module Record based upon the given exported names and evaluation steps. It performs the following steps:
- Return Synthetic Module Record { [[Realm]]: realm, [[Environment]]: undefined, [[Namespace]]: undefined, [[HostDefined]]: hostDefined, [[ExportNames]]: exportNames, [[IsFrozen]]: false, [[EvaluationSteps]]: evaluationSteps }. Editor's Note
It seems we could set up the environment either here or in Instantiate(). I've chosen to do so in Instantiate() for symmetry with Source Text Module Records, but I don't think there's any actual requirement in that regard.
2.2.1.2.2 Concrete Methods
The following are the concrete methods for Synthetic Module Record that implement the corresponding Module Record abstract methods.
Editor's Note
I find having this wrapping sub-clause cleaner and suggest we do the same for Source Text Module Records in the main spec.
2.2.1.2.2.1 Freeze ( )
The Freeze concrete method of a Synthetic Module Record implements the corresponding Module Record abstract method.
It performs the following steps:
- Let module be this Synthetic Module Record.
- Set module.[[IsFrozen]] to true.
2.2.1.2.2.2 GetExportedNames ( exportStarSet )
The GetExportedNames concrete method of a Synthetic Module Record implements the corresponding Module Record abstract method.
It performs the following steps:
- Let module be this Synthetic Module Record.
- Return module.[[ExportNames]].
2.2.1.2.2.3 ResolveExport ( exportName, resolveSet )
The ResolveExport concrete method of a Synthetic Module Record implements the corresponding Module Record abstract method.
It performs the following steps:
- Let module be this Synthetic Module Record.
- If module.[[ExportNames]] does not contain exportName, return null.
- Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: exportName }.
2.2.1.2.2.4 SetModuleExport ( exportName, exportValue )
The abstract operation SetModuleExport can be used to set or change the exported value for a pre-established export of a Synthetic Module Record. It performs the following steps:
- Let module be this Synthetic Module Record.
- Let envRec be module.[[Environment]]'s EnvironmentRecord.
- Perform envRec.SetMutableBinding(exportName, exportValue, true).
2.2.1.2.2.5 Instantiate ( )
The Instantiate concrete method of a Synthetic Module Record implements the corresponding Module Record abstract method.
It performs the following steps:
- Let module be this Synthetic Module Record.
- Let realm be module.[[Realm]].
- Assert: realm is not undefined.
- Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
- Set module.[[Environment]] to env.
- Let envRec be env's EnvironmentRecord.
- For each element exportName in module.[[ExportNames]], do
- Perform ! envRec.CreateMutableBinding(exportName, false).
- Perform ! envRec.InitializeBinding(exportName, undefined).
- Return undefined.
2.2.1.2.2.6 Evaluate ( )
The Evaluate concrete method of a Synthetic Module Record implements the corresponding Module Record abstract method.
It performs the following steps:
- Let module be this Synthetic Module Record.
- Let moduleCxt be a new ECMAScript code execution context.
- Set the Function of moduleCxt to null.
- Assert: module.[[Realm]] is not undefined.
- Set the Realm of moduleCxt to module.[[Realm]].
- Set the ScriptOrModule of moduleCxt to module.
- Set the VariableEnvironment of moduleCxt to module.[[Environment]].
- Set the LexicalEnvironment of moduleCxt to module.[[Environment]].
- Suspend the currently running execution context.
- Push moduleCxt on to the execution context stack; moduleCxt is now the running execution context.
- Let result be the result of performing ? module.[[EvaluationSteps]](module).
- Suspend moduleCxt and remove it from the execution context stack.
- Resume the context that is now on the top of the execution context stack as the running execution context.
- Return Completion(result).
2.2.1.2.3 Example uses of Synthetic Module Records
This non-normative section shows how one could define a few different Synthetic Module Records.
Editor's Note
Although these seem somewhat verbose, that is largely because ECMAScript specification text doesn't generally allow "closures" where you would define an algorithm or set of steps inline. Host environment specification styles are different, and hosts could write their synthetic module creation more compactly and concisely by inlining instead of using separately-defined evaluation steps.
2.2.1.2.3.1 A module wrapping a single object
The following algorithm, given an object object, creates a Synthetic Module Record which default-exports the object. This might be useful, for example, for JSON or CSS modules.
- Return CreateSyntheticModule(«
"default"
», ExampleObjectWrapperModuleEvaluation, the current Realm Record, object).
2.2.1.2.3.1.1 ExampleObjectWrapperModuleEvaluation ( module )
- Perform module.SetModuleExport(
"default"
, module.[[HostDefined]]).
2.2.1.2.3.2 A "builtin module" for addition
The following algorithm creates a Synthetic Module Record with a single export, "add"
, which provides a built-in function object that adds its two arguments.
- Return CreateSyntheticModule(«
"add"
», ExampleAdderModuleEvaluation, the current Realm Record, undefined).
2.2.1.2.3.2.1 ExampleAdderModuleEvaluation ( module )
- Let adderSteps be the algorithm steps defined in Example Adder Functions.
- Let adderFunction be CreateBuiltinFunction(adderSteps).
- Perform moduleSetModuleExport(
"add"
, adderFunction).
2.2.1.2.3.2.2 Example Adder Functions
An example adder function is an anonymous built-in function. When called with arguments arg1 and arg2, the following steps are taken:
2.2.1.3 Runtime Semantics: HostHasBuiltinModule ( referencingScriptOrModule, specifier )
HostHasBuiltinModule is a host-defined abstract operation that provides a means check for a module registered with ModuleSpecifier String, specifier, occurring within the context of the script or module represented by the Script Record or Module Record referencingScriptOrModule. referencingScriptOrModule may also be null, if the resolution is being performed in the context of an import() expression, and there is no active script or module at that time.
The implementation of HostResolveBuiltinModule must conform to the following requirements:
- The normal return value is a Boolean, true if a mtching module can be found and false otherwise.
Multiple different referencingScriptOrModule, specifier pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to specifier as part of the mapping process. A typical normalization process would include actions such as alphabetic case folding and expansion of relative and abbreviated path specifiers.
2.2.1.4 Runtime Semantics: HostCreateBuiltinModule ( referencingScriptOrModule, specifier, module )
HostCreateBuiltinModule is a host-defined abstract operation that adds a module defined by the Synthetic Module Record, module, with the key ModuleSpecifier String, specifier, within the context of the script or module represented by the Script Record or Module Record referencingScriptOrModule.
The implementation of HostCreateBuiltinModule must conform to the following requirements:
- The value of referencingScriptOrModule must not be null.
- If a Module Record corresponding to the pair referencingScriptOrModule, specifier already exists, it is replaced.
2.2.1.5 Runtime Semantics: HostResolveBuiltinModule ( referencingScriptOrModule, specifier )
HostResolveBuiltinModule is a host-defined abstract operation that provides the Synthetic Module Record instance that corresponds to the ModuleSpecifier String, specifier, occurring within the context of the script or module represented by the Script Record or Module Record referencingScriptOrModule. referencingScriptOrModule may also be null, if the resolution is being performed in the context of an import() expression, and there is no active script or module at that time.
Note
It is possible for a host to implement this abstract operation with HostResolveImportedModule.
The implementation of HostResolveBuiltinModule must conform to the following requirements:
- The normal return value must be an instance of a Synthetic Module Record.
- If a Module Record corresponding to the pair referencingScriptOrModule, specifier does not exist or cannot be created, an exception must be thrown.
- Each time this operation is called with a specific referencingScriptOrModule, specifier pair as arguments it must return the same Module Record instance if it completes normally.
Multiple different referencingScriptOrModule, specifier pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to specifier as part of the mapping process. A typical normalization process would include actions such as alphabetic case folding and expansion of relative and abbreviated path specifiers.
2.2.1.6 Runtime Semantics: HostResolveImportedModule ( referencingScriptOrModule, specifier )
HostResolveImportedModule is a host-defined abstract operation that provides the concrete Module Record subclass instance that corresponds to the ModuleSpecifier String, specifier, occurring within the context of the script or module represented by the Script Record or Module Record referencingScriptOrModule. referencingScriptOrModule may also be null, if the resolution is being performed in the context of an import() expression, and there is no active script or module at that time.
Note
An example of when referencingScriptOrModule can be null is in a web browser host. There, if a user clicks on a control given by
<button type="button" onclick="import('./foo.mjs')">Click me</button>
there will be no active script or module at the time the import() expression runs. More generally, this can happen in any situation where the host pushes execution contexts with null ScriptOrModule components onto the execution context stack.
The implementation of HostResolveImportedModule must conform to the following requirements:
- The normal return value must be an instance of a concrete subclass of Module Record.
- If a Module Record corresponding to the pair referencingScriptOrModule, specifier does not exist or cannot be created, an exception must be thrown.
- Each time this operation is called with a specific referencingScriptOrModule, specifier pair as arguments it must return the same Module Record instance if it completes normally.
Multiple different referencingScriptOrModule, specifier pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to specifier as part of the mapping process. A typical normalization process would include actions such as alphabetic case folding and expansion of relative and abbreviated path specifiers.
2.2.1.7 Runtime Semantics: HostImportModuleDynamically ( referencingScriptOrModule, specifier, promiseCapability )
HostImportModuleDynamically is a host-defined abstract operation that performs any necessary setup work in order to make available the module corresponding to the ModuleSpecifier String, specifier, occurring within the context of the script or module represented by the Script Record or Module Record referencingScriptOrModule. (referencingScriptOrModule may also be null, if there is no active script or module when the import() expression occurs.) It then performs FinishDynamicImport to finish the dynamic import process.
The implementation of HostImportModuleDynamically must conform to the following requirements:
- The abstract operation must always complete normally with undefined. Success or failure must instead be signaled as discussed below.
- The host environment must conform to one of the two following sets of requirements:
Success path- At some future time, the host environment must perform FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, NormalCompletion(undefined)).
- Any subsequent call to HostResolveImportedModule after FinishDynamicImport has completed, given the arguments referencingScriptOrModule and specifier, must complete normally.
- The completion value of any subsequent call to HostResolveImportedModule after FinishDynamicImport has completed, given the arguments referencingScriptOrModule and specifier, must be a module which has already been evaluated, i.e. whose Evaluate concrete method has already been called and returned a normal completion.
Failure path - At some future time, the host environment must perform FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, an abrupt completion), with the abrupt completion representing the cause of failure.
- If the host environment takes the success path once for a given referencingScriptOrModule, specifier pair, it must always do so for subsequent calls.
- The operation must not call promiseCapability.[[Resolve]] or promiseCapability.[[Reject]], but instead must treat promiseCapability as an opaque identifying value to be passed through to FinishDynamicImport.
The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to allow HostResolveImportedModule to synchronously retrieve the appropriate Module Record, and then calling its Evaluate concrete method. This might require performing similar normalization as HostResolveImportedModule does.
2.2.1.8 Runtime Semantics: FinishDynamicImport ( referencingScriptOrModule, specifier, promiseCapability, completion )
The abstract operation FinishDynamicImport takes arguments referencingScriptOrModule, specifier, promiseCapability (a PromiseCapability Record), and completion. FinishDynamicImport completes the process of a dynamic import originally started by an import() call, resolving or rejecting the promise returned by that call as appropriate according to completion. It is performed by host environments as part of HostImportModuleDynamically. It performs the following steps when called:
- If completion is an abrupt completion, then perform ! Call(promiseCapability.[[Reject]], undefined, « completion.[[Value]] »).
- Else,
- Assert: completion is a normal completion and completion.[[Value]] is undefined.
- Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
- Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
- Let namespace be GetModuleNamespace(moduleRecord).
- If namespace is an abrupt completion, perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
- Else, perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
2.2.1.9 Runtime Semantics: GetModuleNamespace ( module )
The abstract operation GetModuleNamespace takes argument module. It retrieves the Module Namespace Object representing module's exports, lazily creating it the first time it was requested, and storing it in module.[[Namespace]] for future retrieval. It performs the following steps when called:
- Assert: module is an instance of a concrete subclass of Module Record.
- Assert: If module is a Cyclic Module Record, then module.[[Status]] is not unlinked.
- Let namespace be module.[[Namespace]].
- If namespace is undefined, then
- Let exportedNames be ? module.GetExportedNames().
- Let unambiguousNames be a new empty List.
- For each element name of exportedNames, do
- Let resolution be ? module.ResolveExport(name).
- If resolution is a ResolvedBinding Record, append name to unambiguousNames.
- Set namespace to ModuleNamespaceCreate(module, unambiguousNames).
- Return namespace. Note
The only way GetModuleNamespace can throw is via one of the triggered HostResolveImportedModule calls. Unresolvable names are simply excluded from the namespace at this point. They will lead to a real linking error later unless they are all ambiguous star exports that are not explicitly requested anywhere.
2.2.1.10 Runtime Semantics: Evaluation
Module:[empty]
- Return NormalCompletion(undefined). ModuleBody:ModuleItemList
- Let result be the result of evaluating ModuleItemList.
- If result.[[Type]] is normal and result.[[Value]] is empty, then
- Return NormalCompletion(undefined).
- Return Completion(result). ModuleItemList:ModuleItemListModuleItem
- Let sl be the result of evaluating ModuleItemList.
- ReturnIfAbrupt(sl).
- Let s be the result of evaluating ModuleItem.
- Return Completion(UpdateEmpty(s, sl)). Note
The value of a ModuleItemList is the value of the last value-producing item in the ModuleItemList.
- Return NormalCompletion(empty).