Issue #2229 - add better comments on top level await code

This commit is contained in:
Basilisk-Dev
2026-02-07 21:44:19 -05:00
committed by roytam1
parent 2e88bbe575
commit 903f228fa0
5 changed files with 20 additions and 4 deletions
+2
View File
@@ -661,6 +661,7 @@ function InnerModuleEvaluation(module, stack, index)
// Step 11
let promise;
if (dependencyPromises.length > 0) {
// Defer execution until all async dependency evaluations settle.
promise = AwaitDependencies(dependencyPromises);
promise = callContentFunction(promise.then, promise, function() {
return ExecuteModule(module);
@@ -692,6 +693,7 @@ function InnerModuleEvaluation(module, stack, index)
if (promise !== undefined) {
if (cycleModules !== undefined) {
// For async cycles, finalize status transitions after promise settle.
let onFulfilled = function() {
for (let i = 0; i < cycleModules.length; i++)
ModuleSetStatus(cycleModules[i], MODULE_STATUS_EVALUATED);
+12 -3
View File
@@ -1058,6 +1058,8 @@ ModuleObject::suspend(JSContext* cx, HandleModuleObject self, HandleObject envCh
MOZ_ASSERT(!self->isAsyncEvaluating());
MOZ_ASSERT(envChain);
// Snapshot the interpreter's locals+operand stack so we can resume exactly
// at the await point without re-executing prior bytecode.
ArrayObject* stack = nullptr;
if (nvalues != 0) {
// Snapshot locals+stack for resumption after await.
@@ -1091,7 +1093,8 @@ ModuleObject::resume(JSContext* cx, HandleModuleObject self, bool throwOnResume,
RootedValue rval(cx);
ModuleResumeKind resumeKind =
throwOnResume ? ModuleResumeKind::Throw : ModuleResumeKind::Normal;
// Resume interpreter at the await point (normal or throwing).
// Resume interpreter at the await point (normal or throwing). This mirrors
// generator resumption but uses module evaluation state instead of a genobj.
if (!ResumeModuleExecution(cx, script, envChain, stack, resumeOffset,
resumeKind, value, rval.address()))
{
@@ -1108,6 +1111,8 @@ ModuleObject::resume(JSContext* cx, HandleModuleObject self, bool throwOnResume,
}
if (self->isAsyncEvaluating()) {
// The resumed code hit another top-level await. Chain the new await
// onto the same evaluation promise.
RootedValue awaitValue(cx, rval);
if (!AsyncModuleAwait(cx, self, awaitValue))
return false;
@@ -1136,8 +1141,8 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
RootedScript script(cx, self->script());
// The top-level script if a module is only ever executed once. Clear the
// reference to prevent us keeping this alive unnecessarily.
// The top-level script in a module is executed only once. If it doesn't
// suspend, clear the reference to allow GC. For TLA, keep it until resolve.
bool hasTopLevelAwait = script->hasTopLevelAwait();
Rooted<PromiseObject*> evalPromise(cx);
if (hasTopLevelAwait) {
@@ -1169,6 +1174,7 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
return false;
cx->setPendingException(exc, nullptr);
}
// Execution failed before any await continuation. Script is done.
self->setReservedSlot(ScriptSlot, UndefinedValue());
return false;
}
@@ -1185,6 +1191,7 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
}
MOZ_ASSERT(hasTopLevelAwait);
// Initial suspension: await the value and return the evaluation promise.
RootedValue awaitValue(cx, rval.get());
if (!AsyncModuleAwait(cx, self, awaitValue))
return false;
@@ -1877,6 +1884,8 @@ js::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, Han
return RejectPromiseWithPendingError(cx, promise);
}
// Dynamic import should resolve/reject when the in-flight module
// evaluation completes.
RootedAtom funName(cx, cx->names().empty);
RootedFunction onFulfilled(
cx, NewNativeFunction(cx, DynamicImportResolve, 0, funName,
+2
View File
@@ -4049,6 +4049,8 @@ js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, Hand
MOZ_MUST_USE bool
js::AsyncModuleAwait(JSContext* cx, Handle<ModuleObject*> module, HandleValue value)
{
// Use InternalAwait with module-specific reactions so resume happens on
// module evaluation instead of a generator object.
RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncModuleAwaitedFulfilled));
RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncModuleAwaitedRejected));
+1 -1
View File
@@ -6511,7 +6511,7 @@ BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope)
if (sc->isModuleContext()) {
// JSOP_AWAIT's stack model assumes [promise, gen] -> [resolved].
// In modules we don't push a generator, so compensate for the extra
// stack pop to keep stackDepth accurate.
// stack pop to keep stackDepth accurate (avoids bogus "script too large").
stackDepth++;
if ((uint32_t)stackDepth > maxStackDepth)
maxStackDepth = stackDepth;
+3
View File
@@ -845,6 +845,8 @@ js::ResumeModuleExecution(JSContext* cx, HandleScript script, HandleObject envCh
script->ensureNonLazyCanonicalFunction();
// If resuming with a throw, seed the pending exception for the interpreter
// to pick up at the await resumption point.
if (kind == ModuleResumeKind::Throw)
cx->setPendingExceptionAndCaptureStack(value);
@@ -4035,6 +4037,7 @@ CASE(JSOP_AWAIT)
MOZ_ASSERT(REGS.fp()->isModuleFrame());
// Module frames suspend on await without a generator object.
// We persist state on the ModuleObject and let promise reactions resume.
RootedModuleObject module(cx, REGS.fp()->script()->module());
MOZ_ASSERT(module);