Stage -1 Draft / August 31, 2022

Modules Host Hooks Refactor

9 Executable Code and Execution Contexts

9.3 Realms

Before it is evaluated, all ECMAScript code must be associated with a realm. Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment, all of the ECMAScript code that is loaded within the scope of that global environment, and other associated state and resources.

A realm is represented in this specification as a Realm Record with the fields specified in Table 1:

Table 1: Realm Record Fields
Field Name Value Meaning
[[Intrinsics]]
[[GlobalObject]]
[[GlobalEnv]]
[[TemplateMap]]
[[LoadedModules]] a List of Records { [[Specifier]]: a String, [[Module]]: a Module Record } A map from the specifier strings imported by this realm to the resolved Module Record. The list does not contain two different Records with the same [[Specifier]]. Note
As mentioned in HostLoadImportedModule, [[LoadedModules]] in Realm Records is only used when running an import() expression in a context where there is no active script or module.
[[HostDefined]]

13 ECMAScript Language: Expressions

13.3 Left-Hand-Side Expressions

13.3.10 Import Calls

13.3.10.1 Runtime Semantics: Evaluation

ImportCall : import ( AssignmentExpression )
  1. Let referencingScriptOrModulereferrer be GetActiveScriptOrModule().
  2. If referrer is null, set referrer to the current Realm Record.
  3. Let argRef be the result of evaluating AssignmentExpression.
  4. Let specifier be ? GetValue(argRef).
  5. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  6. Let specifierString be Completion(ToString(specifier)).
  7. IfAbruptRejectPromise(specifierString, promiseCapability).
  8. Perform HostImportModuleDynamically(referencingScriptOrModule, specifierString, promiseCapability).
  9. Let state be a new ModuleLoadState Record { [[Action]]: dynamic-import, [[PromiseCapability]]: promiseCapability }.
  10. Perform HostLoadImportedModule(referrer, specifier, undefined, state).
  11. NOTE: HostLoadImportedModule will call ContinueDynamicImport.
  12. Return promiseCapability.[[Promise]].

13.3.10.1.1 ContinueDynamicImport ( state, module )

The abstract operation ContinueDynamicImport takes arguments state (a ModuleLoadState Record whose [[Action]] is dynamic-import) and module (either a normal completion containing a Module Record or a throw completion) and returns unused. ContinueDynamicImport 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 innerPromise's resolution. It performs the following steps when called:

  1. Let promiseCapability be state.[[PromiseCapability]].
  2. If module is an abrupt completion, then
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « module.[[Value]] »).
    2. Return unused.
  3. Else,
    1. Set module to module.[[Value]].
  4. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures promiseCapability and performs the following steps when called:
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « reason »).
    2. Return unused.
  5. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
  6. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, and onRejected and performs the following steps when called:
    1. Let link be Completion(module.Link()).
    2. If link is an abrupt completion, then
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).
      2. Return unused.
    3. Let evaluatePromise be module.Evaluate().
    4. Let fulfilledClosure be a new Abstract Closure with no parameters that captures module and promiseCapability and performs the following steps when called:
      1. Let namespace be Completion(GetModuleNamespace(module)).
      2. If namespace is an abrupt completion, then
        1. Perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
      3. Else,
        1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
      4. Return unused.
    5. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, "", 0, « »).
    6. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected).
  7. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, "", 0, « »).
  8. Let loadPromise be module.LoadRequestedModules().
  9. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected).
  10. Return unused.

16 ECMAScript Language: Scripts and Modules

16.1 Scripts

16.1.4 Script Records

A Script Record encapsulates information about a script being evaluated. Each script record contains the fields listed in Table 2.

Table 2: Script Record Fields
Field Name Value Type Meaning
[[Realm]] a Realm Record or undefined
[[ECMAScriptCode]] a Parse Node
[[LoadedModules]] a List of Records { [[Specifier]]: a String, [[Module]]: a Module Record } A map from the specifier strings imported by this script to the resolved Module Record. The list does not contain two different Records with the same [[Specifier]].
[[HostDefined]] anything (default value is empty)

16.1.5 ParseScript ( sourceText, realm, hostDefined )

The abstract operation ParseScript takes arguments sourceText (ECMAScript source text), realm, and hostDefined and returns a Script Record or a non-empty List of SyntaxError objects. It creates a Script Record based upon the result of parsing sourceText as a Script. It performs the following steps when called:

  1. Let script be ParseText(sourceText, Script).
  2. If script is a List of errors, return script.
  3. Return Script Record { [[Realm]]: realm, [[ECMAScriptCode]]: script, [[LoadedModules]]: a new empty List, [[HostDefined]]: hostDefined }.

16.2 Modules

16.2.1 Module Semantics

16.2.1.4 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 44. All Module Definition subclasses include at least those fields. Module Record also defines the abstract method list in Table 3. All Module definition subclasses must provide concrete implementations of these abstract methods.

Table 3: Abstract Methods of Module Records
Method Purpose
GetExportedNames([exportStarSet])
ResolveExport(exportName [, resolveSet])
LoadRequestedModules()

Prepares the module for linking by recursively loading all its dependencies, and returns a promise.

Link()

Prepare the module for evaluation by transitively resolving all module dependencies and creating a module Environment Record.

LoadRequestedModules must have completed successfully prior to invoking this method.

Evaluate()

Returns a promise for the evaluation of this module and its dependencies, resolving on successful evaluation or if it has already been evaluated successfully, and rejecting for an evaluation error or if it has already been evaluated unsuccessfully. If the promise is rejected, hosts are expected to handle the promise rejection and rethrow the evaluation error.

Link must have completed successfully prior to invoking this method.

16.2.1.5 Cyclic Module Records

A Cyclic Module Record is used to represent information about a module that can participate in dependency cycles with other modules that are subclasses of the Cyclic Module Record type. Module Records that are not subclasses of the Cyclic Module Record type must not participate in dependency cycles with Source Text Module Records.

In addition to the fields defined in Table 44 Cyclic Module Records have the additional fields listed in Table 4

Table 4: Additional Fields of Cyclic Module Records
Field Name Value Type Meaning
[[Status]] new, unlinked, linking, linked, evaluating, evaluating-async, or evaluated Initially unlinkednew. Transitions to unlinked, linking, linked, evaluating, possibly evaluating-async, evaluated (in that order) as the module progresses throughout its lifecycle. evaluating-async indicates this module is queued to execute on completion of its asynchronous dependencies or it is a module whose [[HasTLA]] field is true that has been executed and is pending top-level completion.
[[EvaluationError]] a throw completion or empty
[[DFSIndex]] an integer or empty
[[DFSAncestorIndex]] an integer or empty
[[RequestedModules]] a List of Strings A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is source text occurrence ordered.
[[LoadedModules]] a List of Record { [[Specifier]]: a String, [[Module]]: a Module Record } A map from the specifier strings used by the module represented by this record to request the importation of a module to the resolved Module Record. The list does not contain two different Records with the same [[Specifier]].
[[CycleRoot]] a Cyclic Module Record or empty
[[HasTLA]] a Boolean
[[AsyncEvaluation]] a Boolean
[[TopLevelCapability]] a PromiseCapability Record or empty
[[AsyncParentModules]] a List of Cyclic Module Records
[[PendingAsyncDependencies]] an integer or empty

In addition to the methods defined in Table 3 Cyclic Module Records have the additional methods listed in Table 5

Table 5: Additional Abstract Methods of Cyclic Module Records
Method Purpose
InitializeEnvironment()
ExecuteModule( [ promiseCapability ] )

A ModuleLoadState Record is a Record that contains information about the loading process of a module graph. It's used to continue loading after a call to HostLoadImportedModule. Each ModuleLoadState Record has the fields defined in Table 6:

Table 6: ModuleLoadState Record Fields
Field Name Value Type Meaning
[[Action]] graph-loading or dynamic-import The action that caused the call to HostLoadImportedModule. It is graph-loading when loading the dependencies of a module; it is dynamic-import when loading a module that was requested by an import() call.
[[PromiseCapability]] a PromiseCapability Record The promise to resolve when the loading process finishes.
[[IsLoading]] a boolean This field is only used if [[Action]] is graph-loading. It is true if the loading process has not finished yet, neither successfully nor with an error.
[[PendingModules]] a non-negative integer This field is only used if [[Action]] is graph-loading. It tracks the number of pending HostResolveImportedModule calls.
[[Visited]] a List of Cyclic Module Records This field is only used if [[Action]] is graph-loading. It is a list of the Cyclic Module Records that have been already loaded by the current loading process, to avoid infinite loops with circular dependencies.
[[HostDefined]] anything (default value is undefined) It contains host-defined data to pass from the LoadRequestedModules caller to HostLoadImportedModule.

16.2.1.5.1 LoadRequestedModules ( [ hostDefined ] )

The LoadRequestedModules concrete method of a Cyclic Module Record module takes optional argument hostDefined and returns a Promise object. It populates the [[LoadedModules]] of all the Modue Records in the dependency graph of module (most of the work is done by the auxiliary function InnerModuleLoading). It takes an optional hostDefined parameter, that is passed to the HostLoadImportedModule hook. It performs the following steps when called:

  1. If hostDefined is not present, let hostDefined be undefined.
  2. Let pc be ! NewPromiseCapability(%Promise%).
  3. Let state be a new ModuleLoadState Record { [[Action]]: graph-loading, [[IsLoading]]: true, [[PendingModules]]: 1, [[Visited]]: a new empty List, [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }.
  4. Perform InnerModuleLoading(state, module).
  5. Return pc.[[Promise]].
Note
The hostDefined parameter can be used to pass additional information necessary to fetch the imported modules. It is used, for example, by HTML to set the correct fetch destination for <link rel="preload" as="..."> tags. Note that import() expressions never set the hostDefined parameter.

16.2.1.5.1.1 InnerModuleLoading ( state, module )

The abstract operation InnerModuleLoading takes arguments state (a ModuleLoadState Record whose [[Action]] is graph-loading) and module (a Module Record) and returns unused. It is used by LoadRequestedModules to recursively perform the actual loading process for module's dependency graph. It performs the following steps when called:

  1. Assert: state.[[IsLoading]] is true.
  2. If module is a Cyclic Module Record, module.[[Status]] is new, and state.[[Visited]] does not contain module, then
    1. Add module to state.[[Visited]].
    2. Let requestedModulesCount be the length of module.[[RequestedModules]].
    3. Set state.[[PendingModules]] to state.[[PendingModules]] + requestedModulesCount.
    4. For each String required of module.[[RequestedModules]], do
      1. If module.[[LoadedModules]] contains a Record record such that record.[[Specifier]] is required, then
        1. Perform ContinueModuleLoading(state, NormalCompletion(record.[[Module]])).
      2. Else,
        1. Perform HostLoadImportedModule(referrer, specifier, state.[[HostDefined]], state).
        2. NOTE: HostLoadImportedModule will call ContinueModuleLoading.
  3. Assert: state.[[PendingModules]] ≥ 1.
  4. Set state.[[PendingModules]] to state.[[PendingModules]] - 1.
  5. If state.[[PendingModules]] = 0, then
    1. Set state.[[IsLoading]] to false.
    2. For each Cyclic Module Record loaded in state.[[Visited]], do
      1. If loaded.[[Status]] is new, set loaded.[[Status]] to unlinked.
    3. Perform ! Call(state.[[PromiseCapability]].[[Resolve]], undefined, « undefined »).

16.2.1.5.1.2 ContinueModuleLoading ( state, result )

The abstract operation ContinueModuleLoading takes arguments state (a ModuleLoadState Record whose [[Action]] is graph-loading) and result (either a normal completion containing a Module Record or a throw completion) and returns unused. It is used to re-enter the loading process after a call to HostLoadImportedModule. It performs the following steps when called:

  1. If state.[[IsLoading]] is false, return.
  2. If result is a normal completion, then
    1. Perform InnerModuleLoading(state, result.[[Value]]).
  3. Else,
    1. Set state.[[IsLoading]] to false.
    2. Perform ! Call(state.[[PromiseCapability]].[[Reject]], undefined, « result.[[Value]] »).

16.2.1.5.2 Link ( )

The Link concrete method of a Cyclic Module Record module takes no arguments and returns either a normal completion containing unused or a throw completion. On success, Link transitions this module's [[Status]] from unlinked to linked. On failure, an exception is thrown and this module's [[Status]] remains unlinked. (Most of the work is done by the auxiliary function InnerModuleLinking.) It performs the following steps when called:

  1. Assert: module.[[Status]] is not linking or evaluatingunlinked, linked, evaluating-async, or evaluated.
  2. Let stack be a new empty List.
  3. Let result be Completion(InnerModuleLinking(module, stack, 0)).
  4. If result is an abrupt completion, then
    1. For each Cyclic Module Record m of stack, do
      1. Assert: m.[[Status]] is linking.
      2. Set m.[[Status]] to unlinked.
    2. Assert: module.[[Status]] is unlinked.
    3. Return ? result.
  5. Assert: module.[[Status]] is linked, evaluating-async, or evaluated.
  6. Assert: stack is empty.
  7. Return unused.

16.2.1.5.2.1 InnerModuleLinking ( module, stack, index )

The abstract operation InnerModuleLinking takes arguments module (a Module Record), stack, and index (a non-negative integer) and returns either a normal completion containing a non-negative integer or a throw completion. It is used by Link to perform the actual linking process for module, as well as recursively on all other modules in the dependency graph. The stack and index parameters, as well as a module's [[DFSIndex]] and [[DFSAncestorIndex]] fields, keep track of the depth-first search (DFS) traversal. In particular, [[DFSAncestorIndex]] is used to discover strongly connected components (SCCs), such that all modules in an SCC transition to linked together. It performs the following steps when called:

  1. If module is not a Cyclic Module Record, then
    1. Perform ? module.Link().
    2. Return index.
  2. If module.[[Status]] is linking, linked, evaluating-async, or evaluated, then
    1. Return index.
  3. Assert: module.[[Status]] is unlinked.
  4. Set module.[[Status]] to linking.
  5. Set module.[[DFSIndex]] to index.
  6. Set module.[[DFSAncestorIndex]] to index.
  7. Set index to index + 1.
  8. Append module to stack.
  9. For each String required of module.[[RequestedModules]], do
    1. Let requiredModule be HostResolveImportedModuleGetImportedModule(module, required).
    2. Assert: requiredModule is not empty, because LoadRequestedModules must have completed successfully prior to invoking this method.
    3. Set index to ? InnerModuleLinking(requiredModule, stack, index).
    4. If requiredModule is a Cyclic Module Record, then
      1. Assert: requiredModule.[[Status]] is either linking, linked, evaluating-async, or evaluated.
      2. Assert: requiredModule.[[Status]] is linking if and only if requiredModule is in stack.
      3. If requiredModule.[[Status]] is linking, then
        1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
  10. Perform ? module.InitializeEnvironment().
  11. Assert: module occurs exactly once in stack.
  12. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]].
  13. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
    1. Let done be false.
    2. Repeat, while done is false,
      1. Let requiredModule be the last element in stack.
      2. Remove the last element of stack.
      3. Assert: requiredModule is a Cyclic Module Record.
      4. Set requiredModule.[[Status]] to linked.
      5. If requiredModule and module are the same Module Record, set done to true.
  14. Return index.

16.2.1.5.3 Evaluate ( )

16.2.1.5.3.1 InnerModuleEvaluation ( module, stack, index )

The abstract operation InnerModuleEvaluation takes arguments module (a Module Record), stack, and index (a non-negative integer) and returns either a normal completion containing a non-negative integer or a throw completion. It is used by Evaluate to perform the actual evaluation process for module, as well as recursively on all other modules in the dependency graph. The stack and index parameters, as well as module's [[DFSIndex]] and [[DFSAncestorIndex]] fields, are used the same way as in InnerModuleLinking. It performs the following steps when called:

  1. If module is not a Cyclic Module Record, then
    1. Let promise be ! module.Evaluate().
    2. Assert: promise.[[PromiseState]] is not pending.
    3. If promise.[[PromiseState]] is rejected, then
      1. Return ThrowCompletion(promise.[[PromiseResult]]).
    4. Return index.
  2. If module.[[Status]] is evaluating-async or evaluated, then
    1. If module.[[EvaluationError]] is empty, return index.
    2. Otherwise, return ? module.[[EvaluationError]].
  3. If module.[[Status]] is evaluating, return index.
  4. Assert: module.[[Status]] is linked.
  5. Set module.[[Status]] to evaluating.
  6. Set module.[[DFSIndex]] to index.
  7. Set module.[[DFSAncestorIndex]] to index.
  8. Set module.[[PendingAsyncDependencies]] to 0.
  9. Set index to index + 1.
  10. Append module to stack.
  11. For each String required of module.[[RequestedModules]], do
    1. Let requiredModule be HostResolveImportedModuleGetImportedModule(module, required).
    2. NOTE: Link must be completed successfully prior to invoking this method, so every requested module is guaranteed to resolve successfully.
    3. Assert: requiredModule is not empty, because LoadRequestedModules must have completed successfully prior to invoking this method.
    4. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
    5. If requiredModule is a Cyclic Module Record, then
      1. Assert: requiredModule.[[Status]] is either evaluating, evaluating-async, or evaluated.
      2. Assert: requiredModule.[[Status]] is evaluating if and only if requiredModule is in stack.
      3. If requiredModule.[[Status]] is evaluating, then
        1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
      4. Else,
        1. Set requiredModule to requiredModule.[[CycleRoot]].
        2. Assert: requiredModule.[[Status]] is evaluating-async or evaluated.
        3. If requiredModule.[[EvaluationError]] is not empty, return ? requiredModule.[[EvaluationError]].
      5. If requiredModule.[[AsyncEvaluation]] is true, then
        1. Set module.[[PendingAsyncDependencies]] to module.[[PendingAsyncDependencies]] + 1.
        2. Append module to requiredModule.[[AsyncParentModules]].
  12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is true, then
    1. Assert: module.[[AsyncEvaluation]] is false and was never previously set to true.
    2. Set module.[[AsyncEvaluation]] to true.
    3. NOTE: The order in which module records have their [[AsyncEvaluation]] fields transition to true is significant. (See 16.2.1.5.2.4.)
    4. If module.[[PendingAsyncDependencies]] is 0, perform ExecuteAsyncModule(module).
  13. Otherwise, perform ? module.ExecuteModule().
  14. Assert: module occurs exactly once in stack.
  15. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]].
  16. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
    1. Let done be false.
    2. Repeat, while done is false,
      1. Let requiredModule be the last element in stack.
      2. Remove the last element of stack.
      3. Assert: requiredModule is a Cyclic Module Record.
      4. If requiredModule.[[AsyncEvaluation]] is false, set requiredModule.[[Status]] to evaluated.
      5. Otherwise, set requiredModule.[[Status]] to evaluating-async.
      6. If requiredModule and module are the same Module Record, set done to true.
      7. Set requiredModule.[[CycleRoot]] to module.
  17. Return index.

16.2.1.6 Source Text Module Records

The following definitions specify the required concrete methods and other abstract operations for Source Text Module Records

16.2.1.6.1 ParseModule ( sourceText, realm, hostDefined )

The abstract operation ParseModule takes arguments sourceText (ECMAScript source text), realm, and hostDefined and returns a Source Text Module Record or a non-empty List of SyntaxError objects. It creates a Source Text Module Record based upon the result of parsing sourceText as a Module. It performs the following steps when called:

  1. Let body be ParseText(sourceText, Module).
  2. If body is a List of errors, return body.
  3. Let requestedModules be the ModuleRequests of body.
  4. Let importEntries be ImportEntries of body.
  5. Let importedBoundNames be ImportedLocalNames(importEntries).
  6. Let indirectExportEntries be a new empty List.
  7. Let localExportEntries be a new empty List.
  8. Let starExportEntries be a new empty List.
  9. Let exportEntries be ExportEntries of body.
  10. For each ExportEntry Record ee of exportEntries, do
    1. If ee.[[ModuleRequest]] is null, then
      1. If ee.[[LocalName]] is not an element of importedBoundNames, then
        1. Append ee to localExportEntries.
      2. Else,
        1. Let ie be the element of importEntries whose [[LocalName]] is the same as ee.[[LocalName]].
        2. If ie.[[ImportName]] is namespace-object, then
          1. NOTE: This is a re-export of an imported module namespace object.
          2. Append ee to localExportEntries.
        3. Else,
          1. NOTE: This is a re-export of a single name.
          2. Append the ExportEntry Record { [[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null, [[ExportName]]: ee.[[ExportName]] } to indirectExportEntries.
    2. Else if ee.[[ImportName]] is all-but-default, then
      1. Assert: ee.[[ExportName]] is null.
      2. Append ee to starExportEntries.
    3. Else,
      1. Append ee to indirectExportEntries.
  11. Let async be body Contains await.
  12. Return Source Text Module Record { [[Realm]]: realm, [[Environment]]: empty, [[Namespace]]: empty, [[CycleRoot]]: empty, [[HasTLA]]: async, [[AsyncEvaluation]]: false, [[TopLevelCapability]]: empty, [[AsyncParentModules]]: « », [[PendingAsyncDependencies]]: empty, [[Status]]: unlinkednew, [[EvaluationError]]: empty, [[HostDefined]]: hostDefined, [[ECMAScriptCode]]: body, [[Context]]: empty, [[ImportMeta]]: empty, [[RequestedModules]]: requestedModules, [[LoadedModules]]: a new empty List, [[ImportEntries]]: importEntries, [[LocalExportEntries]]: localExportEntries, [[IndirectExportEntries]]: indirectExportEntries, [[StarExportEntries]]: starExportEntries, [[DFSIndex]]: empty, [[DFSAncestorIndex]]: empty }.
Note

An implementation may parse module source text and analyse it for Early Error conditions prior to the evaluation of ParseModule for that module source text. However, the reporting of any errors must be deferred until the point where this specification actually performs ParseModule upon that source text.

16.2.1.6.2 GetExportedNames ( [ exportStarSet ] )

The GetExportedNames concrete method of a Source Text Module Record module takes optional argument exportStarSet (a List of Source Text Module Records) and returns either a normal completion containing a List of either Strings or null, or a throw completion. It performs the following steps when called:

  1. If exportStarSet is not present, set exportStarSet to a new empty List.
  2. If exportStarSet contains module, then
    1. Assert: We've reached the starting point of an export * circularity.
    2. Return a new empty List.
  3. Append module to exportStarSet.
  4. Let exportedNames be a new empty List.
  5. For each ExportEntry Record e of module.[[LocalExportEntries]], do
    1. Assert: module provides the direct binding for this export.
    2. Append e.[[ExportName]] to exportedNames.
  6. For each ExportEntry Record e of module.[[IndirectExportEntries]], do
    1. Assert: module imports a specific binding for this export.
    2. Append e.[[ExportName]] to exportedNames.
  7. For each ExportEntry Record e of module.[[StarExportEntries]], do
    1. Let requestedModule be HostResolveImportedModuleGetImportedModule(module, e.[[ModuleRequest]]).
    2. Assert: requestedModule is not empty, because LoadRequestedModules must have completed successfully prior to invoking this method.
    3. Let starNames be ? requestedModule.GetExportedNames(exportStarSet).
    4. For each element n of starNames, do
      1. If SameValue(n, "default") is false, then
        1. If n is not an element of exportedNames, then
          1. Append n to exportedNames.
  8. Return exportedNames.
Note

GetExportedNames does not filter out or throw an exception for names that have ambiguous star export bindings.

16.2.1.6.3 ResolveExport ( exportName [ , resolveSet ] )

The ResolveExport concrete method of a Source Text Module Record module takes argument exportName (a String) and optional argument resolveSet (a List of Records that have [[Module]] and [[ExportName]] fields) and returns either a normal completion containing either a ResolvedBinding Record, null, or ambiguous, or a throw completion.

ResolveExport attempts to resolve an imported binding to the actual defining module and local binding name. The defining module may be the module represented by the Module Record this method was invoked on or some other module that is imported by that module. The parameter resolveSet is used to detect unresolved circular import/export paths. If a pair consisting of specific Module Record and exportName is reached that is already in resolveSet, an import circularity has been encountered. Before recursively calling ResolveExport, a pair consisting of module and exportName is added to resolveSet.

If a defining module is found, a ResolvedBinding Record { [[Module]], [[BindingName]] } is returned. This record identifies the resolved binding of the originally requested export, unless this is the export of a namespace with no local binding. In this case, [[BindingName]] will be set to namespace. If no definition was found or the request is found to be circular, null is returned. If the request is found to be ambiguous, ambiguous is returned.

It performs the following steps when called:

  1. If resolveSet is not present, set resolveSet to a new empty List.
  2. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do
    1. If module and r.[[Module]] are the same Module Record and SameValue(exportName, r.[[ExportName]]) is true, then
      1. Assert: This is a circular import request.
      2. Return null.
  3. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolveSet.
  4. For each ExportEntry Record e of module.[[LocalExportEntries]], do
    1. If SameValue(exportName, e.[[ExportName]]) is true, then
      1. Assert: module provides the direct binding for this export.
      2. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }.
  5. For each ExportEntry Record e of module.[[IndirectExportEntries]], do
    1. If SameValue(exportName, e.[[ExportName]]) is true, then
      1. Let importedModule be HostResolveImportedModuleGetImportedModule(module, e.[[ModuleRequest]]).
      2. Assert: importedModule is not empty, because LoadRequestedModules must have completed successfully prior to invoking this method.
      3. If e.[[ImportName]] is all, then
        1. Assert: module does not provide the direct binding for this export.
        2. Return ResolvedBinding Record { [[Module]]: importedModule, [[BindingName]]: namespace }.
      4. Else,
        1. Assert: module imports a specific binding for this export.
        2. Return ? importedModule.ResolveExport(e.[[ImportName]], resolveSet).
  6. If SameValue(exportName, "default") is true, then
    1. Assert: A default export was not explicitly defined by this module.
    2. Return null.
    3. NOTE: A default export cannot be provided by an export * from "mod" declaration.
  7. Let starResolution be null.
  8. For each ExportEntry Record e of module.[[StarExportEntries]], do
    1. Let importedModule be HostResolveImportedModuleGetImportedModule(module, e.[[ModuleRequest]]).
    2. Assert: importedModule is not empty, because LoadRequestedModules must have completed successfully prior to invoking this method.
    3. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet).
    4. If resolution is ambiguous, return ambiguous.
    5. If resolution is not null, then
      1. Assert: resolution is a ResolvedBinding Record.
      2. If starResolution is null, set starResolution to resolution.
      3. Else,
        1. Assert: There is more than one * import that includes the requested name.
        2. If resolution.[[Module]] and starResolution.[[Module]] are not the same Module Record, return ambiguous.
        3. If resolution.[[BindingName]] is namespace and starResolution.[[BindingName]] is not namespace, or if resolution.[[BindingName]] is not namespace and starResolution.[[BindingName]] is namespace, return ambiguous.
        4. If resolution.[[BindingName]] is a String, starResolution.[[BindingName]] is a String, and SameValue(resolution.[[BindingName]], starResolution.[[BindingName]]) is false, return ambiguous.
  9. Return starResolution.

16.2.1.6.4 InitializeEnvironment ( )

The InitializeEnvironment concrete method of a Source Text Module Record module takes no arguments and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. For each ExportEntry Record e of module.[[IndirectExportEntries]], do
    1. Let resolution be ? module.ResolveExport(e.[[ExportName]]).
    2. If resolution is null or ambiguous, throw a SyntaxError exception.
    3. Assert: resolution is a ResolvedBinding Record.
  2. Assert: All named exports from module are resolvable.
  3. Let realm be module.[[Realm]].
  4. Assert: realm is not undefined.
  5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
  6. Set module.[[Environment]] to env.
  7. For each ImportEntry Record in of module.[[ImportEntries]], do
    1. Let importedModule be HostResolveImportedModuleGetImportedModule(module, in.[[ModuleRequest]]).
    2. Assert: importedModule is not empty, because LoadRequestedModules must have completed successfully prior to invoking this method.
    3. NOTE: The above call cannot fail because imported module requests are a subset of module.[[RequestedModules]], and these have been resolved earlier in this algorithm.
    4. If in.[[ImportName]] is namespace-object, then
      1. Let namespace be ? GetModuleNamespace(importedModule).
      2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).
      3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace).
    5. Else,
      1. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]]).
      2. If resolution is null or ambiguous, throw a SyntaxError exception.
      3. If resolution.[[BindingName]] is namespace, then
        1. Let namespace be ? GetModuleNamespace(resolution.[[Module]]).
        2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).
        3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace).
      4. Else,
        1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]).
  8. Let moduleContext be a new ECMAScript code execution context.
  9. Set the Function of moduleContext to null.
  10. Assert: module.[[Realm]] is not undefined.
  11. Set the Realm of moduleContext to module.[[Realm]].
  12. Set the ScriptOrModule of moduleContext to module.
  13. Set the VariableEnvironment of moduleContext to module.[[Environment]].
  14. Set the LexicalEnvironment of moduleContext to module.[[Environment]].
  15. Set the PrivateEnvironment of moduleContext to null.
  16. Set module.[[Context]] to moduleContext.
  17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
  18. Let code be module.[[ECMAScriptCode]].
  19. Let varDeclarations be the VarScopedDeclarations of code.
  20. Let declaredVarNames be a new empty List.
  21. For each element d of varDeclarations, do
    1. For each element dn of the BoundNames of d, do
      1. If dn is not an element of declaredVarNames, then
        1. Perform ! env.CreateMutableBinding(dn, false).
        2. Perform ! env.InitializeBinding(dn, undefined).
        3. Append dn to declaredVarNames.
  22. Let lexDeclarations be the LexicallyScopedDeclarations of code.
  23. Let privateEnv be null.
  24. For each element d of lexDeclarations, do
    1. For each element dn of the BoundNames of d, do
      1. If IsConstantDeclaration of d is true, then
        1. Perform ! env.CreateImmutableBinding(dn, true).
      2. Else,
        1. Perform ! env.CreateMutableBinding(dn, false).
      3. If d is a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
        1. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.
        2. Perform ! env.InitializeBinding(dn, fo).
  25. Remove moduleContext from the execution context stack.
  26. Return unused.

16.2.1.7 HostResolveImportedModule ( referencingScriptOrModule, specifier )

The host-defined abstract operation HostResolveImportedModule takes arguments referencingScriptOrModule (a Script Record, a Module Record, or null) and specifier (a ModuleSpecifier String) and returns either a normal completion containing a Module Record or a throw completion. It provides the concrete Module Record subclass instance that corresponds to specifier occurring within the context of the script or module represented by referencingScriptOrModule. referencingScriptOrModule may 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.

An implementation of HostResolveImportedModule must conform to the following requirements:

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.

16.2.1.8 HostImportModuleDynamically ( referencingScriptOrModule, specifier, promiseCapability )

The host-defined abstract operation HostImportModuleDynamically takes arguments referencingScriptOrModule (a Script Record, a Module Record, or null), specifier (a ModuleSpecifier String), and promiseCapability (a PromiseCapability Record) and returns unused. It performs any necessary setup work in order to make available the module corresponding to specifier occurring within the context of the script or module represented by referencingScriptOrModule. referencingScriptOrModule may 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.

An implementation of HostImportModuleDynamically must conform to the following requirements:

  • It must return unused. 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
    Failure path
    • At some future time, the host environment must perform FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, promise), where promise is a Promise rejected with an error 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.

16.2.1.9 FinishDynamicImport ( referencingScriptOrModule, specifier, promiseCapability, innerPromise )

The abstract operation FinishDynamicImport takes arguments referencingScriptOrModule, specifier, promiseCapability (a PromiseCapability Record), and innerPromise and returns unused. 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 innerPromise's resolution. It is performed by host environments as part of HostImportModuleDynamically. It performs the following steps when called:

  1. Let fulfilledClosure be a new Abstract Closure with parameters (result) that captures referencingScriptOrModule, specifier, and promiseCapability and performs the following steps when called:
    1. Assert: result is undefined.
    2. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
    3. Assert: Evaluate has already been invoked on moduleRecord and successfully completed.
    4. Let namespace be Completion(GetModuleNamespace(moduleRecord)).
    5. If namespace is an abrupt completion, then
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
    6. Else,
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
    7. Return unused.
  2. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "", « »).
  3. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures promiseCapability and performs the following steps when called:
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »).
    2. Return unused.
  4. Let onRejected be CreateBuiltinFunction(rejectedClosure, 0, "", « »).
  5. Perform PerformPromiseThen(innerPromise, onFulfilled, onRejected).
  6. Return unused.

16.2.1.10 GetImportedModule ( referrer, specifier )

The abstract operation GetImportedModule takes arguments referrer (a Cyclic Module Record) and specifier (a String) and returns a Module Record or empty. It performs the following steps when called:

  1. If referrer.[[LoadedModules]] contains a Record record such that record.[[Specifier]] is specifier, then
    1. Return record.[[Module]].
  2. Return empty.

16.2.1.11 HostLoadImportedModule ( referrer, specifier, hostDefined, payload )

The host-defined abstract operation HostLoadImportedModule takes arguments referrer (a Script Record, a Cyclic Module Record, or a Realm Record), specifier (a String), hostDefined, and payload (a ModuleLoadState Record) and returns unused.

Note

An example of when referrer can be a Realm Record 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.

An implementation of HostLoadImportedModule must conform to the following requirements:

The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to load the appropriate Module Record. Multiple different (referrer, 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 expansion of relative and abbreviated path specifiers.

16.2.1.12 FinishLoadImportedModule ( referrer, specifier, payload, result )

The abstract operation FinishLoadImportedModule takes arguments referrer (a Script Record, a Cyclic Module Record, or a Realm Record), specifier (a String), payload (a ModuleLoadState Record), and result (either a normal completion containing a Module Record or a throw completion) and returns unused. It performs the following steps when called:

  1. If result is a normal completion, then
    1. If referrer.[[LoadedModules]] contains a Record record such that record.[[Specifier]] is specifier, then
      1. Assert: record.[[Module]] is result.[[Value]].
    2. Else, add Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
  2. If state.[[Action]] is graph-loading, then
    1. Perform ContinueModuleLoading(state, result).
  3. Else,
    1. Perform ContinueDynamicImport(state, result).

A Copyright & Software License

Copyright Notice

© 2022 Nicolò Ribaudo

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.