Skip to content

Commit 2547c9d

Browse files
committed
.
1 parent 6cc8660 commit 2547c9d

File tree

1 file changed

+94
-35
lines changed

1 file changed

+94
-35
lines changed

src/Asynkron.JsEngine/JsEngine.cs

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ internal ModuleEntry(string path, ProgramNode program, JsEnvironment environment
5555
internal ModuleNamespace? DeferredNamespace { get; set; }
5656
internal JsObject? ImportMeta { get; set; }
5757
internal object? LastValue { get; set; }
58+
internal bool HasAsyncDependency { get; set; }
5859
}
5960

6061
// Module registry: maps module paths to their exported values
@@ -718,17 +719,28 @@ private async Task StopEventLoopAsync()
718719
moduleKey);
719720
_moduleRegistry[moduleKey] = entry;
720721
}
722+
entry.HasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
723+
new HashSet<string>(StringComparer.Ordinal));
721724
}
722725
else
723726
{
724727
entry = CreateModuleEntry(EnsureStrictProgram(program),
725728
CreateModuleEnvironment(moduleKey),
726729
new JsObject(),
727730
string.Empty);
731+
entry.HasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
732+
new HashSet<string>(StringComparer.Ordinal));
728733
}
729734

730735
EnsureModuleInstantiated(entry);
731-
EnsureModuleEvaluated(entry);
736+
if (entry.IsAsync || entry.HasAsyncDependency)
737+
{
738+
EnsureModuleEvaluatedAsync(entry).GetAwaiter().GetResult();
739+
}
740+
else
741+
{
742+
EnsureModuleEvaluated(entry);
743+
}
732744
return entry.LastValue;
733745
}
734746

@@ -785,17 +797,21 @@ private async Task StopEventLoopAsync()
785797
moduleKey);
786798
_moduleRegistry[moduleKey] = entry;
787799
}
800+
entry.HasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
801+
new HashSet<string>(StringComparer.Ordinal));
788802
}
789803
else
790804
{
791805
entry = CreateModuleEntry(EnsureStrictProgram(program),
792806
CreateModuleEnvironment(moduleKey),
793807
new JsObject(),
794808
string.Empty);
809+
entry.HasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
810+
new HashSet<string>(StringComparer.Ordinal));
795811
}
796812

797813
EnsureModuleInstantiated(entry);
798-
if (entry.IsAsync)
814+
if (entry.IsAsync || entry.HasAsyncDependency)
799815
{
800816
await EnsureModuleEvaluatedAsync(entry).ConfigureAwait(false);
801817
}
@@ -883,17 +899,21 @@ private async Task StopEventLoopAsync()
883899
moduleKey);
884900
_moduleRegistry[moduleKey] = entry;
885901
}
902+
entry.HasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
903+
new HashSet<string>(StringComparer.Ordinal));
886904
}
887905
else
888906
{
889907
entry = CreateModuleEntry(EnsureStrictProgram(program),
890908
CreateModuleEnvironment(moduleKey),
891909
new JsObject(),
892910
string.Empty);
911+
entry.HasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
912+
new HashSet<string>(StringComparer.Ordinal));
893913
}
894914

895915
EnsureModuleInstantiated(entry);
896-
if (entry.IsAsync)
916+
if (entry.IsAsync || entry.HasAsyncDependency)
897917
{
898918
EnsureModuleEvaluatedAsync(entry).GetAwaiter().GetResult();
899919
}
@@ -1449,7 +1469,9 @@ private bool ModuleHasAsyncDependency(
14491469
var importPhase = importStatement.IsDeferred ? ImportPhase.Defer : ImportPhase.Module;
14501470
var imported = LoadModuleForInstantiation(importStatement.ModulePath, modulePath, importPhase, null,
14511471
importStatement.Attributes, computeAsyncDependencies: false);
1452-
if (imported.IsAsync || ModuleHasAsyncDependency(imported.Program, imported.Path, visited))
1472+
if (imported.IsAsync ||
1473+
imported.HasAsyncDependency ||
1474+
ModuleHasAsyncDependency(imported.Program, imported.Path, visited))
14531475
{
14541476
return true;
14551477
}
@@ -1459,7 +1481,9 @@ private bool ModuleHasAsyncDependency(
14591481
{
14601482
var sourceEntry = LoadModuleForInstantiation(fromModule, modulePath, ImportPhase.Module,
14611483
computeAsyncDependencies: false);
1462-
if (sourceEntry.IsAsync || ModuleHasAsyncDependency(sourceEntry.Program, sourceEntry.Path, visited))
1484+
if (sourceEntry.IsAsync ||
1485+
sourceEntry.HasAsyncDependency ||
1486+
ModuleHasAsyncDependency(sourceEntry.Program, sourceEntry.Path, visited))
14631487
{
14641488
return true;
14651489
}
@@ -1470,7 +1494,9 @@ private bool ModuleHasAsyncDependency(
14701494
{
14711495
var sourceEntry = LoadModuleForInstantiation(exportAll.ModulePath, modulePath, ImportPhase.Module,
14721496
computeAsyncDependencies: false);
1473-
if (sourceEntry.IsAsync || ModuleHasAsyncDependency(sourceEntry.Program, sourceEntry.Path, visited))
1497+
if (sourceEntry.IsAsync ||
1498+
sourceEntry.HasAsyncDependency ||
1499+
ModuleHasAsyncDependency(sourceEntry.Program, sourceEntry.Path, visited))
14741500
{
14751501
return true;
14761502
}
@@ -1482,6 +1508,7 @@ private bool ModuleHasAsyncDependency(
14821508
var namespaceEntry = LoadModuleForInstantiation(exportNamespace.ModulePath, modulePath,
14831509
ImportPhase.Module, computeAsyncDependencies: false);
14841510
if (namespaceEntry.IsAsync ||
1511+
namespaceEntry.HasAsyncDependency ||
14851512
ModuleHasAsyncDependency(namespaceEntry.Program, namespaceEntry.Path, visited))
14861513
{
14871514
return true;
@@ -1575,7 +1602,9 @@ private void EnsureModuleEvaluated(ModuleEntry entry)
15751602

15761603
EnsureModuleInstantiated(entry);
15771604

1578-
if (!entry.IsAsync)
1605+
var requiresAsyncEvaluation = entry.IsAsync || entry.HasAsyncDependency;
1606+
1607+
if (!requiresAsyncEvaluation)
15791608
{
15801609
if (entry.Evaluating)
15811610
{
@@ -1598,7 +1627,9 @@ private void EnsureModuleEvaluated(ModuleEntry entry)
15981627
if (entry.EvaluationTask is null)
15991628
{
16001629
entry.Evaluating = true;
1601-
entry.EvaluationTask = EvaluateModuleBodyWithTopLevelAwait(entry, waitForAsync);
1630+
entry.EvaluationTask = entry.IsAsync
1631+
? EvaluateModuleBodyWithTopLevelAwait(entry, waitForAsync)
1632+
: EvaluateModuleBodyWithAsyncDependencies(entry, waitForAsync);
16021633
}
16031634

16041635
if (!waitForAsync)
@@ -2112,7 +2143,7 @@ private enum ImportPhase
21122143
// Load the module synchronously (it's cached if already loaded)
21132144
var referrerPath = callee?.CallingJsEnvironment is JsEnvironment env ? env.ModulePath : _currentModulePath;
21142145
var moduleEntry = LoadModule(specifierString, referrerPath, phase);
2115-
if (moduleEntry.IsAsync)
2146+
if (moduleEntry.IsAsync || moduleEntry.HasAsyncDependency)
21162147
{
21172148
await EnsureModuleEvaluatedAsync(moduleEntry).ConfigureAwait(false);
21182149
}
@@ -2176,10 +2207,14 @@ private ModuleEntry LoadModule(
21762207
if (_moduleRegistry.TryGetValue(resolvedPath, out var cachedEntry))
21772208
{
21782209
EnsureModuleImportMeta(cachedEntry);
2210+
var cachedHasAsyncDependency = ModuleHasAsyncDependency(cachedEntry.Program, cachedEntry.Path,
2211+
new HashSet<string>(StringComparer.Ordinal));
2212+
cachedEntry.HasAsyncDependency = cachedHasAsyncDependency;
2213+
cachedEntry.Environment.IsAsyncModule = cachedEntry.IsAsync;
21792214
EnsureModuleInstantiated(cachedEntry, phase, exportStarSet);
21802215
if (phase == ImportPhase.Module)
21812216
{
2182-
if (cachedEntry.IsAsync)
2217+
if (cachedEntry.IsAsync || cachedEntry.HasAsyncDependency)
21832218
{
21842219
EnsureModuleEvaluatedAsync(cachedEntry, waitForAsync: false);
21852220
}
@@ -2223,10 +2258,15 @@ private ModuleEntry LoadModule(
22232258

22242259
_moduleRegistry[resolvedPath] = entry;
22252260

2261+
var computedHasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
2262+
new HashSet<string>(StringComparer.Ordinal));
2263+
entry.HasAsyncDependency = computedHasAsyncDependency;
2264+
entry.Environment.IsAsyncModule = entry.IsAsync;
2265+
22262266
EnsureModuleInstantiated(entry, phase, exportStarSet);
22272267
if (phase == ImportPhase.Module)
22282268
{
2229-
if (entry.IsAsync)
2269+
if (entry.IsAsync || entry.HasAsyncDependency)
22302270
{
22312271
EnsureModuleEvaluatedAsync(entry, waitForAsync: false);
22322272
}
@@ -2308,11 +2348,7 @@ private ModuleEntry LoadModuleForInstantiation(
23082348
{
23092349
var hasAsyncDependency = ModuleHasAsyncDependency(cachedEntry.Program, cachedEntry.Path,
23102350
new HashSet<string>(StringComparer.Ordinal));
2311-
if (hasAsyncDependency)
2312-
{
2313-
cachedEntry.IsAsync = true;
2314-
}
2315-
2351+
cachedEntry.HasAsyncDependency = hasAsyncDependency;
23162352
cachedEntry.Environment.IsAsyncModule = cachedEntry.IsAsync;
23172353
}
23182354
// Only instantiate, don't evaluate
@@ -2353,6 +2389,7 @@ private ModuleEntry LoadModuleForInstantiation(
23532389
{
23542390
var hasAsyncDependency = ModuleHasAsyncDependency(entry.Program, entry.Path,
23552391
new HashSet<string>(StringComparer.Ordinal));
2392+
entry.HasAsyncDependency = hasAsyncDependency;
23562393
entry.Environment.IsAsyncModule = entry.IsAsync;
23572394
}
23582395

@@ -3315,23 +3352,55 @@ private IReadOnlyList<ModuleEntry> GetModuleDependencies(ModuleEntry entry)
33153352
return dependencies;
33163353
}
33173354

3318-
private async Task<object?> EvaluateModuleBodyWithTopLevelAwait(
3355+
private async Task<object?> EvaluateModuleBodyWithAsyncDependencies(
33193356
ModuleEntry entry,
33203357
bool drainAwaitMicrotasks = true)
33213358
{
33223359
entry.Evaluating = true;
33233360
try
33243361
{
3325-
var dependencyTasks = new List<Task<object?>>();
33263362
foreach (var dependency in GetModuleDependencies(entry))
33273363
{
33283364
EnsureModuleInstantiated(dependency);
3329-
dependencyTasks.Add(EnsureModuleEvaluatedAsync(dependency, waitForAsync: true));
3365+
await EnsureModuleEvaluatedAsync(dependency, waitForAsync: true).ConfigureAwait(false);
33303366
}
33313367

3332-
if (dependencyTasks.Count > 0)
3368+
var previousModulePath = _currentModulePath;
3369+
_currentModulePath = entry.Path;
3370+
try
33333371
{
3334-
await Task.WhenAll(dependencyTasks).ConfigureAwait(false);
3372+
var result = ExecuteModuleBody(
3373+
entry.Program,
3374+
entry.Environment,
3375+
entry.Exports,
3376+
entry.Path,
3377+
drainAwaitMicrotasks);
3378+
entry.LastValue = result;
3379+
entry.Evaluated = true;
3380+
return result;
3381+
}
3382+
finally
3383+
{
3384+
_currentModulePath = previousModulePath;
3385+
}
3386+
}
3387+
finally
3388+
{
3389+
entry.Evaluating = false;
3390+
}
3391+
}
3392+
3393+
private async Task<object?> EvaluateModuleBodyWithTopLevelAwait(
3394+
ModuleEntry entry,
3395+
bool drainAwaitMicrotasks = true)
3396+
{
3397+
entry.Evaluating = true;
3398+
try
3399+
{
3400+
foreach (var dependency in GetModuleDependencies(entry))
3401+
{
3402+
EnsureModuleInstantiated(dependency);
3403+
await EnsureModuleEvaluatedAsync(dependency, waitForAsync: true).ConfigureAwait(false);
33353404
}
33363405

33373406
var result = await Task
@@ -3398,18 +3467,15 @@ private void EvaluateImport(
33983467
}
33993468

34003469
var engine = moduleEnv.RealmState?.Engine;
3401-
var hasBindings = importStatement.DefaultBinding is not null ||
3402-
importStatement.NamespaceBinding is not null ||
3403-
!importStatement.NamedImports.IsEmpty;
3470+
var isAsyncImport = moduleEntry.IsAsync || moduleEntry.HasAsyncDependency;
34043471

34053472
List<Action>? preservedMicrotasks = null;
34063473

34073474
try
34083475
{
34093476
var shouldWaitForAsync =
34103477
!importStatement.IsDeferred &&
3411-
moduleEntry.IsAsync &&
3412-
hasBindings &&
3478+
isAsyncImport &&
34133479
!string.Equals(moduleEntry.Path, referrerPath, StringComparison.Ordinal) &&
34143480
engine is not null;
34153481

@@ -3420,17 +3486,10 @@ importStatement.NamespaceBinding is not null ||
34203486

34213487
if (!importStatement.IsDeferred)
34223488
{
3423-
if (moduleEntry.IsAsync &&
3489+
if (isAsyncImport &&
34243490
!string.Equals(moduleEntry.Path, referrerPath, StringComparison.Ordinal))
34253491
{
3426-
if (!hasBindings)
3427-
{
3428-
EnsureModuleEvaluatedAsync(moduleEntry, waitForAsync: false);
3429-
}
3430-
else
3431-
{
3432-
EnsureModuleEvaluatedAsync(moduleEntry).GetAwaiter().GetResult();
3433-
}
3492+
EnsureModuleEvaluatedAsync(moduleEntry).GetAwaiter().GetResult();
34343493
}
34353494
else
34363495
{

0 commit comments

Comments
 (0)