Closed
Description
I think that the current draft spec incurs infinite recursion when there is the circular dependencies.
The situation is the following,
There are several modules,
A: This is the entry point.
import "B"
B
import "C"
C
import "B"
- At that time, before linking the modules, we call
RequestInstantiateAll(A)
. - In
RequestInstantiateAll(A)
, step 1-b-i-1, we extract the B dependency. - So, step 1-b-i-3, we perform
RequestInstantiateAll(B)
. - Later, it will extract the C dependency. So, step 1-b-i-3 in
RequestInstantiateAll(B)
, we callRequestInstantiateAll(C)
. - Since C has the B dependency, in
RequestInstantiateAll(C)
step 1-b-i-3, we again callRequestInstantiateAll(B)
. - Here, now, since once
RequestInstantiate(B)
is called insideRequestInstantiateAll(B)
, it immediately returns the promiseB.[[Instantiate]]
. But, Now, we stored the status of instantiation in [[Instantiate]], but we didn't store the status of collecting the dependencies. - So, in
RequestInstantiateAll(B)
,RequestInstantiate(B)
returns the stored promise, but it performs the dependency resolution again in 1-a or later. So it will callRequestInstantiateAll(C)
at step 1-b-i-3 again. RequestInstantiateAll(B)
andRequestInstantiateAll(C)
are called repeatedly.
This problem comes from that the current algorithm does not store the status of "resolving the dependencies".
To solve this, I would like to propose introducing the new state ResolveDependencies
. That means, "Now, the instantiation is done, so the entry is ready to resolve the dependencies. And if there's entry.[[ResolveDependencies]] promise and the state is ResolveDependencies
, we're resolving the dependency of the module just now".
The algorithm becomes the following,
CommitInstantiated(loader, key)
- Let instance be Instantiation(loader, optionalInstance, source).
- ReturnIfAbrupt(instance).
- // TODO: edge case: what if instance is a thenable function?
- Fulfill entry.[[Instantiate]] with instance.
- Let deps be a new empty List.
- If instance is a Module Record, then:
- Assert: instance is a Source Text Module Record.
- Set instance.[[RegistryEntry]] to entry.
- For each dep in instance.[[RequestedModules]], do:
- Append the record { [[key]]: dep, [[value]]: undefined } to deps.
- Set entry.[[Dependencies]] to deps.
- Set entry.[[Module]] to instance.
- SetStateToMax(entry, "resolveDependencies").
RequestResolveDependencies(loader, key)
- Let entry be EnsureRegistered(loader, key).
- Let linkStateValue be GetStateValue("link").
- If stateValue is greater than linkStateValue, return a new error promise.
- If entry.[[ResolveDependencies]] is not undefined, return entry.[[ResolveDependencies]].
- Return the result of transforming RequestInstantiate(loader, key) with a fulfillment handler that, when called with argument entry, runs the following steps:
- Let depLoads be a new empty List.
- For each pair in entry.[[Dependencies]], do:
- Let p be the result of transforming Resolve(loader, pair.[[key]], key) with a fulfillment handler that, when called with value depKey, runs the following steps:
- Let depEntry be EnsureRegistered(loader, depKey).
- If depEntry.[[ResolveDependencies]] is not undefined
- Return the result of transforming depEntry.[[Instantiate]] with a fulfillment handler that, when called with value depEntry, runs the following steps:
- Let dep be depEntry.[[Module]].
- Set pair.[[value]] to dep.
- Return depEntry.
- Return the result of transforming depEntry.[[Instantiate]] with a fulfillment handler that, when called with value depEntry, runs the following steps:
- Return the result of transforming RequestCollect(loader, depKey) with a fulfillment handler that, when called with value depEntry, runs the following steps:
- Let dep be depEntry.[[Module]].
- Set pair.[[value]] to dep.
- Return depEntry.
- Append p to depLoads.
- Let p be the result of transforming Resolve(loader, pair.[[key]], key) with a fulfillment handler that, when called with value depKey, runs the following steps:
- Let p be the result of waiting for all depLoads.
- Return the result of transforming p with a fulfillment handler that, when called, runs the following steps:
- SetStateToMax(entry, "link").
- Return entry.