From 205ed37a113c02fda9df96b21a85ad08206e8f3b Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 7 Aug 2026 10:15:26 -0700 Subject: [PATCH] JSModuleLoader::hostLoadImportedModule: propagate a TerminationException from resolve() instead of treating it as a resolution failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Host resolve() hooks can surface a TerminationException (RETURN_IF_EXCEPTION services VM traps, so a terminate request lands at any exception check). The resolution-error path attached error info to it, cached it as the specifier's resolution failure, called rejectWithCaughtException() — which deliberately leaves a TerminationException pending — and then went on into finishLoadingImportedModule() with the exception still set, tripping scope.assertNoException() in continueDynamicImport(). Every other rejectWithCaughtException() in this file is wrapped in RETURN_IF_EXCEPTION for exactly this reason; this path was missing the equivalent. Both callers already handle a null promise with an exception pending. --- Source/JavaScriptCore/runtime/JSModuleLoader.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp index 2b12fd03a0261..0bc095c90b246 100644 --- a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp +++ b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp @@ -660,6 +660,11 @@ JSPromise* JSModuleLoader::hostLoadImportedModule(JSGlobalObject* globalObject, resolved = resolve(globalObject, specifier, referrerKey, scriptFetcher, useImportMap); // 9. If the previous step threw an exception, then: if (Exception* resolutionError = scope.exception()) { + // A TerminationException is not a resolution failure: it can be neither cleared + // (rejectWithCaughtException leaves it pending) nor cached, so let it propagate + // instead of continuing into FinishLoadingImportedModule with it still set. + if (vm.isTerminationException(resolutionError)) [[unlikely]] + RELEASE_AND_RETURN(scope, nullptr); attachErrorInfo(globalObject, resolutionError, nullptr, specifier, moduleRequest.type(), ModuleFailure::Kind::Instantiation); // Cache the resolution error so subsequent calls for the same specifier return the same error object. JSValue errorValue = resolutionError->value();