Merge pull request #1402 from lightpanda-io/defensive_local_scopes
Some checks failed
e2e-test / zig build release (push) Waiting to run
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled

Explicitly creates LocalScope in hard-to-reason callsites
This commit is contained in:
Karl Seguin
2026-01-23 18:59:45 +08:00
committed by GitHub
3 changed files with 73 additions and 11 deletions

View File

@@ -17,9 +17,13 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const log = @import("../../../log.zig");
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
const IS_DEBUG = @import("builtin").mode == .Debug;
const EventTarget = @import("../EventTarget.zig");
const NavigationCurrentEntryChangeEvent = @import("../event/NavigationCurrentEntryChangeEvent.zig");
@@ -43,10 +47,23 @@ pub fn dispatch(self: *NavigationEventTarget, event_type: DispatchType, page: *P
};
};
if (comptime IS_DEBUG) {
if (page.js.local == null) {
log.fatal(.bug, "null context scope", .{.src = "NavigationEventTarget.dispatch", .url = page.url});
std.debug.assert(page.js.local != null);
}
}
const func = @field(self, field) orelse return;
var ls: js.Local.Scope = undefined;
page.js.localScope(&ls);
defer ls.deinit();
return page._event_manager.dispatchWithFunction(
self.asEventTarget(),
event,
page.js.toLocal(@field(self, field)),
ls.toLocal(func),
.{ .context = "Navigation" },
);
}

View File

@@ -17,12 +17,16 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../../js/js.zig");
const log = @import("../../../log.zig");
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
const ReadableStreamDefaultReader = @import("ReadableStreamDefaultReader.zig");
const ReadableStreamDefaultController = @import("ReadableStreamDefaultController.zig");
const IS_DEBUG = @import("builtin").mode == .Debug;
pub fn registerTypes() []const type {
return &.{
ReadableStream,
@@ -137,12 +141,25 @@ pub fn callPullIfNeeded(self: *ReadableStream) !void {
self._pulling = true;
const pull_fn = self._page.js.toLocal(self._pull_fn) orelse return;
if (comptime IS_DEBUG) {
if (self._page.js.local == null) {
log.fatal(.bug, "null context scope", .{.src = "ReadableStream.callPullIfNeeded", .url = self._page.url});
std.debug.assert(self._page.js.local != null);
}
}
// Call the pull function
// Note: In a complete implementation, we'd handle the promise returned by pull
// and set _pulling = false when it resolves
try pull_fn.call(void, .{self._controller});
{
const func = self._pull_fn orelse return;
var ls: js.Local.Scope = undefined;
self._page.js.localScope(&ls);
defer ls.deinit();
// Call the pull function
// Note: In a complete implementation, we'd handle the promise returned by pull
// and set _pulling = false when it resolves
try ls.toLocal(func).call(void, .{self._controller});
}
self._pulling = false;

View File

@@ -17,12 +17,16 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../../js/js.zig");
const log = @import("../../../log.zig");
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
const ReadableStream = @import("ReadableStream.zig");
const ReadableStreamDefaultReader = @import("ReadableStreamDefaultReader.zig");
const IS_DEBUG = @import("builtin").mode == .Debug;
const ReadableStreamDefaultController = @This();
pub const Chunk = union(enum) {
@@ -79,7 +83,19 @@ pub fn enqueue(self: *ReadableStreamDefaultController, chunk: Chunk) !void {
.done = false,
.value = .fromChunk(chunk),
};
self._page.js.toLocal(resolver).resolve("stream enqueue", result);
if (comptime IS_DEBUG) {
if (self._page.js.local == null) {
log.fatal(.bug, "null context scope", .{.src = "ReadableStreamDefaultController.enqueue", .url = self._page.url});
std.debug.assert(self._page.js.local != null);
}
}
var ls: js.Local.Scope = undefined;
self._page.js.localScope(&ls);
defer ls.deinit();
ls.toLocal(resolver).resolve("stream enqueue", result);
}
pub fn close(self: *ReadableStreamDefaultController) !void {
@@ -94,9 +110,21 @@ pub fn close(self: *ReadableStreamDefaultController) !void {
.done = true,
.value = .empty,
};
for (self._pending_reads.items) |resolver| {
self._page.js.toLocal(resolver).resolve("stream close", result);
if (comptime IS_DEBUG) {
if (self._page.js.local == null) {
log.fatal(.bug, "null context scope", .{.src = "ReadableStreamDefaultController.close", .url = self._page.url});
std.debug.assert(self._page.js.local != null);
}
}
for (self._pending_reads.items) |resolver| {
var ls: js.Local.Scope = undefined;
self._page.js.localScope(&ls);
defer ls.deinit();
ls.toLocal(resolver).resolve("stream close", result);
}
self._pending_reads.clearRetainingCapacity();
}