diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 0b31c8b6c..dd4072da7 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -912,6 +912,12 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM const state: *DynamicModuleResolveState = @ptrCast(@alignCast(ctx)); var self = state.context; + if (self.env.terminatePending()) { + var module_source = module_source_ catch return; + module_source.deinit(); + return; + } + var ls: js.Local.Scope = undefined; self.localScope(&ls); defer ls.deinit(); @@ -1024,6 +1030,39 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul }; } +const testing = @import("../../testing.zig"); +test "Context: terminated async module completion does not re-enter V8" { + const frame = try testing.createFrame(); + defer testing.test_session.closeAllPages(); + + var ls: js.Local.Scope = undefined; + frame.js.localScope(&ls); + defer ls.deinit(); + const local = &ls.local; + + const resolver = local.createPromiseResolver(); + const promise = resolver.promise(); + const resolver_global = try resolver.persist(); + defer resolver_global.deinit(); + + var state = DynamicModuleResolveState{ + .module = null, + .context_id = frame.js.id, + .context = frame.js, + .specifier = "https://example.com/late-module.js", + .resolver = resolver_global, + }; + + const env = frame.js.env; + env.terminate(); + js.v8.v8__Isolate__CancelTerminateExecution(env.isolate.handle); + defer env.cancelTerminate(); + + dynamicModuleSourceCallback(&state, error.Abort); + + try testing.expectEqual(js.Promise.State.pending, promise.state()); +} + // Used to make temporarily enter and exit a context, updating and restoring // frame.js: // var hs: js.HandleScope = undefined;