From 4098f92b39bc970167ae8996124037f0017b0e33 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 1 Apr 2024 22:28:29 -0700 Subject: [PATCH 01/12] normative: handle reentrancy invariant --- spec.emu | 75 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/spec.emu b/spec.emu index 8416df1..4a72616 100644 --- a/spec.emu +++ b/spec.emu @@ -74,7 +74,7 @@ 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. If _m_ is not a Cyclic Module Record, or _m_.[[Status]] is not ~evaluated~, then 1. Perform ? EvaluateSync(_m_). 1. Let _exports_ be _O_.[[Exports]]. 1. If _exports_ does not contain _P_, return *undefined*. @@ -92,32 +92,6 @@ contributors: Nicolò Ribaudo

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.

- - AnyDependencyNeedsAsyncEvaluation can return *true* when this [[Get]] operation on a namespace coming from a deferred import is triggered during the evaluation of one of its dependencies, either synchronously or asynchronously. In that case, the module cannot be fully evaluated and some of its exports will not be initialized yet. - - - -

- - AnyDependencyNeedsAsyncEvaluation ( - _module_: a Module Record, - optional _seen_: a List of Module Records, - ): a Boolean - -

-
- - 1. If _seen_ is not provided, let _seen_ be a new empty List. - 1. If _seen_ contains _module_, return *false*. - 1. Append _module_ to _seen_. - 1. If _module_ is not a Cyclic Module Record or _module_.[[Status]] is ~evaluated~, return *false*. - 1. If _module_.[[HasTLA]] is *true*, return *true*. - 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do - 1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifer]]). - 1. If AnyDependencyNeedsAsyncEvaluation(_requiredModule_, _seen_) is *true*, return *true*. - 1. Return *false*. - -
@@ -874,7 +848,7 @@ contributors: Nicolò Ribaudo - 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent. + 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent, unless it was initiated by EvaluateSync. 1. Assert: _module_.[[Status]] is one of ~linked~, ~evaluating-async~, or ~evaluated~. 1. If _module_.[[Status]] is either ~evaluating-async~ or ~evaluated~, set _module_ to _module_.[[CycleRoot]]. 1. If _module_.[[TopLevelCapability]] is not ~empty~, then @@ -916,11 +890,10 @@ contributors: Nicolò Ribaudo 1. If _module_ is not a Cyclic Module Record, then - 1. Let _promise_ be ! _module_.Evaluate(). - 1. Assert: _promise_.[[PromiseState]] is not ~pending~. - 1. If _promise_.[[PromiseState]] is ~rejected~, then - 1. Return ThrowCompletion(_promise_.[[PromiseResult]]). - 1. Perform ? EvaluateSync(_module_). + 1. Let _promise_ be ! _module_.Evaluate(). + 1. Assert: _promise_.[[PromiseState]] is not ~pending~. + 1. If _promise_.[[PromiseState]] is ~rejected~, then + 1. Return ThrowCompletion(_promise_.[[PromiseResult]]). 1. Return _index_. 1. If _module_.[[Status]] is either ~evaluating-async~ or ~evaluated~, then 1. If _module_.[[EvaluationError]] is ~empty~, return _index_. @@ -996,11 +969,12 @@ contributors: Nicolò Ribaudo
description
-
It synchronously evaluates _module_, which must not have asynchronous dependencies.
+
It synchronously evaluates _module_, throwing if it has asynchronous dependencies or is already evaluating.
- 1. Assert: If _module_ is a Cyclic Module Record, _module_.[[HasTLA]] is *false* and all its asynchronous transitive dependencies have already been evaluated. + 1. If _module_ is a Cyclic Module Record, then + 1. Perform ? ValidateSyncExecution(_m_). 1. Let _promise_ be ! _module_.Evaluate(). 1. Assert: _promise_.[[PromiseState]] is either ~fulfilled~ or ~rejected~. 1. If _promise_.[[PromiseState]] is ~rejected~, then @@ -1009,7 +983,32 @@ contributors: Nicolò Ribaudo - + +

+ + ValidateSyncExecution ( + _module_: a Cyclic Module Record, + optional _seen_: a List of Module Records, + ): a Boolean + +

+
+ + 1. If _seen_ is not provided, let _seen_ be a new empty List. + 1. If _seen_ contains _module_, return ~unused~. + 1. Append _module_ to _seen_. + 1. If _module_.[[Status]] is ~evaluated~, return ~unused~. + 1. If _module_.[[Status]] is ~evaluating~ or ~evaluating-async~, throw a *ReferenceError* exception. + 1. Assert: _module_.[[Status]] is ~linked~. + 1. If _module_.[[HasTLA]] is *true*, throw a *ReferenceError* exception. + 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do + 1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifer]]). + 1. Perform ? ValidateSyncExecution(_requiredModule_, _seen_). + 1. Return ~unused~. + +
+ +

GatherAsynchronousTransitiveDependencies ( @@ -1021,7 +1020,7 @@ contributors: Nicolò Ribaudo

description
-
+
Collects the direct post-order list of asynchronous unexecuted transitive dependencies, stopping the depth-first search for a branch when an async dependency is found.
@@ -1029,7 +1028,7 @@ contributors: Nicolò Ribaudo 1. If _seen_ contains _module_, return ~unused~. 1. Append _module_ to _seen_. 1. If _module_ is not a Cyclic Module Record, return ~unused~. - 1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return ~unused~. + 1. If _module_.[[Status]] is ~evaluating~, ~evaluating-async~ or ~evaluated~, return ~unused~. 1. If _module_.[[HasTLA]] is *true*, then 1. If _result_ does not contain _module_, append _module_ to _result_. 1. Return ~unused~. From 07b34abc136c9926db52e0ffd8a9e730a8bb3f43 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 1 Apr 2024 23:21:43 -0700 Subject: [PATCH 02/12] fixups --- spec.emu | 61 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/spec.emu b/spec.emu index 4a72616..ea4d572 100644 --- a/spec.emu +++ b/spec.emu @@ -74,7 +74,9 @@ 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 _m_.[[Status]] is not ~evaluated~, then + 1. If _m_ is not a Cyclic Module Record, then + 1. Perform ? EvaluateSync(_m_). + 1. Otherwise if _m_.[[Status]] is not ~evaluated~ and ! ReadyForSyncExecution(_m_) is *true*, then 1. Perform ? EvaluateSync(_m_). 1. Let _exports_ be _O_.[[Exports]]. 1. If _exports_ does not contain _P_, return *undefined*. @@ -92,6 +94,33 @@ contributors: Nicolò Ribaudo

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.

+ + +

+ + ReadyForSyncExecution ( + _module_: a Cyclic Module Record, + optional _seen_: a List of Module Records, + ): a Boolean + +

+
+ + 1. If _seen_ is not provided, let _seen_ be a new empty List. + 1. If _seen_ contains _module_, return *true*. + 1. Append _module_ to _seen_. + 1. If _module_.[[Status]] is ~evaluated~, return *true*. + 1. If _module_.[[Status]] is ~evaluating~ or ~evaluating-async~, return *false*. + 1. Assert: _module_.[[Status]] is ~linked~. + 1. If _module_.[[HasTLA]] is *true*, return *false*. + 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do + 1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifer]]). + 1. If ! ReadyForSyncExecution(_requiredModule_, _seen_) is *false*, then + 1. Return *false*. + 1. Return *true*. + +
+
@@ -969,12 +998,11 @@ contributors: Nicolò Ribaudo
description
-
It synchronously evaluates _module_, throwing if it has asynchronous dependencies or is already evaluating.
+
It synchronously evaluates _module_ provided it is ready for synchronous execution.
- 1. If _module_ is a Cyclic Module Record, then - 1. Perform ? ValidateSyncExecution(_m_). + 1. Assert: If _module_ is a Cyclic Module Record, _module_.[[HasTLA]] is *false* and all its asynchronous transitive dependencies have already been evaluated. 1. Let _promise_ be ! _module_.Evaluate(). 1. Assert: _promise_.[[PromiseState]] is either ~fulfilled~ or ~rejected~. 1. If _promise_.[[PromiseState]] is ~rejected~, then @@ -983,31 +1011,6 @@ contributors: Nicolò Ribaudo - -

- - ValidateSyncExecution ( - _module_: a Cyclic Module Record, - optional _seen_: a List of Module Records, - ): a Boolean - -

-
- - 1. If _seen_ is not provided, let _seen_ be a new empty List. - 1. If _seen_ contains _module_, return ~unused~. - 1. Append _module_ to _seen_. - 1. If _module_.[[Status]] is ~evaluated~, return ~unused~. - 1. If _module_.[[Status]] is ~evaluating~ or ~evaluating-async~, throw a *ReferenceError* exception. - 1. Assert: _module_.[[Status]] is ~linked~. - 1. If _module_.[[HasTLA]] is *true*, throw a *ReferenceError* exception. - 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do - 1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifer]]). - 1. Perform ? ValidateSyncExecution(_requiredModule_, _seen_). - 1. Return ~unused~. - -
-

From 88261946e71a0419c9880e7d2bdd0d87feabd012 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 1 Apr 2024 23:26:19 -0700 Subject: [PATCH 03/12] simplify --- spec.emu | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec.emu b/spec.emu index ea4d572..a509789 100644 --- a/spec.emu +++ b/spec.emu @@ -919,10 +919,11 @@ contributors: Nicolò Ribaudo 1. If _module_ is not a Cyclic Module Record, then - 1. Let _promise_ be ! _module_.Evaluate(). - 1. Assert: _promise_.[[PromiseState]] is not ~pending~. - 1. If _promise_.[[PromiseState]] is ~rejected~, then - 1. Return ThrowCompletion(_promise_.[[PromiseResult]]). + 1. Let _promise_ be ! _module_.Evaluate(). + 1. Assert: _promise_.[[PromiseState]] is not ~pending~. + 1. If _promise_.[[PromiseState]] is ~rejected~, then + 1. Return ThrowCompletion(_promise_.[[PromiseResult]]). + 1. Perform ? EvaluateSync(_module_). 1. Return _index_. 1. If _module_.[[Status]] is either ~evaluating-async~ or ~evaluated~, then 1. If _module_.[[EvaluationError]] is ~empty~, return _index_. From 9ee06d574b83cd47ffa2a1e388f7591ead9584a6 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 1 Apr 2024 23:29:04 -0700 Subject: [PATCH 04/12] readd note --- spec.emu | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec.emu b/spec.emu index a509789..9e9fa38 100644 --- a/spec.emu +++ b/spec.emu @@ -94,6 +94,9 @@ contributors: Nicolò Ribaudo

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.

+ + ReadyForSyncExecution can return *false* when this [[Get]] operation on a namespace coming from a deferred import is triggered during the evaluation of one of its dependencies, either synchronously or asynchronously. In that case, the module cannot be fully evaluated and some of its exports will not be initialized yet. +

From 35581c713a632479651f12e36ac187e319af441e Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 2 Apr 2024 09:46:17 -0700 Subject: [PATCH 05/12] Update spec.emu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolò Ribaudo --- spec.emu | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec.emu b/spec.emu index 9e9fa38..b63cb54 100644 --- a/spec.emu +++ b/spec.emu @@ -880,7 +880,8 @@ contributors: Nicolò Ribaudo - 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent, unless it was initiated by EvaluateSync. + 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.. + 1. Assert: _module_ and all of its recursive dependencies have [[Status]] set to one of ~linked~, ~evaluated~, ~evaluating-async~, or ~evaluated~. 1. Assert: _module_.[[Status]] is one of ~linked~, ~evaluating-async~, or ~evaluated~. 1. If _module_.[[Status]] is either ~evaluating-async~ or ~evaluated~, set _module_ to _module_.[[CycleRoot]]. 1. If _module_.[[TopLevelCapability]] is not ~empty~, then From e9094d5d5351b57f52847410e5a676d6f561abd5 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 2 Apr 2024 11:21:28 -0700 Subject: [PATCH 06/12] fixup evaluating-async handling --- spec.emu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec.emu b/spec.emu index b63cb54..833587e 100644 --- a/spec.emu +++ b/spec.emu @@ -1036,8 +1036,8 @@ contributors: Nicolò Ribaudo 1. If _seen_ contains _module_, return ~unused~. 1. Append _module_ to _seen_. 1. If _module_ is not a Cyclic Module Record, return ~unused~. - 1. If _module_.[[Status]] is ~evaluating~, ~evaluating-async~ or ~evaluated~, return ~unused~. - 1. If _module_.[[HasTLA]] is *true*, then + 1. If _module_.[[Status]] is ~evaluating~ or ~evaluated~, return ~unused~. + 1. If _module_.[[Status]] is ~evaluating-async~ or _module_.[[HasTLA]] is *true*, then 1. If _result_ does not contain _module_, append _module_ to _result_. 1. Return ~unused~. 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do From 3de1cd6a051ac70507c778af7257604d00732bd7 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 2 Apr 2024 11:24:26 -0700 Subject: [PATCH 07/12] fixups --- spec.emu | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec.emu b/spec.emu index 833587e..3ea3373 100644 --- a/spec.emu +++ b/spec.emu @@ -123,7 +123,6 @@ contributors: Nicolò Ribaudo 1. Return *true*. - @@ -1036,7 +1035,7 @@ contributors: Nicolò Ribaudo 1. If _seen_ contains _module_, return ~unused~. 1. Append _module_ to _seen_. 1. If _module_ is not a Cyclic Module Record, return ~unused~. - 1. If _module_.[[Status]] is ~evaluating~ or ~evaluated~, return ~unused~. + 1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return ~unused~. 1. If _module_.[[Status]] is ~evaluating-async~ or _module_.[[HasTLA]] is *true*, then 1. If _result_ does not contain _module_, append _module_ to _result_. 1. Return ~unused~. From 0744c505e9bf1149e8453f658c5136a711cffc2e Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 2 Apr 2024 11:25:54 -0700 Subject: [PATCH 08/12] simplify diff --- spec.emu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.emu b/spec.emu index 3ea3373..b1d6cb9 100644 --- a/spec.emu +++ b/spec.emu @@ -1036,7 +1036,7 @@ contributors: Nicolò Ribaudo 1. Append _module_ to _seen_. 1. If _module_ is not a Cyclic Module Record, return ~unused~. 1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return ~unused~. - 1. If _module_.[[Status]] is ~evaluating-async~ or _module_.[[HasTLA]] is *true*, then + 1. If _module_.[[HasTLA]] is *true* or _module_.[[Status]] is ~evaluating-async~, then 1. If _result_ does not contain _module_, append _module_ to _result_. 1. Return ~unused~. 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do From f1512b8feafe176be595691934883015ede1a67a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 2 Apr 2024 11:30:59 -0700 Subject: [PATCH 09/12] grammar --- spec.emu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.emu b/spec.emu index b1d6cb9..7bbeff5 100644 --- a/spec.emu +++ b/spec.emu @@ -519,7 +519,7 @@ contributors: Nicolò Ribaudo a List of StringsModuleRequest Records - A List of all the |ModuleSpecifier| strings used by the module represented by this record to request the importation of a module, alongside with their maximum specified import phase (~defer~ or ~evaluation~). The List is in source text occurrence order. + A List of all the |ModuleSpecifier| strings used by the module represented by this record to request the importation of a module, along with their maximum specified import phase (~defer~ or ~evaluation~). The List is in source text occurrence order. From 130ddd95a42b3097102791c95a0222df851ca5fc Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 2 Apr 2024 11:36:05 -0700 Subject: [PATCH 10/12] invert assertion to call out evaluating --- spec.emu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.emu b/spec.emu index 7bbeff5..6ede844 100644 --- a/spec.emu +++ b/spec.emu @@ -880,7 +880,7 @@ contributors: Nicolò Ribaudo 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.. - 1. Assert: _module_ and all of its recursive dependencies have [[Status]] set to one of ~linked~, ~evaluated~, ~evaluating-async~, or ~evaluated~. + 1. Assert: None of _module_ or any of its recursive dependencies have [[Status]] set to ~evaluating~ or a status earlier than ~linked~. 1. Assert: _module_.[[Status]] is one of ~linked~, ~evaluating-async~, or ~evaluated~. 1. If _module_.[[Status]] is either ~evaluating-async~ or ~evaluated~, set _module_ to _module_.[[CycleRoot]]. 1. If _module_.[[TopLevelCapability]] is not ~empty~, then From 3dd0025d3ad0ef95a7689380fa5ee150b20f88b7 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 4 Apr 2024 10:12:13 -0700 Subject: [PATCH 11/12] remove evaluating-async stop condition for now --- spec.emu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.emu b/spec.emu index 6ede844..e510541 100644 --- a/spec.emu +++ b/spec.emu @@ -1036,7 +1036,7 @@ contributors: Nicolò Ribaudo 1. Append _module_ to _seen_. 1. If _module_ is not a Cyclic Module Record, return ~unused~. 1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return ~unused~. - 1. If _module_.[[HasTLA]] is *true* or _module_.[[Status]] is ~evaluating-async~, then + 1. If _module_.[[HasTLA]] is *true* or _module_.[[Status]], then 1. If _result_ does not contain _module_, append _module_ to _result_. 1. Return ~unused~. 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do From d93f0aadc91a74e28f7cf5b5fbfa3671b94514d1 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 5 Apr 2024 09:53:51 -0700 Subject: [PATCH 12/12] Update spec.emu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolò Ribaudo --- spec.emu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.emu b/spec.emu index e510541..a9d814b 100644 --- a/spec.emu +++ b/spec.emu @@ -1036,7 +1036,7 @@ contributors: Nicolò Ribaudo 1. Append _module_ to _seen_. 1. If _module_ is not a Cyclic Module Record, return ~unused~. 1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return ~unused~. - 1. If _module_.[[HasTLA]] is *true* or _module_.[[Status]], then + 1. If _module_.[[HasTLA]] is *true*, then 1. If _result_ does not contain _module_, append _module_ to _result_. 1. Return ~unused~. 1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do