mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
Issue #2229 - add better comments on top level await code
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user