Files
browser/src/browser/js/Promise.zig
Karl Seguin 61cc7bb949 refactor: Rework how v8::Globals are managed
We typically persist a v8::Value by calling persist() or temp() on our v8
wrappers. Both persist() and temp() create a v8::Global, but persist() tracks
it in the page's globals ArrayList while temp() tracks it in the page's temps
HashMap.

Globals are only freed when the page is torn down, hence a simple ArrayList is
all we need. Temps can be freed at any point, hence HashMap is used. (Temps are
also freed on teardown, but they can be freed before that too).

This commit removes Temp (js.Value.Temp, js.Promise.Temp, js.Function.Temp)
and all the mechinery around them (e.g. pseudo Global generic). There are now
only Globals and any global can be freed at any time (while still being tracked
in the page in order to be able to free any un-freed on page teardown).

Many files were touched because .Temp -> .Global and .temp() -> .persist().

This commit is based off the indexeddb branch because indexeddb introduced
"bare" globals (globals which aren't tracked by the Page). Building this new
model to include "Bare" globals avoids a messy merge later.
2026-07-04 22:14:17 +08:00

95 lines
2.7 KiB
Zig

// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("js.zig");
const v8 = js.v8;
const Promise = @This();
local: *const js.Local,
handle: *const v8.Promise,
pub fn toObject(self: Promise) js.Object {
return .{
.local = self.local,
.handle = @ptrCast(self.handle),
};
}
pub fn toValue(self: Promise) js.Value {
return .{
.local = self.local,
.handle = @ptrCast(self.handle),
};
}
pub fn thenAndCatch(self: Promise, on_fulfilled: js.Function, on_rejected: js.Function) !Promise {
if (v8.v8__Promise__Then2(self.handle, self.local.handle, on_fulfilled.handle, on_rejected.handle)) |handle| {
return .{
.local = self.local,
.handle = handle,
};
}
return error.PromiseChainFailed;
}
pub const State = enum(u32) {
pending = v8.kPending,
fulfilled = v8.kFulfilled,
rejected = v8.kRejected,
};
pub fn state(self: Promise) State {
return @enumFromInt(v8.v8__Promise__State(self.handle));
}
/// Settled value (fulfillment or rejection). Caller must check `state` first.
pub fn result(self: Promise) js.Value {
return .{
.local = self.local,
.handle = v8.v8__Promise__Result(self.handle).?,
};
}
/// Suppress the global unhandled-rejection callback when handling the
/// rejection inline.
pub fn markAsHandled(self: Promise) void {
v8.v8__Promise__MarkAsHandled(self.handle);
}
pub fn persist(self: Promise) !Global {
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
}
pub const Global = struct {
slot: *js.GlobalSlot,
pub fn deinit(self: Global) void {
self.slot.release();
}
pub const release = deinit;
pub fn local(self: Global, l: *const js.Local) Promise {
return .{
.local = l,
.handle = @ptrCast(v8.v8__Global__Get(&self.slot.handle, l.isolate.handle)),
};
}
};