From a232084173998f1c4515bd099b57063b5ccec215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 28 Mar 2024 18:44:53 +0100 Subject: [PATCH 1/2] Revert "Make `ns.foo` trigger evaluation also for non-existing properties (#33)" This reverts commit 9552727c4118228280d6fb68de126b333f2f41ae. --- index.html | 4 ++-- spec.emu | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index f0cd255..1fe371c 100644 --- a/index.html +++ b/index.html @@ -2628,7 +2628,7 @@

Stage 2 Draft / March 27, 2024

Deferred Imports Evaluation

+

Stage 2 Draft / March 22, 2024

Deferred Imports Evaluation

10 Ordinary and Exotic Objects Behaviours

@@ -2683,7 +2683,7 @@

10.4.6 Module Namespace Exotic Objects

10.4.6.8 [[Get]] ( P, Receiver )

The [[Get]] internal method of a module namespace exotic object O takes arguments P (a property key) and Receiver (an ECMAScript language value) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

-
  1. If P is a Symbol, then
    1. Return ! OrdinaryGet(O, P, Receiver).
  2. Let m be O.[[Module]].
  3. If m is not a Cyclic Module Record, or both m.[[Status]] is linked and AnyDependencyNeedsAsyncEvaluation(m) is false, then
    1. Perform ? EvaluateSync(m).
  4. Let exports be O.[[Exports]].
  5. If exports does not contain P, return undefined.
  6. Let m be O.[[Module]].
  7. Let binding be m.ResolveExport(P).
  8. Assert: binding is a ResolvedBinding Record.
  9. Let targetModule be binding.[[Module]].
  10. Assert: targetModule is not undefined.
  11. If binding.[[BindingName]] is namespace, then
    1. Return GetModuleNamespace(targetModule).
  12. Let targetEnv be targetModule.[[Environment]].
  13. If targetEnv is empty, throw a ReferenceError exception.
  14. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).
+
  1. If P is a Symbol, then
    1. Return ! OrdinaryGet(O, P, Receiver).
  2. Let exports be O.[[Exports]].
  3. If exports does not contain P, return undefined.
  4. Let m be O.[[Module]].
  5. If m is not a Cyclic Module Record, or both m.[[Status]] is linked and AnyDependencyNeedsAsyncEvaluation(m) is false, then
    1. Perform ? EvaluateSync(m).
  6. Let binding be m.ResolveExport(P).
  7. Assert: binding is a ResolvedBinding Record.
  8. Let targetModule be binding.[[Module]].
  9. Assert: targetModule is not undefined.
  10. If binding.[[BindingName]] is namespace, then
    1. Return GetModuleNamespace(targetModule).
  11. Let targetEnv be targetModule.[[Environment]].
  12. If targetEnv is empty, throw a ReferenceError exception.
  13. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).
Note 1

ResolveExport is side-effect free. Each time this operation is called with a specific exportName, resolveSet pair as arguments it must return the same result. An implementation might choose to pre-compute or cache the ResolveExport results for the [[Exports]] of each module namespace exotic object.

diff --git a/spec.emu b/spec.emu index 8416df1..c27ca3f 100644 --- a/spec.emu +++ b/spec.emu @@ -73,12 +73,11 @@ contributors: Nicolò Ribaudo 1. If _P_ is a Symbol, then 1. Return ! OrdinaryGet(_O_, _P_, _Receiver_). - 1. Let _m_ be _O_.[[Module]]. - 1. If _m_ is not a Cyclic Module Record, or both _m_.[[Status]] is ~linked~ and AnyDependencyNeedsAsyncEvaluation(_m_) is *false*, then - 1. Perform ? EvaluateSync(_m_). 1. Let _exports_ be _O_.[[Exports]]. 1. If _exports_ does not contain _P_, return *undefined*. - 1. Let _m_ be _O_.[[Module]]. + 1. Let _m_ be _O_.[[Module]]. + 1. If _m_ is not a Cyclic Module Record, or both _m_.[[Status]] is ~linked~ and AnyDependencyNeedsAsyncEvaluation(_m_) is *false*, then + 1. Perform ? EvaluateSync(_m_). 1. Let _binding_ be _m_.ResolveExport(_P_). 1. Assert: _binding_ is a ResolvedBinding Record. 1. Let _targetModule_ be _binding_.[[Module]]. From 30929ff2705ff31fa902010ebe8ef846566fa51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 28 Mar 2024 18:52:39 +0100 Subject: [PATCH 2/2] Throw when doing `import defer *` of a module with no exports --- index.html | 6 +++--- spec.emu | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 1fe371c..7238aef 100644 --- a/index.html +++ b/index.html @@ -2628,7 +2628,7 @@

Stage 2 Draft / March 22, 2024

Deferred Imports Evaluation

+

Stage 2 Draft / March 28, 2024

Deferred Imports Evaluation

10 Ordinary and Exotic Objects Behaviours

@@ -2813,7 +2813,7 @@

13.3.10.2 EvaluateImportCall ( specifi

13.3.10.2.1 ContinueDynamicImport ( promiseCapability, payload, moduleCompletion )

The abstract operation ContinueDynamicImport takes arguments promiseCapability (a PromiseCapability Record), payload (a DynamicImportState Record), and moduleCompletion (either a normal completion containing a Module Record or a throw completion) and returns unused. It completes the process of a dynamic import originally started by an import() call, resolving or rejecting the promise returned by that call as appropriate. It performs the following steps when called:

-
  1. Let promiseCapability be payload.[[PromiseCapability]].
  2. Let phase be payload.[[Phase]].
  3. If moduleCompletion is an abrupt completion, then
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »).
    2. Return UNUSED.
  4. Let module be moduleCompletion.[[Value]].
  5. Let loadPromise be module.LoadRequestedModules().
  6. 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.
  7. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
  8. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, phase 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()EMPTY.
    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 GetModuleNamespace(module).
      2. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace »).
      3. Return UNUSED.
    5. If phase is defer, then
      1. Let evaluationList be a new empty List.
      2. Perform GatherAsynchronousTransitiveDependencies(module, evaluationList).
      3. If evaluationList is empty, then
        1. Perform fulfilledClosure().
        2. Return UNUSED.
      4. Let asyncDepsEvaluationPromises be a new empty List.
      5. For each Module Record dep of evaluationList, append dep.Evaluate() to asyncDepsEvaluationPromises.
      6. Let iterator be CreateListIteratorRecord(asyncDepsEvaluationPromises).
      7. Let pc be ! NewPromiseCapability(%Promise%).
      8. Set evaluatePromise to ! PerformPromiseAll(iterator, %Promise%, pc, %Promise.resolve%).
    6. Else,
      1. Assert: phase is evaluation.
      2. Set evaluatePromise to module.Evaluate().
    7. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, "", 0, « »).
    8. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected).
    9. Return UNUSED.
  9. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, "", 0, « »).
  10. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected).
  11. Return UNUSED.
+
  1. Let promiseCapability be payload.[[PromiseCapability]].
  2. Let phase be payload.[[Phase]].
  3. If moduleCompletion is an abrupt completion, then
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »).
    2. Return UNUSED.
  4. Let module be moduleCompletion.[[Value]].
  5. Let loadPromise be module.LoadRequestedModules().
  6. 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.
  7. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
  8. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, phase and onRejected and performs the following steps when called:
    1. If phase is defer and module.GetExportedNames() is empty, then
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a new SyntaxError »).
      2. Return UNUSED.
    2. Let link be Completion(module.Link()).
    3. If link is an abrupt completion, then
      1. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).
      2. Return UNUSED.
    4. Let evaluatePromise be module.Evaluate()EMPTY.
    5. 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 GetModuleNamespace(module).
      2. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace »).
      3. Return UNUSED.
    6. If phase is defer, then
      1. Let evaluationList be a new empty List.
      2. Perform GatherAsynchronousTransitiveDependencies(module, evaluationList).
      3. If evaluationList is empty, then
        1. Perform fulfilledClosure().
        2. Return UNUSED.
      4. Let asyncDepsEvaluationPromises be a new empty List.
      5. For each Module Record dep of evaluationList, append dep.Evaluate() to asyncDepsEvaluationPromises.
      6. Let iterator be CreateListIteratorRecord(asyncDepsEvaluationPromises).
      7. Let pc be ! NewPromiseCapability(%Promise%).
      8. Set evaluatePromise to ! PerformPromiseAll(iterator, %Promise%, pc, %Promise.resolve%).
    7. Else,
      1. Assert: phase is evaluation.
      2. Set evaluatePromise to module.Evaluate().
    8. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, "", 0, « »).
    9. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected).
    10. Return UNUSED.
  9. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, "", 0, « »).
  10. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected).
  11. Return UNUSED.
@@ -3302,7 +3302,7 @@

16.2.1.5.2 Link ( )

16.2.1.5.2.1 InnerModuleLinking ( module, stack, index )

The abstract operation InnerModuleLinking takes arguments module (a Module Record), stack (a List of Cyclic Module Records), 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 one of 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
  10. For each ModuleRequest Record required of module.[[RequestedModules]], do
    1. Let requiredModule be GetImportedModule(module, required.[[Specifier]]).
    2. Set index to ? InnerModuleLinking(requiredModule, stack, index).
    3. If requiredModule is a Cyclic Module Record, then
      1. Assert: requiredModule.[[Status]] is one of linking, linked, evaluating-async, or evaluated.
      2. Assert: requiredModule.[[Status]] is linking if and only if stack contains requiredModule.
      3. If requiredModule.[[Status]] is linking, then
        1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
  11. Perform ? module.InitializeEnvironment().
  12. Assert: module occurs exactly once in stack.
  13. Assert: module.[[DFSAncestorIndex]]module.[[DFSIndex]].
  14. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
    1. Let done be false.
    2. Repeat, while done is false,
      1. Let requiredModule be the last element of 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.
  15. Return index.
+
  1. If module is not a Cyclic Module Record, then
    1. Perform ? module.Link().
    2. Return index.
  2. If module.[[Status]] is one of 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
  10. For each ModuleRequest Record required of module.[[RequestedModules]], do
    1. Let requiredModule be GetImportedModule(module, required.[[Specifier]]).
    2. If required.[[Phase]] is defer and requiredModule.GetExportedNames() is empty, throw a new SyntaxError.
    3. Set index to ? InnerModuleLinking(requiredModule, stack, index).
    4. If requiredModule is a Cyclic Module Record, then
      1. Assert: requiredModule.[[Status]] is one of linking, linked, evaluating-async, or evaluated.
      2. Assert: requiredModule.[[Status]] is linking if and only if stack contains requiredModule.
      3. If requiredModule.[[Status]] is linking, then
        1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
  11. Perform ? module.InitializeEnvironment().
  12. Assert: module occurs exactly once in stack.
  13. Assert: module.[[DFSAncestorIndex]]module.[[DFSIndex]].
  14. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
    1. Let done be false.
    2. Repeat, while done is false,
      1. Let requiredModule be the last element of 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.
  15. Return index.
diff --git a/spec.emu b/spec.emu index c27ca3f..aeb8f6a 100644 --- a/spec.emu +++ b/spec.emu @@ -256,6 +256,9 @@ contributors: Nicolò Ribaudo 1. Return ~UNUSED~. 1. Let _onRejected_ be CreateBuiltinFunction(_rejectedClosure_, 1, *""*, « »). 1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, _phase_ and _onRejected_ and performs the following steps when called: + 1. If _phase_ is ~defer~ and _module_.GetExportedNames() is empty, then + 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « a new *SyntaxError* »). + 1. Return ~UNUSED~. 1. Let _link_ be Completion(_module_.Link()). 1. If _link_ is an abrupt completion, then 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _link_.[[Value]] »). @@ -840,6 +843,7 @@ contributors: Nicolò Ribaudo 1. For each String _required_ of _module_.[[RequestedModules]], do 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do 1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]). + 1. If _required_.[[Phase]] is ~defer~ and _requiredModule_.GetExportedNames() is empty, throw a new *SyntaxError*. 1. Set _index_ to ? InnerModuleLinking(_requiredModule_, _stack_, _index_). 1. If _requiredModule_ is a Cyclic Module Record, then 1. Assert: _requiredModule_.[[Status]] is one of ~linking~, ~linked~, ~evaluating-async~, or ~evaluated~.