From 7c9941c62922b5af84846b5c77f57d166f583c2c Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 14 Jan 2026 20:22:48 +0800 Subject: [PATCH] Make Promise, PromiseResolver and Module have explicit globals. See bb06900b6f84abaccc7ecfd386af1a9dc0029c50 for an explanation. --- src/browser/js/Context.zig | 86 ++++++++++++------- src/browser/js/Module.zig | 29 +++++-- src/browser/js/Promise.zig | 33 +++++-- src/browser/js/PromiseResolver.zig | 29 +++++-- src/browser/webapi/CustomElementRegistry.zig | 8 +- src/browser/webapi/animation/Animation.zig | 10 +-- src/browser/webapi/navigation/Navigation.zig | 20 ++--- src/browser/webapi/navigation/root.zig | 2 +- src/browser/webapi/net/Fetch.zig | 8 +- src/browser/webapi/streams/ReadableStream.zig | 12 +-- .../ReadableStreamDefaultController.zig | 14 +-- 11 files changed, 169 insertions(+), 82 deletions(-) diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index f19fccda..2e9f6bed 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -79,10 +79,10 @@ identity_map: std.AutoHashMapUnmanaged(usize, js.Global(js.Object)) = .empty, // we now simply persist every time persist() is called. global_values: std.ArrayList(v8.Global) = .empty, global_objects: std.ArrayList(v8.Global) = .empty, -global_modules: std.ArrayList(js.Global(js.Module)) = .empty, -global_promises: std.ArrayList(js.Global(js.Promise)) = .empty, +global_modules: std.ArrayList(v8.Global) = .empty, +global_promises: std.ArrayList(v8.Global) = .empty, global_functions: std.ArrayList(v8.Global) = .empty, -global_promise_resolvers: std.ArrayList(js.Global(js.PromiseResolver)) = .empty, +global_promise_resolvers: std.ArrayList(v8.Global) = .empty, // Our module cache: normalized module specifier => module. module_cache: std.StringHashMapUnmanaged(ModuleEntry) = .empty, @@ -100,19 +100,19 @@ script_manager: ?*ScriptManager, const ModuleEntry = struct { // Can be null if we're asynchrously loading the module, in // which case resolver_promise cannot be null. - module: ?js.Module = null, + module: ?js.Module.Global = null, // The promise of the evaluating module. The resolved value is // meaningless to us, but the resolver promise needs to chain // to this, since we need to know when it's complete. - module_promise: ?js.Promise = null, + module_promise: ?js.Promise.Global = null, // The promise for the resolver which is loading the module. // (AKA, the first time we try to load it). This resolver will // chain to the module_promise and, when it's done evaluating // will resolve its namespace. Any other attempt to load the // module willchain to this. - resolver_promise: ?js.Promise = null, + resolver_promise: ?js.Promise.Global = null, }; pub fn fromC(c_context: *const v8.Context) *Context { @@ -150,7 +150,7 @@ pub fn deinit(self: *Context) void { } for (self.global_modules.items) |*global| { - global.deinit(); + v8.v8__Global__Reset(global); } for (self.global_functions.items) |*global| { @@ -158,11 +158,11 @@ pub fn deinit(self: *Context) void { } for (self.global_promises.items) |*global| { - global.deinit(); + v8.v8__Global__Reset(global); } for (self.global_promise_resolvers.items) |*global| { - global.deinit(); + v8.v8__Global__Reset(global); } if (self.handle_scope) |*scope| { @@ -472,6 +472,21 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp return .{ .ctx = self, .handle = @ptrCast(value.local().handle) }; } + if (T == js.Promise.Global) { + // Auto-convert Global to local for bridge + return .{ .ctx = self, .handle = @ptrCast(value.local().handle) }; + } + + if (T == js.PromiseResolver.Global) { + // Auto-convert Global to local for bridge + return .{ .ctx = self, .handle = @ptrCast(value.local().handle) }; + } + + if (T == js.Module.Global) { + // Auto-convert Global to local for bridge + return .{ .ctx = self, .handle = @ptrCast(value.local().handle) }; + } + if (T == js.Value) { return value; } @@ -841,6 +856,16 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: js.Value) !?T { js.Value.Global => { return try js_value.persist(); }, + js.Promise.Global => { + if (!js_value.isPromise()) { + return null; + } + const promise = js.Promise{ + .ctx = self, + .handle = @ptrCast(js_value.handle), + }; + return try promise.persist(); + }, else => { if (!js_value.isObject()) { return null; @@ -1286,7 +1311,7 @@ fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]co const entry = self.module_cache.getPtr(normalized_specifier).?; if (entry.module) |m| { - return m.handle; + return m.local().handle; } var source = try self.script_manager.?.waitForImport(normalized_specifier); @@ -1299,7 +1324,7 @@ fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]co const mod = try self.compileModule(source.src(), normalized_specifier); try self.postCompileModule(mod, normalized_specifier); entry.module = try mod.persist(); - return entry.module.?.handle; + return entry.module.?.local().handle; } // Will get passed to ScriptManager and then passed back to us when @@ -1307,11 +1332,11 @@ fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]co const DynamicModuleResolveState = struct { // The module that we're resolving (we'll actually resolve its // namespace) - module: ?js.Module, + module: ?js.Module.Global, context_id: usize, context: *Context, specifier: [:0]const u8, - resolver: js.PromiseResolver, + resolver: js.PromiseResolver.Global, }; fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !js.Promise { @@ -1320,7 +1345,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c // This is easy, there's already something responsible // for loading the module. Maybe it's still loading, maybe // it's complete. Whatever, we can just return that promise. - return gop.value_ptr.resolver_promise.?; + return gop.value_ptr.resolver_promise.?.local(); } const resolver = try self.createPromiseResolver().persist(); @@ -1334,7 +1359,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c .resolver = resolver, }; - const promise = try resolver.promise().persist(); + const promise = try resolver.local().promise().persist(); if (!gop.found_existing) { // this module hasn't been seen before. This is the most @@ -1352,13 +1377,13 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c // Next, we need to actually load it. self.script_manager.?.getAsyncImport(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| { const error_msg = self.newString(@errorName(err)); - _ = resolver.reject("dynamic module get async", error_msg); + _ = resolver.local().reject("dynamic module get async", error_msg); }; // For now, we're done. but this will be continued in // `dynamicModuleSourceCallback`, once the source for the // moduel is loaded. - return promise; + return promise.local(); } // So we have a module, but no async resolver. This can only @@ -1374,20 +1399,21 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c // If the module hasn't been evaluated yet (it was only instantiated // as a static import dependency), we need to evaluate it now. if (gop.value_ptr.module_promise == null) { - const mod = gop.value_ptr.module.?; + const mod_global = gop.value_ptr.module.?; + const mod = mod_global.local(); const status = mod.getStatus(); if (status == .kEvaluated or status == .kEvaluating) { // Module was already evaluated (shouldn't normally happen, but handle it). // Create a pre-resolved promise with the module namespace. const module_resolver = try self.createPromiseResolver().persist(); - _ = module_resolver.resolve("resolve module", mod.getModuleNamespace()); - gop.value_ptr.module_promise = try module_resolver.promise().persist(); + _ = module_resolver.local().resolve("resolve module", mod.getModuleNamespace()); + gop.value_ptr.module_promise = try module_resolver.local().promise().persist(); } else { // the module was loaded, but not evaluated, we _have_ to evaluate it now const evaluated = mod.evaluate() catch { std.debug.assert(status == .kErrored); - _ = resolver.reject("module evaluation", self.newString("Module evaluation failed")); - return promise; + _ = resolver.local().reject("module evaluation", self.newString("Module evaluation failed")); + return promise.local(); }; std.debug.assert(evaluated.isPromise()); gop.value_ptr.module_promise = try evaluated.toPromise().persist(); @@ -1402,7 +1428,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c // But we can skip direclty to `resolveDynamicModule` which is // what the above callback will eventually do. self.resolveDynamicModule(state, gop.value_ptr.*); - return promise; + return promise.local(); } fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptManager.ModuleSource) void { @@ -1410,7 +1436,7 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM var self = state.context; var ms = module_source_ catch |err| { - _ = state.resolver.reject("dynamic module source", self.newString(@errorName(err))); + _ = state.resolver.local().reject("dynamic module source", self.newString(@errorName(err))); return; }; @@ -1427,7 +1453,7 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM .caught = caught, .specifier = state.specifier, }); - _ = state.resolver.reject("dynamic compilation failure", self.newString(caught.exception orelse "")); + _ = state.resolver.local().reject("dynamic compilation failure", self.newString(caught.exception orelse "")); return; }; }; @@ -1471,8 +1497,8 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul } defer caller.context.runMicrotasks(); - const namespace = s.module.?.getModuleNamespace(); - _ = s.resolver.resolve("resolve namespace", namespace); + const namespace = s.module.?.local().getModuleNamespace(); + _ = s.resolver.local().resolve("resolve namespace", namespace); } }.callback, @ptrCast(state)); @@ -1491,19 +1517,19 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul } defer ctx.runMicrotasks(); - _ = s.resolver.reject("catch callback", js.Value{ + _ = s.resolver.local().reject("catch callback", js.Value{ .ctx = ctx, .handle = v8.v8__FunctionCallbackInfo__Data(callback_handle).?, }); } }.callback, @ptrCast(state)); - _ = module_entry.module_promise.?.thenAndCatch(then_callback, catch_callback) catch |err| { + _ = module_entry.module_promise.?.local().thenAndCatch(then_callback, catch_callback) catch |err| { log.err(.js, "module evaluation is promise", .{ .err = err, .specifier = state.specifier, }); - _ = state.resolver.reject("module promise", self.newString("Failed to evaluate promise")); + _ = state.resolver.local().reject("module promise", self.newString("Failed to evaluate promise")); }; } diff --git a/src/browser/js/Module.zig b/src/browser/js/Module.zig index ef8dec57..44d14b43 100644 --- a/src/browser/js/Module.zig +++ b/src/browser/js/Module.zig @@ -89,18 +89,37 @@ pub fn getScriptId(self: Module) u32 { return @intCast(v8.v8__Module__ScriptId(self.handle)); } -pub fn persist(self: Module) !Module { +pub fn persist(self: Module) !Global { var ctx = self.ctx; - - const global = js.Global(Module).init(ctx.isolate.handle, self.handle); + var global: v8.Global = undefined; + v8.v8__Global__New(ctx.isolate.handle, self.handle, &global); try ctx.global_modules.append(ctx.arena, global); - return .{ + .handle = global, .ctx = ctx, - .handle = global.local(), }; } +pub const Global = struct { + handle: v8.Global, + ctx: *js.Context, + + pub fn deinit(self: *Global) void { + v8.v8__Global__Reset(&self.handle); + } + + pub fn local(self: *const Global) Module { + return .{ + .ctx = self.ctx, + .handle = @ptrCast(v8.v8__Global__Get(&self.handle, self.ctx.isolate.handle)), + }; + } + + pub fn isEqual(self: *const Global, other: Module) bool { + return v8.v8__Global__IsEqual(&self.handle, other.handle); + } +}; + const Requests = struct { ctx: *const v8.Context, handle: *const v8.FixedArray, diff --git a/src/browser/js/Promise.zig b/src/browser/js/Promise.zig index f520a9d6..437b327a 100644 --- a/src/browser/js/Promise.zig +++ b/src/browser/js/Promise.zig @@ -47,14 +47,37 @@ pub fn thenAndCatch(self: Promise, on_fulfilled: js.Function, on_rejected: js.Fu } return error.PromiseChainFailed; } -pub fn persist(self: Promise) !Promise { +pub fn persist(self: Promise) !Global { var ctx = self.ctx; - - const global = js.Global(Promise).init(ctx.isolate.handle, self.handle); + var global: v8.Global = undefined; + v8.v8__Global__New(ctx.isolate.handle, self.handle, &global); try ctx.global_promises.append(ctx.arena, global); - return .{ + .handle = global, .ctx = ctx, - .handle = global.local(), }; } + +pub const Global = struct { + handle: v8.Global, + ctx: *js.Context, + + pub fn deinit(self: *Global) void { + v8.v8__Global__Reset(&self.handle); + } + + pub fn local(self: *const Global) Promise { + return .{ + .ctx = self.ctx, + .handle = @ptrCast(v8.v8__Global__Get(&self.handle, self.ctx.isolate.handle)), + }; + } + + pub fn isEqual(self: *const Global, other: Promise) bool { + return v8.v8__Global__IsEqual(&self.handle, other.handle); + } + + pub fn promise(self: *const Global) Promise { + return self.local(); + } +}; diff --git a/src/browser/js/PromiseResolver.zig b/src/browser/js/PromiseResolver.zig index a12d3a26..86f11cac 100644 --- a/src/browser/js/PromiseResolver.zig +++ b/src/browser/js/PromiseResolver.zig @@ -75,14 +75,33 @@ fn _reject(self: PromiseResolver, value: anytype) !void { ctx.runMicrotasks(); } -pub fn persist(self: PromiseResolver) !PromiseResolver { +pub fn persist(self: PromiseResolver) !Global { var ctx = self.ctx; - - const global = js.Global(PromiseResolver).init(ctx.isolate.handle, self.handle); + var global: v8.Global = undefined; + v8.v8__Global__New(ctx.isolate.handle, self.handle, &global); try ctx.global_promise_resolvers.append(ctx.arena, global); - return .{ + .handle = global, .ctx = ctx, - .handle = global.local(), }; } + +pub const Global = struct { + handle: v8.Global, + ctx: *js.Context, + + pub fn deinit(self: *Global) void { + v8.v8__Global__Reset(&self.handle); + } + + pub fn local(self: *const Global) PromiseResolver { + return .{ + .ctx = self.ctx, + .handle = @ptrCast(v8.v8__Global__Get(&self.handle, self.ctx.isolate.handle)), + }; + } + + pub fn isEqual(self: *const Global, other: PromiseResolver) bool { + return v8.v8__Global__IsEqual(&self.handle, other.handle); + } +}; diff --git a/src/browser/webapi/CustomElementRegistry.zig b/src/browser/webapi/CustomElementRegistry.zig index 591dca01..956dd8bf 100644 --- a/src/browser/webapi/CustomElementRegistry.zig +++ b/src/browser/webapi/CustomElementRegistry.zig @@ -30,7 +30,7 @@ const CustomElementDefinition = @import("CustomElementDefinition.zig"); const CustomElementRegistry = @This(); _definitions: std.StringHashMapUnmanaged(*CustomElementDefinition) = .{}, -_when_defined: std.StringHashMapUnmanaged(js.PromiseResolver) = .{}, +_when_defined: std.StringHashMapUnmanaged(js.PromiseResolver.Global) = .{}, const DefineOptions = struct { extends: ?[]const u8 = null, @@ -106,7 +106,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu } if (self._when_defined.fetchRemove(name)) |entry| { - entry.value.resolve("whenDefined", constructor); + entry.value.local().resolve("whenDefined", constructor); } } @@ -126,7 +126,7 @@ pub fn whenDefined(self: *CustomElementRegistry, name: []const u8, page: *Page) const gop = try self._when_defined.getOrPut(page.arena, name); if (gop.found_existing) { - return gop.value_ptr.promise(); + return gop.value_ptr.local().promise(); } errdefer _ = self._when_defined.remove(name); const owned_name = try page.dupeString(name); @@ -135,7 +135,7 @@ pub fn whenDefined(self: *CustomElementRegistry, name: []const u8, page: *Page) gop.key_ptr.* = owned_name; gop.value_ptr.* = resolver; - return resolver.promise(); + return resolver.local().promise(); } fn upgradeNode(self: *CustomElementRegistry, node: *Node, page: *Page) !void { diff --git a/src/browser/webapi/animation/Animation.zig b/src/browser/webapi/animation/Animation.zig index b6167f7c..8fc6c99a 100644 --- a/src/browser/webapi/animation/Animation.zig +++ b/src/browser/webapi/animation/Animation.zig @@ -23,8 +23,8 @@ const Animation = @This(); _effect: ?js.Object.Global = null, _timeline: ?js.Object.Global = null, -_ready_resolver: ?js.PromiseResolver = null, -_finished_resolver: ?js.PromiseResolver = null, +_ready_resolver: ?js.PromiseResolver.Global = null, +_finished_resolver: ?js.PromiseResolver.Global = null, pub fn init(page: *Page) !*Animation { return page._factory.create(Animation{}); @@ -47,10 +47,10 @@ pub fn getPending(_: *const Animation) bool { pub fn getFinished(self: *Animation, page: *Page) !js.Promise { if (self._finished_resolver == null) { const resolver = try page.js.createPromiseResolver().persist(); - resolver.resolve("Animation.getFinished", self); + resolver.local().resolve("Animation.getFinished", self); self._finished_resolver = resolver; } - return self._finished_resolver.?.promise(); + return self._finished_resolver.?.local().promise(); } pub fn getReady(self: *Animation, page: *Page) !js.Promise { @@ -59,7 +59,7 @@ pub fn getReady(self: *Animation, page: *Page) !js.Promise { const resolver = try page.js.createPromiseResolver().persist(); self._ready_resolver = resolver; } - return self._ready_resolver.?.promise(); + return self._ready_resolver.?.local().promise(); } pub fn getEffect(self: *const Animation) ?js.Object.Global { diff --git a/src/browser/webapi/navigation/Navigation.zig b/src/browser/webapi/navigation/Navigation.zig index c257d1d2..d2fabff1 100644 --- a/src/browser/webapi/navigation/Navigation.zig +++ b/src/browser/webapi/navigation/Navigation.zig @@ -92,8 +92,8 @@ pub fn getTransition(_: *const Navigation) ?NavigationTransition { } const NavigationReturn = struct { - committed: js.Promise, - finished: js.Promise, + committed: js.Promise.Global, + finished: js.Promise.Global, }; pub fn back(self: *Navigation, page: *Page) !NavigationReturn { @@ -278,9 +278,9 @@ pub fn navigateInner( if (is_same_document) { page.url = new_url; - committed.resolve("navigation push", {}); + committed.local().resolve("navigation push", {}); // todo: Fire navigate event - finished.resolve("navigation push", {}); + finished.local().resolve("navigation push", {}); _ = try self.pushEntry(url, .{ .source = .navigation, .value = state }, page, true); } else { @@ -291,9 +291,9 @@ pub fn navigateInner( if (is_same_document) { page.url = new_url; - committed.resolve("navigation replace", {}); + committed.local().resolve("navigation replace", {}); // todo: Fire navigate event - finished.resolve("navigation replace", {}); + finished.local().resolve("navigation replace", {}); _ = try self.replaceEntry(url, .{ .source = .navigation, .value = state }, page, true); } else { @@ -306,9 +306,9 @@ pub fn navigateInner( if (is_same_document) { page.url = new_url; - committed.resolve("navigation traverse", {}); + committed.local().resolve("navigation traverse", {}); // todo: Fire navigate event - finished.resolve("navigation traverse", {}); + finished.local().resolve("navigation traverse", {}); } else { try page.scheduleNavigation(url, .{ .reason = .navigation, .kind = kind }, .script); } @@ -327,8 +327,8 @@ pub fn navigateInner( try self._proto.dispatch(.{ .currententrychange = event }, page); return .{ - .committed = committed.promise(), - .finished = finished.promise(), + .committed = try committed.local().promise().persist(), + .finished = try finished.local().promise().persist(), }; } diff --git a/src/browser/webapi/navigation/root.zig b/src/browser/webapi/navigation/root.zig index e007c714..26c963ac 100644 --- a/src/browser/webapi/navigation/root.zig +++ b/src/browser/webapi/navigation/root.zig @@ -47,7 +47,7 @@ pub const NavigationState = struct { // https://developer.mozilla.org/en-US/docs/Web/API/NavigationTransition pub const NavigationTransition = struct { - finished: js.Promise, + finished: js.Promise.Global, from: NavigationHistoryEntry, navigation_type: NavigationType, }; diff --git a/src/browser/webapi/net/Fetch.zig b/src/browser/webapi/net/Fetch.zig index 7a2d7f33..688f3a26 100644 --- a/src/browser/webapi/net/Fetch.zig +++ b/src/browser/webapi/net/Fetch.zig @@ -36,7 +36,7 @@ _page: *Page, _url: []const u8, _buf: std.ArrayList(u8), _response: *Response, -_resolver: js.PromiseResolver, +_resolver: js.PromiseResolver.Global, pub const Input = Request.Input; pub const InitOpts = Request.InitOpts; @@ -77,7 +77,7 @@ pub fn init(input: Input, options: ?InitOpts, page: *Page) !js.Promise { .done_callback = httpDoneCallback, .error_callback = httpErrorCallback, }); - return fetch._resolver.promise(); + return fetch._resolver.local().promise(); } pub fn deinit(self: *Fetch) void { @@ -149,13 +149,13 @@ fn httpDoneCallback(ctx: *anyopaque) !void { .len = self._buf.items.len, }); - return self._resolver.resolve("fetch done", self._response); + return self._resolver.local().resolve("fetch done", self._response); } fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void { const self: *Fetch = @ptrCast(@alignCast(ctx)); self._response._type = .@"error"; // Set type to error for network failures - self._resolver.reject("fetch error", @errorName(err)); + self._resolver.local().reject("fetch error", @errorName(err)); } const testing = @import("../../../testing.zig"); diff --git a/src/browser/webapi/streams/ReadableStream.zig b/src/browser/webapi/streams/ReadableStream.zig index 84025a6a..af50d8bc 100644 --- a/src/browser/webapi/streams/ReadableStream.zig +++ b/src/browser/webapi/streams/ReadableStream.zig @@ -170,7 +170,7 @@ pub fn cancel(self: *ReadableStream, reason: ?[]const u8, page: *Page) !js.Promi if (self._state != .readable) { if (self._cancel) |c| { if (c.resolver) |r| { - return r.promise(); + return r.local().promise(); } } return page.js.resolvePromise(.{}); @@ -201,19 +201,19 @@ pub fn cancel(self: *ReadableStream, reason: ?[]const u8, page: *Page) !js.Promi .done = true, .value = .empty, }; - for (self._controller._pending_reads.items) |resolver| { - resolver.resolve("stream cancelled", result); + for (self._controller._pending_reads.items) |*resolver| { + resolver.local().resolve("stream cancelled", result); } self._controller._pending_reads.clearRetainingCapacity(); - c.resolver.?.resolve("ReadableStream.cancel", {}); - return c.resolver.?.promise(); + c.resolver.?.local().resolve("ReadableStream.cancel", {}); + return c.resolver.?.local().promise(); } const Cancel = struct { callback: ?js.Function.Global = null, reason: ?[]const u8 = null, - resolver: ?js.PromiseResolver = null, + resolver: ?js.PromiseResolver.Global = null, }; pub const JsApi = struct { diff --git a/src/browser/webapi/streams/ReadableStreamDefaultController.zig b/src/browser/webapi/streams/ReadableStreamDefaultController.zig index 72956f06..da23f77e 100644 --- a/src/browser/webapi/streams/ReadableStreamDefaultController.zig +++ b/src/browser/webapi/streams/ReadableStreamDefaultController.zig @@ -42,7 +42,7 @@ _page: *Page, _stream: *ReadableStream, _arena: std.mem.Allocator, _queue: std.ArrayList(Chunk), -_pending_reads: std.ArrayList(js.PromiseResolver), +_pending_reads: std.ArrayList(js.PromiseResolver.Global), _high_water_mark: u32, pub fn init(stream: *ReadableStream, high_water_mark: u32, page: *Page) !*ReadableStreamDefaultController { @@ -59,7 +59,7 @@ pub fn init(stream: *ReadableStream, high_water_mark: u32, page: *Page) !*Readab pub fn addPendingRead(self: *ReadableStreamDefaultController, page: *Page) !js.Promise { const resolver = try page.js.createPromiseResolver().persist(); try self._pending_reads.append(self._arena, resolver); - return resolver.promise(); + return resolver.local().promise(); } pub fn enqueue(self: *ReadableStreamDefaultController, chunk: Chunk) !void { @@ -79,7 +79,7 @@ pub fn enqueue(self: *ReadableStreamDefaultController, chunk: Chunk) !void { .done = false, .value = .fromChunk(chunk), }; - resolver.resolve("stream enqueue", result); + resolver.local().resolve("stream enqueue", result); } pub fn close(self: *ReadableStreamDefaultController) !void { @@ -94,8 +94,8 @@ pub fn close(self: *ReadableStreamDefaultController) !void { .done = true, .value = .empty, }; - for (self._pending_reads.items) |resolver| { - resolver.resolve("stream close", result); + for (self._pending_reads.items) |*resolver| { + resolver.local().resolve("stream close", result); } self._pending_reads.clearRetainingCapacity(); } @@ -109,8 +109,8 @@ pub fn doError(self: *ReadableStreamDefaultController, err: []const u8) !void { self._stream._stored_error = try self._page.arena.dupe(u8, err); // Reject all pending reads - for (self._pending_reads.items) |resolver| { - resolver.reject("stream errror", err); + for (self._pending_reads.items) |*resolver| { + resolver.local().reject("stream errror", err); } self._pending_reads.clearRetainingCapacity(); }