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
30 changes: 23 additions & 7 deletions Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,11 @@ static void checkSafeToRecurse(JSGlobalObject* globalObject, ThrowScope& scope)
throwRangeError(globalObject, scope, "Maximum call stack size exceeded"_s);
}

#if USE(BUN_JSC_ADDITIONS)
unsigned AbstractModuleRecord::innerModuleEvaluation(JSGlobalObject* globalObject, Vector<AbstractModuleRecord*, 8>& stack, unsigned index, int64_t asyncOrderWatermark)
#else
unsigned AbstractModuleRecord::innerModuleEvaluation(JSGlobalObject* globalObject, Vector<AbstractModuleRecord*, 8>& stack, unsigned index)
#endif
{
// InnerModuleEvaluation(module, stack, index)
// https://tc39.es/ecma262/#sec-innermoduleevaluation
Expand Down Expand Up @@ -1146,13 +1150,21 @@ unsigned AbstractModuleRecord::innerModuleEvaluation(JSGlobalObject* globalObjec
// skip when the cycle root's pendingAsyncDependencies is 0, i.e. its
// ExecuteModule/ExecuteAsyncModule has already been called and the
// bindings before its first await are initialised. For an SCC still
// queued behind an async dep the root's count is > 0.
// queued behind an async dep the root's count is > 0. That alone is
// still insufficient: a TLA dep with no async deps of its own has
// count 0 yet may have only run to its first await within this same
// DFS, leaving post-await bindings TDZ (#30259). We additionally
// require asyncEvaluationOrder() < asyncOrderWatermark, i.e. the
// dep entered EvaluatingAsync in a *prior* Evaluate() call.
bool depWasAlreadyEvaluatingAsync = false;
if (auto* depCyclic = dynamicDowncast<CyclicModuleRecord>(requiredModule))
depWasAlreadyEvaluatingAsync = depCyclic->status() == Status::EvaluatingAsync;
#endif
// 11.b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
unsigned result = requiredModule->innerModuleEvaluation(globalObject, stack, index, asyncOrderWatermark);
#else
// 11.b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).
unsigned result = requiredModule->innerModuleEvaluation(globalObject, stack, index);
#endif
RETURN_IF_EXCEPTION(scope, invalid);
index = result;
// 11.c. If requiredModule is a Cyclic Module Record, then
Expand Down Expand Up @@ -1199,11 +1211,15 @@ unsigned AbstractModuleRecord::innerModuleEvaluation(JSGlobalObject* globalObjec
#if USE(BUN_JSC_ADDITIONS)
// See note above the recursive call: skip the spec-mandated
// wait to avoid self-deadlock and match old-loader behaviour.
// Only skip when the cycle root's body has already been
// entered (pendingAsyncDependencies == 0); otherwise the SCC
// is still queued behind an async dep and its bindings are
// TDZ, so the wait is required.
if (!depWasAlreadyEvaluatingAsync || cyclic->pendingAsyncDependencies().value_or(1)) {
// Only skip when the cycle root entered EvaluatingAsync in a
// *prior* Evaluate() (order < asyncOrderWatermark) — a dep
// that became EvaluatingAsync earlier in *this* DFS is a
// sibling whose post-await bindings are still TDZ (#30259) —
// and its body has already been entered
// (pendingAsyncDependencies == 0).
if (!depWasAlreadyEvaluatingAsync
|| cyclic->asyncEvaluationOrder().order() >= asyncOrderWatermark
|| cyclic->pendingAsyncDependencies().value_or(1)) {
#endif
// 11.c.v.1. Set module.[[PendingAsyncDependencies]] to module.[[PendingAsyncDependencies]] + 1.
module->setPendingAsyncDependencies(module->pendingAsyncDependencies().value() + 1);
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/runtime/AbstractModuleRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,11 @@ class AbstractModuleRecord : public JSInternalFieldObjectImpl<2> {
WriteBarrier<Unknown> internalField(Field field) const { return Base::internalField(static_cast<uint32_t>(field)); }

void evaluateModuleSync(JSGlobalObject*);
#if USE(BUN_JSC_ADDITIONS)
unsigned innerModuleEvaluation(JSGlobalObject*, Vector<AbstractModuleRecord*, 8>& stack, unsigned index, int64_t asyncOrderWatermark);
#else
unsigned innerModuleEvaluation(JSGlobalObject*, Vector<AbstractModuleRecord*, 8>& stack, unsigned index);
#endif
unsigned innerModuleLinking(JSGlobalObject*, Vector<CyclicModuleRecord*, 8>& stack, unsigned index, RefPtr<ScriptFetcher>);

DECLARE_VISIT_CHILDREN;
Expand Down
7 changes: 7 additions & 0 deletions Source/JavaScriptCore/runtime/CyclicModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,14 @@ JSPromise* CyclicModuleRecord::evaluate(JSGlobalObject* globalObject)
// 7. Set module.[[TopLevelCapability]] to capability.
module->setTopLevelCapability(vm, capability);
// 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)).
#if USE(BUN_JSC_ADDITIONS)
// Snapshot the async-order counter so the DFS can tell deps that were
// already EvaluatingAsync before this Evaluate() (re-entrant deadlock
// case, see note at 11.c.v) from siblings that transition during it.
module->innerModuleEvaluation(globalObject, stack, 0, vm.moduleAsyncEvaluationCount());
#else
module->innerModuleEvaluation(globalObject, stack, 0);
#endif
// 9. If result is an abrupt completion, then
if (Exception* exception = scope.exception()) {
JSModuleLoader::attachErrorInfo(globalObject, exception, module, moduleKey(), moduleType(), JSModuleLoader::ModuleFailure::Kind::Evaluation);
Expand Down
3 changes: 3 additions & 0 deletions Source/JavaScriptCore/runtime/VM.h
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,9 @@ class VM : public ThreadSafeRefCountedWithSuppressingSaferCPPChecking<VM> {
void notifyDebuggerHookInjected() { m_isDebuggerHookInjected = true; }
bool isDebuggerHookInjected() const { return m_isDebuggerHookInjected; }
int64_t incrementModuleAsyncEvaluationCount() { return m_moduleAsyncEvaluationCount++; }
#if USE(BUN_JSC_ADDITIONS)
int64_t moduleAsyncEvaluationCount() const { return m_moduleAsyncEvaluationCount; }
#endif

#if ENABLE(WEBASSEMBLY_DEBUGGER)
JS_EXPORT_PRIVATE Wasm::DebugState* NODELETE debugState();
Expand Down
Loading