Skip to content
Closed
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
24 changes: 23 additions & 1 deletion Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1347,9 +1347,31 @@ unsigned AbstractModuleRecord::innerModuleEvaluation(JSGlobalObject* globalObjec
// sibling whose post-await bindings are still TDZ (#30259) —
// and its body has already been entered
// (pendingAsyncDependencies == 0).
//
// The watermark alone is insufficient for the cross-Evaluate()
// dynamic-import case (#30651): two independent dynamic
// imports of the same TLA dep get fresh watermarks each, so
// the second one sees `order < watermark` and skips even
// though no deadlock is possible. Narrow further when the
// embedder is feeding us dynamic-import referrers (the VM
// set is non-empty): the Nitro self-deadlock the skip was
// written for only happens when the dep is the initiator
// of *this* Evaluate(), i.e. the module whose body is
// paused at `await import()`. For any other kind of
// await (setTimeout, fetch, another promise) or an
// unrelated parallel dynamic import, the dep will resume
// on its own and the spec wait is what we want.
//
// Gate the fourth condition on `hasPendingDynamicImport()`
// so embedders that don't yet plumb the referrer through
// fall back to the looser pre-#30651 behaviour — otherwise
// the spec wait would deadlock the Nitro-style tests.
bool discriminateByInitiator = vm.hasPendingDynamicImport();
bool depIsAwaitingDynamicImport = discriminateByInitiator && vm.isModuleAwaitingDynamicImport(cyclic);
if (!depWasAlreadyEvaluatingAsync
|| cyclic->asyncEvaluationOrder().order() >= asyncOrderWatermark
|| cyclic->pendingAsyncDependencies().value_or(1)) {
|| cyclic->pendingAsyncDependencies().value_or(1)
|| (discriminateByInitiator && !depIsAwaitingDynamicImport)) {
#endif
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// 12.b.v.1. Set module.[[PendingAsyncDependencies]] to module.[[PendingAsyncDependencies]] + 1.
module->setPendingAsyncDependencies(module->pendingAsyncDependencies().value() + 1);
Expand Down
36 changes: 34 additions & 2 deletions Source/JavaScriptCore/runtime/JSMicrotask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,16 @@ static void moduleLoadTopSettled(JSGlobalObject* globalObject, VM& vm, ThrowScop
if (context->useImportMap())
innerLoadFlags.add(ModuleLoadFlag::UseImportMap);
if (context->dynamic()) {
combinedCell = ModuleLoaderPayload::create(vm, statePromise, context->deferred());
auto* payload = ModuleLoaderPayload::create(vm, statePromise, context->deferred());
#if USE(BUN_JSC_ADDITIONS)
// Carry the initiator (the module that did `import(X)`) from
// the creating ModuleLoadingContext to the ModuleLoaderPayload
// that outlives it. dynamicImportLoadSettled reads it back to
// push/pop around the target's Evaluate(). See #30651.
if (auto* initiator = context->dynamicImportInitiator())
payload->setDynamicImportInitiator(vm, initiator);
#endif
combinedCell = payload;
loadPromise = globalObject->moduleLoader()->loadModule(globalObject, globalObject, request, combinedCell, scriptFetcher, innerLoadFlags);
} else {
combinedCell = ModuleGraphLoadingState::create(vm, statePromise, scriptFetcher);
Expand Down Expand Up @@ -1346,10 +1355,16 @@ static void dynamicImportLoadSettled(JSGlobalObject* globalObject, VM& vm, Throw
// Step-4 rejectedClosure or Step-6 linkAndEvaluateClosure
//
// continueDynamicImport: loadPromise settled
// arguments[0] = capabilityPromise
// arguments[0] = capabilityPromise (Bun: ModuleLoaderPayload carrying
// capabilityPromise + dynamic-import initiator)
// arguments[1] = resolution or error
// arguments[2] = AbstractModuleRecord*
#if USE(BUN_JSC_ADDITIONS)
auto* dynamicPayload = uncheckedDowncast<ModuleLoaderPayload>(arguments[0]);
auto* capabilityPromise = dynamicPayload->promise();
#else
auto* capabilityPromise = uncheckedDowncast<JSPromise>(arguments[0]);
#endif
auto* module = uncheckedDowncast<AbstractModuleRecord>(arguments[2]);
auto status = static_cast<JSPromise::Status>(payload);
if (status != JSPromise::Status::Fulfilled) {
Expand All @@ -1372,8 +1387,25 @@ static void dynamicImportLoadSettled(JSGlobalObject* globalObject, VM& vm, Throw
}

if (!deferred) {
#if USE(BUN_JSC_ADDITIONS)
// Push the initiator (the module whose body is awaiting this
// dynamic import) onto the VM set for the duration of the target's
// Evaluate(). innerModuleEvaluation's 11.c.v reads this to tell
// the Nitro self-deadlock (dep == initiator) from unrelated
// parallel dynamic imports (no match) — see #30651.
CyclicModuleRecord* initiator = dynamicPayload->dynamicImportInitiator();
if (initiator)
vm.pushDynamicImportInitiator(initiator);
#endif

// 6.c. Let evaluatePromise be module.Evaluate().
JSPromise* evaluatePromise = module->evaluate(globalObject);

#if USE(BUN_JSC_ADDITIONS)
if (initiator)
vm.popDynamicImportInitiator(initiator);
#endif

if (scope.exception()) [[unlikely]] {
capabilityPromise->rejectWithCaughtException(vm, scope);
return;
Expand Down
50 changes: 50 additions & 0 deletions Source/JavaScriptCore/runtime/JSModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,11 @@ void JSModuleLoader::provideFetch(JSGlobalObject* globalObject, const Identifier
entry->provideFetch(globalObject, jsSourceCode); // can throw
}

#if USE(BUN_JSC_ADDITIONS)
JSPromise* JSModuleLoader::loadModule(JSGlobalObject* globalObject, const Identifier& specifier, RefPtr<ScriptFetchParameters> parameters, RefPtr<ScriptFetcher> scriptFetcher, OptionSet<ModuleLoadFlag> flags, CyclicModuleRecord* dynamicImportInitiator)
#else
JSPromise* JSModuleLoader::loadModule(JSGlobalObject* globalObject, const Identifier& specifier, RefPtr<ScriptFetchParameters> parameters, RefPtr<ScriptFetcher> scriptFetcher, OptionSet<ModuleLoadFlag> flags)
#endif
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
Expand Down Expand Up @@ -381,6 +385,10 @@ JSPromise* JSModuleLoader::loadModule(JSGlobalObject* globalObject, const Identi
AbstractModuleRecord::ModuleRequest request { specifier, ScriptFetchParameters::create(type) };
#endif
auto* context = ModuleLoadingContext::create(vm, request, WTF::move(scriptFetcher), flags);
#if USE(BUN_JSC_ADDITIONS)
if (flags.contains(ModuleLoadFlag::Dynamic) && dynamicImportInitiator)
context->setDynamicImportInitiator(vm, dynamicImportInitiator);
#endif

JSPromise* intermediatePromise = JSPromise::create(vm, globalObject->promiseStructure());
intermediatePromise->markAsHandled();
Expand Down Expand Up @@ -448,7 +456,31 @@ JSPromise* JSModuleLoader::requestImportModule(JSGlobalObject* globalObject, con
OptionSet<ModuleLoadFlag> flags { ModuleLoadFlag::Evaluate, ModuleLoadFlag::Dynamic };
if (deferred)
flags.add(ModuleLoadFlag::Deferred);
#if USE(BUN_JSC_ADDITIONS)
// Resolve the referrer URL to a CyclicModuleRecord (the caller of
// `import()`) so loadModule can stash it on the ModuleLoadingContext.
// dynamicImportLoadSettled pushes it onto the VM's
// m_modulesAwaitingDynamicImport set around the target's Evaluate(),
// which lets innerModuleEvaluation 11.c.v tell the Nitro self-deadlock
// (cycle initiator == dep) from an unrelated parallel dynamic import
// (no match — spec wait is correct). See #30651.
CyclicModuleRecord* initiator = nullptr;
if (!referrer.isNull() && !referrer.isSymbol() && !referrer.isEmpty()) {
for (auto type : { ScriptFetchParameters::Type::JavaScript, ScriptFetchParameters::Type::WebAssembly, ScriptFetchParameters::Type::JSON, ScriptFetchParameters::Type::HostDefined }) {
if (ModuleRegistryEntry* entry = getRegisteredMayBeNull(referrer, type)) {
if (auto* record = entry->record()) {
if (auto* cyclic = dynamicDowncast<CyclicModuleRecord>(record)) {
initiator = cyclic;
break;
}
}
}
}
}
JSPromise* promise = loadModule(globalObject, resolved, WTF::move(parameters), WTF::move(scriptFetcher), flags, initiator);
#else
JSPromise* promise = loadModule(globalObject, resolved, WTF::move(parameters), WTF::move(scriptFetcher), flags);
#endif
RETURN_IF_EXCEPTION(scope, nullptr);

JSPromise* resultPromise = JSPromise::create(vm, globalObject->promiseStructure());
Expand Down Expand Up @@ -956,7 +988,11 @@ void JSModuleLoader::finishLoadingImportedModule(JSGlobalObject* globalObject, c
} else {
// 3.a. Perform ContinueDynamicImport(payload, result).
auto* dynamicPayload = uncheckedDowncast<ModuleLoaderPayload>(payload);
#if USE(BUN_JSC_ADDITIONS)
continueDynamicImport(globalObject, dynamicPayload, result, WTF::move(scriptFetcher), dynamicPayload->deferred());
#else
continueDynamicImport(globalObject, dynamicPayload->promise(), result, WTF::move(scriptFetcher), dynamicPayload->deferred());
#endif
RETURN_IF_EXCEPTION(scope, void());
}

Expand Down Expand Up @@ -1008,14 +1044,22 @@ void JSModuleLoader::continueModuleLoading(JSGlobalObject* globalObject, ModuleG
scope.release();
}

#if USE(BUN_JSC_ADDITIONS)
void JSModuleLoader::continueDynamicImport(JSGlobalObject* globalObject, ModuleLoaderPayload* dynamicPayload, ModuleCompletion completion, RefPtr<ScriptFetcher> scriptFetcher, bool deferred)
#else
void JSModuleLoader::continueDynamicImport(JSGlobalObject* globalObject, JSPromise* promise, ModuleCompletion completion, RefPtr<ScriptFetcher> scriptFetcher, bool deferred)
#endif
{
// ContinueDynamicImport(promiseCapability, moduleCompletion)
// https://tc39.es/ecma262/#sec-ContinueDynamicImport

VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);

#if USE(BUN_JSC_ADDITIONS)
JSPromise* promise = dynamicPayload->promise();
#endif

// 1. If moduleCompletion is an abrupt completion, then
if (Exception** exception = std::get_if<Exception*>(&completion)) {
// 1.a. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »).
Expand All @@ -1030,7 +1074,13 @@ void JSModuleLoader::continueDynamicImport(JSGlobalObject* globalObject, JSPromi
JSPromise* loadPromise = loadRequestedModules(globalObject, module, WTF::move(scriptFetcher));
RETURN_IF_EXCEPTION(scope, void());
// 4-8. Link and evaluate using microtask dispatch instead of closures.
#if USE(BUN_JSC_ADDITIONS)
// Route via the payload (not the promise) so dynamicImportLoadSettled
// can recover the initiator for #30651's push/pop.
loadPromise->performPromiseThenWithInternalMicrotask(vm, deferred ? InternalMicrotask::DynamicImportDeferLoadSettled : InternalMicrotask::DynamicImportLoadSettled, dynamicPayload, module);
#else
loadPromise->performPromiseThenWithInternalMicrotask(vm, deferred ? InternalMicrotask::DynamicImportDeferLoadSettled : InternalMicrotask::DynamicImportLoadSettled, promise, module);
#endif
// 9. Return UNUSED.
scope.release();
}
Expand Down
10 changes: 10 additions & 0 deletions Source/JavaScriptCore/runtime/JSModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@

namespace JSC {

class CyclicModuleRecord;
class ErrorInstance;
class JSPromise;
class JSModuleNamespaceObject;
class JSModuleRecord;
class JSSourceCode;
class ModuleLoaderPayload;
class ModuleRegistryEntry;
class SourceOrigin;

Expand Down Expand Up @@ -92,7 +94,11 @@ class JSModuleLoader final : public JSCell {
// APIs to control the module loader.
void provideFetch(JSGlobalObject*, const Identifier& key, ScriptFetchParameters::Type, SourceCode&&);
void provideFetch(JSGlobalObject*, const Identifier& key, ScriptFetchParameters::Type, JSSourceCode*);
#if USE(BUN_JSC_ADDITIONS)
JSPromise* loadModule(JSGlobalObject*, const Identifier& moduleName, RefPtr<ScriptFetchParameters>, RefPtr<ScriptFetcher>, OptionSet<ModuleLoadFlag>, CyclicModuleRecord* dynamicImportInitiator = nullptr);
#else
JSPromise* loadModule(JSGlobalObject*, const Identifier& moduleName, RefPtr<ScriptFetchParameters>, RefPtr<ScriptFetcher>, OptionSet<ModuleLoadFlag>);
#endif
JSPromise* linkAndEvaluateModule(JSGlobalObject*, const Identifier& moduleKey, RefPtr<ScriptFetchParameters>, RefPtr<ScriptFetcher>);
JSPromise* requestImportModule(JSGlobalObject*, const Identifier& moduleName, const Identifier& referrer, RefPtr<ScriptFetchParameters>, RefPtr<ScriptFetcher>, bool deferred = false);

Expand Down Expand Up @@ -159,7 +165,11 @@ class JSModuleLoader final : public JSCell {
JSPromise* hostLoadImportedModule(JSGlobalObject*, const ModuleReferrer&, const ModuleRequest&, JSCell* payload, RefPtr<ScriptFetcher>, bool useImportMap);
JSPromise* loadModule(JSGlobalObject*, const ModuleReferrer&, const ModuleRequest&, JSCell* payload, RefPtr<ScriptFetcher>, OptionSet<ModuleLoadFlag>);
void continueModuleLoading(JSGlobalObject*, ModuleGraphLoadingState*, ModuleCompletion result);
#if USE(BUN_JSC_ADDITIONS)
void continueDynamicImport(JSGlobalObject*, ModuleLoaderPayload*, ModuleCompletion, RefPtr<ScriptFetcher>, bool deferred);
#else
void continueDynamicImport(JSGlobalObject*, JSPromise*, ModuleCompletion, RefPtr<ScriptFetcher>, bool deferred);
#endif
JSPromise* loadRequestedModules(JSGlobalObject*, AbstractModuleRecord*, RefPtr<ScriptFetcher>);

static JSPromise* makeModule(JSGlobalObject*, const Identifier& moduleKey, JSSourceCode*);
Expand Down
15 changes: 15 additions & 0 deletions Source/JavaScriptCore/runtime/ModuleLoaderPayload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ void ModuleLoaderPayload::visitChildrenImpl(JSCell* cell, Visitor& visitor)
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_promise);
visitor.append(thisObject->m_fulfillment);
#if USE(BUN_JSC_ADDITIONS)
visitor.append(thisObject->m_dynamicImportInitiator);
#endif
}

DEFINE_VISIT_CHILDREN(ModuleLoaderPayload);
Expand All @@ -65,4 +68,16 @@ ModuleLoaderPayload* ModuleLoaderPayload::create(VM& vm, JSPromise* promise, boo
return instance;
}

#if USE(BUN_JSC_ADDITIONS)
CyclicModuleRecord* ModuleLoaderPayload::dynamicImportInitiator() const
{
return m_dynamicImportInitiator.get();
}

void ModuleLoaderPayload::setDynamicImportInitiator(VM& vm, CyclicModuleRecord* module)
{
m_dynamicImportInitiator.set(vm, this, module);
}
#endif

} // namespace JSC
15 changes: 15 additions & 0 deletions Source/JavaScriptCore/runtime/ModuleLoaderPayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

namespace JSC {

class CyclicModuleRecord;

// Wraps the dynamic-import target promise for top-level dynamic loadModule. Acts as the
// host-defined "payload" passed back via FinishLoadingImportedModule, and additionally
// holds the AND-join state used to combine loadPromise and statePromise.
Expand Down Expand Up @@ -64,13 +66,26 @@ class ModuleLoaderPayload final : public JSCell {
return !--m_remainingFulfillments;
}

#if USE(BUN_JSC_ADDITIONS)
// The initiator CyclicModuleRecord whose body is awaiting this import's
// result. Used by dynamicImportLoadSettled to push/pop around the
// target's Evaluate() so innerModuleEvaluation 11.c.v can tell the
// Nitro self-deadlock from an unrelated parallel dynamic import. See
// #30651.
CyclicModuleRecord* dynamicImportInitiator() const;
void setDynamicImportInitiator(VM&, CyclicModuleRecord*);
#endif

private:
ModuleLoaderPayload(VM&, Structure*, JSPromise*, bool deferred);

void finishCreation(VM&);

WriteBarrier<JSPromise> m_promise;
WriteBarrier<Unknown> m_fulfillment;
#if USE(BUN_JSC_ADDITIONS)
WriteBarrier<CyclicModuleRecord> m_dynamicImportInitiator;
#endif
uint8_t m_remainingFulfillments { 2 };
bool m_deferred { false };
};
Expand Down
15 changes: 15 additions & 0 deletions Source/JavaScriptCore/runtime/ModuleLoadingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ JSModuleLoader::ModuleReferrer ModuleLoadingContext::referrer() const
return uncheckedDowncast<JSGlobalObject>(ref);
}

#if USE(BUN_JSC_ADDITIONS)
void ModuleLoadingContext::setDynamicImportInitiator(VM& vm, CyclicModuleRecord* module)
{
m_dynamicImportInitiator.set(vm, this, module);
}

CyclicModuleRecord* ModuleLoadingContext::dynamicImportInitiator() const
{
return m_dynamicImportInitiator.get();
}
#endif

template<typename Visitor>
void ModuleLoadingContext::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
Expand All @@ -98,6 +110,9 @@ void ModuleLoadingContext::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(thisObject->m_entry);
visitor.append(thisObject->m_referrer);
visitor.append(thisObject->m_module);
#if USE(BUN_JSC_ADDITIONS)
visitor.append(thisObject->m_dynamicImportInitiator);
#endif
}

DEFINE_VISIT_CHILDREN(ModuleLoadingContext);
Expand Down
14 changes: 14 additions & 0 deletions Source/JavaScriptCore/runtime/ModuleLoadingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

namespace JSC {

class CyclicModuleRecord;
class ModuleRegistryEntry;
class ScriptFetcher;

Expand Down Expand Up @@ -78,6 +79,16 @@ class ModuleLoadingContext final : public JSCell {
bool dynamic() const { return m_flags.contains(ModuleLoadFlag::Dynamic); }
bool useImportMap() const { return m_flags.contains(ModuleLoadFlag::UseImportMap); }
bool deferred() const { return m_flags.contains(ModuleLoadFlag::Deferred); }
#if USE(BUN_JSC_ADDITIONS)
// Dynamic-import-only: the CyclicModuleRecord whose body is awaiting
// this import's result. Set by loadModule's dynamic overload if the
// caller can name a module referrer; used by dynamicImportLoadSettled
// to push/pop around the target's Evaluate() so innerModuleEvaluation's
// 11.c.v can distinguish the Nitro self-deadlock from a parallel
// unrelated dynamic import (#30651).
void setDynamicImportInitiator(VM&, CyclicModuleRecord*);
CyclicModuleRecord* dynamicImportInitiator() const;
#endif

private:
ModuleLoadingContext(VM&, Structure*, Step, const JSModuleLoader::ModuleReferrer&, AbstractModuleRecord::ModuleRequest&&, JSCell* payload, ModuleRegistryEntry*, RefPtr<ScriptFetcher>);
Expand All @@ -91,6 +102,9 @@ class ModuleLoadingContext final : public JSCell {
WriteBarrier<Unknown> m_referrer;
WriteBarrier<AbstractModuleRecord> m_module;
OptionSet<ModuleLoadFlag> m_flags;
#if USE(BUN_JSC_ADDITIONS)
WriteBarrier<CyclicModuleRecord> m_dynamicImportInitiator;
#endif
};

} // namespace JSC
19 changes: 19 additions & 0 deletions Source/JavaScriptCore/runtime/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2117,4 +2117,23 @@ Wasm::DebugState* VM::debugState()
}
#endif

#if USE(BUN_JSC_ADDITIONS)
void VM::pushDynamicImportInitiator(CyclicModuleRecord* module)
{
if (module)
m_modulesAwaitingDynamicImport.add(module);
}

void VM::popDynamicImportInitiator(CyclicModuleRecord* module)
{
if (module)
m_modulesAwaitingDynamicImport.remove(module);
}

bool VM::isModuleAwaitingDynamicImport(CyclicModuleRecord* module) const
{
return module && m_modulesAwaitingDynamicImport.contains(module);
}
Comment on lines +2121 to +2136

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Assert VM-thread ownership and balanced push/pop here.

These helpers drive the re-entrancy exception, but they currently fail open: cross-thread access to m_modulesAwaitingDynamicImport is unchecked, and an unmatched popDynamicImportInitiator() silently removes nothing. Add API-lock assertions to all three helpers and assert membership before removing so a mis-bracketed caller trips immediately instead of corrupting the skip state.

Proposed hardening
 `#if` USE(BUN_JSC_ADDITIONS)
 void VM::pushDynamicImportInitiator(CyclicModuleRecord* module)
 {
+    ASSERT(currentThreadIsHoldingAPILock());
     if (module)
         m_modulesAwaitingDynamicImport.add(module);
 }
 
 void VM::popDynamicImportInitiator(CyclicModuleRecord* module)
 {
-    if (module)
+    ASSERT(currentThreadIsHoldingAPILock());
+    if (module) {
+        ASSERT(m_modulesAwaitingDynamicImport.contains(module));
         m_modulesAwaitingDynamicImport.remove(module);
+    }
 }
 
 bool VM::isModuleAwaitingDynamicImport(CyclicModuleRecord* module) const
 {
+    ASSERT(currentThreadIsHoldingAPILock());
     return module && m_modulesAwaitingDynamicImport.contains(module);
 }
 `#endif`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void VM::pushDynamicImportInitiator(CyclicModuleRecord* module)
{
if (module)
m_modulesAwaitingDynamicImport.add(module);
}
void VM::popDynamicImportInitiator(CyclicModuleRecord* module)
{
if (module)
m_modulesAwaitingDynamicImport.remove(module);
}
bool VM::isModuleAwaitingDynamicImport(CyclicModuleRecord* module) const
{
return module && m_modulesAwaitingDynamicImport.contains(module);
}
`#if` USE(BUN_JSC_ADDITIONS)
void VM::pushDynamicImportInitiator(CyclicModuleRecord* module)
{
ASSERT(currentThreadIsHoldingAPILock());
if (module)
m_modulesAwaitingDynamicImport.add(module);
}
void VM::popDynamicImportInitiator(CyclicModuleRecord* module)
{
ASSERT(currentThreadIsHoldingAPILock());
if (module) {
ASSERT(m_modulesAwaitingDynamicImport.contains(module));
m_modulesAwaitingDynamicImport.remove(module);
}
}
bool VM::isModuleAwaitingDynamicImport(CyclicModuleRecord* module) const
{
ASSERT(currentThreadIsHoldingAPILock());
return module && m_modulesAwaitingDynamicImport.contains(module);
}
`#endif`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Source/JavaScriptCore/runtime/VM.cpp` around lines 2097 - 2112, Add
VM-thread/API-lock assertions and a membership check to harden dynamic-import
helpers: in VM::pushDynamicImportInitiator, VM::popDynamicImportInitiator and
VM::isModuleAwaitingDynamicImport assert that the VM/API lock (the VM thread
ownership) is held before touching m_modulesAwaitingDynamicImport; additionally,
in VM::popDynamicImportInitiator assert that
m_modulesAwaitingDynamicImport.contains(module) is true before calling remove so
an unmatched pop immediately fails rather than silently no-op. Use the project’s
existing VM/API lock assertion macro or helper when adding these checks.

#endif

} // namespace JSC
Loading
Loading