Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions JSTests/modules/dynamic-import-tla-cycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { shouldBe } from "./resources/assert.js";

let settled = 0;
setTimeout(() => {
if (settled != 4)
throw new Error("dynamic import() inside a TLA cycle deadlocked: " + settled + "/4 settled");
if (settled != 5)
throw new Error("dynamic import() inside a TLA cycle deadlocked: " + settled + "/5 settled");
}, 1000);

for (const name of ["direct", "helper", "helper-after-await", "all"]) {
for (const name of ["direct", "helper", "helper-after-await", "all", "for-await"]) {
const { ns } = await import("./dynamic-import-tla-cycle/entry-" + name + ".js");
shouldBe(ns.v, name);
++settled;
Expand Down
2 changes: 2 additions & 0 deletions JSTests/modules/dynamic-import-tla-cycle/child-for-await.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { helper } from "./entry-for-await.js";
export const v = helper();
3 changes: 3 additions & 0 deletions JSTests/modules/dynamic-import-tla-cycle/entry-for-await.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { loadForAwait } from "./loader.js";
export function helper() { return "for-await"; }
export const ns = await loadForAwait("./child-for-await.js");
7 changes: 7 additions & 0 deletions JSTests/modules/dynamic-import-tla-cycle/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ export async function loadAfterAwait(specifier) {
export async function loadAll(specifier) {
return await Promise.all([import(specifier), Promise.resolve(1)]);
}

export async function loadForAwait(specifier) {
await null;
function* lazily() { yield import(specifier); }
for await (const ns of lazily())
return ns;
}
36 changes: 23 additions & 13 deletions Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "JSPromise.h"
#if USE(BUN_JSC_ADDITIONS)
#include "InternalFieldTuple.h"
#include "JSAsyncFromSyncIterator.h"
#include "JSAsyncFunctionGenerator.h"
#include "JSPromiseCombinatorsGlobalContext.h"
#endif
Expand Down Expand Up @@ -1254,23 +1255,32 @@ static bool importPromiseGatesAsyncDependency(JSPromise* importPromise, CyclicMo
}
};

// A promise, or the async function or module body that an await or for-await resumes.
auto followPromiseOrDriver = [&](JSCell* cell) {
if (!cell)
return;
if (auto* promise = dynamicDowncast<JSPromise>(cell))
work.append(promise);
else if (auto* generator = dynamicDowncast<JSAsyncFunctionGenerator>(cell))
follow(generator->context());
else if (auto* module = dynamicDowncast<AbstractModuleRecord>(cell))
found = resumesDependency(module);
};

auto visitReaction = [&](InternalMicrotask task, JSValue cell, JSValue context) -> bool {
switch (task) {
case InternalMicrotask::AsyncFunctionResume:
case InternalMicrotask::AsyncGeneratorDriverResume: {
JSCell* driver = unwrapContext(context);
if (!driver)
break;
if (auto* generator = dynamicDowncast<JSAsyncFunctionGenerator>(driver))
follow(generator->context());
else if (auto* module = dynamicDowncast<AbstractModuleRecord>(driver))
found = resumesDependency(module);
case InternalMicrotask::AsyncModuleExecutionResume:
case InternalMicrotask::AsyncGeneratorDriverResume:
followPromiseOrDriver(unwrapContext(context));
break;
}
case InternalMicrotask::AsyncModuleExecutionResume: {
JSCell* driver = unwrapContext(context);
if (auto* module = driver ? dynamicDowncast<AbstractModuleRecord>(driver) : nullptr)
found = resumesDependency(module);
case InternalMicrotask::AsyncFromSyncIteratorContinue:
case InternalMicrotask::AsyncFromSyncIteratorDone: {
// for-await over sync values that are promises: the pending step settles the
// iterator's result promise, or resumes its driver, with this promise's value.
JSCell* iteratorCell = unwrapContext(context);
if (auto* iterator = iteratorCell ? dynamicDowncast<JSAsyncFromSyncIterator>(iteratorCell) : nullptr)
followPromiseOrDriver(iterator->target());
break;
}
case InternalMicrotask::PromiseAllResolveJob:
Expand Down
5 changes: 5 additions & 0 deletions Source/JavaScriptCore/runtime/JSAsyncFromSyncIterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ class JSAsyncFromSyncIterator final : public JSNonFinalObject {
return { target, result };
}

#if USE(BUN_JSC_ADDITIONS)
// The promise or driver the pending step settles or resumes; null between steps.
JSObject* target() const { return m_target.pointer(); }
#endif

JSValue cachedDriverResult() const { return m_cachedResult.get(); }
void setCachedDriverResult(VM& vm, JSObject* result) { m_cachedResult.set(vm, this, result); }

Expand Down
Loading