Skip to content

Commit 674b8dd

Browse files
committed
[GR-37628] Backport: Recent changes in the specification of top level await.
PullRequest: js/2390
2 parents 428c3ad + 9ab1fa8 commit 674b8dd

File tree

7 files changed

+129
-109
lines changed

7 files changed

+129
-109
lines changed

graal-js/src/com.oracle.truffle.js.parser/src/com/oracle/truffle/js/parser/GraalJSEvaluator.java

Lines changed: 87 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import java.util.List;
5757
import java.util.Map;
5858
import java.util.Set;
59+
import java.util.TreeSet;
5960
import java.util.function.Supplier;
6061

6162
import com.oracle.js.parser.ir.Expression;
@@ -609,13 +610,14 @@ public void moduleInstantiation(JSRealm realm, JSModuleRecord moduleRecord) {
609610
throw e;
610611
}
611612

612-
assert moduleRecord.getStatus() == Status.Linked || moduleRecord.getStatus() == Status.Evaluated;
613+
assert moduleRecord.getStatus() == Status.Linked || moduleRecord.getStatus() == Status.EvaluatingAsync || moduleRecord.getStatus() == Status.Evaluated;
613614
assert stack.isEmpty();
614615
}
615616

616617
private int innerModuleInstantiation(JSRealm realm, JSModuleRecord moduleRecord, Deque<JSModuleRecord> stack, int index0) {
617618
int index = index0;
618-
if (moduleRecord.getStatus() == Status.Linking || moduleRecord.getStatus() == Status.Linked || moduleRecord.getStatus() == Status.Evaluated) {
619+
if (moduleRecord.getStatus() == Status.Linking || moduleRecord.getStatus() == Status.Linked || moduleRecord.getStatus() == Status.EvaluatingAsync ||
620+
moduleRecord.getStatus() == Status.Evaluated) {
619621
return index;
620622
}
621623
assert moduleRecord.getStatus() == Status.Unlinked;
@@ -630,7 +632,7 @@ private int innerModuleInstantiation(JSRealm realm, JSModuleRecord moduleRecord,
630632
JSModuleRecord requiredModule = hostResolveImportedModule(moduleRecord, requestedModule);
631633
index = innerModuleInstantiation(realm, requiredModule, stack, index);
632634
assert requiredModule.getStatus() == Status.Linking || requiredModule.getStatus() == Status.Linked ||
633-
requiredModule.getStatus() == Status.Evaluated : requiredModule.getStatus();
635+
requiredModule.getStatus() == Status.EvaluatingAsync || requiredModule.getStatus() == Status.Evaluated : requiredModule.getStatus();
634636
assert (requiredModule.getStatus() == Status.Linking) == stack.contains(requiredModule);
635637
if (requiredModule.getStatus() == Status.Linking) {
636638
moduleRecord.setDFSAncestorIndex(Math.min(moduleRecord.getDFSAncestorIndex(), requiredModule.getDFSAncestorIndex()));
@@ -674,9 +676,9 @@ public Object moduleEvaluation(JSRealm realm, JSModuleRecord moduleRecord) {
674676
JSModuleRecord module = moduleRecord;
675677
Deque<JSModuleRecord> stack = new ArrayDeque<>(4);
676678
if (realm.getContext().isOptionTopLevelAwait()) {
677-
assert module.getStatus() == Status.Linked || module.getStatus() == Status.Evaluated;
678-
if (module.getStatus() == Status.Evaluated) {
679-
module = getAsyncCycleRoot(module);
679+
assert module.getStatus() == Status.Linked || module.getStatus() == Status.EvaluatingAsync || module.getStatus() == Status.Evaluated;
680+
if (module.getStatus() == Status.EvaluatingAsync || module.getStatus() == Status.Evaluated) {
681+
module = module.getCycleRoot();
680682
}
681683
if (module.getTopLevelCapability() != null) {
682684
return module.getTopLevelCapability().getPromise();
@@ -685,9 +687,10 @@ public Object moduleEvaluation(JSRealm realm, JSModuleRecord moduleRecord) {
685687
module.setTopLevelCapability(capability);
686688
try {
687689
innerModuleEvaluation(realm, module, stack, 0);
688-
assert module.getStatus() == Status.Evaluated;
690+
assert module.getStatus() == Status.EvaluatingAsync || module.getStatus() == Status.Evaluated;
689691
assert module.getEvaluationError() == null;
690-
if (!module.isAsyncEvaluating()) {
692+
if (!module.isAsyncEvaluation()) {
693+
assert module.getStatus() == Status.Evaluated;
691694
JSFunction.call(JSArguments.create(Undefined.instance, capability.getResolve(), Undefined.instance));
692695
}
693696
assert stack.isEmpty();
@@ -720,39 +723,20 @@ public Object moduleEvaluation(JSRealm realm, JSModuleRecord moduleRecord) {
720723
}
721724
throw e;
722725
}
723-
assert module.getStatus() == Status.Evaluated && module.getEvaluationError() == null;
726+
assert module.getStatus() == Status.EvaluatingAsync || module.getStatus() == Status.Evaluated;
727+
assert module.getEvaluationError() == null;
724728

725729
assert stack.isEmpty();
726730
Object result = module.getExecutionResult();
727731
return result == null ? Undefined.instance : result;
728732
}
729733
}
730734

731-
@TruffleBoundary
732-
private static JSModuleRecord getAsyncCycleRoot(JSModuleRecord moduleRecord) {
733-
// GetAsyncCycleRoot ( module )
734-
JSModuleRecord module = moduleRecord;
735-
assert module.getStatus() == Status.Evaluated;
736-
737-
if (module.getAsyncParentModules().size() == 0) {
738-
return module;
739-
}
740-
while (module.getDFSIndex() > module.getDFSAncestorIndex()) {
741-
assert module.getAsyncParentModules().size() != 0;
742-
JSModuleRecord nextCycleModule = module.getAsyncParentModules().remove(0);
743-
assert nextCycleModule.getDFSAncestorIndex() <= module.getDFSAncestorIndex();
744-
module = nextCycleModule;
745-
}
746-
747-
assert module.getDFSIndex() == module.getDFSAncestorIndex();
748-
return module;
749-
}
750-
751735
@TruffleBoundary
752736
private int innerModuleEvaluation(JSRealm realm, JSModuleRecord moduleRecord, Deque<JSModuleRecord> stack, int index0) {
753737
// InnerModuleEvaluation( module, stack, index )
754738
int index = index0;
755-
if (moduleRecord.getStatus() == Status.Evaluated) {
739+
if (moduleRecord.getStatus() == Status.EvaluatingAsync || moduleRecord.getStatus() == Status.Evaluated) {
756740
if (moduleRecord.getEvaluationError() == null) {
757741
return index;
758742
} else {
@@ -777,26 +761,29 @@ private int innerModuleEvaluation(JSRealm realm, JSModuleRecord moduleRecord, De
777761
// Note: Instantiate must have completed successfully prior to invoking this method,
778762
// so every requested module is guaranteed to resolve successfully.
779763
index = innerModuleEvaluation(realm, requiredModule, stack, index);
780-
assert requiredModule.getStatus() == Status.Evaluating || requiredModule.getStatus() == Status.Evaluated : requiredModule.getStatus();
764+
assert requiredModule.getStatus() == Status.Evaluating || requiredModule.getStatus() == Status.EvaluatingAsync ||
765+
requiredModule.getStatus() == Status.Evaluated : requiredModule.getStatus();
781766
assert (requiredModule.getStatus() == Status.Evaluating) == stack.contains(requiredModule);
782767
if (requiredModule.getStatus() == Status.Evaluating) {
783768
moduleRecord.setDFSAncestorIndex(Math.min(moduleRecord.getDFSAncestorIndex(), requiredModule.getDFSAncestorIndex()));
784769
} else {
785-
requiredModule = getAsyncCycleRoot(requiredModule);
786-
assert requiredModule.getStatus() == Status.Evaluated;
770+
requiredModule = requiredModule.getCycleRoot();
771+
assert requiredModule.getStatus() == Status.EvaluatingAsync || requiredModule.getStatus() == Status.Evaluated;
787772
if (requiredModule.getEvaluationError() != null) {
788773
throw JSRuntime.rethrow(moduleRecord.getEvaluationError());
789774
}
790775
}
791-
if (requiredModule.isAsyncEvaluating()) {
776+
if (requiredModule.isAsyncEvaluation()) {
792777
moduleRecord.incPendingAsyncDependencies();
793778
requiredModule.appendAsyncParentModules(moduleRecord);
794779
}
795780
}
796-
if (moduleRecord.getPendingAsyncDependencies() > 0) {
797-
moduleRecord.setAsyncEvaluating(true);
798-
} else if (moduleRecord.isTopLevelAsync()) {
799-
moduleAsyncExecution(realm, moduleRecord);
781+
if (moduleRecord.getPendingAsyncDependencies() > 0 || moduleRecord.hasTLA()) {
782+
assert !moduleRecord.isAsyncEvaluation();
783+
moduleRecord.setAsyncEvaluatingOrder(realm.nextAsyncEvaluationOrder());
784+
if (moduleRecord.getPendingAsyncDependencies() == 0) {
785+
moduleAsyncExecution(realm, moduleRecord);
786+
}
800787
} else {
801788
Object result = moduleExecution(realm, moduleRecord, null);
802789
moduleRecord.setExecutionResult(result);
@@ -807,7 +794,12 @@ private int innerModuleEvaluation(JSRealm realm, JSModuleRecord moduleRecord, De
807794
if (moduleRecord.getDFSAncestorIndex() == moduleRecord.getDFSIndex()) {
808795
while (true) {
809796
JSModuleRecord requiredModule = stack.pop();
810-
requiredModule.setStatus(Status.Evaluated);
797+
if (!requiredModule.isAsyncEvaluation()) {
798+
requiredModule.setStatus(Status.Evaluated);
799+
} else {
800+
requiredModule.setStatus(Status.EvaluatingAsync);
801+
}
802+
requiredModule.setCycleRoot(moduleRecord);
811803
if (requiredModule.equals(moduleRecord)) {
812804
break;
813805
}
@@ -819,9 +811,8 @@ private int innerModuleEvaluation(JSRealm realm, JSModuleRecord moduleRecord, De
819811
@TruffleBoundary
820812
private static void moduleAsyncExecution(JSRealm realm, JSModuleRecord module) {
821813
// ExecuteAsyncModule ( module )
822-
assert module.getStatus() == Status.Evaluating || module.getStatus() == Status.Evaluated;
823-
assert module.isTopLevelAsync();
824-
module.setAsyncEvaluating(true);
814+
assert module.getStatus() == Status.Evaluating || module.getStatus() == Status.EvaluatingAsync;
815+
assert module.hasTLA();
825816
PromiseCapabilityRecord capability = NewPromiseCapabilityNode.createDefault(realm);
826817
DynamicObject onFulfilled = createCallAsyncModuleFulfilled(realm, module);
827818
DynamicObject onRejected = createCallAsyncModuleRejected(realm, module);
@@ -885,71 +876,91 @@ public Object execute(VirtualFrame frame) {
885876
return JSFunctionData.createCallOnly(context, callTarget, 1, "");
886877
}
887878

879+
private static void gatherAvailableAncestors(JSModuleRecord module, Set<JSModuleRecord> execList) {
880+
// GatherAvailableAncestors ( module, execList )
881+
for (JSModuleRecord m : module.getAsyncParentModules()) {
882+
if (!execList.contains(m) && m.getCycleRoot().getEvaluationError() == null) {
883+
assert m.getStatus() == Status.EvaluatingAsync;
884+
assert m.getEvaluationError() == null;
885+
assert m.isAsyncEvaluation();
886+
assert m.getPendingAsyncDependencies() > 0;
887+
m.decPendingAsyncDependencies();
888+
if (m.getPendingAsyncDependencies() == 0) {
889+
execList.add(m);
890+
if (!m.hasTLA()) {
891+
gatherAvailableAncestors(m, execList);
892+
}
893+
}
894+
}
895+
}
896+
}
897+
888898
@TruffleBoundary
889899
private static Object asyncModuleExecutionFulfilled(JSRealm realm, JSModuleRecord module, Object dynamicImportResolutionResult) {
890-
assert module.getStatus() == Status.Evaluated;
891-
if (!module.isAsyncEvaluating()) {
900+
if (module.getStatus() == Status.Evaluated) {
892901
assert module.getEvaluationError() != null;
893902
return Undefined.instance;
894903
}
904+
assert module.getStatus() == Status.EvaluatingAsync;
905+
assert module.isAsyncEvaluation();
895906
assert module.getEvaluationError() == null;
896-
module.setAsyncEvaluating(false);
897-
for (JSModuleRecord m : module.getAsyncParentModules()) {
898-
if (module.getDFSIndex() != module.getDFSAncestorIndex()) {
899-
assert m.getDFSAncestorIndex() <= module.getDFSAncestorIndex();
900-
}
901-
m.decPendingAsyncDependencies();
902-
if (m.getPendingAsyncDependencies() == 0 && m.getEvaluationError() == null) {
903-
assert m.isAsyncEvaluating();
904-
JSModuleRecord cycleRoot = getAsyncCycleRoot(m);
905-
if (cycleRoot.getEvaluationError() != null) {
906-
return Undefined.instance;
907-
}
908-
if (m.isTopLevelAsync()) {
909-
moduleAsyncExecution(realm, m);
910-
} else {
911-
try {
912-
moduleExecution(realm, m, null);
913-
asyncModuleExecutionFulfilled(realm, m, dynamicImportResolutionResult);
914-
} catch (Exception e) {
915-
asyncModuleExecutionRejected(realm, m, e);
907+
module.setStatus(Status.Evaluated);
908+
if (module.getTopLevelCapability() != null) {
909+
assert module.getCycleRoot() == module;
910+
JSFunction.call(JSArguments.create(Undefined.instance, module.getTopLevelCapability().getResolve(), dynamicImportResolutionResult));
911+
}
912+
Set<JSModuleRecord> execList = new TreeSet<>(new Comparator<JSModuleRecord>() {
913+
@Override
914+
public int compare(JSModuleRecord o1, JSModuleRecord o2) {
915+
return Long.compare(o1.getAsyncEvaluatingOrder(), o2.getAsyncEvaluatingOrder());
916+
}
917+
});
918+
gatherAvailableAncestors(module, execList);
919+
for (JSModuleRecord m : execList) {
920+
if (m.getStatus() == Status.Evaluated) {
921+
assert m.getEvaluationError() != null;
922+
} else if (m.hasTLA()) {
923+
moduleAsyncExecution(realm, m);
924+
} else {
925+
try {
926+
moduleExecution(realm, m, null);
927+
m.setStatus(Status.Evaluated);
928+
if (m.getTopLevelCapability() != null) {
929+
assert m.getCycleRoot() == m;
930+
JSFunction.call(JSArguments.create(Undefined.instance, m.getTopLevelCapability().getResolve(), dynamicImportResolutionResult));
916931
}
932+
} catch (Exception e) {
933+
asyncModuleExecutionRejected(realm, m, e);
917934
}
918935
}
919936
}
920-
if (module.getTopLevelCapability() != null) {
921-
assert module.getDFSIndex() == module.getDFSAncestorIndex();
922-
JSFunction.call(JSArguments.create(Undefined.instance, module.getTopLevelCapability().getResolve(), dynamicImportResolutionResult));
923-
}
924937
return Undefined.instance;
925938
}
926939

927940
@TruffleBoundary
928941
private static Object asyncModuleExecutionRejected(JSRealm realm, JSModuleRecord module, Object error) {
929942
assert error != null : "Cannot reject a module creation with null error";
930-
assert module.getStatus() == Status.Evaluated;
931-
if (!module.isAsyncEvaluating()) {
943+
if (module.getStatus() == Status.Evaluated) {
932944
assert module.getEvaluationError() != null;
933945
return Undefined.instance;
934946
}
947+
assert module.getStatus() == Status.EvaluatingAsync;
948+
assert module.isAsyncEvaluation();
935949
assert module.getEvaluationError() == null;
936950
module.setEvaluationError(JSRuntime.getException(error));
937-
module.setAsyncEvaluating(false);
951+
module.setStatus(Status.Evaluated);
938952
for (JSModuleRecord m : module.getAsyncParentModules()) {
939-
if (module.getDFSIndex() != module.getDFSAncestorIndex()) {
940-
assert m.getDFSAncestorIndex() == module.getDFSAncestorIndex();
941-
}
942953
asyncModuleExecutionRejected(realm, m, error);
943954
}
944955
if (module.getTopLevelCapability() != null) {
945-
assert module.getDFSIndex() == module.getDFSAncestorIndex();
956+
assert module.getCycleRoot() == module;
946957
JSFunction.call((DynamicObject) module.getTopLevelCapability().getReject(), Undefined.instance, new Object[]{error});
947958
}
948959
return Undefined.instance;
949960
}
950961

951962
private static Object moduleExecution(JSRealm realm, JSModuleRecord moduleRecord, PromiseCapabilityRecord capability) {
952-
if (!moduleRecord.isTopLevelAsync()) {
963+
if (!moduleRecord.hasTLA()) {
953964
assert capability == null;
954965
return JSFunction.call(JSArguments.create(Undefined.instance, JSFunction.create(realm, moduleRecord.getFunctionData()), moduleRecord));
955966
} else {

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/promise/ImportCallNode.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ protected Object finishDynamicImport(JSRealm realm, JSModuleRecord moduleRecord,
335335
// Note: PromiseReactionJob performs the promise rejection and resolution.
336336
assert moduleRecord == context.getEvaluator().hostResolveImportedModule(context, referencingScriptOrModule, moduleRequest);
337337
// Evaluate has already been invoked on moduleRecord and successfully completed.
338-
assert moduleRecord.isEvaluated();
338+
assert moduleRecord.hasBeenEvaluated();
339339
return context.getEvaluator().getModuleNamespace(moduleRecord);
340340
}
341341
}
@@ -356,7 +356,7 @@ public Object execute(VirtualFrame frame) {
356356
try {
357357
JSModuleRecord moduleRecord = context.getEvaluator().hostResolveImportedModule(context, referencingScriptOrModule, moduleRequest);
358358
JSRealm realm = getRealm();
359-
if (moduleRecord.isTopLevelAsync()) {
359+
if (moduleRecord.hasTLA()) {
360360
context.getEvaluator().moduleInstantiation(realm, moduleRecord);
361361
Object innerPromise = context.getEvaluator().moduleEvaluation(realm, moduleRecord);
362362
assert JSPromise.isJSPromise(innerPromise);
@@ -365,7 +365,7 @@ public Object execute(VirtualFrame frame) {
365365
promiseThenNode.execute((DynamicObject) innerPromise, resolve, reject, moduleLoadedCapability);
366366
} else {
367367
Object result = finishDynamicImport(realm, moduleRecord, referencingScriptOrModule, moduleRequest);
368-
if (moduleRecord.isAsyncEvaluating()) {
368+
if (moduleRecord.isAsyncEvaluation()) {
369369
// Some module import started an async loading chain. The top-level
370370
// capability will reject/resolve the dynamic import promise.
371371
PromiseCapabilityRecord topLevelCapability = moduleRecord.getTopLevelCapability();

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/JSRealm.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ public class JSRealm {
441441

442442
private final JSConsoleUtil consoleUtil;
443443
private JSModuleLoader moduleLoader;
444+
private long lastAsyncEvaluationOrder;
444445

445446
/**
446447
* ECMA2017 8.7 Agent object.
@@ -2755,4 +2756,8 @@ public void closeInnerContexts() {
27552756
}
27562757
}
27572758

2759+
public long nextAsyncEvaluationOrder() {
2760+
return ++lastAsyncEvaluationOrder;
2761+
}
2762+
27582763
}

0 commit comments

Comments
 (0)